アンドレスは素晴らしい答えを出します。私の元のコードの問題は、「for_each」がシーケンス型しかとらないことです。コンパイラが int の T を評価するとき、「for_each」に int 引数を渡すため、失敗します。Adries のソリューションの背後にあるアイデアは、シーケンス固有のクラス (以下の DecImplSeq_s) で「for_each」を非表示にし、非シーケンス フィールドに代替クラス (DecImplVoid_s) を提供することです。次に、ファサード クラスを作成して、シーケンス フィールドと非シーケンス フィールド (DecCalc_s) のデコードを分割します。
以下の最初の例では、Adres の考えを示すために、共通のヘッダーを使用しています。
/* compile with g++ 4.4.6: g++ -I boost_1_35_0 test.cpp */
#include <typeinfo>
#include <string>
#include <boost/fusion/include/sequence.hpp>
#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/is_sequence.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/lexical_cast.hpp>
#include <cxxabi.h>
#include <stdio.h>
using namespace boost::fusion;
Adres のサンプルから直接派生したソリューションの共通コード:
template <typename T2> struct Dec_s;
struct AppendToTextBox {
template <typename T> void operator()(T& t) const {
//decode T and t as the original code here...
Dec_s<T>::decode(t);
}
};
template <typename T2> struct DecImplSeq_s {
typedef DecImplSeq_s<T2> type;
static void decode(T2 & f) { for_each(f, AppendToTextBox()); };
};
template <typename T2> struct DecImplVoid_s {
typedef DecImplVoid_s<T2> type;
static void decode(T2 & f) { };
};
template <typename T2> struct DecCalc_s {
typedef typename
boost::mpl::eval_if< traits::is_sequence<T2>, DecImplSeq_s<T2>, DecImplVoid_s<T2> >
::type type;
};
template <typename T2> struct Dec_s : public DecCalc_s<T2>::type { };
上記の一般的なコードを使用する方法は次のとおりです。
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<Bar_s>::decode(f);
return 0;
}
高度なブースト トリックを使用しないより簡単な別のソリューションとして、「eval_if」を使用せずに、プリミティブ型ごとに特殊なデコーダー クラスを実装できます。このソリューションを使用するには、構造体のプリミティブ型ごとに特殊化を行う必要があります。
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 <typename T2> struct Dec_s { static void decode(T2 & f); };
struct AppendToTextBox {
template <typename T>
void operator()(T& t) const {
//decode T and t as the original code here...
Dec_s<T>::decode(t);
}
};
template <typename T2> void Dec_s<T2>::decode(T2 & f) {
for_each(f, AppendToTextBox());
};
template<> void Dec_s<int >::decode(int & f) {};
template<> void Dec_s<char>::decode(char & f) {};
int main(int argc, char *argv[]) {
Bar_s f = { 2, { 3, "abcd" } };
Dec_s<Bar_s>::decode(f);
return 0;
}
いくつかの漸進的な調査の後、ここに完全な例があります。最新のブースト機能を使用していますが、1.35.0 のような初期のブースト バージョンではビルドされません。ブースト 1.47.0 および 1.51.0 でうまく動作します。
共通ヘッダー部分:
#include <typeinfo>
#include <string>
#include <boost/fusion/include/sequence.hpp>
#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/is_sequence.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/type_traits.hpp> // is_array, is_class, remove_bounds
#include <boost/lexical_cast.hpp>
#include <cxxabi.h>
#include <stdio.h>
extern int dec_indents; /* 0, 4, 8, ... */
struct NL {
static void print() { printf("\n");
for (int i=0; i<dec_indents; i++) printf(" ");
}
};
using namespace boost::fusion;
次に、出力フォーマットを備えた一般的なデコーダー:
template <typename T2> struct Dec_s;
template <typename S, typename N> struct Comma {
static inline void comma() { printf(" , "); }
};
template <typename S> struct Comma<S, typename
boost::mpl::prior<typename boost::fusion::result_of::size<S>::type >::type> {
static inline void comma() {}
};
template <typename S, typename N> struct DecImplSeqItr_s {
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 decode(S& s) {
printf(" \"%s\" = ", name_t::call() );
Dec_s<current_t>::decode(boost::fusion::at<N>(s));
Comma<S, N>::comma(); // Insert comma or not
DecImplSeqItr_s<S, next_t>::decode(s);
}
};
template <typename S>
struct DecImplSeqItr_s<S, typename boost::fusion::result_of::size<S>::type > {
static inline void decode(S& s) { }
};
template <typename S>
struct DecImplSeqStart_s:DecImplSeqItr_s<S, boost::mpl::int_<0> > {};
template <typename S> struct DecImplSeq_s {
typedef DecImplSeq_s<S> type;
static void decode(S & s) {
printf(" struct start --- { --- ");
dec_indents += 4;
NL::print();
DecImplSeqStart_s<S>::decode(s);
dec_indents -= 4;
NL::print();
printf(" struct done --- } --- ");
NL::print();
};
};
template <typename T2> struct DecImplArray_s {
typedef DecImplArray_s<T2> type;
typedef typename boost::remove_bounds<T2>::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<size; idx++) {
Dec_s<slice_t>::decode(t[idx]);
if (idx < size-1) {
NL::print(); printf(" , ");
}
}
dec_indents -= 4;
NL::print();
printf(" array done --- ] --- \n");
NL::print();
}
};
template <typename T2> struct DecImplVoid_s {
typedef DecImplVoid_s<T2> 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 <typename T2> struct DecCalc_s {
typedef
typename boost::mpl::eval_if< traits::is_sequence<T2>, DecImplSeq_s<T2>,
typename boost::mpl::eval_if< boost::is_array<T2>,
boost::mpl::identity< DecImplArray_s<T2> >,
DecImplVoid_s<T2> > >
::type type;
};
template <typename T2> struct Dec_s : public DecCalc_s<T2>::type { };
この一般的なデコーダーを使用するには、それを .h ファイルに入れ、次の .c コードを使用します。
/* 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<Bar_s>::decode(f);
return 0;
}