我的系统非常“重”(大量代码),但速度非常快,并且功能非常丰富(跨平台 C++)。我不确定你想在设计上走多远,但这是我所做的最重要的部分:
DatumState- 持有“类型”的“枚举”的类,加上本机值,这是所有原始类型之间的“联合”,包括void*. 此类与所有类型解耦,可用于任何本机/原始类型和“引用”void*类型。由于 " enum" 也有 " VALUE_OF" 和 " REF_TO" 上下文,因此此类可以呈现为“完全包含” a float(或某些原始类型),或“引用但不拥有” a float(或某些原始类型)。(我实际上有“ VALUE_OF”、“ REF_TO”和“ PTR_TO”上下文,所以我可以在逻辑上存储一个值,一个不能为空的引用,
Datum- 完全包含 a 的类DatumState,但它扩展了其接口以适应各种“知名”类型(如MyDate、MyColor、MyFileName等)。这些知名类型实际上存储在成员void*内部DatumState。但是,由于“ enum”部分DatumState具有“ VALUE_OF”和“ REF_TO”上下文,它可以表示“ pointer-to-MyDate”或“ value-of-MyDate”。
DatumStateHandle- 使用(众所周知的)类型(如MyDate, MyColor,MyFileName等)参数化的帮助模板类。这是用于Datum从众所周知的类型中提取状态的访问器。默认实现适用于大多数类,但任何具有特定访问语义的类只会覆盖其特定模板参数化/实现,用于该模板类中的一个或多个成员函数。
Macros, helper functions, and some other supporting stuffDatum- 为了简化在我的/中“添加”众所周知的类型Variant,我发现将逻辑集中到几个宏中很方便,提供一些支持功能,如运算符重载,并在我的代码中建立一些其他约定。
作为这个实现的“副作用”,我得到了很多好处,包括引用和值语义、所有类型的“null”选项以及对所有类型的异构容器的支持。
例如,您可以创建一组整数并为其编制索引:
int my_ints[10];
Datum d(my_ints, 10/*count*/);
for(long i = 0; i < d.count(); ++i)
{
d[i] = i;
}
类似地,某些数据类型由字符串或枚举索引:
MyDate my_date = MyDate::GetDateToday();
Datum d(my_date);
cout << d["DAY_OF_WEEK"] << endl;
cout << d[MyDate::DAY_OF_WEEK] << endl; // alternative
我可以存储项目集(本机)或集Datum(包装每个项目)。对于任何一种情况,我都可以递归地“解包”:
MyDate my_dates[10];
Datum d(my_dates, 10/*count*/);
for(long i = 0; i < d.count(); ++i)
{
cout << d[i][MyDate::DAY_OF_WEEK] << endl;
}
有人可能会争辩说我的 " REF_TO" 和 " VALUE_OF" 语义是矫枉过正的,但它们对于 "set-unwrapping" 是必不可少的。
我已经Variant用九种不同的设计完成了这个“”的事情,我目前是“最重的”(大多数代码),但我最喜欢的一个(几乎是最快的,对象占用空间很小),我已经弃用了其他八种设计供我使用。
我的设计的“缺点”是:
- 对象是通过
static_cast<>()a访问的void*
(类型安全且相当快,但需要间接;但是,副作用是设计支持“ null”的存储。)
Datum由于通过接口公开了众所周知的类型,因此编译时间更长(但DatumState如果您不想要众所周知的类型 API,您可以使用)。
无论您的设计如何,我都会推荐以下内容:
使用“ enum”或其他东西来告诉您“类型”,与“值”分开。(我知道您可以将它们压缩成一个“ int”或带有位包装的东西,但是随着新类型的引入,访问速度很慢并且维护起来非常棘手。)
依靠模板或其他东西来集中操作,使用特定类型(覆盖)处理的机制(假设您要处理非平凡类型)。
游戏的名称是“添加新类型时的简化维护”(或者至少对我来说是这样)。就像一篇好的学期论文一样,如果你重写、重写、重写,以保持或增加你的功能,因为你不断删除维护系统所需的代码(例如,最大限度地减少适应新类型所需的工作量)到您现有的Variant基础设施)。
祝你好运!