I want to tell how we used lists of basic types for processing messages. Messages are structures inherited from small base structures. All useful information is stored in the basic structures. For processing, you need to know from which basic structures the message being processed was inherited. Everything you need to work with type lists we found in Boost.MPL. I chose
boost :: mpl :: vector as the list of types. To go through the list of types
boost :: mpl :: for_each .
The source data here is the same as in the
previous article .
Hidden textstruct Base1 {}; struct Base2 {}; struct Base3 {}; struct Derived12: public Base1, public Base2 {}; struct Derived23: public Base2, public Base3 {};
In reality, we have much more basic structures and messages created on their basis.
In the simplest version, for boost :: mpl :: for_each, you need to specify a list of types as a template parameter, and a class with a method operator () (T), where T is a type from the list as an argument. You can make the method template, but this is not exactly what we need. Therefore, we will overload operator () for all base structures. The list of types is now manually announced in each message. In the first approximation, we get:
struct Derived12: public Base1, public Base2 { boost::mpl::vector<Base1, Base2> types; }; struct Derived23: public Base2, public Base3 { boost::mpl::vector<Base2, Base3> types; }; class Describer { public: void operator()(Base1) { std::cout << " Base1\n"; } void operator()(Base2) { std::cout << " Base2\n"; } void operator()(Base3) { std::cout << " Base3\n"; } }; void main() { Derived12 d12; boost::for_each<Derived12::types>(Describer()); }
As a result of the performance will
displayed Getting information from Base1
Getting information from Base2
This problem has two problems:
- The value of d12 is not used at all;
- The function boost :: mpl :: for_each creates instances of the base structures.
With the first problem, everything is simple - let the value be transmitted and stored on the Describer constructor, and the class itself will be template. The second problem is more serious, since in addition to the cost of creating objects, there are additional restrictions on structures - they must have a constructor without parameters and cannot be abstract. I decided to overload operator () with a pointer. In this case, the type list must contain pointers to types or you can use the second option for_each, with the transfer of the template for transformation, and use this option. As a template for the transformation, take boost :: add_pointer. To check that all base structures are processed, add a template operator () containing BOOST_STATIC_ASSERT (false). This will give a compilation error if a new base structure appears. As a result, we get:
template<typename T> class Describer { public: Describer(const T& v): v(v) {} void operator()(Base1*) { std::cout << " Base1\n"; } void operator()(Base2*) { std::cout << " Base2\n"; } void operator()(Base3*) { std::cout << " Base3\n"; } template<typename U> void operator()(U*) { BOOST_STATIC_ASSERT(false); } private: const T& v; }; void main() { Derived12 d12; boost::for_each< Derived12::types, boost::add_pointer<boost::mpl::_> > ( Describer<Derived12>(d12) ); }
Now we will try to simplify the establishment of lists of types involved in inheritance. Let's declare the full list of types of base structures and use the algorithm
boost :: mpl :: copy_if . Which will copy in the new list all elements meeting the specified condition. As a condition, we take the inheritance check
boost :: is_base_of .
typedef boost::mpl::vector<Base1, Base2, Base3> FullTypesList; template<typename T, typename BaseList> struct MakeTypesList { typedef typename boost::mpl::copy_if< BaseList, boost::is_base_of< boost::mpl::_, T > >::type TypesList; };
For convenience, we add in Describer operator () without parameters, which will call for_each.
void Describer::operator()() { boost::mpl::for_each< typename MakeTypesList<T,FullTypesList>::TypesList, add_pointer<boost::mpl::_> >( boost::ref(*this) ); }
The wrapper boost :: ref is needed so that the describer copy operator is not called.
')
Final version struct Base1 {}; struct Base2 {}; struct Base3 {}; typedef boost::mpl::vector<Base1, Base2, Base3> FullTypesList; template<typename T, typename BaseList> struct MakeTypesList { typedef typename boost::mpl::copy_if< BaseList, boost::is_base_of< boost::mpl::_, T > >::type TypesList; }; template<typename T> class Describer { public: Describer(const T& v): v(v) {} void operator()() { boost::mpl::for_each< typename MakeTypesList<T,FullTypesList>::TypesList, add_pointer<boost::mpl::_> >( boost::ref(*this) ); } void operator()(Base1*) { std::cout << " Base1\n"; } void operator()(Base2*) { std::cout << " Base2\n"; } void operator()(Base3*) { std::cout << " Base3\n"; } template<typename U> void operator()(U*) { BOOST_STATIC_ASSERT(false); } private: const T& v; };
If there are a lot of classes processing structures in this way, it is more reasonable to declare lists of base classes for messages separately. It turned out that the message structure is not used independently - it is the base class for the template class that implements the common interface of all messages and in it we define the list of basic types. We call this list when calling for_each. You can make a wrapper pattern and use it. Simple option
wrapper pattern template<typename T> struct Wrapper: public T { typedef typename MakeTypesList<T, FullTypesList>::TypesList TypesList; } void Describer::operator() { boost::mpl::for_each< typename T::TypesList, add_pointer<boost::mpl::_> >( boost::ref(*this) ); }
Update:Note for BOOST_STATIC_ASSERT in the template Describer :: operator ()In this example, G ++ will generate a compilation error on BOOST_STATIC_ASSERT (false). G ++, unlike MS Visual C ++, checks the template body, even if it is not instantiated. All names that are independent of the template parameter must be known at the time the template is defined. If any construction causes a compilation error and does not depend on the template parameter, then the compilation error will be. You can do the following:
template <typename T> struct Describer { template<typename U> void operator() { BOOST_STATIC_ASSERT(sizeof(U) == 0); } }
Update2: Thanks to
nickolaym for
interesting comments with the option of automatically generating a list of base classes.