New content are available at http://tis-method.org/. All articles from this site will be moved to the new location.

суббота, 23 октября 2010 г.

Chain of responsibility

"Chain of responsibility" - the well-known pattern, exhaustively described in GoF. Of course, in most cases, boost::signal and boost::signal2 possible to realize almost any design. We can assume that the pattern of "chain of responsibility" is "observer" pattern with N = 1.
Nevertheless, there are rare situations where pattern "chain of responsibility" is a reasonable choice. This may be cases in which the channel signal is always single, when the signal transmission should go with a minimum of overhead, when the channel is already running in a secure multi-threaded environment or connects composite parts of one whole component.

Front-end
For example, consider the schematic design of a system built on layers of processing. Target class should allow the dynamic creation of a one-way communication between an arbitrary number of objects and provide an interface for signal propagation.
/// chain function
typedef boost::function< voidint const ) >
  chain_t;
///
class layer : protected z3d::chain_of_resp< LOKI_TYPELIST_1( chain_t ) >,
  public boost::enable_shared_from_this< layer >,
  public boost::noncopyable
{
  typedef z3d::chain_of_resp< LOKI_TYPELIST_1( chain_t ) >
    base_t;
  ///
  std::string
    name;

protected:
  /// init cons
  layer( std::string const& n ) : name( n )
  {}

public:
  ///
  typedef boost::shared_ptr< layer >
    layer_ptr_t;
  /// working function
  void chain( int const arg )
  {
    std::cout << name << "::chain(" << arg << ")" << std::endl;
    if ( !( base_t::empty< chain_t >() ) )
      base_t::call< chain_t, void >( arg + 1 );
  }
  /// making chain between layers
  layer_ptr_t attach( layer_ptr_t l )
  {
    base_t::bind( boost::bind( &layer::chain, l, _1 ) );
    return ( shared_from_this() );
  }
  ///
  static layer_ptr_t create( std::string const& name )
  { return layer_ptr_t( new layer( name ) ); }
};

void main()
{
  layer::layer_ptr_t lA2 = layer::create( "A2" );
  layer::layer_ptr_t lA1 = layer::create( "A1" )->attach( lA2->attach( layer::create( "A3" ) ) );
  std::cout << std::endl << "A1 -> A2 -> A3 chain" << std::endl;   lA1->chain( 0 );
  ///
  layer::layer_ptr_t lB1 = layer::create( "B1" );
  lB1->attach( lA2 );
  std::cout << std::endl << "B1 -> A2 -> A3 chain" << std::endl;   lB1->chain( 0 );
}
layer class contains a single channel - layer::chain. The method layer::attach allows you to associate two objects with communication channel. The example creates two channels, the output shows the processing and transmission of signals over a channel between the layers.

Output:
A1 -> A2 -> A3 chain
A1::chain(0)
A2::chain(1)
A3::chain(2)

B1 -> A2 -> A3 chain
B1::chain(0)
A2::chain(1)
A3::chain(2)

Sources
z3d::chain_of_resp.hpp

Use cases:
z3d::utests::chain_of_resp_ut01.hpp
z3d::utests::chain_of_resp_ut02.hpp
z3d::utests::chain_of_resp_ut03.hpp

суббота, 9 октября 2010 г.

Classes linear hierarchy

Getting to the problem, I'm always analyzing it by checklist.
One of point is to determine how to use compile-time and run-time opportunities. This topic is about one compile-time tool, which allows me to use a flexible layout of objects.

It is based on the possibility to manipulate C++ types at compile-time and use a partial specialization of templates for generating linear hierarchies of classes. This technique is used in the Loki and Boost libraries.

template < class, size_t >
struct slot;

template < size_t I >
struct slot< Loki::NullType, I >
{
 ///
 typedef Loki::NullType
  value_type;
 ///
 value_type
  value;
};

template < typename T, typename U, size_t I >
struct slot< Loki::Typelist< T, U >, I > : public slot< U, I + 1 >
{
 ///
 typedef T
  value_type;
 ///
 value_type
  value;
};

Template appointment - generate a classes linear hierarchy. Number of inherited classes equal to the number of types in the types list template parameter plus one.
Next design:
class С1 : public z3d::slot< LOKI_TYPELIST_4(int, char, float, long) >{};

will generate the following schematic nested structure:
class С1
{
  {
    {
      {
        {
          {
            Loki::NullType value;
          }
          long value;
        };
        float value;
      };
      char value;
    };
    int value;
  };
};

Access to elements of the structure can be as an index, so by specifying the type:
С1 с1;
c1.set_value( boost::mpl::int_< 0 >(), 1 );
c1.set_value( boost::mpl::int_< 1 >(), 'A' );
std::cout << c1.get_value( boost::mpl::identity< float >() );

Class z3d::slot also provides a mechanism to initialize the values in the constructor:
С1 с1( INITLIST_1( 1, Loki::NullType(), 1.f, Loki::NullType() );

Template class z3d::scope openly inherited from z3d::slot and adds the ability to initialize the value using a class method:
class S1 : public z3d::scope< LOKI_TYPELIST_4(int, char, float, long) >
{};

S1 s1;
s1.assign( INITLIST_1( 1, Loki::NullType(), 1.f, Loki::NullType() );


Results
Template classes z3d::slot and z3d::scope are the basic elements to build more complex structures: implementation of patterns "Chain of responsibility", "State", to reflect the dynamic relationships between objects.

Sources
z3d::slot
z3d::scope