gpt4 book ai didi

serialize with Cereal in MFC(在MFC中使用Cereal进行序列化)

转载 作者:bug小助手 更新时间:2023-10-25 19:42:19 30 4
gpt4 key购买 nike



I'm trying to use cereal (C++ serialization library) in the MFC project.

我正尝试在MFC项目中使用GREAL(C++序列化库)。


It works fine if I add an "internal serialize function" inside of the custom struct that I want to serialize.
But, if I separately define "external serialize function" outside of the struct, then it gives a compilation error:
"cereal could not find any output serialization functions for the provided type and archive combination."

如果我在想要序列化的定制结构中添加一个“内部序列化函数”,它就可以很好地工作。但是,如果我在结构之外单独定义“外部序列化函数”,则会给出一个编译错误:“GREAL无法为所提供的类型和归档组合找到任何输出序列化函数。”


So, it seems the external serialize functions that I defined are not found correctly, and the documentation says:

因此,似乎没有正确找到我定义的外部序列化函数,文档中写道:



External serialization functions should be placed either in the same namespace as the types they serialize or in the cereal namespace so that the compiler can find them properly.



But, my custom struct and the external serialize functions are commonly defined in the project's View class header file:

但是,我的定制结构和外部序列化函数通常在项目的View类头文件中定义:


class CmyprojectView : public CView
{
// other declarations...

// Custom struct that I want to serialize.
struct MyStruct { ... }

// External serialize function
template <class Archive>
void serialize(Archive & ar, CmyprojectView::MyStruct & s){
ar(
CEREAL_NVP(s.x),
CEREAL_NVP(s.y),
CEREAL_NVP(s.z)
);
}
}

Any advice on why my serialize functions are not found?

对于为什么找不到我的序列化函数有什么建议吗?




Thanks to @IInspectable's comments, I decided to let the custom struct (to be serialized) stay in the View class header file (CmyprojectView.h), and move the "external serialize function"(a term coined by cereal library) to another header file, wrapped in cereal namespace (it also works without the namespace, though).

多亏了@IInspectable的注释,我决定让定制结构(要序列化)保留在View类的头文件(Cmyproject tView.h)中,并将“外部序列化函数”(一个术语,由谷物库创造)移到另一个用谷类名称空间包装的头文件中(不过,它也可以在没有名称空间的情况下工作)。


One remaining question is, even if the struct to be serialized is declared as private, the external serialize function can access it - how is this possible?

还有一个问题是,即使要序列化的结构被声明为私有的,外部序列化函数也可以访问它--这怎么可能呢?


Any additional advice would be appreciated!

如有其他建议,我们将不胜感激!


P.S. When using cereal, don't forget to use the RAII concept, i.e. wrap it with { } such as {cereal::JSONOutputArchive oarchive(ss); oarchive(s);}, although it's not shown in IInspectable's sample code since the main function's scope is enough and also for brevity I guess.

附注:当使用GREAGE时,不要忘记使用RAII概念,也就是用{}包装它,比如:{graal::JSONOutputArchive oarchive(Ss);oarchive(S);},尽管它没有显示在IInspectable的样例代码中,因为我猜main函数的作用域已经足够了,而且为了简单起见。


更多回答

There is no convincing reason why the serialize function template must be a class member. Make it a free function instead. If it needs to access CmyprojectView's internal data, make it a friend. I don't know the cereal library, but I would guess that serialize can be a hidden friend, too.

没有令人信服的理由说明序列化函数模板必须是类成员。改为将其设置为免费功能。如果它需要访问CmyprojectView的内部数据,就让它成为朋友。我不知道谷类食品图书馆,但我猜Serize也可以是一个隐藏的朋友。

@IInspectable Thank you for your advice :) I also think creating another header file containing the custom struct and serialize functions would be better, too (not only does it compile well, but also it seems better design). Still, I was curious why the above code does not compile, but maybe I should read about the 'hidden friend' as you advised. Thanks!

@IInspectable感谢您的建议:)我还认为创建另一个包含自定义结构和序列化函数的头文件也会更好(它不仅编译良好,而且看起来设计也更好)。尽管如此,我还是很好奇为什么上面的代码不能编译,但也许我应该像你建议的那样读一读《隐藏的朋友》。谢谢!

The above does not compile because the serialize function template is a class member of the (unrelated) CmyprojectView class. This class is unknown to the cereal library, so it cannot find the serialize function template (and even if it could, it would have no way of knowing where to get a CmyprojectView instance to call the function on). The library will consider either a class member of the class to be serialized or a free function.

以上代码无法编译,因为Serialize函数模板是(不相关的)Cmyproject tView类的类成员。这个类对于GREAL库是未知的,因此它找不到序列化函数模板(即使它可以,它也无法知道从哪里获得Cmyproject tView实例来调用该函数)。该库将考虑要序列化的类的类成员或自由函数。

