跳过层次结构中的中间类,并使用boost ::序列化
Skip intermediate classes in hierarchy inheritance with boost::serialization
上下文:我有一个类似树的结构,代表我想使用 boost::serialization
序列化的expr。主要问题是所有班级都有非默认的构造函数和const子女。为了克服此问题,我关注DOC并超载load_construct_data
和save_construct_data
(最终完成了所有工作(。
我的问题是关于代码中的Mul
类。为了分解代码,我开发了一个模板类Op2
,该类别用于定义运算符,例如 add 或 Mul
(此处仅在此处显示Mul
(通过这些类上的CRTP。在Mul::serialize
中,我将Expr
直接注册为Mul
的基类,并完全跳过Op2
。代码有效,Valgrind很高兴,但是正确吗?还是boost ::序列化需要对完整的类层次结构?
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/serialization.hpp>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
//forward declaration of my structs
struct Expr;
struct Mul;
struct Int;
//forward declarations of custom boost functions to friend them in the class
namespace b_ser = boost::serialization;
namespace boost {
namespace serialization {
template <class Archive>
void load_construct_data(Archive &ar, Mul *e, const unsigned int);
template <class Archive>
void save_construct_data(Archive &ar, const Mul *a, const unsigned int);
template <class Archive>
void load_construct_data(Archive &ar, Int *e, const unsigned int);
template <class Archive>
void save_construct_data(Archive &ar, const Int *a, const unsigned int);
} // namespace serialization
} // namespace boost
//memory manager
std::vector<std::unique_ptr<Expr>> pool;
// AST
struct Expr {
virtual ~Expr() {}
virtual std::vector<Expr const *> children() const = 0;
virtual std::string identity() const = 0;
void print(int p) const {
std::cout << std::setw(p) << ' ';
std::cout << identity() << "n";
for (auto a_kid : children()) {
a_kid->print(p + 2);
}
}
void self_register() const {
if (std::find_if(pool.begin(), pool.end(), [this](auto const &stored_ptr) {
return this == stored_ptr.get();
}) == pool.end()) {
pool.push_back(std::unique_ptr<Expr>(const_cast<Expr *>(this)));
}
for (auto ptr : children()) {
ptr->self_register();
}
}
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {}
};
struct Int : Expr {
int const n;
std::vector<Expr const *> children() const override { return {}; }
std::string identity() const override {
return "Int[" + std::to_string(n) + "]@";
}
Int(int nn) : n(nn) {}
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar &boost::serialization::base_object<Expr>(*this);
}
template <class Archive>
friend void b_ser::save_construct_data(Archive &ar, const Int *i,
const unsigned int) {
ar << i->n;
}
template <class Archive>
friend void b_ser::load_construct_data(Archive &ar, Int *i,
const unsigned int) {
int n;
ar >> n;
::new (i) Int(n);
}
};
template <class T> struct Op2 : Expr {
std::vector<Expr const *> children() const override { return {l, r}; }
std::string identity() const override { return T::message; }
Op2(Expr const *ll, Expr const *rr) : l(ll), r(rr) {}
protected:
Expr const *l;
Expr const *r;
};
struct Mul : Op2<Mul> {
using Op2::Op2;
static auto const constexpr message = "Mul";
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar &boost::serialization::base_object<Expr>(*this);
}
template <class Archive>
friend void b_ser::save_construct_data(Archive &ar, const Mul *a,
const unsigned int) {
ar << a->l;
ar << a->r;
}
template <class Archive>
friend void b_ser::load_construct_data(Archive &ar, Mul *e,
const unsigned int) {
Expr *l, *r;
ar >> l;
ar >> r;
::new (e) Mul(l, r);
e->self_register();
}
};
template <class T, class... Args> T *store(Args... args) {
auto to_store = std::make_unique<T>(std::forward<Args>(args)...);
auto raw_ptr = to_store.get();
pool.push_back(std::move(to_store));
return raw_ptr;
}
BOOST_CLASS_EXPORT(Expr)
BOOST_CLASS_EXPORT(Int)
BOOST_CLASS_EXPORT(Mul)
int main(int argc, char *argv[]) {
{
auto deux = store<Int>(2);
auto trois = store<Int>(3);
auto m_23 = store<Mul>(trois, deux);
auto quatre = store<Int>(4);
auto root = store<Mul>(m_23, quatre);
Expr *e_root = root;
root->print(2);
std::ofstream of("arxiv");
boost::archive::text_oarchive oa(of);
oa << e_root;
}
std::cout << "==================="
<< "n";
{
std::ifstream isf("arxiv");
boost::archive::text_iarchive is(isf);
Expr *expr;
is >> expr;
expr->print(2);
}
return 0;
}
首先,我认为您必须使用MSVC,因为您的代码不是有效的标准C 。
修复事物表明它似乎在Clang和GCC上正常工作。
启用地址齐射器迅速揭示了我/相信/可能起源于内部内部的单例代码的错误。我暂时会忽略它。
由于这些错误,我很长时间看了这件事,试图查看该代码是否应该归咎于代码。
这样做的时候,我发现很多事情都可以变得更简单。
-
如果您只添加 private 默认构造函数,则可以在不构造data的情况下进行。
关于您的课程的重要一件事是不变的,很容易证明不变性是通过这种方式进行的。
-
您无需每个二进制操作员的子类,因为它们实际上没有行为。考虑提供
message
频段范围内 -
而不是制作
vector<unique_ptr>
,您可以使用隐式拥有其元素的指针容器。这使得查找等效指针a lot 更容易:namespace memory_management { struct address_less { bool operator()(Expr const& a, Expr const& b) const { return &a < &b; } }; static boost::ptr_set<Expr, address_less> pool; } void Expr::self_register() { memory_management::pool.insert(this); }
在所有情况下,所有内容都短得多:
// AST
struct Expr {
virtual ~Expr() = default;
virtual std::vector<Expr const *> children() const { return {}; }
virtual std::string identity() const = 0;
void print(int p) const {
std::cout << std::setw(p) << ' ';
std::cout << identity() << "n";
for (auto a_kid : children()) {
a_kid->print(p + 2);
}
}
protected:
Expr() { self_register(); }
private:
void self_register();
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &, unsigned) {}
};
namespace memory_management {
struct address_less {
bool operator()(Expr const& a, Expr const& b) const { return &a < &b; }
};
static boost::ptr_set<Expr, address_less> pool;
}
void ::Expr::self_register() { memory_management::pool.insert(this); }
struct Int : Expr {
std::string identity() const override { return "Int[" + std::to_string(n) + "]@"; }
Int(int nn) : n(nn) {}
private:
int const n = 0;
friend class boost::serialization::access;
Int() = default;
template <class Archive> void serialize(Archive &ar, unsigned) {
ar & boost::serialization::base_object<Expr>(*this)
& const_cast<int&>(n);
}
};
namespace Tags {
struct Mul;
struct Div;
struct Plus;
struct Minus;
template <typename T> constexpr char const* const message = "Unknown";
template <> constexpr char const* const message<Mul> = "Mul";
template <> constexpr char const* const message<Div> = "Div";
template <> constexpr char const* const message<Plus> = "Plus";
template <> constexpr char const* const message<Minus> = "Minus";
}
template <class T> struct Op2 : Expr {
std::vector<Expr const *> children() const override { return { l, r }; }
std::string identity() const override { return Tags::message<T>; }
Op2(Expr *ll, Expr *rr) : l(ll), r(rr) {}
protected:
friend class boost::serialization::access;
Op2() = default;
Expr *l = nullptr;
Expr *r = nullptr;
template <class Archive> void serialize(Archive &ar, unsigned) {
ar & boost::serialization::base_object<Expr>(*this)
& l & r;
}
};
using Mul = Op2<Tags::Mul>;
using Div = Op2<Tags::Div>;
using Plus = Op2<Tags::Plus>;
using Minus = Op2<Tags::Minus>;
奖金:AST建筑物的DSL
我认为能够说:
会更好Expr const* root((as_expr(3) * 2) + 5 + (as_expr(7) / 25));
所以,让我们这样做:
namespace builder {
struct Atom {
Atom(Expr* expr) : expr(expr) {}
Atom(int i) : expr(new Int(i)) {}
Expr* expr;
explicit operator Expr const*() const { return expr; }
};
template <typename T>
Atom as_expr(T&& v) { return std::forward<T>(v); }
Atom operator+(Atom a, Atom b) { return new Plus(a.expr, b.expr); }
Atom operator-(Atom a, Atom b) { return new Minus(a.expr, b.expr); }
Atom operator*(Atom a, Atom b) { return new Mul(a.expr, b.expr); }
Atom operator/(Atom a, Atom b) { return new Div(a.expr, b.expr); }
}
实时演示
活在coliru
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/ptr_container/ptr_set.hpp>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
// AST
struct Expr {
virtual ~Expr() = default;
virtual std::vector<Expr const *> children() const { return {}; }
virtual std::string identity() const = 0;
void print(int p) const {
std::cout << std::setw(p) << ' ';
std::cout << identity() << "n";
for (auto a_kid : children()) {
a_kid->print(p + 2);
}
}
protected:
Expr() { self_register(); }
private:
void self_register();
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &, unsigned) {}
};
namespace memory_management {
struct address_less {
bool operator()(Expr const& a, Expr const& b) const { return &a < &b; }
};
static boost::ptr_set<Expr, address_less> pool;
}
void ::Expr::self_register() { memory_management::pool.insert(this); }
struct Int : Expr {
std::string identity() const override { return "Int[" + std::to_string(n) + "]@"; }
Int(int nn) : n(nn) {}
private:
int const n = 0;
friend class boost::serialization::access;
Int() = default;
template <class Archive> void serialize(Archive &ar, unsigned) {
ar & boost::serialization::base_object<Expr>(*this)
& const_cast<int&>(n);
}
};
namespace Tags {
struct Mul;
struct Div;
struct Plus;
struct Minus;
template <typename T> constexpr char const* const message = "Unknown";
template <> constexpr char const* const message<Mul> = "Mul";
template <> constexpr char const* const message<Div> = "Div";
template <> constexpr char const* const message<Plus> = "Plus";
template <> constexpr char const* const message<Minus> = "Minus";
}
template <class T> struct Op2 : Expr {
std::vector<Expr const *> children() const override { return { l, r }; }
std::string identity() const override { return Tags::message<T>; }
Op2(Expr *ll, Expr *rr) : l(ll), r(rr) {}
protected:
friend class boost::serialization::access;
Op2() = default;
Expr *l = nullptr;
Expr *r = nullptr;
template <class Archive> void serialize(Archive &ar, unsigned) {
ar & boost::serialization::base_object<Expr>(*this)
& l & r;
}
};
using Mul = Op2<Tags::Mul>;
using Div = Op2<Tags::Div>;
using Plus = Op2<Tags::Plus>;
using Minus = Op2<Tags::Minus>;
namespace builder {
struct Atom {
Atom(Expr* expr) :expr(expr){}
Atom(int i) :expr(new Int(i)){}
Expr* expr;
explicit operator Expr const*() const { return expr; }
};
template <typename T>
Atom as_expr(T&& v) { return std::forward<T>(v); }
Atom operator+(Atom a, Atom b) { return new Plus(a.expr, b.expr); }
Atom operator-(Atom a, Atom b) { return new Minus(a.expr, b.expr); }
Atom operator*(Atom a, Atom b) { return new Mul(a.expr, b.expr); }
Atom operator/(Atom a, Atom b) { return new Div(a.expr, b.expr); }
}
BOOST_CLASS_EXPORT(Expr)
BOOST_CLASS_EXPORT(Int)
BOOST_CLASS_EXPORT(Mul)
BOOST_CLASS_EXPORT(Div)
BOOST_CLASS_EXPORT(Plus)
BOOST_CLASS_EXPORT(Minus)
int main() {
std::cout << std::unitbuf;
{
using builder::as_expr;
Expr const* root((as_expr(3) * 2) + 5 + (as_expr(7) / 25));
root->print(2);
std::ofstream of("arxiv");
boost::archive::text_oarchive oa(of);
oa << root;
}
std::cout << "===================n";
{
std::ifstream isf("arxiv");
boost::archive::text_iarchive is(isf);
Expr *expr = nullptr;
is >> expr;
expr->print(2);
}
memory_management::pool.clear(); // no memory leaks
}
打印
Plus
Plus
Mul
Int[3]@
Int[2]@
Int[5]@
Div
Int[7]@
Int[25]@
===================
Plus
Plus
Mul
Int[3]@
Int[2]@
Int[5]@
Div
Int[7]@
Int[25]@
- C++boost序列化多态性问题
- 将 boost 序列化对象的 asio::streambuf 表示转换为 Beast 的 DynamicBody req.body()
- 如何使用 Boost 序列化mersenne_twister_engine?
- boost::序列化中的派生类偏移量计算.有效吗?
- C++ Boost - 序列化错误 - 将"const B"作为"this"参数
- 序列化模式类似于boost::序列化
- 使用 boost::序列化代码将 *this 传递给模板函数会产生错误
- 使用 boost::序列化序列化 std::vector of unique_ptr 在 Linux 上失败
- 使用 boost::序列化将派生类指针序列化为向量时出现问题
- 跳过层次结构中的中间类,并使用boost ::序列化
- 使用Boost序列化std ::乘坐
- Boost::序列化存储结构时的堆栈溢出错误
- 2D 矢量未正确保存并加载 boost::序列化库
- 使用Boost序列化保存和检索多个对象
- 使用boost::序列化递归图结构时,如何防止堆栈溢出
- 特征矩阵+Boost::序列化/C++17
- C++Boost序列化:输入流错误
- 将Boost序列化文本存档转换为结构化格式
- Microsoft Guid guid serialization使用Boost序列化
- Boost::序列化 - 如何序列化需要数据分配但没有默认构造函数的数据结构