如何在 C++ 中存储变体数据
我正在创建一个存储有关特定数据源的元数据的类。 元数据采用树状结构,与 XML 的结构非常相似。 元数据值可以是整数、小数或字符串值。
我很好奇 C++ 中是否有一种好方法来存储这种情况的变体数据。 我希望变体使用标准库,因此我避免使用可用的 COM、Ole 和 SQL VARIANT 类型。
我当前的解决方案看起来像这样:
enum MetaValueType
{
MetaChar,
MetaString,
MetaShort,
MetaInt,
MetaFloat,
MetaDouble
};
union MetaUnion
{
char cValue;
short sValue;
int iValue;
float fValue;
double dValue;
};
class MetaValue
{
...
private:
MetaValueType ValueType;
std::string StringValue;
MetaUnion VariantValue;
};
MetaValue 类具有各种 Get 函数,用于获取当前存储的变体值,但它最终会使每个值的查询成为一大块 if/else if 语句,以找出我要的值寻找。
我还探索过将值仅存储为字符串,并执行转换以获取不同的变体类型,但据我所知,这会导致一堆内部字符串解析和错误处理,这并不漂亮,打开解决了浮点值的一大堆精度和数据丢失问题,并且仍然不能消除上述 if/else if 查询问题。
有没有人使用标准库实现或看到过更干净的用于 C++ 变体数据类型的东西?
I'm in the process of creating a class that stores metadata about a particular data source. The metadata is structured in a tree, very similar to how XML is structured. The metadata values can be integer, decimal, or string values.
I'm curious if there is a good way in C++ to store variant data for a situation like this. I'd like for the variant to use standard libraries, so I'm avoiding the COM, Ole, and SQL VARIANT types that are available.
My current solution looks something like this:
enum MetaValueType
{
MetaChar,
MetaString,
MetaShort,
MetaInt,
MetaFloat,
MetaDouble
};
union MetaUnion
{
char cValue;
short sValue;
int iValue;
float fValue;
double dValue;
};
class MetaValue
{
...
private:
MetaValueType ValueType;
std::string StringValue;
MetaUnion VariantValue;
};
The MetaValue class has various Get functions for obtaining the currently stored variant value, but it ends up making every query for a value a big block of if/else if statements to figure out which value I'm looking for.
I've also explored storing the value as only a string, and performing conversions to get different variant types out, but as far as I've seen this leads to a bunch of internal string parsing and error handling which isn't pretty, opens up a big old can of precision and data loss issues with floating point values, and still doesn't eliminate the query if/else if issue stated above.
Has anybody implemented or seen something that's cleaner to use for a C++ variant data type using standard libraries?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
从 C++17 开始,有
std::variant
。如果您还不能使用它,您可能需要 Boost.Variant 。
std::any< 提供了类似但不同的多态性建模类型/code>
(以及 C++17 之前的版本,
作为附加指针,您可以查找“类型擦除”。
As of C++17, there’s
std::variant
.If you can’t use that yet, you might want Boost.Variant. A similar, but distinct, type for modelling polymorphism is provided by
std::any
(and, pre-C++17, Boost.Any).Just as an additional pointer, you can look for “type erasure”.
虽然 Konrad 的答案(使用现有的标准化解决方案)当然比编写自己的容易出现错误的版本更好,但 boost 变体有一些开销,特别是在复制构造和内存方面。
常见的定制方法是以下修改后的工厂模式:
Derived
类实现该接口。create
函数创建一个工厂类,其签名为:template; Base * Factory::create ();
这会在堆上内部创建一个
Derived<_T>
对象,并重新调整动态转换指针。 针对您想要实施的每个类进行专门化。最后,定义一个
Variant
包装器,其中包含此Base *
指针并定义模板 get 和 set 函数。 诸如getType()
、isEmpty()
、赋值和相等运算符等实用函数都可以在这里适当实现。根据实用函数和工厂实现,受支持的类将需要支持一些基本函数,例如赋值或复制构造。
While Konrad's answer (using an existing standardized solution) is certainly preferable to writing your own bug-prone version, the boost variant has some overheads, especially in copy construction and memory.
A common customized approach is the following modified Factory Pattern:
Derived
class.create
function with signature:template <typename _T> Base * Factory::create ();
This internally creates a
Derived<_T>
object on the heap, and retuns a dynamic cast pointer. Specialize this for each class you want implemented.Finally, define a
Variant
wrapper that contains thisBase *
pointer and defines template get and set functions. Utility functions likegetType()
,isEmpty()
, assignment and equality operators, etc can be appropriately implemented here.Depending on the utility functions and the factory implementation, supported classes will need to support some basic functions like assignment or copy construction.
您还可以采用更 C 风格的解决方案,该解决方案将在您的系统上有一个 double 大小的 void* ,以及您正在使用的类型的枚举。 它相当干净,但对于那些对系统的原始字节感到完全满意的人来说绝对是一个解决方案。
You can also go down to a more C-ish solution, which would have a void* the size of a double on your system, plus an enum for which type you're using. It's reasonably clean, but definitely a solution for someone who feels wholly comfortable with the raw bytes of the system.
C++17 现在有
std::variant
这正是您正在寻找的。std::variant
C++17 now has
std::variant
which is exactly what you're looking for.std::variant
尽管这个问题已经回答了很长时间,但为了记录,我想提一下 Qt 库中的 QVariant 也执行此操作。
Although the question had been answered for a long time, for the record I would like to mention that QVariant in the Qt libraries also does this.