优秀答案推荐

Intro


The cereal library is a flexible serialization framework. One of its features is extensibility, making its algorithms readily available to user-defined types. Extensibility support is generously provided by repurposing C++'s overload resolution rules.

谷物库是一个灵活的序列化框架。它的特征之一是可扩展性,使其算法易于用于用户定义的类型。通过重新调整C++的S重载解析规则的用途,慷慨地提供了可扩展性支持。


Problem Statement


The code in question fails to play by those rules.

有问题的代码未能遵守这些规则。


Before diving in too deep, here's a minimal version of the code that reproduces the issue1:

在深入研究之前,下面是重现该问题的代码的最小版本1:


#include <cereal/archives/json.hpp>

#include <sstream>

struct CmyprojectView {
struct MyStruct {
float x;
};

template <class Archive>
void serialize(Archive& ar, CmyprojectView::MyStruct& s) {
ar(CEREAL_NVP(s.x));
}
};

int main() {
auto s { CmyprojectView::MyStruct() };

std::stringstream ss;
cereal::JSONOutputArchive oarchive(ss);
oarchive(s);
}

Dumping the above into a newly created console application project (and setting up the cereal library) produces the following compiler diagnostic:

将上述代码转储到新创建的控制台应用程序项目中(并设置谷物库)会产生以下编译器诊断:



error C2338: static_assert failed: 'cereal could not find any output serialization functions for the provided type and archive combination. [...]



The diagnostic is spot-on but doesn't otherwise elucidate where the compiler went looking for a serialization function. Thankfully, the documentation covers the rules for "external serialization":

诊断是准确的,但不会以其他方式说明编译器去哪里寻找序列化函数。值得庆幸的是,文档涵盖了“外部序列化”的规则:



External serialization functions should be placed either in the same namespace as the types they serialize or in the cereal namespace so that the compiler can find them correctly.



This is a bit hand-wavy2 but gets the main point across: If the serialize() function template is not a class member of the class being serialized it needs to be a free function (template) that lives in one of two namespaces. In the sample above, however, the serialize() function template is a class member of an unrelated class.

这是一个有点手动的2,但得到了要点:如果serialize()函数模板不是被序列化的类的类成员,它需要是一个自由函数(模板),存在于两个名称空间之一。然而,在上面的示例中,serialize()函数模板是一个不相关类的类成员。


Speculation


I suppose part of the confusion here is that C++ uses the same token (::) to delineate class and namespace hierarchy boundaries. CmyprojectView::serialize could refer to either a free function (template) in the CmyprojectView namespace, or a class member of the CmyprojectView class. In the sample above it is the latter, and cereal won't consider it as an external serialization function for the MyStruct class (whose fully qualified name is ::CmyprojectView::MyStruct).

我认为这里的部分混淆是因为C++使用相同的标记(::)来描述类和命名空间层次结构边界。SERIALIZE可以引用Cmyproject tView命名空间中的自由函数(模板),也可以引用Cmyproject tView类的类成员。在上面的示例中,它是后者,并且GREAL不会将其视为MyStruct类(其完全限定名为::Cmyproject tView::MyStruct)的外部序列化函数。


Solution


To resolve the issue, the serialize() function template needs to be either a free function template in the same namespace that MyStruct lives in, or a class member of the MyStruct class.

要解决该问题,Serialize()函数模板需要是MyStruct所在的同一命名空间中的自由函数模板,或者是MyStruct类的类成员。


Establishing the first option is as simple as slapping the friend keyword onto the function template definition:

建立第一个选项非常简单,只需将Friend关键字添加到函数模板定义中:


template <class Archive>
friend void serialize(Archive& ar, CmyprojectView::MyStruct& s) {
ar(CEREAL_NVP(s.x));
}

This accomplishes two things:

这可以完成两件事:



  1. It grants serialize() access to the private parts of the enclosing class (CmyprojectView).

  2. Crucially, it also turns what used to be a class member into a free function (template)3.


With just this change, things will (magically4) start to compile. While there is nothing technically wrong with this code I will say that it is difficult to read than it needs to be. A solution that's easier to comprehend is to make serialize() a class member of MyStuct. This is what cereal calls "internal serialization":

只需进行这一更改,就可以(神奇地)开始编译。虽然这段代码在技术上没有任何错误,但我要说的是,它比实际需要的更难阅读。一种更容易理解的解决方案是使Serialize()成为MyStuct的类成员。这就是谷类食品所说的“内部序列化”:


struct MyStruct {
float x;

template <class Archive>
void serialize(Archive& ar) {
ar(CEREAL_NVP(x));
}
};

