Processing the structure according to the list of basic types

    I want to tell how we used lists of basic types for message processing. Messages are structures inherited from small base structures. All useful information is stored in basic structures. For processing, you need to know from which basic structures the message being processed was inherited. All that is needed to work with type lists we found in Boost.MPL. As a list of types, we chose boost :: mpl :: vector . To go through the list of types boost :: mpl :: for_each .

    The initial data here are the same as in the previous article .
    Hidden text
    struct 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 case, boost :: mpl :: for_each needs to specify a list of types as a template parameter, and a class with the operator () (T) method as an argument, where T is a type from the list. You can make the method boilerplate, but this is not quite what we need. Therefore, we overload operator () for all base structures. The list of types so far will be manually declared in each message. In a first approximation, we get:
    struct Derived12: public Base1, public Base2
    {
        boost::mpl::vector types;
    };
    struct Derived23: public Base2, public Base3
    {
        boost::mpl::vector 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(Describer());
    }
    

    As a result of execution will be
    bred
    Retrieving Information from Base1
    Retrieving Information from Base2
    

    There are two problems with this one:
    1. The value of d12 is not used in any way;
    2. The boost :: mpl :: for_each function instantiates base structures.

    The first problem is simple - let the value be passed and stored on the Describer constructor, and the class itself will be template. The second problem is more serious, since in addition to the costs of creating objects, restrictions are additionally imposed on the structures - they must have a constructor without parameters and cannot be abstract. I decided to overload operator () with a pointer. In this case, the list of types should contain pointers to types, or you can use the second for_each option, with the template for the transformation, we will use this option. As a template for the transformation, take boost :: add_pointer. To verify that all basic structures are being 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
    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
        void operator()(U*)
        {
            BOOST_STATIC_ASSERT(false);
        }
    private:
        const T& v;
    };
    void main()
    {
        Derived12 d12;
        boost::for_each< Derived12::types, 
                         boost::add_pointer >
            ( Describer(d12) );
    }
    


    Now let's try to simplify the establishment of lists of types involved in inheritance. We declare a complete list of types of basic structures and use the boost :: mpl :: copy_if algorithm . Which will copy to the new list all the elements that satisfy the specified condition. As a condition, we take the inheritance check boost :: is_base_of .
    typedef boost::mpl::vector FullTypesList;
    template
    struct MakeTypesList
    {
        typedef typename boost::mpl::copy_if<
            BaseList,
            boost::is_base_of< boost::mpl::_, T > >::type TypesList;
    };
    


    For convenience, add operator () to Describer without parameters, which will call for_each.
    void Describer::operator()()
    {
        boost::mpl::for_each<
            typename MakeTypesList::TypesList,
            add_pointer >( boost::ref(*this) );
    }
    

    The boost :: ref wrapper is needed so that the copy statement for Describer is not called.

    Final version
    struct Base1 {};
    struct Base2 {};
    struct Base3 {};
    typedef boost::mpl::vector FullTypesList;
    template
    struct MakeTypesList
    {
        typedef typename boost::mpl::copy_if<
            BaseList,
            boost::is_base_of< boost::mpl::_, T > >::type TypesList;
    };
    template
    class Describer
    {
    public:
        Describer(const T& v):
            v(v)
        {}
        void operator()()
        {
            boost::mpl::for_each<
                typename MakeTypesList::TypesList,
                add_pointer >( 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
        void operator()(U*)
        {
            BOOST_STATIC_ASSERT(false);
        }
    private:
        const T& v;
    };
    //Списки типов в Derived12 и Derived23 больше не нужны.
    struct Derived12: public Base1, public Base2 {};
    struct Derived23: public Base2, public Base3 {};
    void main()
    {
        Derived12 mes12;
        ( Describer(mes12) )();
    }
    



    If there are many classes of processing structures in this way, then 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 a list of base types. This list is accessed when calling for_each. You can make a wrapper template and use it. Simple option
    wrapper template
    template
    struct Wrapper: public T
    {
        typedef typename MakeTypesList::TypesList TypesList;
    }
    void Describer::operator()
    {
        boost::mpl::for_each<
            typename T::TypesList,
            add_pointer >( boost::ref(*this) );
    }
    


    Update:
    Note on BOOST_STATIC_ASSERT in template Describer :: operator ()
    For this example, G ++ will throw a compilation error on BOOST_STATIC_ASSERT (false). G ++, unlike MS Visual C ++, checks the body of the template, even if it is not instantiated. All template-independent names must be known at the time the template is defined. If some construction causes a compilation error and does not depend on the template parameter, then there will be a compilation error. You can do the following:
    template 
    struct Describer
    {
        template
        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.


    Also popular now: