是否有针对新成员的编译器强化实施完整性的模式
Is there a pattern for compiler-enforced implementation completeness regarding a new member?
想象一个较大的项目,其中包含一些参数结构:
struct pars {
int foo;
};
使用此结构作为参数,实现了其他功能,例如:
// (de)serialization into different formats
static pars FromString(string const &text);
static string ToString(pars const &data);
static pars FromFile(string const &filename);
// [...]
// comparison / calculation / verification
static bool equals(pars l, pars r);
static pars average(pars a, pars b);
static bool isValid(pars p);
// [...]
// you-name-it
现在想象需要将新成员添加到该结构:
struct pars {
int foo;
int bar; // new member
};
是否有设计模式可以打破构建或发出警告,直到适应所有必要代码?
?示例:
- 如果我要将
int foo
更改为string foo
,我不会错过任何需要更改的代码行。 - 如果
int foo
需要更改为unsigned int foo
,我可以将foo
重命名为foo_u
,并将编译器指向我的适应性。
一个部分解决方案是使成员private
并仅从构造函数中进行设置,该构建器必须用所有参数调用:
pars::pars(int _foo, int _bar)
: foo(_foo), bar(_bar)
{ }
这确保了PAR的正确创建,但不能使用用法 - 因此,这会在FromString()
中捕获丢失的适应,但在ToString()
中却没有。
单元测试只会在测试期间揭示此类问题(我正在寻找编译时间方法),也只有(DE)序列化部分,而不是在任何地方都考虑到新的bar
(在比较/计算中/验证/...功能)。
将流操作从流的源或目的地解除。
一个非常简单的示例:
#include <sstream>
#include <fstream>
struct pars
{
int foo;
int bar;
static constexpr auto current_version = 2;
};
std::istream &deserialise(std::istream &is, pars &model)
{
int version;
is >> version;
is >> model.foo;
if (version > 1) {
is >> model.bar;
}
return is;
}
std::ostream &serialise(std::ostream &os, const pars &model)
{
os << model.current_version << " ";
os << model.foo << " ";
// a version 2 addition
os << model.bar<< " ";
return os;
}
static pars FromString(std::string const &text)
{
std::istringstream iss(text);
auto result = pars();
deserialise(iss, result);
return result;
}
static std::string ToString(pars const &data)
{
std::ostringstream oss;
serialise(oss, data);
return oss.str();
}
static pars FromFile(std::string const &filename)
{
auto file = std::ifstream(filename);
auto result = pars();
deserialise(file, result);
return result;
}
还可以查看:
boost.serialization http://www.boost.org/doc/libs/1_64_0/libs/serialization/doc/index.html
谷物https://github.com/uscilab/cereal
等。
强制执行此操作的模式。
选择一个名称,例如members_of
。使用ADL和标签,使members_of(tag<T>)
返回整体恒定成员指针的元组,向T
的成员。
这必须写一次。然后可以使用许多斑点。
我会在14岁时将其写入C 17中,而更早的详细信息。
template<class T>struct tag_t{constexpr tag_t(){}};
template<class T>constexpr tag_t<t> tag{};
template<auto X>using val_t=std::integral_constant<decltype(X), X>;
template<auto X>constexpr val_k<X> val{};
struct pars {
int foo;
friend constexpr auto members_of( tag_t<pars> ){
return std::make_tuple( val<&pars::foo> );
}
};
当您添加成员时,还必须将其添加到朋友members_of
。
template<class...Fs>
struct overload:Fs...{
using Fs::operator()...;
overload(Fs...fs):Fs(std::move(fs))... {}
};
overload
让您超载lambdas。
终于写一个foreach_tuple_element。
static pars FromString(string const &text){
pars retval;
foreach_tuple_element( members_of(tag<pars>), overload{
[&](val_t<&pars::foo>){
// code to handle pars.foo
}
});
return retval;
}
当您向pars
和members_of
添加新成员bar
时,上述代码会断开,因为foreach找不到val_t<&pars::bar>
的过载。
static pars FromString(string const &text){
pars retval;
foreach_tuple_element( members_of(tag<pars>), overload{
[&](val_t<&pars::foo>){
// code to handle pars.foo
},
[&](val_t<&pars::bar>){
// code to handle pars.bar
}
});
return retval;
}
现在它将编译。
针对序列化/次要化,您需要两个方法(一个Arg的类型在内外或外说),而字符串for/from只是一个特殊情况序列化/次要化。
template<class A, class Self,
std::enable_if_t<std::is_same<pars, std::decay_t<Self>>{}, int> =0
>
friend void Archive(A& a, Self& self) {
ArchiveBlock(a, archive_tag("pars"), 3, [&]{
Archive(a, self.foo);
Archive(a, self.bar);
});
}
这是统一序列化/挑选方法(没有上述成员指针)的示例。您在输出流和primitive const&
上覆盖Archive
,在输入流和primitive&
上覆盖。
对于几乎所有其他所有内容,您都会使用共同的结构来读取和从档案中写作。这使您的输入结构保持相同。
ArchiveBlock( Archive&, tag, tag version, lambda )
将lambda
包裹在您拥有的任何归档块结构中。例如,您的档案块可能在其标题中包含长度信息,从而使较早的Deserializer可以在最后跳过添加的数据。它也会读写块;在写作时,它会在编写身体之前写出块标头和其他任何内容(可能会跟踪长度并备份到记录长度后,一旦他们知道了)。在阅读时,它将确保该标签存在(并选择您选择丢失的标签;跳过?),如果您想支持较新的读者阅读更新的作家写的内容。
在您需要保持代码与数据保持一致的更一般情况下,此答案可能会解决问题。序列化和挑选化是非常特殊的情况,因为与大多数C 代码不同,您必须将未来的二进制布局 exthert 的二进制布局。就像写库界面一样;需要更多的护理。
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 为什么在保护模式下继承升级不起作用
- 如何在全屏模式下(在OpenGL中)使背景透明
- 为什么使用__LINE_的代码在发布模式下在MSVC下编译,而不是在调试模式下
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 此模式的C++RegEx
- avrogencpp能为模式中的每种类型生成单独的头文件吗
- 使用可变模板的Broadcaster/Listener模式
- c++方法参数只能在linux的发布模式下自行更改
- 资源管理设计模式
- 使用 mod_gsoap 部署服务时,如何在 Gsoap 中更改 soap 上下文的模式?
- C++ 无法在字符数组中使用 for 循环打印字母模式
- 小字符串优化(调试与发布模式)
- 可视化C++:发布模式的运行时库作为'Multi-threaded Debug DLL'
- 如何设计具有不同类型的通知和观察器的观察者模式?
- 在C++的一系列数字中查找重复模式
- 是否允许使用带有"w+"模式的 freopen 进行标准设置?
- C++ 使用存储在动态数组中的文本文件中的数据查找模式
- С++ wxWidgets:代码架构,设计原则和模式
- 是否有针对新成员的编译器强化实施完整性的模式