2010-09-18 28 views
1

D'accord, j'ai une bibliothèque que j'ai écrite en C qui lit un fichier et donne accès à ses données. Les données sont tapé, alors j'utilise pointeur vide et quelques fonctions accesseurs:Quel serait le meilleur moyen de conclure ce pointeur vide?

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

Pour différents types (built-ins: TAG_BYTE (char), TAG_SHORT (int16_t), TAG_INT (int32_t), TAG_LONG (int64_t), TAG_FLOAT (float), TAG_DOUBLE (double), chaîne_étiquette (char *) et quelques peu plus de types de données complexes, liste_balise (struct nbt_list), TAG_COMPOUND (struct nbt_compound), TAG_BYTE_ARRAY (struct nbt_byte_array).

J'essaie maintenant pour mapper cela en C++ d'une manière élégante mais je peux 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 

Cela se sent bien trop verbeux .. une meilleure façon?

Répondre

3

Cela peut faire le travail:

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

et l'utiliser comme ceci:

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

Vous voudrez peut-être ajouter des spécialisations comme

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

Cela semble vraiment prometteur, merci! Mais cela fonctionnera-t-il quand 'getValue()' est un membre de la classe?J'ai oublié d'ajouter cela à la question, toutes ces fonctions font partie d'une classe 'Tag' – LukeN

+0

@LukeN: Yep ça va. Les classes TypeTag peuvent être des classes membres de Tag ou externes à Tag de classe (je les ferais personnellement des classes privées de Tag). –

+0

@LukeN: Notez que vous devez dans ce cas déclarer les spécialisations * en dehors de la classe, en tant que template <> vector Tag :: getValue () {...} '. –

0

Vous pouvez modéliser la fonction.

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

Pas vraiment une réponse directe, mais jetez un oeil à VARIANT (sous Windows), et les classes wrapper CComVariant et _variant_t correspondantes: cela ne fondamentalement la même chose, et vous pourriez être en mesure d'obtenir un aperçu de comment c'est fait là-bas.

J'ai fait des choses comme des moulages de modèles, où la complexité est dans l'objet, mais il est facile à utiliser, mais YMMV.

+0

Variante (et CComVariant) doit être évitée comme le diable. –

+0

Aussi, je suis sur Linux et j'essaie de maintenir une certaine indépendance du système d'exploitation :) – LukeN

0

Il existe certainement des moyens d'utiliser template voodoo pour tout réduire à une fonction get<X>() (avec éventuellement quelques spécialisations). Mais là vous échangez la simplicité de l'écriture de ces fonctions getX pour probablement avoir à écrire quelque chose comme une petite infrastructure de caractères de type pour le mécanisme de modèle et/ou certaines spécialisations de modèle.

Si le nombre de types différents pris en charge ne change jamais (ou très rarement), alors la plus simple et la plus facilement compréhensible peut être simplement ces diverses fonctions getX. Il peut être verbeux, mais ces fonctions doivent être très rapides à écrire et à déboguer. D'autre part, si vous êtes à l'aise avec les modèles, vous pouvez certainement essayer cette approche.

0

Vous pouvez utiliser un coup de pouce: :une variante. Ce type peut stocker n'importe lequel de ses arguments de modèle et peut être interrogé pour ce qu'il contient. Tenez-en simplement un groupe dans un vecteur, puis retournez une référence à votre variante préférée.