Two stackoverflow answers suggest the approach using fusion adapt_struct to iterate over struct fields. The approach looks nice. However, how do you iterate into a field whi
Andres gives an excellent answer. The problem in my original code is that "for_each" takes only sequence types. When compiler evaluates the T for an int, it passes to "for_each" an int argument thus it fails. The idea behind Adries' solution is to hide "for_each" in a sequence-specific class (DecImplSeq_s below), and provide an alternative class (DecImplVoid_s) for non-sequence fields. Then create a facade class to divide the decoding of sequence and non-sequence fields (DecCalc_s).
The common header goes with the first example below to show Adres' idea.
/* compile with g++ 4.4.6: g++ -I boost_1_35_0 test.cpp */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace boost::fusion;
The common code of the solution derived directly from Adres' sample:
template struct Dec_s;
struct AppendToTextBox {
template void operator()(T& t) const {
//decode T and t as the original code here...
Dec_s::decode(t);
}
};
template struct DecImplSeq_s {
typedef DecImplSeq_s type;
static void decode(T2 & f) { for_each(f, AppendToTextBox()); };
};
template struct DecImplVoid_s {
typedef DecImplVoid_s type;
static void decode(T2 & f) { };
};
template struct DecCalc_s {
typedef typename
boost::mpl::eval_if< traits::is_sequence, DecImplSeq_s, DecImplVoid_s >
::type type;
};
template struct Dec_s : public DecCalc_s::type { };
Here is how you can use the common code above:
struct Foo_s { int i; char k[100]; };
struct Bar_s { int v; Foo_s w; };
BOOST_FUSION_ADAPT_STRUCT( Foo_s, (int, i) (char, k[100]) )
BOOST_FUSION_ADAPT_STRUCT( Bar_s, (int, v) (Foo_s, w) )
int main(int argc, char *argv[]) {
Bar_s f = { 2, { 3, "abcd" } };
Dec_s::decode(f);
return 0;
}
Another solution that is more straightforward without using advanced boost tricks, you can implement a specialized decoder class for each primitive types, without using "eval_if". To use this solution, you need to do a specialization for each primitive type in your structs.
struct Foo_s { int i; char k[100]; };
BOOST_FUSION_ADAPT_STRUCT( Foo_s, (int, i) (char, k[100]) )
struct Bar_s { int v; Foo_s w; };
BOOST_FUSION_ADAPT_STRUCT( Bar_s, (int, v) (Foo_s, w) )
template struct Dec_s { static void decode(T2 & f); };
struct AppendToTextBox {
template
void operator()(T& t) const {
//decode T and t as the original code here...
Dec_s::decode(t);
}
};
template void Dec_s::decode(T2 & f) {
for_each(f, AppendToTextBox());
};
template<> void Dec_s::decode(int & f) {};
template<> void Dec_s::decode(char & f) {};
int main(int argc, char *argv[]) {
Bar_s f = { 2, { 3, "abcd" } };
Dec_s::decode(f);
return 0;
}
After some progressive exploration, here is a complete example. It uses more recent boost features, but does not build with early boost versions like 1.35.0. It works well with boost 1.47.0 and 1.51.0.
The common header part:
#include
#include
#include
#include
#include
#include
#include
#include // is_array, is_class, remove_bounds
#include
#include
#include
extern int dec_indents; /* 0, 4, 8, ... */
struct NL {
static void print() { printf("\n");
for (int i=0; i
Then the common decoder with output formatting:
template struct Dec_s;
template struct Comma {
static inline void comma() { printf(" , "); }
};
template struct Comma::type >::type> {
static inline void comma() {}
};
template struct DecImplSeqItr_s {
typedef typename boost::fusion::result_of::value_at::type current_t;
typedef typename boost::mpl::next::type next_t;
typedef boost::fusion::extension::struct_member_name name_t;
static inline void decode(S& s) {
printf(" \"%s\" = ", name_t::call() );
Dec_s::decode(boost::fusion::at(s));
Comma::comma(); // Insert comma or not
DecImplSeqItr_s::decode(s);
}
};
template
struct DecImplSeqItr_s::type > {
static inline void decode(S& s) { }
};
template
struct DecImplSeqStart_s:DecImplSeqItr_s > {};
template struct DecImplSeq_s {
typedef DecImplSeq_s type;
static void decode(S & s) {
printf(" struct start --- { --- ");
dec_indents += 4;
NL::print();
DecImplSeqStart_s::decode(s);
dec_indents -= 4;
NL::print();
printf(" struct done --- } --- ");
NL::print();
};
};
template struct DecImplArray_s {
typedef DecImplArray_s type;
typedef typename boost::remove_bounds::type slice_t;
static const size_t size = sizeof(T2) / sizeof(slice_t);
static inline void decode(T2 & t) {
printf(" array start --- [ --- ");
dec_indents += 4;
NL::print();
for(size_t idx=0; idx::decode(t[idx]);
if (idx < size-1) {
NL::print(); printf(" , ");
}
}
dec_indents -= 4;
NL::print();
printf(" array done --- ] --- \n");
NL::print();
}
};
template struct DecImplVoid_s {
typedef DecImplVoid_s type;
static void decode(T2 & t) {
int status = 0;
const char *realname = abi::__cxa_demangle(typeid(t).name(),0,0,&status);
printf(" type %s", realname);
NL::print();
};
};
template struct DecCalc_s {
typedef
typename boost::mpl::eval_if< traits::is_sequence, DecImplSeq_s,
typename boost::mpl::eval_if< boost::is_array,
boost::mpl::identity< DecImplArray_s >,
DecImplVoid_s > >
::type type;
};
template struct Dec_s : public DecCalc_s::type { };
To use this common decoder, you can put it into a .h file, and use the following .c code:
/* compile with g++ 4.5.1: g++ -I boost_1_47_0 test.cpp */
#include "common_decoder.h"
using namespace boost::fusion;
int dec_indents=0;
struct Foo_s { int i; typedef char j_t[10]; Foo_s::j_t j; };
BOOST_FUSION_ADAPT_STRUCT( Foo_s, (int, i) (Foo_s::j_t, j) )
struct Bar_s { int v; typedef Foo_s w_t[2]; Bar_s::w_t w; };
BOOST_FUSION_ADAPT_STRUCT( Bar_s, (int, v) (Bar_s::w_t, w) )
int main(int argc, char *argv[]) {
Bar_s f = { 2, {{ 3, "abcd" },{ 4, "defg" }} };
Dec_s::decode(f);
return 0;
}