【问题标题】:How do I add boost archive serialization support for boost fusion maps?如何为 boost fusion maps 添加 boost 归档序列化支持?
【发布时间】:2014-04-15 19:54:05
【问题描述】:

我想添加能够通过 boost 序列化接口序列化 boost 融合映射的功能。我尝试了以下方法:

#include <iostream>
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/include/map.hpp>
#include <boost/fusion/container/map/map_fwd.hpp>
#include <boost/fusion/include/map_fwd.hpp>
#include <boost/fusion/sequence/intrinsic/at_key.hpp>
#include <boost/fusion/include/at_key.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/archive/text_iarchive.hpp>

struct fieldOne {};
struct fieldTwo {};
typedef boost::fusion::map<
    boost::fusion::pair<fieldOne, int>,
    boost::fusion::pair<fieldTwo, double> 
  > tFusionMap;

int main() {
  tFusionMap map;
  boost::archive::text_iarchive ar(std::cin);

  std::cin >> map; /* no compile error */
  ar & map; /* compiler error: */

  return 0;
}

这给了我编译器错误:

struct boost::fusion::map<boost::fusion::pair<fieldOne, int>, 
boost::fusion::pair<fieldTwo, double> >’ has no member named ‘serialize’

所以显然我需要自己实现这个。在网上搜索了一些指导后,我找到了这些答案

Boost fusion serialization of a class using BOOST_FUSION_ADAPT_ADT

How to serialize fusion::vector?

但是,所有这些答案(以及网络上的许多其他答案)都依赖于使用自定义函数调用序列化,例如:

fusion_serialize(ar, m);

但是,我想以我可以调用的方式序列化地图:

ar & m;

这样我就可以在其他模板函数中使用序列化。有没有办法做到这一点?

我已经尝试将它添加到我的源文件中

namespace boost {
  namespace serialization {

    template<class Archive, typename T, std::size_t num_dims>
    void serialize( Archive & ar, T & map, const unsigned int version ) {
      fusion_serialize(ar,map);
    }
  }
}

但是,这太笼统了,因为如果模板不是融合映射,模板将匹配任何类型并生成编译错误。我看不到如何修改上述内容,以便 serialize 函数定义仅适用于 boost::fusion::map 类型。有什么建议吗?

【问题讨论】:

  • template void serialize(Archive & ar, tFusionMap& m, const unsigned int version) 应该可以工作
  • 这仅适用于我的tFusionMap,但是,我希望序列化适用于所有 fusioan 地图

标签: c++ serialization boost


【解决方案1】:

您通常可以为任何 Fusion 地图实现序列化:

namespace boost { namespace serialization {

    struct saver {
        template <typename Ar, typename Pair>
            void operator()(Ar& ar, Pair& data) const
            {
                ar & data.second;
            }
    };

    template <typename Ar, typename... TArgs>
        void serialize(Ar& ar, boost::fusion::map<TArgs...>& fmap, unsigned /*version*/)
        {
            using phoenix::ref;
            using phoenix::arg_names::arg1;
            static const phoenix::function<saver> save {};

            fusion::for_each(fmap, save(ref(ar), arg1));
        }

} }

现在,这可行:

#include <boost/archive/text_oarchive.hpp>
#include <iostream>

typedef boost::fusion::map<
    boost::fusion::pair<struct fieldOne, int>,
    boost::fusion::pair<struct fieldTwo, double> 
  > tFusionMap;

int main() {
    tFusionMap map { 42 , M_PI };
    boost::archive::text_oarchive oa(std::cout);
    oa & map;
}

Live On Coliru

打印:

22 serialization::archive 10 0 0 42 3.1415926535897931

反序列化同时实现。


为了完整性:

#include <boost/fusion/include/map.hpp>
#include <boost/fusion/algorithm.hpp>
#include <boost/phoenix/phoenix.hpp>
#include <boost/phoenix/fusion.hpp>

【讨论】:

  • 哇,谢谢!没有 C++11 有没有办法做到这一点?
  • 是的,但它会更加冗长。您必须对 N 类型的地图使用多种专业化(可变参数在这里可以节省时间)。其余的,没有重大挑战。
  • 好的。所以如果没有 C++11,我需要为每个可能的参数数量添加模板特化?
  • 这就是演习,是的。如果你想偷懒,你可以用预处理器宏做“假可变参数”。参见例如BOOST.PP
