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 &
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
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
.
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*