Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 对模板化类链使用可变模板生成序列化_C++_Qt_Templates_Variadic Templates_Template Templates - Fatal编程技术网

C++ 对模板化类链使用可变模板生成序列化

C++ 对模板化类链使用可变模板生成序列化,c++,qt,templates,variadic-templates,template-templates,C++,Qt,Templates,Variadic Templates,Template Templates,我有一个急切的项目,在这个项目中,我试图通过编写以下内容尽可能轻松地实现结构的序列化: class Data { const QString& string(); void setString(QString string); ... }; const QString stringName() { return "string"; } template class <class Invokee, typename ContentType, const QString

我有一个急切的项目,在这个项目中,我试图通过编写以下内容尽可能轻松地实现结构的序列化:

class Data {
  const QString& string();
  void setString(QString string);
  ...
};

const QString stringName() { return "string"; }

template class <class Invokee, typename ContentType, const QString(*NameFunction)(), const ContentType& (Invokee::* Getter)() const> Field;

void serialize() {
  Data data{...};
  QJsonObject serialized 
    = serialize<Data, Field1, Field2, ...>;
}

我在琢磨我做错了什么,或者这是否可能。

这是可能的,但正确的工具不是模板。要深入挖掘类型参数,就像您希望提取
字段
的所有模板参数一样,您需要使用部分模板专门化

由于在C++17中,这一切都可以简化一点,因此我将把它分为两部分:

C++11解决方案 首先,简化
字段
,使其成为常规模板:

模板<
类被调用者,
typename ContentType,
常量QString(*NameFunction)(),
const ContentType&(被调用方::*Getter)()const>
结构域;
函数模板不支持部分模板专门化,因此下一步是生成虚拟结构。实际上,您可以从字段中推断出我们需要的所有内容,因此字段是唯一必要的类型参数:

模板
结构对象序列化程序;
现在,它变得有趣了。将
字段的每个参数
转换为一个参数包,并展开它们以获得专用类型:

模板<
类型名被调用者,
类型名。。。内容类型,
常量QString(*…名称函数)(),
const ContentType&(被调用方::*…Getter)()const>
结构对象序列化程序
{ /* ... */ }
在这个怪物模板的主体中,使用call操作符来定义实际的函数。此函数的主体应将
object
的属性设置为提取到字段的值

由于无法将参数包实际扩展为语句,因此必须使用技巧。我将使用来自的技巧将语句隐藏在
std::initializer\u list
中,以这样一种方式,除了赋值之外的所有语句都是常量折叠的:

consteprvoid操作符()(QJsonObject&object,constinvokee&Invokee){
无效(标准::初始值设定项列表){
(void(object[NameFunction()]=(invokee.*Getter)(),nullptr)。。。
});
}
然后你可以用一个方便的函数来隐藏这个结构。我将它重新排列了一点,因此
Invokee
是从参数中推导出来的:

template <typename... Fields, typename Invokee>
void serializeToObject(QJsonObject& object, const Invokee& invokee) {
    ObjectSerializer<Fields...>{}(object, invokee);
}
但当你进行部分专业化时,你仍然可以推断出所有这些信息。您还可以使用折叠表达式简化“展开赋值”技巧:

模板<
类型名被调用者,
类型名。。。内容类型,
常量QString(*…名称函数)(),
const ContentType&(被调用方::*…Getter)()const>
结构对象序列化程序{
模板
constexpr void操作符()(QJsonObject和object,const Invokee和Invokee){
(void(object[NameFunction()]=(invokee.*Getter)(),…);
}
};
所以现在,
serializeToObject
每个字段只需要两个模板参数,而不是4个:

serializeToObject<
领域
>(对象、数据);
演示:

作品在叮当声中找到。但是哎哟,这导致gcc爆炸():

RTL过程中的
:展开
:在函数“void serializeToObject(QJsonObject&,const Invokee&)[带字段={Field};Invokee=Data]”中:
:34:34:内部编译器错误:分段错误
34 | ObjectSerializer{}(object,invokee);
|     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
请提交完整的bug报告,
(我将很快发送完整的错误报告)

简化的C++17解决方案(使用gcc解决方案) 这个gcc错误很糟糕,但可以通过使用不同的类型序列化每个字段来解决:

模板
结构字段序列化器;
模板
结构字段序列化程序{
void操作符(){
对象[NameFunction()]=(invokee.*Getter)();
}  
};
模板
void serializeToObject(QJsonObject和object、const Invokee和Invokee){
(void(FieldSerializer{}(object,invokee)),…);
}
这会生成比您希望的更多的类型,但不像递归解决方案那样多的类型

演示:



编辑:我对这个答案做了几次修改,首先添加了C++17简化,然后切换到一个非递归的解决方案,希望有更好的编译时间。

[OT]:返回const对象是无用的(甚至更糟,因为它禁止一些优化)。您可能会对
BOOST\u HANA\u DEFINE\u STRUCT
或类似的解决方案感兴趣。(当您使用Qt时,可能会有所帮助)。为什么不在常量字段命名函数中使用现成的序列化库,例如?@Jarod42?你有这方面的文章吗?“我很想把它读出来。”弗拉基米尔贝索夫:嗯,额外的一个图书馆永远是一个需要跟踪的地方。但我可以看到如果这不是问题的话,人们会如何使用它。太棒了。我想给你买杯啤酒好的先生,这打开了很多其他的模版把戏,我一定要试试。我认为编译器可能会为此生成大量的中间结构。对,所有递归解决方案都涉及对编译有相当负面影响的TypeSploion。如果您可以只扩展参数包或使用折叠表达式,那就更好了,但我不认为您可以扩展部分专门化。但我会是猴子的叔叔:。。。叮当作响。这是我的错!所以woohoo,我开始提交一个bug报告。好的,我修改了解决方案,添加了一个非递归的解决方案,以及一个解决我发现的gcc bug的方法。
void tryOut() {
  Data data;
  data.setString("testString");
  QJsonObject object{};

  serializeToObject
      <
      Data,
      Field<Data, QString, stringName, &Data::string>
      >
  (object, testClass);
}
candidate template ignored: couldn't infer template argument 'NameFunction'
void serializeToObject(QJsonObject& object, Invokee& invokee) {
template <typename... Fields, typename Invokee>
void serializeToObject(QJsonObject& object, const Invokee& invokee) {
    ObjectSerializer<Fields...>{}(object, invokee);
}
during RTL pass: expand
<source>: In function 'void serializeToObject(QJsonObject&, const Invokee&) [with Fields = {Field<stringName, &Data::string>}; Invokee = Data]':
<source>:34:34: internal compiler error: Segmentation fault
   34 |     ObjectSerializer<Fields...>{}(object, invokee);
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
Please submit a full bug report,