Что было бы лучшим способом обернуть этот пустой указатель? - PullRequest
1 голос
/ 18 сентября 2010

Хорошо, у меня есть библиотека, которую я написал на C, которая читает файл и предоставляет доступ к его данным.Данные набираются, поэтому я использую указатель void и несколько функций доступа:

typedef struct nbt_tag
{
    nbt_type type; /* Type of the value */
    char *name;    /* tag name */

    void *value;   /* value to be casted to the corresponding type */

} nbt_tag;

int64_t *nbt_cast_long(nbt_tag *t)
{
    if (t->type != TAG_LONG) return NULL;

    return (int64_t *)t->value;
}

Для разных типов (встроенные: TAG_BYTE (char), TAG_SHORT (int16_t), TAG_INT(int32_t), TAG_LONG (int64_t), TAG_FLOAT (float), TAG_DOUBLE (double), TAG_STRING (char *) и несколько более сложных типов данных, TAG_List (struct nbt_list), TAG_COMPOUND(struct nbt_compound), TAG_BYTE_ARRAY (struct nbt_byte_array).

Я сейчас пытаюсь изобразить это на C ++ элегантным способом, но не могу это сделать ...

char getByte();                     // TAG_BYTE
int16_t getShort();                 // TAG_SHORT
int32_t getInt();                   // TAG_INT
int64_t getLong();                  // TAG_LONG
float getFloat();                   // TAG_FLOAT
double getDouble();                 // TAG_DOUBLE
std::string getString();            // TAG_STRING
std::vector<char> getByteArray();   // TAG_BYTE_ARRAY
std::vector<Tag> getCompound();     // TAG_COMPOUND

Это кажется слишком многословным ... лучше?

Ответы [ 5 ]

3 голосов
/ 18 сентября 2010

Это может сделать работу:

template <int> struct TypeTag {};
template <> struct TypeTag<TAG_BYTE> { typedef char type; };
// ...
template <> struct TypeTag<TAG_COMPOUND> { typedef vector<Tag> type; };

template <int tag>
typename TypeTag<tag>::type getValue(nbt_tab* t)
{
    if (t->type != tag) ... // throw an exception
    return *reinterpret_cast<typename TypeTag<tag>::type*>(t-value);
}

и использовать ее так:

char x = getValue<TAG_BYTE>(t);
vector<Tag> v = getValue<TAG_COMPOUND>(t);

Вы можете добавить специализации, такие как

template <>
vector<Tag> getValue<TAG_COMPOUND>(nbt_tab* t)
{
    if (t->type != TAG_COMPOUND) ... // throw something
    vector<Tag> ans(/* size */); // [rely on named return value optimization]

    // Fill the vector with your data

    return ans; // [Pray for the copy constructor not to get called]
}
0 голосов
/ 18 сентября 2010

Вы можете использовать boost :: варианта.Этот тип может хранить любой из его аргументов шаблона и может запрашиваться, для которых он содержит.Просто держите их несколько в векторе, а затем возвращайте ссылку на ваш любимый вариант.

0 голосов
/ 18 сентября 2010

Не совсем прямой ответ, но взгляните на VARIANT (в Windows) и соответствующие классы-обертки CComVariant и _variant_t: это делает в основном то же самое, и вы можете получить некоторое представление о том, как это делается там.

Я сделал такие вещи, как приведение шаблонов, где сложность заключается в объекте, но его легко использовать, но YMMV.

0 голосов
/ 18 сентября 2010

Конечно, есть способы использовать шаблон voodoo, чтобы свести все это к функции get<X>() (возможно, с несколькими специализациями). Но там вы компенсируете простоту написания этих getX функций за то, что вам, вероятно, придется написать что-то вроде инфраструктуры с маленькими чертами типа для механизма шаблонов и / или некоторых специализаций шаблонов.

Если число поддерживаемых различных типов никогда (или очень редко) никогда не изменится, то простейшими и наиболее понятными могут быть только эти различные функции getX. Это может быть многословно, но эти функции должны быть очень быстрыми для написания и отладки.

С другой стороны, если вы знакомы с шаблонами, вы наверняка можете попробовать этот подход.

0 голосов
/ 18 сентября 2010

Вы можете шаблонизировать функцию.

template <typename T>
typename T get();
...