44 votes

Quel typeums Enum en C ++ utilisez-vous?

C'est la connaissance commune que construit-dans les énumérations en C++ ne sont pas typesafe. Je me demandais ce qui les classes de la mise en œuvre de typesafe les enums sont utilisées là-bas... J'utilise moi-même la suite de "vélo", mais elle est un peu verbeux et limitée:

typesafeenum.h:

struct TypesafeEnum
{
// Construction:
public:
    TypesafeEnum(): id (next_id++), name("") {}
    TypesafeEnum(const std::string& n): id(next_id++), name(n) {}

// Operations:
public:
    bool operator == (const TypesafeEnum& right) const;
    bool operator != (const TypesafeEnum& right) const;
    bool operator < (const TypesafeEnum& right) const;

    std::string to_string() const { return name; }

// Implementation:
private:
    static int next_id;
    int id;
    std::string name;
};

typesafeenum.cpp:

int TypesafeEnum::next_id = 1;

bool TypesafeEnum::operator== (const TypesafeEnum& right) const 
{ return id == right.id; }

bool TypesafeEnum::operator!= (const TypesafeEnum& right) const 
{ return !operator== (right); }

bool TypesafeEnum::operator< (const TypesafeEnum& right) const  
{ return id < right.id; }

Utilisation:

class Dialog 
{
 ...
    struct Result: public TypesafeEnum
    {
        static const Result CANCEL("Cancel");
        static const Result OK("Ok");
    };


    Result doModal();
 ...
};

const Dialog::Result Dialog::Result::OK;
const Dialog::Result Dialog::Result::CANCEL;

Plus: Je pense que j'aurais du être plus précis sur les exigences. Je vais essayer de résumer:

Priorité 1: la définition d'un enum variable à une valeur non valide doit être impossible (une erreur de compilation), sans exceptions.

Priorité 2: Conversion d'une valeur d'enum vers/à partir d'un int devrait être possible avec une seule fonction explicite/appel de la méthode.

Priorité 3: compact, élégant et pratique de déclaration et d'utilisation que possible

Priorité 4: Convertir les valeurs de l'enum et de cordes.

Priorité 5: (optionnel) Possibilité d'effectuer une itération sur les valeurs de l'enum.

42voto

Josh Kelley Points 24438

Je suis en train de jouer avec le coup de pouce.Enum proposition de la Boost Voûte (filename enum_rev4.6.zip). Bien qu'il n'a jamais été officiellement soumis pour inclusion dans Boost, il est utilisable en l'état. (Documentation est incomplète, elle est faite par claire code source et de bons tests.)

Coup de pouce.Enum permet de déclarer un enum comme ceci:

BOOST_ENUM_VALUES(Level, const char*,
    (Abort)("unrecoverable problem")
    (Error)("recoverable problem")
    (Alert)("unexpected behavior")
    (Info) ("expected behavior")
    (Trace)("normal flow of execution")
    (Debug)("detailed object state listings")
)

Et qu'automatiquement étendre à ceci:

class Level : public boost::detail::enum_base<Level, string>
{
public:
    enum domain
    {
        Abort,
        Error,
        Alert,
        Info,
        Trace,
        Debug,
    };

    BOOST_STATIC_CONSTANT(index_type, size = 6);

    Level() {}
    Level(domain index) : boost::detail::enum_base<Level, string>(index) {}

    typedef boost::optional<Level> optional;
    static optional get_by_name(const char* str)
    {
        if(strcmp(str, "Abort") == 0) return optional(Abort);
        if(strcmp(str, "Error") == 0) return optional(Error);
        if(strcmp(str, "Alert") == 0) return optional(Alert);
        if(strcmp(str, "Info") == 0) return optional(Info);
        if(strcmp(str, "Trace") == 0) return optional(Trace);
        if(strcmp(str, "Debug") == 0) return optional(Debug);
        return optional();
    }

private:
    friend class boost::detail::enum_base<Level, string>;
    static const char* names(domain index)
    {
        switch(index)
        {
        case Abort: return "Abort";
        case Error: return "Error";
        case Alert: return "Alert";
        case Info: return "Info";
        case Trace: return "Trace";
        case Debug: return "Debug";
        default: return NULL;
        }
    }

    typedef boost::optional<value_type> optional_value;
    static optional_value values(domain index)
    {
        switch(index)
        {
        case Abort: return optional_value("unrecoverable problem");
        case Error: return optional_value("recoverable problem");
        case Alert: return optional_value("unexpected behavior");
        case Info: return optional_value("expected behavior");
        case Trace: return optional_value("normal flow of execution");
        case Debug: return optional_value("detailed object state listings");
        default: return optional_value();
        }
    }
};

