5

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 <typeinfo>
#include <string>
#include <iostream>
#include <boost/fusion/include/sequence.hpp>
#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/adapt_adt.hpp>

#include <boost/lexical_cast.hpp>

using namespace boost::fusion;

struct Foo
{
    int integer_value;
    bool boolean_value;
};


class Bar
{
    int integer_value;
    bool boolean_value;
public:
    Bar(int i_val, bool b_val):integer_value(i_val),boolean_value(b_val) {}
    int     get_integer_value() const       { return integer_value; }
    void    set_integer_value(int i_val)    { integer_value = i_val; }
    bool    get_boolean_value() const       { return boolean_value; }
    void    set_boolean_value(bool b_val)   { boolean_value = b_val; }
};

BOOST_FUSION_ADAPT_STRUCT(
    Foo,
    (int, integer_value)
    (bool, boolean_value)    
)

BOOST_FUSION_ADAPT_ADT(
    Bar,
    (int, int, obj.get_integer_value() , obj.set_integer_value(val))
    (bool, bool, obj.get_boolean_value(), obj.set_boolean_value(val))    
)


struct DisplayMembers
{

    template <typename T>
    void operator()(T& t) const {
        std::cout << typeid(t).name() << " : " << boost::lexical_cast<std::string>(t) << std::endl;
    }

};

int main(int argc, char *argv[]) 
{
  struct Foo f = { 33, false};
  for_each(f, DisplayMembers());

  Bar b(34,true);
  for_each(b, DisplayMembers());
  return 0;
}

In the above example the result is

int : 33
bool : 0
struct boost::fusion::extension::adt_attribute_proxy<class Bar,0,0> : 34
struct boost::fusion::extension::adt_attribute_proxy<class Bar,1,0> : 1

I want the result as

int : integer_value : 33
bool : boolean_value : 0
int : integer_value : 34
bool : boolean_value : 1
genpfault
  • 51,148
  • 11
  • 85
  • 139
balas bellobas
  • 237
  • 3
  • 12
  • `typeid(t).name()` has implementatation defined bahaviour. – magras Oct 15 '14 at 10:40
  • The `DisplayMembers` functor needs to accept `T const&` because the ADT adapted struct only returns by value (and temporaries don't bind to non-const references, unless you're using MSVC compiler extensions) – sehe Oct 15 '14 at 11:15

2 Answers2

7

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

joao
  • 3,517
  • 1
  • 31
  • 43
5

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*

sehe
  • 374,641
  • 47
  • 450
  • 633
  • I was having a similar visitor and it worked fine for struct sequences adapted with boost fusion. When i sequence a class, **name_t::call()** method does not seem to be supported. So i was curious how to get the class member variables information. – balas bellobas Oct 15 '14 at 11:26
  • @balasbellobas That's a limitation in Fusion's `*ADAPT_ADT`. See my last paragraph for an idea on how to work around that. Meanwhile, see the demo **[Live On Coliru](http://coliru.stacked-crooked.com/a/054208f457161fdb)** – sehe Oct 15 '14 at 12:10
  • Thanks for the reply. I had a similar approach to recursively going down the structure definitions. I don't get the idea on how we could overcome the limitation of the **call()** method. The fusion adapt macro definition is BOOST_FUSION_ADAPT_ADT( type_name, ... ). If my understanding is correct the type_name corresponds to the class name. – balas bellobas Oct 15 '14 at 15:00
  • @balasbellobas Hehehe. Twice you say "I had a similar approach". It would have been nice to (a) share that (b) mention you knew it was just the *ADAPT_ADT that was causing trouble. I'll see what I can come up – sehe Oct 15 '14 at 15:13
  • Ah! I get it now. You think that *ADAPT_STRUCT is for "struct" andhence *ADAPT_ADT must be for "classes". I didn't detect any of this logic because in C++ `class` is exactly the same thing as `struct`. So you _meant_ to say abstract a class using *ADT. – sehe Oct 15 '14 at 15:19
  • I have changed the code you had shared and made it to visit on a sequence adapted from a C++ class. [Refer code](coliru.stacked-crooked.com/a/7089d1dc427f7cab). The [boost documentation](http://www.boost.org/doc/libs/1_56_0/libs/fusion/doc/html/fusion/adapted/adapt_adt.html) for the *ADAPT_ADT show a struct with private members and providing accesor functions. So, i felt that same could be used for C++ classes. – balas bellobas Oct 15 '14 at 15:58
  • @balasbellobas Obviously, I agree, since `class` === `struct`. It was just not clear from your prose. I'll see whether I can demo what I think is possible for ADT adapted types – sehe Oct 15 '14 at 16:06
  • If you can do that, it would help me a lot. I apologize for not being clear in the beginning. – balas bellobas Oct 15 '14 at 16:11
  • @balasbellobas Added as **BONUS MATERIAL**. Note I tried to make it clean, but I'm not up to speed with BOOST_PP enough. Here's the ADT adapted class **[Live On Coliru](http://coliru.stacked-crooked.com/a/6cc59d82c2dbba24)**. This shows what needs to be done. – sehe Oct 15 '14 at 22:08
  • Thanks . I will try to understand the solution. Thanks for spending time on this. – balas bellobas Oct 16 '14 at 07:03
  • I made a mistake and it did not work earlier. So I raised this query. Later, I corrected my mistake and it works now. – balas bellobas Nov 12 '14 at 09:56
  • I made a modification to the serialize code to perform de-serialization. The compiler is unable to substitute the adt_proxy type to the value type in question. Please refer to the code attached.[Love On Coliru](http://coliru.stacked-crooked.com/a/d72baea2f84ca9ee) – balas bellobas Nov 12 '14 at 10:22
  • @balasbellobas there's a button on the top right if you want to pose questions. Seriously, a lot more people will be looking at it then – sehe Nov 12 '14 at 10:23
  • This is a modification to the code that you has suggested. So i thought you will be better person to see where i am missing. – balas bellobas Nov 12 '14 at 10:42
  • Posting a question doesn't make it unlikely that I'd look at it. On the contrary. You can always just send a link to the question here in case you worry :) – sehe Nov 12 '14 at 10:47
  • Posted a new [question](http://stackoverflow.com/questions/26885379/boost-fusion-error-in-substituting-the-adt-proxy-type-to-the-value-type) – balas bellobas Nov 12 '14 at 11:02