可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Lets suppose I have such classes hierarchy:
enum class Type { DUMMY }; struct Base { int a; explicit Base(int a) : a(a) {} virtual ~Base() {} virtual Type type() = 0; }; struct Foo1 : public Base { double b; Foo1(int a, double b) : Base{a}, b(b) {} Type type() override { return Type::DUMMY; } };
all derived from Base
using single inheritance and not defined any virtual
methods, except overriding type()
method.
And I want to have meta info for each derived from Base
to serialization and debug output. And as I see boost fusion is what I want:
#include <iostream> #include <string> #include <boost/fusion/include/adapt_struct.hpp> #include <boost/fusion/include/at_c.hpp> #include <boost/fusion/include/for_each.hpp> #include <boost/fusion/include/mpl.hpp> #include <boost/fusion/include/zip.hpp> #include <boost/mpl/range_c.hpp> namespace fusion = boost::fusion; namespace mpl = boost::mpl; enum class Type { DUMMY }; struct Base { int a; explicit Base(int a) : a(a) {} virtual ~Base() {} virtual Type type() = 0; }; struct Foo1 : public Base { double b; Foo1(int a, double b) : Base{a}, b(b) {} Type type() override { return Type::DUMMY; } }; BOOST_FUSION_ADAPT_STRUCT(Foo1, (double, b)) template <typename Sequence> struct XmlFieldNamePrinter { XmlFieldNamePrinter(const Sequence &seq) : seq_(seq) {} const Sequence &seq_; template <typename Index> void operator()(Index idx) const { std::string field_name = fusion::extension::struct_member_name<Sequence, idx>::call(); std::cout << '<' << field_name << '>' << fusion::at<Index>(seq_) << "</" << field_name << ">\n"; ; } }; template <typename Sequence> void printXml(Sequence const &v) { typedef mpl::range_c<unsigned, 0, fusion::result_of::size<Sequence>::value> Indices; fusion::for_each(Indices(), XmlFieldNamePrinter<Sequence>(v)); } int main() { Foo1 saveMe = {3, 3.4}; printXml(saveMe); }
But how handle Base
data memebers? I do not want include their description into BOOST_FUSION_ADAPT_STRUCT(Foo1
, like this:
BOOST_FUSION_ADAPT_STRUCT(Foo1, (int, a) (double, b))
because of I have to do it for every struct that inherit from Base
, so I would prefer syntax similar to this(not compiled of course):
BOOST_FUSION_ADAPT_STRUCT(Base, (int, a)) BOOST_FUSION_ADAPT_STRUCT(Foo1, (Base, __parent__) (double, b))
How can I achieve similar syntax?
回答1:
You need to treat Fusion sequences separately, e.g. using SFINAE:
template <typename Index, typename IsSeq = IsSeq<Index> > typename boost::disable_if<IsSeq, void>::type operator()(Index idx) const { std::string field_name = fusion::extension::struct_member_name<Sequence, idx>::call(); std::cout << '<' << field_name << '>' << fusion::at<Index>(seq_) << "</" << field_name << ">\n"; } template <typename Index, typename IsSeq = IsSeq<Index> > typename boost::enable_if<IsSeq, void>::type operator()(Index idx) const { std::string field_name = fusion::extension::struct_member_name<Sequence, idx>::call(); std::cout << "<" << field_name << ">\n\t"; typedef typename IsSeq::sequence_type SubSeq; typedef mpl::range_c<unsigned, 0, fusion::result_of::size<SubSeq>::value> SubIndices; fusion::for_each(SubIndices(), XmlFieldNamePrinter<SubSeq>(fusion::at<Index>(seq_))); std::cout << "</" << field_name << ">\n"; }
The implementation of the IsSeq<Index>
helper trait is below. Now you can use:
BOOST_FUSION_ADAPT_STRUCT(Base, a) BOOST_FUSION_ADAPT_STRUCT(Foo1, base, b)
And get
<base> <a>3</a> </base> <b>3.4</b>
LIVE DEMO
Live On Coliru
#include <iostream> #include <string> #include <boost/fusion/include/adapt_struct.hpp> #include <boost/fusion/include/at_c.hpp> #include <boost/fusion/include/for_each.hpp> #include <boost/fusion/include/mpl.hpp> #include <boost/fusion/include/zip.hpp> #include <boost/mpl/range_c.hpp> namespace fusion = boost::fusion; namespace mpl = boost::mpl; enum class Type { DUMMY }; struct Base { int a; explicit Base(int a) : a(a) {} virtual ~Base() {} virtual Type type() = 0; Base &base = *this; }; struct Foo1 : public Base { double b; Foo1(int a, double b) : Base{ a }, b(b) {} Type type() override { return Type::DUMMY; } }; BOOST_FUSION_ADAPT_STRUCT(Base, a) BOOST_FUSION_ADAPT_STRUCT(Foo1, base, b) template <typename Sequence> struct XmlFieldNamePrinter { XmlFieldNamePrinter(const Sequence &seq) : seq_(seq) {} const Sequence &seq_; template <typename Index, typename T = typename fusion::result_of::at_c<Sequence, Index::value>::type, typename BareT = typename boost::remove_reference<T>::type > struct IsSeq : mpl::bool_<fusion::traits::is_sequence<BareT>::value> { typedef BareT sequence_type; // if true_ }; template <typename Index, typename IsSeq = IsSeq<Index> > typename boost::disable_if<IsSeq, void>::type operator()(Index idx) const { std::string field_name = fusion::extension::struct_member_name<Sequence, idx>::call(); std::cout << '<' << field_name << '>' << fusion::at<Index>(seq_) << "</" << field_name << ">\n"; } template <typename Index, typename IsSeq = IsSeq<Index> > typename boost::enable_if<IsSeq, void>::type operator()(Index idx) const { std::string field_name = fusion::extension::struct_member_name<Sequence, idx>::call(); std::cout << "<" << field_name << ">\n\t"; typedef typename IsSeq::sequence_type SubSeq; typedef mpl::range_c<unsigned, 0, fusion::result_of::size<SubSeq>::value> SubIndices; fusion::for_each(SubIndices(), XmlFieldNamePrinter<SubSeq>(fusion::at<Index>(seq_))); std::cout << "</" << field_name << ">\n"; } }; template <typename Sequence> void printXml(Sequence const &v) { typedef mpl::range_c<unsigned, 0, fusion::result_of::size<Sequence>::value> Indices; fusion::for_each(Indices(), XmlFieldNamePrinter<Sequence>(v)); } int main() { Foo1 saveMe = { 3, 3.4 }; printXml(saveMe); }