Question

Je suis fichier-io portage d'écriture ensemble de fonctions de c dans une classe C ++. « Les nombres magiques » (constantes sans nom) ne manquent pas.

Les fonctions lire un en-tête de fichier qui a un certain nombre d'entrées spécifiques dont les emplacements sont actuellement désignés par des nombres magiques.

on m'a enseigné par un programmeur vétéran quelques années en arrière que l'utilisation de « nombres magiques » est intrinsèquement mauvais, et donc, je l'ai depuis essayé d'éviter d'utiliser des constantes sans nom dans mon port. Donc, je veux créer une sorte de liste des constantes de l'endroit où les entrées sont stockées.

Jusqu'à présent, je suis venu avec deux solutions qui semblent relativement sûrs - utiliser un ensemble fermé d'espace de noms de constantes ou d'un espace de noms ENUM clos.

Puis-je utiliser soit la solution en toute sécurité? Y aurait-il des avantages à un sur l'autre?

par exemple.
1

namespace hdr_pos {
   const unsigned int item_1_pos=4;
   const unsigned int item_2_pos=8;
   const unsigned int item_3_pos=12;
   const unsigned int item_4_pos=24;
   const unsigned int item_5_pos=32;
};

2

namespace hdr_pos {
   enum e {
      item_1_pos=4,
      item_2_pos=8,
      item_3_pos=12,
      item_4_pos=24,
      item_5_pos=32
   };
};

Y at-il de toute façon d'éviter les doublons, pour attraper si je change les positions en raison d'une mise à jour future de l'en-tête de fichier, mais oublie de changer l'un d'eux?

S'il vous plaît garder ce fait et non subjective. S'il n'y a pas d'avantage que vous connaissez, ne hésitez pas à répondre.

Remarque: J'utiliser des noms plus descriptifs, bien sûr, dans ma mise en œuvre effective; Je viens d'appeler article choses _ <#> _... des exemples bien ...

Était-ce utile?

La solution

Je vois deux avantages à utiliser un ENUM. Tout d'abord, certains débogueurs peuvent se traduire par des constantes de nouveau dans les noms de variables enum (ce qui peut rendre le débogage plus facile dans certains cas). En outre, vous pouvez déclarer une variable d'un type énuméré qui ne peut contenir une valeur de cette énumération. Cela peut vous donner une forme supplémentaire de vérification de type que vous auriez pas simplement en utilisant des constantes.

Vérifier si une valeur est dupliqué peut dépendre de votre compilateur particulier. La meilleure façon de le faire serait probablement d'écrire un script externe qui analysera votre définition enum et rapport si une valeur ou non est dupliqué (vous pouvez exécuter ce dans le cadre de votre processus de construction si vous le souhaitez).

Autres conseils

Je l'ai traité cette situation avant, pour les codes d'erreur.

Je l'ai vu des gens en utilisant les énumérations des codes d'erreur, et cela pose des problèmes:

  1. vous pouvez attribuer un entier à l'ENUM qui ne correspond pas à aucune valeur (trop mauvais)
  2. la valeur elle-même est déclarée dans un en-tête, ce qui signifie que les pauses de compatibilité de code réassignation de code d'erreur (ce qui se passe ...), vous devez aussi prendre soin lors de l'ajout d'éléments ...
  3. vous devez définir tous les codes dans la même tête, même si souvent, parfois un peu de code sont naturellement limités à une petite partie de l'application, car les énumérations ne peuvent pas être « étendu »
  4. il n'y a pas de contrôle qu'un même code n'est pas attribué deux fois
  5. vous ne pouvez pas parcourir les différents champs d'un enum

Lors de la conception ma solution de codes d'erreur, je choisi donc une autre route: constantes dans un espace de noms, définis dans les fichiers source, dont les points d'adresse 2 et 3. Pour gagner en sécurité de type cependant, les constantes ne sont pas int, mais un Code spécifique classe:

namespace error { class Code; }

Ensuite, je peux définir plusieurs fichiers d'erreur:

// error/common.hpp

namespace error
{
  extern Code const Unknown;
  extern Code const LostDatabaseConnection;
  extern Code const LostNASConnection;
}

// error/service1.hpp
// error/service2.hpp

Je ne l'ai pas résolu la question de la distribution arbitraire si (constructeur est explicite, mais public), parce que dans mon cas, on m'a demandé à des codes d'erreur avant renvoyés par d'autres serveurs, et je ne voulais pas avoir à les connaître tout (cela aurait été trop fragile)

Cependant, je ne pensait, en faisant l'usage privé application constructeur nécessaire et d'un constructeur, nous allons même obtenir 4. et 5. dans un swoop:

// error/code.hpp

namespace error
{
  class Code;

  template <size_t constant> Code const& Make(); // not defined here

  class Code: boost::totally_ordered<Code>
  {
  public:
    Code(): m(0) {} // Default Construction is useful, 0 is therefore invalid

    bool operator<(Code const& rhs) const { return m < rhs.m; }
    bool operator==(Code const& rhs) const { return m == rhs.m; }

  private:
    template <size_t> friend Code const& Make();
    explicit Code(size_t c): m(c) { assert(c && "Code - 0 means invalid"); }

    size_t m;
  };

  std::set<Code> const& Codes();
}


// error/privateheader.hpp (inaccessible to clients)

namespace error
{
  std::set<Code>& PrivateCodes() { static std::set<Code> Set; return Set; }

  std::set<Code> const& Codes() { return PrivateCodes(); }

  template <size_t constant>
  Code const& Make()
  {
    static std::pair< std::set<Code>::iterator, bool > r
      = PrivateCodes().insert(Code(constant));
    assert(r.second && "Make - same code redeclared");
    return *(r.first);
  }
}

//
// We use a macro trick to create a function whose name depends
// on the code therefore, if the same value is assigned twice, the
// linker should complain about two functions having the same name
// at the condition that both are located into the same namespace
//
#define MAKE_NEW_ERROR_CODE(name, value)         \
  Make<value>(); void _make_new_code_##value ();


// error/common.cpp

#include "error/common.hpp"

#include "privateheader.hpp"

namespace error
{
  Code const Unkown = MAKE_NEW_ERROR_CODE(1)
  /// ....
}

Un peu plus de travail (pour le cadre), et seul lien temps / check-temps d'exécution de la même vérification d'affectation. Bien qu'il soit facile à diagnostiquer les doublons simplement en balayant le motif MAKE_NEW_ERROR_CODE

Amusez-vous!

Le titre de votre question suggère que la principale raison pour laquelle vous avez des doutes sur l'utilisation d'un ENUM est que vos constantes sont non itérative . Mais dans les types enum de C sont non itérative déjà. Vous devez sauter à travers des cerceaux assez peu pour faire un type ENUM itérative.

Je dirais que si vos constantes sont liées par la nature, alors ENUM est une assez bonne idée, que les constantes sont itératives ou non. Le principal inconvénient de énumérations bien est l'absence totale de contrôle de type. Dans de nombreux cas, vous préférez peut-être avoir un contrôle strict sur les types de vos valeurs constantes (comme les ont non signé) et que quelque chose de ENUM ne pouvez pas vous aider (au moins encore).

Une chose à garder à l'esprit est que vous ne pouvez pas prendre l'adresse d'un enum:

const unsigned* my_arbitrary_item = &item_1_pos;

Si elles sont purement constantes et ne nécessitent aucune substance d'exécution (comme ne peut pas ENUM avec une valeur d'initialisation non ENUM) alors ils devraient juste être ints const non signés. Bien sûr, le ENUM est moins de frappe, mais qui est d'ailleurs le point.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top