Boost fusion sequence type and name identification for structs and class

前端 未结 2 815
無奈伤痛
無奈伤痛 2021-01-05 23:14

I am trying to use boost fusion for one of my projects and I an figuring out how to get type names and variable names for structures and classes.

#include &         


        
相关标签:
2条回答
  • 2021-01-05 23:49

    I distilled the answer by sehe into something much simpler, provided you are using C++14

    #include <iostream>
    
    #include <boost/fusion/include/algorithm.hpp>
    #include <boost/fusion/adapted/struct/adapt_struct.hpp>
    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/mpl/range_c.hpp>
    
    struct MyStruct {
        std::string foo;
        double bar;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(MyStruct,
                              foo,
                              bar)
    
    namespace fuz = boost::fusion;
    namespace mpl = boost::mpl;
    
    int main(int argc, char* argv[]) {
      MyStruct dummy{"yo",3.14};
    
      fuz::for_each(mpl::range_c<
                    unsigned, 0, fuz::result_of::size<MyStruct>::value>(),
                    [&](auto index){
          std::cout << "Name: "
                    << fuz::extension::struct_member_name<MyStruct,index>::call()
                    << " Value: "
                    << fuz::at_c<index>(dummy) << std::endl; 
        });
    
    }
    

    Outputs:

    Name: foo Value: yo
    Name: bar Value: 3.14
    

    See it live on coliru

    0 讨论(0)
  • 2021-01-05 23:50

    There's boost::fusion::extension::struct_member_name<S, N::value> to access the names.

    Here's a generic fusion object visitor that I use:

    namespace visitor {
    
        template <typename Flavour, typename T> struct VisitorApplication;
    
        namespace detail
        {
            template <typename V, typename Enable = void>
            struct is_vector : boost::mpl::false_ { };
    
            template <typename T>
            struct is_vector<std::vector<T>, void> : boost::mpl::true_ { };
    
            namespace iteration
            {
                // Iteration over a sequence
                template <typename FusionVisitorConcept, typename S, typename N>
                    struct members_impl
                    {
                        // Type of the current member
                        typedef typename boost::fusion::result_of::value_at<S, N>::type   current_t;
                        typedef typename boost::mpl::next<N>::type                        next_t;
                        typedef boost::fusion::extension::struct_member_name<S, N::value> name_t;
    
                        static inline void handle(FusionVisitorConcept& visitor, const S& s)
                        {
                            visitor.start_member(name_t::call());
                            VisitorApplication<FusionVisitorConcept, current_t>::handle(visitor, boost::fusion::at<N>(s));
                            visitor.finish_member(name_t::call());
                            members_impl<FusionVisitorConcept, S, next_t>::handle(visitor, s);
                        }
                    };
    
                // End condition of sequence iteration
                template <typename FusionVisitorConcept, typename S>
                    struct members_impl<FusionVisitorConcept, S, typename boost::fusion::result_of::size<S>::type>
                    {
                        static inline void handle(FusionVisitorConcept const&, const S&) { /*Nothing to do*/ }
                    };
    
                // Iterate over struct/sequence. Base template
                template <typename FusionVisitorConcept, typename S>
                    struct Struct : members_impl<FusionVisitorConcept, S, boost::mpl::int_<0>> {};
    
        } // iteration
    
        template <typename FusionVisitorConcept, typename T>
            struct array_application
            {
                typedef array_application<FusionVisitorConcept, T> type;
    
                typedef typename T::value_type value_type;
    
                static inline void handle(FusionVisitorConcept& visitor, const T& t)
                {
                    visitor.empty_array();
                    for (auto& el : t)
                        VisitorApplication<FusionVisitorConcept, value_type>::handle(visitor, el);
                }
            };
    
        template <typename FusionVisitorConcept, typename T>
            struct struct_application
            {
                typedef struct_application<FusionVisitorConcept, T> type;
    
                static inline void handle(FusionVisitorConcept& visitor, const T& t)
                {
                    visitor.empty_object();
                    iteration::Struct<FusionVisitorConcept, T>::handle(visitor, t);
                }
            };
    
        template <typename FusionVisitorConcept, typename T, typename Enable = void>
            struct value_application
            {
                typedef value_application<FusionVisitorConcept, T> type;
    
                static inline void handle(FusionVisitorConcept& visitor, const T& t) {
                    visitor.value(t);
                }
            };
    
        template <typename FusionVisitorConcept, typename T>
            struct value_application<FusionVisitorConcept, boost::optional<T> >
            {
                typedef value_application<FusionVisitorConcept, boost::optional<T> > type;
    
                static inline void handle(FusionVisitorConcept& visitor, const boost::optional<T>& t) {
                    if (t)
                        VisitorApplication<FusionVisitorConcept, T>::handle(visitor, *t);
                    else
                        ; // perhaps some default action?
                }
            };
    
        template <typename FusionVisitorConcept, typename T>
            struct select_application
            {
                typedef
                    //typename boost::mpl::eval_if<boost::is_array<T>,                  boost::mpl::identity<array_application<FusionVisitorConcept, T>>,
                    typename boost::mpl::eval_if<detail::is_vector<T>,                  boost::mpl::identity<array_application <FusionVisitorConcept, T>>,
                    typename boost::mpl::eval_if<boost::fusion::traits::is_sequence<T>, boost::mpl::identity<struct_application<FusionVisitorConcept, T>>,
                    boost::mpl::identity<value_application<FusionVisitorConcept, T>>
                    > >::type type;
            };
    
        } // detail
    
        template <typename FusionVisitorConcept, typename T>
            struct VisitorApplication : public detail::select_application<FusionVisitorConcept, T>::type
        {
        };
    }
    
    template <typename FusionVisitorConcept, typename T>
    void apply_fusion_visitor(FusionVisitorConcept& visitor, T const& o)
    {
        visitor::VisitorApplication<FusionVisitorConcept, T>::handle(visitor, o);
    }
    

    You can use it by supplying a visitor, e.g. for xml-like output:

    struct DisplayMemberVisitor {
        typedef std::string result_type;
    
        DisplayMemberVisitor() { ss << std::boolalpha; }
    
        std::string complete() { return ss.str(); }
    
        void start_member (const char* name) { 
            ss << "<" << name << ">";
        }
        void finish_member(const char* name) { 
            ss << "</" << name << ">";
        }
    
        template <typename T> void value(T const& value) {
            ss << value;
        }
    
        void empty_object() { }
        void empty_array()  { }
    
    private:
        std::stringstream ss;
    };
    

    See it Live On Coliru where (including some debug output) it prints:

    <integer_value>33</integer_value><boolean_value>false</boolean_value><integer_value>34</integer_value><boolean_value>true</boolean_value>

    Note that the ADT adaptation macro doesn't include a name (because none is available). You can probably quite easily make a macro FUSION_ADAPT_KEYD_ADT that also accepts a name and generates the relevant specializations of boost::fusion::extension::struct_member_name.

    BONUS MATERIAL

    Adding member name traits to ADT adapted members

    Here's a simplistic approach that shows what little amount of work needs to be done.

    #define MY_ADT_MEMBER_NAME(CLASSNAME, IDX, MEMBERNAME)                                                                                   \
            namespace boost { namespace fusion { namespace extension {                                                                       \
                template <> struct struct_member_name<CLASSNAME, IDX> { typedef char const *type; static type call() { return #MEMBERNAME; } \
            }; } } }
    
    MY_ADT_MEMBER_NAME(Bar, 0, integer_value)
    MY_ADT_MEMBER_NAME(Bar, 1, boolean_value)
    

    This defines a macro to avoid most of the repetition. If you are a BOOST_PP whizkid you could somehow weave this into an adt_ex.hpp¹ header of sorts, so you could instead say:

    BOOST_FUSION_ADAPT_ADT(Bar, // NOTE THIS PSEUDO-CODE
        (integer_value, int,  int,  obj.get_integer_value(), obj.set_integer_value(val))
        (boolean_value, bool, bool, obj.get_boolean_value(), obj.set_boolean_value(val)))
    

    For now here's the ADT adapted trick Live On Coliru

    ¹ in case you're interested, here's a tarball of a prepared adt_ex tree (drop in alongsize adt.hpp): adt_ex.tgz as a starting point. It's just adt* but with macros and header guards renamed to adt_ex*

    0 讨论(0)
提交回复
热议问题