2010-10-21 4 views
3

Il ya quelque temps, j'ai demandé des constantes std :: string correct idiom for std::string constants?. Ce que j'en ai retiré était et non pour utiliser les constantes std :: string mais utiliser des constantes de chaîne de caractères. Alors quel est le meilleur langage pour queidiome correct pour les constantes de chaîne de caractères (pas std :: string) en C++

#define FOO "foo" 

const char * const FOO = "foo"; 

const char FOO[] = "foo"; 

caractéristiques Souhaitable

  • get longueur au moment de la compilation. 1 & 3 mais pas 2 (sizeof ne fonctionne pas sur 2)
  • peut être inclus dans .h sans le linker se plaindre. tous (je pense)
  • pas de copies multiples en .o, en sortie liée . dépend du compilateur (probablement)

Il semble donc que # 3 est mieux, mais Scott Meyers dit utiliser # 2 (efficace C++ item # 1)

résumé des réponses

  1. utilisation joyeux modèle de code compliqué
  2. utilisation # 3

Le code du modèle se sent comme surpuissant. Donc pour l'instant je vais aveC# 3;

Mais je vais ruminer sur le code du modèle, la version macroisée le rend OKish; mais je n'aime pas le fait que ce n'est pas portable (qui sait, peut-être que gcc décidera que c'est faux aussi)

Répondre

0

Vos caractéristiques souhaitées sont contradictoires.

  1. Longueur au moment de la compilation
  2. défini dans le fichier d'en-tête
  3. copie unique dans toutes les unités de compilation

Pour obtenir (1) et (2), vous devez déclarer la variable comme static ou mettez-le dans un espace de noms sans nom. Cependant, cela provoquera une définition dans chaque unité de compilation, ce qui va à l'encontre de (3). Pour obtenir (2) et (3), vous devez déclarer la variable extern, mais vous n'obtiendrez pas (1). Pour obtenir (2) et (3), vous devez déclarer la variable extern.

Maintenant, si votre éditeur de liens est intelligent, il peut optimiser loin les copies multiples, mais je ne suis pas sûr si la norme le permet ...

Je recommande la syntaxe const char FOO[] = "foo"; déclaré dans un espace de noms sans nom ou static s'il doit être trouvé dans un espace de noms spécifique. Si la chaîne est très grande, alors je vais pour un extern.

+0

Euh, les fonctionnalités ne sont pas contradictoires. Argh, maintenant pour expliquer que je vais devoir ajouter une réponse. Ainsi soit-il.Cheers, –

+0

#define fonctionne bien avec sizeof – pm100

+0

@ pm100: il se comporte comme static en ce qu'il vous donne (1) et (2) au détriment de générer une copie dans chaque unité de compilation (bien qu'il soit garanti de ne pas être généré sauf s'il est réellement utilisé). –

0

Voici comment je le vois. Je n'utiliserais aucun de ceux-ci tels quels. D'abord, je suis incliné par # 2, mais, tenez compte du fait que vous devez déclarer la variable comme extern dans le .h, et en sélectionner quelques-uns.cpp pour stocker réellement la chaîne:

// .h 
extern const char*const STR; 

// .cpp 
const char*const STR = "abc"; 

Le seul inconvénient, ne pas avoir la longueur lors de l'exécution, ne me semble pas une vraie raison de passer à autre option. Si vous avez plusieurs chaînes, vous pouvez toujours avoir un ensemble de constantes entières (ou #define s) pour spécifier la longueur de chaque chaîne, par exemple STR_LEN. Si vous en avez beaucoup, vous ne les écrirez pas de toute façon, et vous pourrez alors générer automatiquement les constantes ..._LEN en même temps.

+0

Si la longueur n'est pas nécessaire à la compilation, il n'y a plus de raison de ne pas utiliser 'std :: string'. –

+0

@ André: Peut-être le surcroît de mémoire dynamique utilisé par le 'std :: sting'. Mais oui, en général, il n'y a pas grand intérêt à ne pas utiliser 'std :: string'. –

+0

en fait pas - const * char const peut être placé en toute sécurité dans plusieurs fichiers d'en-tête – pm100

4

Pour les fonctionnalités que vous voulez, ...

  • longueur get au moment de la compilation,
  • peuvent être inclus dans .h sans linker se plaindre tous,
  • pas plusieurs copies en .o, en sortie reliée,

... vous pouvez utiliser le langage constant basé sur un modèle, comme

template< class Dummy > 
struct Foo_ 
{ 
    static char const s[]; 
}; 

template< class Dummy > 
char const Foo_<Dummy>::s[] = "Blah blah"; 

typedef Foo_<void> Foo; // Now you can refer to Foo:s 


#include <iostream> 
using namespace std; 
int main() 
{ 
    cout << sizeof(Foo::s) << " bytes: \"" << Foo::s << "\"\n"; 
} 

Vous pouvez envelopper la génération dans une macro. Cependant, autant que je sache, la seule utilité pratique est de supporter le code char/wchar_t-agnostic, et pour cela la douleur peut être plus grande que le gain.

EDIT:
versions MSVC 7.1 à travers 10.0 n'accepte pas correctement la sizeof. Ce qui suit est une solution de contournement qui compile bien avec g ++ 4.4.1, Comeau Online 4.3.10.1, MSVC 7.1 et MSVC 10.0.

#include <stddef.h> 

typedef ptrdiff_t Size; 

// Substitute a more general countOf 
template< Size n > 
struct SizedBuf { char sizer[n]; }; 

template< class Type, Size n > 
SizedBuf<n> countOf_(Type (&)[n]) { return n; } 

#define COUNT_OF(array) sizeof(countOf_(array).sizer) 

#define DEF_STRING(name, value)        \ 
    template<class>           \ 
    struct name##_constant_          \ 
    {               \ 
     static char const str[];        \ 
     static Size const length = COUNT_OF(value) - 1; \ 
    };               \ 
                   \ 
    template< class Type >          \ 
    char const name##_constant_<Type>::str[] = value;   \ 
                   \ 
    template< class Type >          \ 
    Size const name##_constant_<Type>::length;    \ 
                   \ 
    typedef name##_constant_<void> name; 


DEF_STRING(a, "Argh, MSVC!") 
DEF_STRING(b, "Blah blah") 
DEF_STRING(c, "Currently there's no 'inline' for data in C++.") 


#include <iostream> 

template< char const* s > 
void foo() { std::cout << "foo() says: " << s << std::endl; } 

int main() 
{ 
    using namespace std; 

    int const x[a::length] = {}; // Showing off compile time constant. 
    foo<a::str>();     // Showing off external linkage. 

    cout << a::length << " characters: \"" << a::str << "\"." << endl; 
} 

Vive & HTH.,

+0

+1. Bonne idée. Fou, mais gentil :) –

