2009-08-20 7 views
2

J'utilise des gabarits pour implémenter une conversion vérifiée de gamme de int à enum. Il ressemble à ceci:problème de spécialisation de fonction de gabarit

template<typename E> 
E enum_cast(const int &source); 

La fonction de modèle placée plus ou moins dans le root-directoy du projet. Lors de la définition d'une nouvelle ENUM qui est prévu pour être attribué des valeurs à partir d'un fichier de configuration comme ceci:

enum ConfigEnum { 
    ConfigEnumOption1 = 'A' 
    , ConfigEnumOption2 = 'B' 
    , ConfigEnumInvalid }; 

ConfigEnum option = XmlNode.iAttribute("option"); 

Je définis une spécialisation de modèle pour ce type particulier de ENUM dans un .cpp fichier pour le module ce ENUM est utilisé dans .

template<> 
ConfigEnum enum_cast(const int &source) { 
    switch(source) { 
    case ConfigEnumOption1 : return ConfigEnumOption1; 
    case ConfigEnumOption2 : return ConfigEnumOption2; 
    default return ConfigEnumInvalid; 
} 

maintenant, l'affectation d'un int au ENUM devient:

ConfigEnum option = enum_cast<ConfigEnum>(XmlNode.iAttribute("option")); 

qui veille à ce que l'ENUM est AllWays dans la plage valide. Notez que je n'ai pas toujours le contrôle sur ces énumérations donc cela semble être une solution raisonnable et facilement configurable.

Quoi qu'il en soit, cela fonctionne très bien (même si je ne suis pas shure tout le code donné ici est correct parce que je me souviens juste de la mémoire en ce moment)

Le problème vient du fait qu'il pourrait être souhaitable d'utiliser cette construction "enum_cast" s'étendant sur la base de code chaque fois qu'un in est assigné à une énumération. Après tout cela peut être appliqué par une simple opération de recherche et de remplacement. Bien sûr, je ne veux pas définir ces spécialisations pour tous et chaque énumération, mais seulement pour ceux qui ont besoin de la vérification de gamme pour le moment. Je préférerais ajouter des spécialisations de gabarit pour les types d'énumération lorsque le besoin s'en fait sentir et utiliser l'opérateur d'affectation lorsqu'aucune spécialisation n'est définie.

Ainsi:

InternalEnum internal = enum_cast<InternalEnum>(internal_integer); 

appellerait effecively interne = internal_integer. Je suppose que je dois dire au compilateur d'utiliser une certaine implémentation "par défaut" pour tous les types d'énumération qui n'ont pas de spécialisation.

Mon premier pari a été de donner la fonction de modèle d'origine d'une mise en œuvre comme celui-ci:

template<typename E> 
E enum_cast(const int &source) { 
    E copy = source; 
    return copy; 
}; 

Malheureusement maintenant c'est AllWays appelé au lieu des specialiazations données dans les fichiers .cpp plus profonds dans le projet répertoire arbre.

Des pensées?

Merci à l'avance Arne

Répondre

4

Les spécialisations explicites doivent être visibles partout où elles sont utilisées. Et comme ce sont des définitions, elles ne peuvent pas être répétées dans chaque unité de compilation. Ainsi, dans le fichier d'en-tête où vous définissez un ENUM que vous voulez vérifier que vous dites

#include "enum_cast.h" 
enum Foo { Foo_A, Foo_B, Foo_C }; 
template<> Foo enum_cast<Foo>(int source); 

et dans le cpp correspondant vous donner la définition.

+0

merci pour l'indice - comme d'habitude c'est facile une fois que vous connaissez la réponse ;-) – Arne

0

Ce

#include <iostream> 

enum e1 { a, b, c, e1_invalid }; 
enum e2 { x, y, z, e2_invalid }; 

template<typename E> 
E enum_cast(int source) 
{ 
    std::cout << "default implementation\n"; 
    return static_cast<E>(source); 
} 

template<> 
e2 enum_cast<e2>(int source) 
{ 
    std::cout << "specialization\n"; 
    switch(source) { 
     case x: return x; 
     case y: return y; 
     case z: return z; 
    } 
    return e2_invalid; 
} 

int main(int /*argc*/, char* /*argv*/[]) 
{ 
    std::cout << static_cast<int>(enum_cast<e1>(1)) << '\n'; 
    std::cout << static_cast<int>(enum_cast<e2>(1)) << '\n'; 
    return 1; 
} 

Travaux sur ma machine (TM). Il imprime

default implementation 
1 
specialization 
1 
+0

probablement parce que toutes les spécialisations résident dans le même fichier. Dans mon cas, les définitions enum et les spécialisations sont généralement dans des fichiers séparés. Je suppose que le commentaire par AProgrammer va remédier à mon problème, bien que – Arne

+0

@Arne: Je suppose que c'est vrai. – sbi

1

ne pourriez-vous utiliser une classe de traits pour décrire chaque ENUM:

const int LARGE = 65536; 

template<typename> 
struct EnumTrait 
{ 
    enum {LOW = -LARGE}; 
    enum {HIGH = LARGE}; 
}; 

template<typename ENUM> 
static ENUM enum_cast (int i) 
{ 
    if (i < EnumTrait<ENUM>::LOW || i > EnumTrait<ENUM>::HIGH) 
     throw std::runtime_error ("Out of bounds"); 
    return static_cast<ENUM> (i); 
} 

enum Colour {RED = 0, GREEN, BLUE}; 

template<> 
struct EnumTrait<Colour> 
{ 
    enum {LOW = RED}; 
    enum {HIGH = BLUE}; 
}; 

enum Location {HERE = 0, THERE, NOWHERE}; 
// No EnumTraits class. 

int main (int argc, char* argv[]) 
{ 
    int i = 2; 

    Colour c = enum_cast<Colour> (i); 
    std::cout << "c=" << c << std::endl; 

    Location l = enum_cast<Location> (i); 
    std::cout << "l=" << l << std::endl; 

    return 0; 
} 

Normalement, la définition enum est accompagnée d'une spécialisation EnumTraits. Pour toute énumération hors de votre contrôle, la vérification des limites utilise simplement les traits par défaut.

+0

Je vais considérer cette approche pour les projets futurs, mais pour l'instant, j'ai besoin d'atteindre l'échéance .. merci beaucoup! – Arne

Questions connexes