Вопрос

Я нахожусь в процессе создания класса, в котором хранятся метаданные о конкретном источнике данных.Метаданные структурированы в виде дерева, очень похожего на то, как структурирован XML.Значения метаданных могут быть целочисленными, десятичными или строковыми значениями.

Мне любопытно, есть ли в C ++ хороший способ хранить вариантные данные для подобной ситуации.Я бы хотел, чтобы в этом варианте использовались стандартные библиотеки, поэтому я избегаю доступных типов вариантов COM, Ole и SQL.

Мое текущее решение выглядит примерно так:

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 ++, используя стандартные библиотеки?

Это было полезно?

Решение

Начиная с C ++ 17, существует std::variant.

Если вы пока не можете этим воспользоваться, возможно, вам захочется Наддув.Вариант.Аналогичный, но отличный тип для моделирования полиморфизма предоставляется std::any (и, до C++17, Повышение.Любое).

Просто в качестве дополнительного указателя вы можете поискать “удаление типа”.

Другие советы

Хотя ответ Конрада (с использованием существующего стандартизированного решения), безусловно, предпочтительнее написания вашей собственной версии, подверженной ошибкам, вариант boost имеет некоторые накладные расходы, особенно при создании копии и памяти.

Распространенным индивидуальным подходом является следующий модифицированный Заводской шаблон:

  1. Создайте базовый интерфейс для универсального объекта, который также инкапсулирует тип объекта (либо в виде перечисления), либо используя 'typeid' (предпочтительнее).
  2. Теперь реализуйте интерфейс с помощью шаблона Derived класс.
  3. Создайте фабричный класс с шаблоном . create функция с подписью:

template <typename _T> Base * Factory::create ();

Это внутренне создает Derived<_T> объект в куче и повторно запускает динамический указатель приведения.Специализируйте это для каждого класса, который вы хотите реализовать.

Наконец, определите Variant оболочка, которая содержит это Base * указатель и определяет функции получения и установки шаблонов.Служебные функции, такие как getType(), isEmpty(), операторы присваивания и равенства и т.д. Могут быть соответствующим образом реализованы здесь.

В зависимости от служебных функций и заводской реализации поддерживаемые классы должны будут поддерживать некоторые базовые функции, такие как присваивание или создание копии.

Вы также можете перейти к более C-образному решению, которое имело бы void * размером с double в вашей системе, плюс перечисление, для какого типа вы используете.Это достаточно чистое, но определенно решение для тех, кто чувствует себя полностью комфортно с необработанными байтами системы.

C ++ 17 теперь имеет std::variant это именно то, что вы ищете.

ЗППП:: вариант

Хотя ответ на этот вопрос был дан уже давно, для протокола я хотел бы упомянуть, что Квантовый также делает это.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top