+1

Vous voulez expliquer exactement quel avantage vous obtenez sur la 'const char FOO [] =" foo ";' syntaxe? J'ai du mal à passer * à la fois * par un gabarit bizarre et une macro, et perdre le support pour changer facilement de 'char' à' wchar_t' tout en générant une copie dans chaque unité de compilation ... –

+0

@ André: votre suggestion ne serait pas conforme à la troisième exigence du PO. Le commentaire sur "changer facilement" est également vide de sens, désolé. Bravo, hth., –

0

Je pense que vous avez pris une mauvaise idée loin de votre question précédente.
Les chaînes ont tendance à être clairsemées et ne sont pas bonnes pour la recherche, utilisez plutôt des chiffres.

Vous ne semblez toujours pas obtenir la différence entre déclarer dans un fichier .h et définir le stockage dans un fichier .cpp, évitant ainsi plusieurs copies. Même si vous aviez plusieurs copies (avec des noms de constante différents), vous auriez toujours le problème que vous avez mentionné dans votre question précédente.

a un défaut fatal. Je ne peux pas avoir le code de niveau du module statique qui utilise ces chaînes car ils pourraient ne pas avoir été construit encore

La seule façon d'éviter cela est d'amener les constantes dans le même champ avec ce que vous avez actuellement au niveau du module statique.

Mettez tous les éléments liés dans une classe!

+0

para 2- Je comprends précisément les différences. Dans d'autres cas, nous parlons de la duplication inoffensive de littéraux, dans d'autres cas, nous parlons de barfs de liens, et je sais exactement ce qui produit les liens et ce qui ne fonctionne pas. un bon à avoir, pas de ballonnement – pm100

+0

deuxième les questions concernant la classe n'a rien à voir avec elle.Si j'ai la statique globale alors l'ordre de chargement n'est pas défini.Je peux réorganiser mon code de sorte que je n'ai pas statique globale, mais ce n'est pas le J'observe que si vous avez une statique globale que la suggestion ne fonctionne pas – pm100

0

Ceci est juste le macro'd de réponse Alf:

#include <iostream> 

#define string_constant(pName, pLiteral)     \ 
     template <typename = void>       \ 
     struct pName##_detail        \ 
     {             \ 
      static const char value[];      \ 
     };             \ 
                  \ 
     template <typename T>        \ 
     const char pName##_detail<T>::value[] = pLiteral; \ 
                  \ 
     typedef pName##_detail<> pName 

string_constant(my_constant, "this is a literal"); 

int main() 
{ 
    std::cout << my_constant::value << std::endl; 
    std::cout << sizeof my_constant::value << std::endl; 
} 

codepad. Ne semble pas fonctionner dans VS2010. :/

+0

re "Ne semble pas fonctionner dans VS2010", je n'ai pas vérifié mais une différence entre ce qui précède code et le mien est que ce qui précède utilise un paramètre de modèle par défaut. Cheers & hth., –

+0

@Alf: Cela ne devrait pas faire la différence (c'est juste plus propre pour moi). Mais je l'ai changé de toute façon juste pour être sûr, même erreur. On dirait encore un autre cas de non-conformité. (FFS) – GManNickG

+0

g ++ et Comeau en ligne acceptent le code, MSVC 10 (et j'ai également vérifié avec 7.1) ne le font pas.Vérification du paramétrage du template il semble que MSVC considère à tort que le type du tableau est incomplet dans 'main'. En C++ 0x, le projet N3092 §3.9/6 traite de l'achèvement d'un type de tableau dans une unité de traduction (passant de incomplet à complet lorsque la taille devient connue). Je suis désolé, je ne connais pas de solution de contournement MSVC. J'ai essayé quelques trucs, aucun n'a fonctionné. :-( –

Questions connexes