2010-03-11 7 views
0

Pouvez-vous écrire des directives de préprocesseur pour vous renvoyer un std :: string ou char *?Écrire des directives de préprocesseur pour obtenir une chaîne

Par exemple: Dans le cas des entiers:

#define square(x) (x*x) 

int main() 
{ 
    int x = square(5); 
} 

Je cherche à faire la même chose mais avec des cordes comme un modèle switch-case. si le passage 1, il devrait retourner "un" et 2 pour "deux" ainsi de suite ..

+4

Pourquoi ne pas simplement une fonction normale? –

+0

Ce que Martin a dit - et votre exemple de macro 'square()' montre une des raisons pour lesquelles: il est si facile d'obtenir des macros erronées. La macro 'square()' devrait ressembler plus à '#define square (x) ((x) * (x))' pour éviter les problèmes avec des choses comme 'square (1 + 4)' renvoyant 9 au lieu de 25. Même avec ce correctif, il est difficile d'empêcher un comportement incorrect avec des arguments qui ont des effets secondaires. Une fonction évite ces problèmes et n'aura probablement aucun impact notable sur les performances (en particulier si elle peut être transformée en «inline»). –

+0

En utilisant des macros simples ou des fonctions en ligne, les performances sont-elles atteintes uniquement au moment de la compilation ou de l'exécution? – cpx

Répondre

2

Vous ne voulez pas faire cela avec des macros en C++; une fonction est très bien:

char const* num_name(int n, char const* default_=0) { 
    // you could change the default_ to something else if desired 

    static char const* names[] = {"Zero", "One", "Two", "..."}; 
    if (0 <= n && n < (sizeof names/sizeof *names)) { 
    return names[n]; 
    } 
    return default_; 
} 

int main() { 
    cout << num_name(42, "Many") << '\n'; 
    char const* name = num_name(35); 
    if (!name) { // using the null pointer default_ value as I have above 
    // name not defined, handle however you like 
    } 
    return 0; 
} 

De même que carré doit être une fonction:

inline int square(int n) { 
    return n * n; 
} 

(Bien que, dans la pratique carré est pas très utile, vous devriez juste multiplier directement. Comme une curiosité, bien que je ne le recommanderais pas dans ce cas (la fonction ci-dessus est bien), un toupet e équivalent méta-programmation serait:

template<unsigned N> // could also be int if desired 
struct NumName { 
    static char const* name(char const* default_=0) { return default_; } 
}; 
#define G(NUM,NAME) \ 
template<> struct NumName<NUM> { \ 
    static char const* name(char const* default_=0) { return NAME; } \ 
}; 
G(0,"Zero") 
G(1,"One") 
G(2,"Two") 
G(3,"Three") 
// ... 
#undef G 

Notez que le principal moyen de l'exemple TMP n'est que vous devez utiliser des constantes de compilation du temps au lieu d'un int.

+0

Cela me fait me demander si ce problème est réalisable avec la métaprogrammation de modèle d'une manière sournoise astucieuse ... –

+0

@Chris: Bien sûr, vous devez utiliser' NumName :: name() '(appel une fonction statique inline) et se spécialiser pour toutes les valeurs qui vous intéressent. Je ne vois pas le besoin, cependant. –

+0

@Roger - Je me demandais plus si cela nous permettrait de faire des astuces pour simplifier des nombres plus grands dans des combinaisons de plus petits nombres, mais le système de gabarit ne permet pas de gammes (disons, 'template <> struct NumName <21-29>') donc ça finit par ne pas être très utile. –

1

A #define Une directive de préprocesseur substitue la chaîne de caractères dans le code source. La construction case...when que vous voulez est toujours pas trivial:

#define x(i) ((i)==1?"One":((i)==2?"Two":"Many")) 

pourrait être un début - mais définir quelque chose comme

static char* xsof[] = ["One", "Two", "Many"]; 

et

#define x(i) xsof[max(0, min((i)-1, (sizeof xsof/sizeof xsof[0] - 1)))] 

semble plus raisonnable et plus performants.

Édition: selon la suggestion de Chris Lutz, la seconde macro s'ajuste automatiquement à la définition xsof; par Mark, a fait le compte 1 base.

+2

Remplacez '2' dans la macro par' (sizeof xsof/sizeof xsof [0] - 1) 'et vous disposez d'une excellente solution maintenable. –

+0

Est-ce que le '(i)' dans la macro donnée ne devrait pas vraiment être '(i) - 1'? –

+0

@Michael, ah oui, il avait dit qu'il voulait compter d'un - montage à corriger, tx. @Chris, bonne idée, montage pour l'incorporer. –

0

Vous ne pouvez pas transformer des entiers en chaînes de sorte que 1 ---> "Un", 2 ---> "Deux", etc. sauf en énumérant chaque valeur.

Vous pouvez convertir une valeur d'argument dans une chaîne avec le préprocesseur C:

#define STRINGIZER(x) #x 
#define EVALUATOR(x) STRINGIZER(x) 
#define NAME(x)   EVALUATOR(x) 

NAME(123) // "123" 

#define N 123 
#define M 234 

NAME(N+M) // "123+234" 

Voir aussi SO 1489932.

1

Je l'ai vu ...

#define STRING_1() "ONE" 
#define STRING_2() "TWO" 
#define STRING_3() "THREE" 
... 

#define STRING_A_NUMBER_I(n) STRING_##n() 

#define STRING_A_NUMBER(n) STRING_A_NUMBER_I(n) 

Je belive cette étape supplémentaire est pour vous assurer que n est évalué, donc si vous passez 1 + 2, il se transforme à 3 avant de passer à STRING_A_NUMBER_I, ce semble un peu esquiver, quelqu'un peut-il élaborer?

+0

Boost utilise cette méthode pour faire ses trucs de génération de code préprocesseur. C'était amusant de déchiffrer comment tout cela a fonctionné. En outre, @Chris Lutz, cela fonctionnera si "STRING _ ## n" a les parenthèses ouvertes/fermées –

+0

pouvez-vous définir 'STRING_X' à un nombre unique ici? – cpx

+0

@Dave: désolé, je ne sais pas ce que vous voulez dire exactement aussi, vous devez vous assurer que vous passez dans un nombre réel, par exemple, vous ne pouvez pas passer 1 + 2, vous devez passer 3 – matt

Questions connexes