Il satisfait à toutes les cinq priorités que vous de la liste.

18voto

Charlie Points 17807

Une bonne méthode de compromis est la suivante:

 struct Flintstones {
   enum E {
      Fred,
      Barney,
      Wilma
   };
};

Flintstones::E fred = Flintstones::Fred;
Flintstones::E barney = Flintstones::Barney;
 

Votre version n'est pas typée sécurité, mais son utilisation est meilleure que celle des énumérations standard, et vous pouvez toujours tirer parti de la conversion d'entier lorsque vous en avez besoin.

12voto

Roddy Points 32503

J'utilise des énumérations C ++ 0x typesafe . J'utilise des modèles / macros d'aide qui fournissent la fonctionnalité chaîne à / de.

 enum class Result { Ok, Cancel};
 

6voto

nlaq Points 11379

Je ne. Beaucoup trop de frais généraux pour peu d'avantages. De plus, être capable de classer des énumérations dans différents types de données pour la sérialisation est un outil très pratique. Je n'ai jamais vu un cas où une énumération "Type safe" mériterait le surcoût et la complexité où C ++ offre déjà une mise en oeuvre suffisante.

2voto

Luis Machuca Points 427

Je suis personnellement en utilisant une version adaptée de la typesafe enum idiome. Il ne fournit pas tous les cinq "exigences" que vous avez indiqué dans votre modifier, mais je suis en total désaccord avec certains d'entre eux de toute façon. Par exemple, je ne vois pas comment Prio#4 (conversion des valeurs de chaînes de caractères) n'a rien à voir avec le type de sécurité. La plupart du temps, représentation de chaîne de valeurs individuelles doit être distincte de la définition du type, de toute façon (pense i18n pour une simple raison pourquoi). Priorité n ° 5 (iteratio, qui est facultatif) est l'une des plus belles choses que j'aimerais voir naturellement qui se passe dans les énumérations, de sorte que je me sentais triste qu'il apparaît comme "facultatif" dans votre demande, mais il semble que c'est mieux traitée par le biais d'un distinct itération du système telles que l' begin/end fonctions ou un enum_iterator, qui fait d'eux de travailler de façon transparente avec STL et C++11 foreach.

Otoh, que cette simple idiome bien fournit Priorité N ° 3 Priorité n ° 1 grâce au fait que la plupart du temps que des enveloppements enums avec plus d'informations de type. Sans oublier qu'il est une solution très simple qui, pour la plupart, ne nécessite aucune dépendance externe en-têtes, il est donc assez facile à transporter. Il a aussi l'avantage de faire des énumérations de portée à la C++11:

// This doesn't compile, and if it did it wouldn't work anyway
enum colors { salmon, .... };
enum fishes { salmon, .... };

// This, however, works seamlessly.
struct colors_def { enum type { salmon, .... }; };
struct fishes_def { enum type { salmon, .... }; };

typedef typesafe_enum<colors_def> colors;
typedef typesafe_enum<fishes_def> fishes;

Le seul "trou" cette solution fournit, c'est qu'il ne traite pas le fait qu'il n'a pas empêcher enums de différents types (ou un enum et un int) d'être directement comparés, parce que lorsque vous utilisez des valeurs directement vous forcer la conversion implicite en int:

if (colors::salmon == fishes::salmon) { .../* Ooops! */... }

Mais jusqu'à présent, j'ai trouvé ces problèmes peuvent être résolus simplement en offrant une meilleure comparaison pour le compilateur, par exemple, explicitement, en fournissant un opérateur qui compare deux différents enum types, puis en les forçant à s'échouer:

// I'm using backports of C++11 utilities like static_assert and enable_if
template <typename Enum1, typename Enum2>
typename enable_if< (is_enum<Enum1>::value && is_enum<Enum2>::value) && (false == is_same<Enum1,Enum2>::value) , bool >
::type operator== (Enum1, Enum2) {
    static_assert (false, "Comparing enumerations of different types!");
}

Bien qu'il ne semble pas casser le code jusqu'à présent, et il ne explicitement traiter le problème spécifique sans faire autre chose, je ne suis pas certain que telle chose est une chose que l'on "doit" faire (je le soupçonne d'interférer avec enums prend déjà part à des opérateurs de conversion déclaré ailleurs; je serais heureux de recevoir vos commentaires à ce sujet).

En combinant ceci avec le dessus de typesafe idiome donne quelque chose qui est relativement proche du C++11 enum class dans humanibility (la lisibilité et la maintenabilité) sans avoir à faire quelque chose de trop obscur. Et je dois avouer que c'était amusant à faire, je n'avais jamais pensé à fait demander le compilateur si j'avais à faire avec enums ou pas...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X