开发者

What would be the best way to wrap up this void pointer?

开发者 https://www.devze.com 2023-01-16 21:41 出处:网络
Alright, I have library I wrote in C that reads a file and provides access to its\' data. The data is typed, so I\'m using void pointer and a few accessor functions:

Alright, I have library I wrote in C that reads a file and provides access to its' data. The data is typed, so I'm using void pointer and a few accessor functions:

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开发者_JAVA百科_tag *t)
{
    if (t->type != TAG_LONG) return NULL;

    return (int64_t *)t->value;
}

For different types (built-ins: TAG_BYTE (char), TAG_SHORT (int16_t), TAG_INT (int32_t), TAG_LONG (int64_t), TAG_FLOAT (float), TAG_DOUBLE (double), TAG_STRING (char *) and a few slightly more complex data types, TAG_List (struct nbt_list), TAG_COMPOUND (struct nbt_compound), TAG_BYTE_ARRAY (struct nbt_byte_array).

I'm now trying to map this to C++ in an elegant fashion but I can't get it done...

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

This feels way too verbose.. any better way?


This can do the job:

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);
}

and use it like this:

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

You may want to add specializations like

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]
}


You could template the function.

template <typename T>
typename T get();


Not really a direct answer, but take a look at VARIANT (in Windows), and the corresponding CComVariant and _variant_t wrapper classes: this does basically the same thing, and you might be able to get some insight from how it's done there.

I have done stuff like template casts, where the complexity is in the object, but it's easy to use, but YMMV.


There are certainly ways to use template voodoo to to reduce it all to a get<X>() function (with possibly a few specializations). But there you trade off the simplicity of writing those getX functions for probably having to write something like a small type-traits infrastructure for the template mechanism and/or some template specializations.

If the number of different types supported is never (or very rarely ever) going to change, then the simplest and most easily understandable might just be those various getX functions. It may be verbose, but those functions ought to be very quick to write and debug.

On the other hand, if you're comfortable with templates you could certainly try that approach.


You can use a boost::variant. This type can store any of it's template arguments and can be queried for which it contains. Simply hold a bunch of them in a vector and then return a reference to your favourite variant.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号