This moves the serialization code into the class it is operating on, making it easy to discover. The code will also continue to work regardless of whether MyStruct is a nested class, or lives in an arbitrary namespace.

这会将序列化代码移到它正在操作的类中,使其更容易被发现。无论MyStruct是嵌套类还是位于任意命名空间中,代码都将继续工作。


The only wrinkle with this design is that the serialize() function template needs to be publicly visible, making it look like it was part of the class' regular interface as opposed to an artifact of the serialization framework.

这种设计的唯一问题是Serialize()函数模板需要公开可见,使其看起来像是类的常规接口的一部分,而不是序列化框架的构件。


This can be addressed by making cereal::access a friend:

这可以通过使谷类食品::访问朋友:


struct MyStruct {
float x;

private:
friend cereal::access;

template <class Archive>
void serialize(Archive& ar) {
ar(CEREAL_NVP(x));
}
};

That's better but feels a bit clumsy still. serialize() is a class member of MyStruct, and shows up in documentation or code completion hints for this class. We can fix this by going full circle to the first solution above, and re-apply the hidden friend idiom. Except, this time around the implementation goes into the MyStruct class:

这好多了,但感觉还是有点笨拙。Serialize()是MyStruct的一个类成员,出现在该类的文档或代码完成提示中。我们可以通过转到上面的第一个解决方案来解决这个问题,并重新应用隐藏的朋友习语。只是,这一次实现进入了MyStruct类:


struct MyStruct {
private:
float x;

template <class Archive>
friend void serialize(Archive& ar, MyStruct& s) {
ar(CEREAL_NVP(s.x));
}
};

Nothing changed concerning readability: This is still a challenge to comprehend. serialize() looks a lot like a class member, but isn't.

可读性方面没有什么变化:这仍然是一个难以理解的挑战。Serialize()看起来很像类成员,但不是。


There are, however, several benefits to this:

然而,这样做有几个好处:



  • Unlike a non-friend free function, serialize() has full access to all internal data, public, protected, and private.

  • serialize() is not a class member and doesn't show up in documentation or code completion hints.

  • The serialize() function template "follows" the class around. If MyStruct is nested inside a class, the function template will show up in the outer class' namespace. If MyStruct is moved to an arbitrary namespace, the function template will show up in that namespace.

  • It is tough to call serialize() inadvertently.

  • The class and its corresponding serialization infrastructure are spelled out in close proximity.


Conclusion


All proposed solutions solve the issue at hand. There are trade-offs to be made, and the choice of design is primarily down to personal preference.

所有提出的解决方案都解决了手头的问题。这需要权衡取舍,设计的选择主要取决于个人喜好。




1 MFC types have been stripped. The issue is unrelated to the MFC, and the solution(s) work with or without the MFC getting involved.

已剥离%1 MFC类型。这个问题与MFC无关,解决方案(S)在MFC介入或不介入的情况下都有效。


2 I don't know the exact semantics of "should", nor why the global namespace is (allegedly) not considered.

2我不知道“应该”的确切语义,也不知道为什么(据称)不考虑全局命名空间。


3 Into some namespace. I'm confident that the rules that govern this are well-defined and were implemented in good faith. Honestly, though, life is too short to follow down that rabbit hole.

3转换为某个名称空间。我相信,管理这一点的规则是明确的,并且是真诚地实施的。不过,老实说,生命太短暂了,不能追随那个兔子洞。


4 See The Power of Hidden Friends in C++.

4参见C++中隐藏朋友的力量。


更多回答

What a wonderful answer, thanks a ton! Your speculation is right, I think I didn't clearly differentiate the two concepts(namespace and class). Please let me ask some questions: 1) You said "If MyStruct is nested inside a class, the function template will show up in the outer class' namespace." - what does it mean "outer class's namespace"? It's a bit confusing because the two words "class" and "namespace" are used at the same time.

多么精彩的回答,非常感谢!你的猜测是对的,我想我没有明确区分这两个概念(命名空间和类)。请让我问一些问题:1)你说“如果MyStruct嵌套在类中,函数模板将显示在外部类的命名空间中。“-“外部类的命名空间”是什么意思?这有点令人困惑,因为同时使用了“类”和“命名空间”这两个词。

2) You said making friend changes the function to be a 'free function'. Could you elaborate more on it? Is it automatically done internally by the language? 3) How the external serialize function can access the struct to be serialized, even if the struct is declared as private? (As I asked in the modified question). Please let me know if you think it would be better to post these as a separate question. Thank you!

2)你说交友把功能变成了‘免费功能’。你能详细说明一下吗?它是由语言在内部自动完成的吗?3)外部序列化函数如何访问要序列化的结构,即使该结构被声明为私有的?(正如我在经修改的质询中所问)。如果您认为将这些作为单独的问题发布会更好,请让我知道。谢谢!

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com