【解决方案2】:

(这是一个 C++11 解决方案,也许它可以通过 Boost.PP 激发 C++98 的灵感,但它的工作量要大得多。)

是的,

[that] 太笼统了,因为模板会匹配任何类型并生成 如果不是融合图则编译错误。

...这使得serialize 函数与序列化库本身中包含的其他声明发生冲突(歧义)。

因此,在@sehe 答案之上构建更多通用性,您可以使用以下内容。请注意,在这种情况下,您不需要 #include 或假设序列,因此它适用于 所有 融合序列。关键是template &lt;class...&gt; class Sequence的使用。

#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/support/is_sequence.hpp>
#include <boost/serialization/nvp.hpp>
#include <typeinfo> // typeid
#include <string>
#include <boost/fusion/support/pair.hpp>

namespace boost{
namespace fusion{

template<typename Archive>
struct item_serializer{
    Archive& ar;
    item_serializer(Archive& ar) : ar(ar){}
    template<typename T>
    void operator()(T& item) const{
        ar & boost::serialization::make_nvp((std::string("item_") + typeid(T).name()).c_str(), item);
    //  ar & BOOST_SERIALIZATION_NVP(item); // for more conservative xml tag name
    }
};

template<class Archive, class T1, class T2>
void serialize(Archive& ar, pair<T1, T2>& p, const unsigned int /*file_version*/){
    T2& second = p.second;
    ar & BOOST_SERIALIZATION_NVP(second);
}

template<
    class Archive, template <class...> class Sequence, typename... Args
    , typename = typename boost::enable_if_c<traits::is_sequence<Sequence<Args...>>::value>::type
>
void serialize(Archive& ar, Sequence<Args...>& s, const unsigned int /*file_version*/){
    item_serializer<Archive> sr(ar);
    for_each(s, sr);
}

}}

例子:

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/sequence/comparison/equal_to.hpp>
#include <sstream>

int main(){
    boost::fusion::vector<int, char, double> vector_src(
        3, '4', 5.41
    ), vector_dst;

    boost::fusion::map<
        boost::fusion::pair<struct fieldOne, int>,
        boost::fusion::pair<struct fieldTwo, double> 
    > map_src{42, M_PI}, map_dst;

    std::ostringstream oss;
    boost::archive::xml_oarchive oa(oss);
    oa << BOOST_SERIALIZATION_NVP(vector_src);
    oa << BOOST_SERIALIZATION_NVP(map_src);

    std::cout << oss.str();

    std::istringstream iss(oss.str());
    boost::archive::xml_iarchive ia(iss);
    ia >> BOOST_SERIALIZATION_NVP(vector_dst);
    ia >> BOOST_SERIALIZATION_NVP(map_dst);

    assert(vector_src == vector_dst);
    assert(map_src == map_dst);
}

输出:

<vector_src class_id="0" tracking_level="0" version="0">
    <item_i>3</item_i>
    <item_c>52</item_c>
    <item_d>5.4100000000000001</item_d>
</vector_src>
<map_src class_id="1" tracking_level="0" version="0">
    <item_N5boost6fusion4pairIZ4mainE8fieldOneiEE class_id="2" tracking_level="0" version="0">
        <second>42</second>
    </item_N5boost6fusion4pairIZ4mainE8fieldOneiEE>
    <item_N5boost6fusion4pairIZ4mainE8fieldTwodEE class_id="3" tracking_level="0" version="0">
        <second>3.1415926535897931</second>
    </item_N5boost6fusion4pairIZ4mainE8fieldTwodEE>
</map_src>

【讨论】:

  • 谢谢!这是一个非常优雅的解决方案。理想情况下,我想在没有 C++11 的情况下做到这一点,但我想这只能通过丑陋的预处理器宏魔法来完成 :-)。
  • 也许BOOST.PP可以,只是我不打算尝试。
猜你喜欢
  • 2012-07-16
  • 2010-11-11
  • 2011-08-21
  • 2011-08-26
  • 2014-06-11
  • 1970-01-01
  • 2011-08-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多