gpt4 book ai didi

c++ - 如果您有很多节点类型,则访问者模式

转载 作者:行者123 更新时间:2023-12-04 11:33:44 24 4
gpt4 key购买 nike

我们有什么?
我们正在开发的软件系统需要在组件之间交换大量数据。数据的结构我们称之为变量树。这些数据本质上是组件之间的接口(interface)。代表特定接口(interface)的 C++ 代码是从接口(interface)描述自动生成的。进行实际数据交换有不同的底层实现,如 OPC/UA,但大部分代码都被屏蔽了。重要的节点类型是那些存储值和值数组的节点类型,它们几乎可以为任何类型实例化。

class node { /* whatever all nodes have in common */ };

class value_node : public node { /* polymorphic access to value */ };

template<typename T>
class typed_value_node : public value_node { /* type-safe access to value */ };

// imagine pretty much the same for array_node and typed_array_node
因此,用于遍历这些树中节点的访问者基类具有接受所有整数类型(有符号和无符号)、所有浮点类型、 bool 值和字符串的函数,无论是常量节点还是非常量节点。 (我们目前计划将 enum 类型映射到 int/string 对,但对此没有一成不变的设置。)所有这些重载都存在于值和数组中。
目前,大约有 70 个重载:
class visitor {
public:
virtual ~visitor() = default;

virtual void accept( typed_value_node< char >&) = 0;
virtual void accept(const typed_value_node< char >&) = 0;


virtual void accept( typed_value_node< signed char >&) = 0;
virtual void accept(const typed_value_node< signed char >&) = 0;

...

virtual void accept( typed_value_node< signed long long>&) = 0;
virtual void accept(const typed_value_node< signed long long>&) = 0;


virtual void accept( typed_value_node<unsigned char >&) = 0;
virtual void accept(const typed_value_node<unsigned char >&) = 0;

...

virtual void accept( typed_value_node<unsigned long long>&) = 0;
virtual void accept(const typed_value_node<unsigned long long>&) = 0;


virtual void accept( typed_value_node<bool >&) = 0;
virtual void accept(const typed_value_node<bool >&) = 0;
...


// repeat for typed_array_node
};
为了能够实际处理这个,我们使用 CRTP 来制作一个访问者实现调用派生类的函数模板:
template<typename Derived>
class visitor_impl : public visitor {
public:
void accept( typed_value_node<char>& node) override
{static_cast<Derived*>(this)->do_visit(node);}
void accept(const typed_value_node<char>& node) override
{static_cast<Derived*>(this)->do_visit(node);}

// etc.
};
这使得处理某些类型的节点变得可以忍受:
class my_value_node_visitor : public visitor_impl<my_value_node_visitor> {
public:
template<typename T>
void accept(const typed_value_node<T>&) {/* I wanna see these*/}

template<typename T>
void accept(const T&) {/* I don't care about those */}
};
我们想要什么?
在电气工程繁重的应用领域开始一个新的 C++ 软件组件,我们决定使用一个编译时检查单元库(又名“维度分析库”)。单元库很棒,因为它们使用类型系统在编译时检查代码的正确性。他们通过创建几乎无限数量的类型来实现这一点,这些类型不仅对底层的内置类型(int、double、...)进行编码,而且对物理单位(质量、能量)、尺度(milli-、mega -) 和一些标签(有功/无功/视在功率,开尔文/摄氏度)。
有什么问题?
可以从接口(interface)描述中轻松生成具有正确物理单位的树节点。但是如果单元类型的值存储在树节点中,这将使我们的访问者基类需要数千个重载来接受所有使用的不同节点类型,提示开发人员在节点需要以前未使用的单元时添加新的单元,或者在一个以前没有使用过的规模。我们可以想出一些巧妙的模板滥用来在编译时从类型列表生成所有这些虚函数。然而,当产生的虚拟表大小是否会成为问题的问题出现时,我开始怀疑我们当前的方法是否真的仍然是解决问题的最佳方法。
常识说,如果你有 许多节点类型少数算法要遍历它们,您应该 使用虚函数 在节点本身。如果,OTOH,你有 多种算法少数节点类型 ,您应该使用 访客模式 .常识还说,如果两者兼而有之,那你就完蛋了。
我的感觉是,到目前为止,我们几乎无法适应少节点类型/多算法的抽屉。随着编译时单元的类型扩散,我的胆量说我们非常适合多个抽屉。简而言之:我们可能会被搞砸。
我们现在干什么?
在去年年底之前一直与 C++03 紧密相连(嵌入式世界进展缓慢),我们当然不太熟悉其他 C++ 程序员已经使用了近十年的许多工具。所以我希望我们在这里错过了一个明显的解决方案。
或者也许我们遗漏了一些不那么明显的东西?

最佳答案

以防万一有人在类似情况下多年后发现这一点,并想知道我们做了什么:
使用单位数量的节点现在来自其底层 POD 的节点(通常是 double)。这些节点还提供虚拟函数来确定数量的单位(enum)和规模(毫、千)。对于纯 POD 节点,这些默认为“无单位”和“无规模”。从它们派生的节点存储数量覆盖那些以返回适当的数据。
因此,它是通过双重分派(dispatch)(不能将整数与浮点数混淆)和需要在运行时完成的检查(是否附加了单元?)的编译时安全性的混合,从而防止了重载数量爆炸成千。
这意味着我们可以使用的单位数量目前受到在编译时列出所有单位的枚举的限制,但我们发现这在实践中是可以接受的,我们目前只使用大约 2 打单位。但是,如果这成为一个问题,我们可以通过运行时单元类型对其进行建模,使用代表基本单元的幂列表,就像编译时单元类型一样。

关于c++ - 如果您有很多节点类型,则访问者模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64175906/

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