如何在 C++ 中存储变体数据

发布于 2024-07-07 14:04:59 字数 822 浏览 11 评论 0原文

我正在创建一个存储有关特定数据源的元数据的类。 元数据采用树状结构,与 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

呆橘 2024-07-14 14:04:59

从 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”.

佼人 2024-07-14 14:04:59

虽然 Konrad 的答案(使用现有的标准化解决方案)当然比编写自己的容易出现错误的版本更好,但 boost 变体有一些开销,特别是在复制构造和内存方面。

常见的定制方法是以下修改后的工厂模式:

  1. 为通用对象创建一个基本接口,该接口也封装了对象类型(作为枚举),或使用“typeid”(首选)。
  2. 现在使用模板 Derived 类实现该接口。
  3. 使用模板化的 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:

  1. Create a Base interface for a generic object that also encapsulates the object type (either as an enum), or using 'typeid' (preferable).
  2. Now implement the interface using a template Derived class.
  3. Create a factory class with a templateized 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 this Base * pointer and defines template get and set functions. Utility functions like getType(), 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.

廻憶裏菂餘溫 2024-07-14 14:04:59

您还可以采用更 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.

欲拥i 2024-07-14 14:04:59

C++17 现在有 std::variant 这正是您正在寻找的。

std::variant

类模板 std::variant 表示类型安全的联合。 一个
std::variant 的实例在任何给定时间要么持有值 1
其替代类型,或者在错误的情况下 - 无值(此
状态很难实现,请参阅 valueless_by_exception)。

与联合一样,如果变体持有某个对象类型 T 的值,则
T 的对象表示直接在对象内分配
变体本身的表示。 不允许变体
分配额外的(动态)内存。

C++17 now has std::variant which is exactly what you're looking for.

std::variant

The class template std::variant represents a type-safe union. An
instance of std::variant at any given time either holds a value of one
of its alternative types, or in the case of error - no value (this
state is hard to achieve, see valueless_by_exception).

As with unions, if a variant holds a value of some object type T, the
object representation of T is allocated directly within the object
representation of the variant itself. Variant is not allowed to
allocate additional (dynamic) memory.

久随 2024-07-14 14:04:59

尽管这个问题已经回答了很长时间,但为了记录,我想提一下 Qt 库中的 QVariant 也执行此操作。

因为 C++ 禁止联合体包含非默认类型
构造函数或析构函数,最有趣的 Qt 类不能是
用于工会。 如果没有 QVariant,这将是一个问题
QObject::property() 以及用于数据库工作等。

QVariant 对象一次保存单个 type() 的单个值。
(某些 type() 是多值的,例如字符串列表。)您可以
找出变体所持有的类型 T,将其转换为不同的类型
使用convert()输入,使用toT()函数之一获取其值
(例如,toSize())并检查类型是否可以转换为
使用 canConvert() 的特定类型。

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.

Because C++ forbids unions from including types that have non-default
constructors or destructors, most interesting Qt classes cannot be
used in unions. Without QVariant, this would be a problem for
QObject::property() and for database work, etc.

A QVariant object holds a single value of a single type() at a time.
(Some type()s are multi-valued, for example a string list.) You can
find out what type, T, the variant holds, convert it to a different
type using convert(), get its value using one of the toT() functions
(e.g., toSize()) and check whether the type can be converted to a
particular type using canConvert().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文