【问题标题】:c++/boost fusion handle parent classc++/boost fusion处理父类
【发布时间】:2016-02-16 22:07:55
【问题描述】:

假设我有这样的类层次结构:

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; }
};

全部从 Base 派生,使用单一继承且未定义 任何virtual 方法,但覆盖type() 方法除外。

并且我希望每个从Base 派生的元信息用于序列化和调试输出。正如我所见,提升融合是我想要的:

#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);
}

但是如何处理Base 数据成员? 我不想将他们的描述包含在BOOST_FUSION_ADAPT_STRUCT(Foo1 中, 像这样:

BOOST_FUSION_ADAPT_STRUCT(Foo1,
              (int, a)
              (double, b))

因为我必须为从Base 继承的每个结构都这样做, 所以我更喜欢类似于这个的语法(当然没有编译):

BOOST_FUSION_ADAPT_STRUCT(Base, (int, a))

BOOST_FUSION_ADAPT_STRUCT(Foo1,
              (Base, __parent__)
              (double, b))

我怎样才能实现类似的语法?

【问题讨论】:

    标签: c++ c++11 serialization boost boost-fusion


    【解决方案1】:

    您需要单独处理 Fusion 序列,例如使用 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";
    }
    

    IsSeq&lt;Index&gt; helper trait 的实现如下。现在您可以使用:

    BOOST_FUSION_ADAPT_STRUCT(Base, a)
    BOOST_FUSION_ADAPT_STRUCT(Foo1, base, b)
    

    得到

    <base>
        <a>3</a>
    </base>
    <b>3.4</b>
    

    现场演示

    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);
    }
    

    【讨论】:

    • 如果你想看看我是怎么做的,recorded live session(从大约 58 分钟开始;experiment
    • 谢谢,但是没有新字段(基础)可以吗?我创建了很多struct Base并派生了,难道不是Base的虚成员函数,比如Base &amp;base() { return *this; }吗?
    • 没有。除非您创建更多定义宏的元数据,否则这是不可能的。
    猜你喜欢
    • 1970-01-01
    • 2012-10-17
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多