有没有办法自动生成或至少缩短(某些参数,如 __all__)序列化函数?

Is there a way to auto-generate or at least shorten (some parameter like __all__) the serialize function?

我正在处理一个消息(请求和响应)作为结构传输的网络。为了实现这一点,我求助于 boost-serialization,效果很好!但是有这么多类型的消息和响应,很难在所有的消息和响应中都包含序列化函数,有没有捷径可以为所有结构自动生成方法或者至少一次公开每个成员变量?

示例:

#pragma once

#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/serialization.hpp>
struct Test
{
    public:
        int a;
        int b;
        template<typename archive> void serialize(archive& ar, const unsigned version) {
        ar & a; 
        ar & b;
    }
};

我相信我回答了这个问题 :

struct Test {
    int a,b;
    template<typename Ar> void serialize(Ar& ar, unsigned) { ar & a & b; }
};

请注意,如果您也将序列化设为自由函数(使用 ADL 查找):

struct Test {
    int a,b;
};

template<typename Ar> void serialize(Ar& ar, Test& o, unsigned) {
    ar & o.a & o.b;
}

所以你可以单独拥有序列化代码。最后,如果您有一组预定义的存档,则序列化函数根本不需要是模板:

using OArchive = boost::archive::binary_oarchive;
using IArchive = boost::archive::binary_iarchive;

struct Test {
    int a,b;
    void serialize(OArchive& ar, ...) const { ar & a & b; }
    void serialize(IArchive& ar, ...)       { ar & a & b; }
};

当然这会产生一些重复。我很可爱地忽略了可变参数的版本参数,但在翻转尺寸上它更 const-正确。

其他想法

如果您的结构是二进制可序列化的,请将它们标记为:

  • 或明确将它们视为 blob:make_binary_object - 在这种情况下,您不需要任何序列化方法:

    Live On Coliru

    #include <boost/archive/text_oarchive.hpp>
    #include <boost/serialization/binary_object.hpp>
    #include <iostream>
    using boost::serialization::make_binary_object;
    
    struct Test { int a,b; };
    
    int main() {
        boost::archive::text_oarchive oa(std::cout, boost::archive::no_header);
    
        Test req {13,31};
        oa << make_binary_object(&req, sizeof(req));
    }
    

    版画

    DQAAAB8AAAA=
    

元编程来拯救

Fair warning, as a C programmer you probably want to bail out and just use more preprocessor magic/code generators here

假设您有更多消息(可以嵌套):

namespace Messages {
    struct FooMsg { int a, b; };
    struct BarMsg { std::string c; double d; };
    struct QuxMsg { FooMsg e; BarMsg f; };
}

您可以将它们调整为 Fusion Sequences:

BOOST_FUSION_ADAPT_STRUCT(Messages::FooMsg, a, b)
BOOST_FUSION_ADAPT_STRUCT(Messages::BarMsg, c, d)
BOOST_FUSION_ADAPT_STRUCT(Messages::QuxMsg, e, f)

好消息是现在您可以跨这些序列编写通用代码,所以让我们介绍一下我们自己的 serialization wrapper:

namespace Messages {
    template <typename T>
    struct MsgSerializationWrapper {
        T& ref;
    };

    template <typename T>
    static inline MsgSerializationWrapper<T> wrap(T& msg) { return {msg}; }

现在您可以为任何包装的消息实现序列化:

    template <typename Ar, typename Msg>
    void serialize(Ar& ar, MsgSerializationWrapper<Msg> wrapped, unsigned) {
        boost::fusion::for_each(wrapped.ref, [&ar](auto& field) { ar & wrap(field); });
    }

当然,我们需要一些 sfinae 来检测包装类型何时不是融合序列,并以正常方式对其进行序列化。

完整演示

Live On Coliru

#include <boost/archive/text_oarchive.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <iostream>

namespace Messages {
    struct FooMsg { int a, b; };
    struct BarMsg { std::string c; double d; };
    struct QuxMsg { FooMsg e; BarMsg f; };
}

BOOST_FUSION_ADAPT_STRUCT(Messages::FooMsg, a, b)
BOOST_FUSION_ADAPT_STRUCT(Messages::BarMsg, c, d)
BOOST_FUSION_ADAPT_STRUCT(Messages::QuxMsg, e, f)

namespace Messages {
    template <typename T>
    struct MsgSerializationWrapper {
        T& ref;
    };

    template <typename T>
    static inline MsgSerializationWrapper<T> wrap(T& msg) { return {msg}; }

    template <typename Ar, typename Msg>
    std::enable_if_t<boost::fusion::traits::is_sequence<Msg>::value>
    serialize(Ar& ar, MsgSerializationWrapper<Msg> wrapped, unsigned) {
        boost::fusion::for_each(wrapped.ref, [&ar](auto& field) { ar & wrap(field); });
    }

    template <typename Ar, typename Primitive>
    std::enable_if_t<not boost::fusion::traits::is_sequence<Primitive>::value>
    serialize(Ar& ar, MsgSerializationWrapper<Primitive> wrapped, unsigned) {
        ar & wrapped.ref;
    }
}

int main() {
    boost::archive::text_oarchive oa(std::cout);

    Messages::QuxMsg req { 
        Messages::FooMsg { 42, 99 },
        Messages::BarMsg { "hello world\n", 3.14e100 },
    };

    oa << wrap(req);
}

版画

22 serialization::archive 17 0 0 0 0 0 0 42 99 0 0 0 0 12 hello world
 0 0 3.13999999999999984e+100