Search code examples
c++boost-fusion

Is it possible to generate a fusion map from an adapted struct?


Let A be:

struct A {
  int a;
  std::string b;
  struct keys {
    struct a;
    struct b;
  };
};

I would like to generate a fusion::map from the struct such that it contains the fusion::pairs: fusion::pair<A::keys::a, int> and fusion::pair<A::keys::b, std::string>. Something like

A a;
fusion::make_map<A>(a)

I've tried with BOOST_FUSION_ADAPT_ASSOC_STRUCT

BOOST_FUSION_ADAPT_ASSOC_STRUCT(
    A,
    (int,  a, A::keys::a)
    (std::string, b, A::keys::b)

)

This adapts A to be used as an associative sequence, but I haven't found a way to construct a map from it. In particular, if I iterate over it I get only the values. If I could iterate over the keys that would be really helpful, because then I could zip the values and the keys to build a map, but I haven't found a way to do this yet.


Solution

  • You should use Associative Iterator interface - it provides result_of::key_of<I>::type metafunciton.

    I have found code in my old records, and extracted relevant part. I used it during implementation of SoA vector (as I understand from chat - you are implementing it too), but eventually switched to just explicit definition of fusion::map. I think I did that in order to have unified interface of normal structure and structure of references (i.e. both are accessed via type tag).

    Live Demo on Coliru

    namespace demo
    {
        struct employee
        {
            std::string name;
            int age;
        };
    }
    
    namespace keys
    {
        struct name;
        struct age;
    }
    
    BOOST_FUSION_ADAPT_ASSOC_STRUCT
    (
        demo::employee,
        (std::string, name, keys::name)
        (int, age, keys::age)
    )
    
    template<typename> void type_is();
    
    int main()
    {
        type_is<as_fusion_map<demo::employee>::type>();
    }
    

    Result is:

    main.cpp:(.text.startup+0x5): undefined reference to `void type_is<
    
    boost::fusion::map
    <
        boost::fusion::pair<keys::name, std::string>,
        boost::fusion::pair<keys::age, int>,
        boost::fusion::void_,
        boost::fusion::void_,
        boost::fusion::void_,
        boost::fusion::void_,
        boost::fusion::void_,
        boost::fusion::void_,
        boost::fusion::void_,
        boost::fusion::void_
    >
    
    >()'
    

    Here is full implementation

    //             Copyright Evgeny Panasyuk 2012.
    // Distributed under the Boost Software License, Version 1.0.
    //    (See accompanying file LICENSE_1_0.txt or copy at
    //          http://www.boost.org/LICENSE_1_0.txt)
    
    
    // Reduced from larger case, some includes may not be needed
    
    #include <boost/fusion/algorithm/transformation/transform.hpp>
    #include <boost/fusion/sequence/intrinsic/value_at_key.hpp>
    #include <boost/fusion/include/adapt_assoc_struct.hpp>
    #include <boost/fusion/sequence/intrinsic/at_key.hpp>
    #include <boost/fusion/view/transform_view.hpp>
    #include <boost/fusion/view/zip_view.hpp>
    #include <boost/fusion/container/map.hpp>
    #include <boost/fusion/algorithm.hpp>
    
    #include <boost/mpl/transform.hpp>
    #include <boost/mpl/vector.hpp>
    
    #include <boost/static_assert.hpp>
    #include <boost/type_traits.hpp>
    
    #include <iostream>
    #include <iterator>
    #include <ostream>
    #include <string>
    
    #include <boost/mpl/push_front.hpp>
    #include <boost/fusion/sequence.hpp>
    #include <boost/fusion/iterator.hpp>
    #include <boost/fusion/iterator/next.hpp>
    #include <boost/fusion/iterator/equal_to.hpp>
    #include <boost/fusion/iterator/key_of.hpp>
    #include <boost/fusion/iterator/value_of.hpp>
    
    using namespace boost;
    using namespace std;
    
    using fusion::at_key;
    using fusion::at_c;
    
    // ____________________________________________________________________________________ //
    
    namespace res_of=boost::fusion::result_of;
    using namespace boost::fusion;
    
    template<typename Vector,typename First,typename Last,typename do_continue>
    struct to_fusion_map_iter;
    
    template<typename Vector,typename First,typename Last>
    struct to_fusion_map_iter<Vector,First,Last,mpl::false_>
    {
        typedef typename res_of::next<First>::type Next;
        typedef typename mpl::push_front
        <
            typename to_fusion_map_iter
            <
                Vector,
                Next,
                Last,
                typename res_of::equal_to<Next,Last>::type
            >::type,
            fusion::pair
            <
                typename res_of::key_of<First>::type,
                typename res_of::value_of_data<First>::type
            >
        >::type type;
    };
    template<typename Vector,typename First,typename Last>
    struct to_fusion_map_iter<Vector,First,Last,mpl::true_>
    {
        typedef Vector type;
    };
    
    template<typename FusionAssociativeSequence>
    struct as_fusion_map
    {
        typedef typename res_of::begin<FusionAssociativeSequence>::type First;
        typedef typename res_of::end<FusionAssociativeSequence>::type Last;
        typedef typename res_of::as_map
        <
            typename to_fusion_map_iter
            <
                mpl::vector<>,
                First,
                Last,
                typename res_of::equal_to<First,Last>::type
            >::type
        >::type type;
    };
    
    // ____________________________________________________________________________________ //
    
    // Defenition of structure:
    
    namespace demo
    {
        struct employee
        {
            std::string name;
            int age;
        };
    }
    
    namespace keys
    {
        struct name;
        struct age;
    }
    
    BOOST_FUSION_ADAPT_ASSOC_STRUCT
    (
        demo::employee,
        (std::string, name, keys::name)
        (int, age, keys::age)
    )
    
    // ____________________________________________________________________________________ //
    template<typename> void type_is();
    
    int main()
    {
        type_is<as_fusion_map<demo::employee>::type>();
    }