2009-04-28 13 views
24

Titres alternatifs (pour faciliter la recherche)C macros pour créer des chaînes

  • Convertir un jeton de préprocesseur à une chaîne
  • Comment faire une chaîne char de la valeur d'une C macro?

Original Question

Je voudrais utiliser C#define pour construire des chaînes littérales au moment de la compilation.

La chaîne sont des domaines qui changent pour le débogage, etc. libérer

Je voudrais une quelque chose comme ceci:

#ifdef __TESTING 
    #define IV_DOMAIN domain.org   //in house testing 
#elif __LIVE_TESTING 
    #define IV_DOMAIN test.domain.com  //live testing servers 
#else 
    #define IV_DOMAIN domain.com   //production 
#endif 

// Sub-Domain 
#define IV_SECURE "secure.IV_DOMAIN"    //secure.domain.org etc 
#define IV_MOBILE "m.IV_DOMAIN" 

Mais le préprocesseur n'évalue rien dans les « »

  1. Y a-t-il un moyen de contourner ce problème?
  2. Est-ce une bonne idée?
+0

Possible dupliquer de [Convertir un jeton de préprocesseur en chaîne] (http://stackoverflow.com/questions/240353/convert-a-preprocessor -token-to-a-string) –

Répondre

31

En C, les littéraux de chaîne sont concaténés automatiquement. Par exemple,

const char * s1 = "foo" "bar"; 
const char * s2 = "foobar"; 

s1 et s2 sont la même chaîne.

Alors, pour votre problème, la réponse (sans coller jeton) est

#ifdef __TESTING 
    #define IV_DOMAIN "domain.org" 
#elif __LIVE_TESTING 
    #define IV_DOMAIN "test.domain.com" 
#else 
    #define IV_DOMAIN "domain.com" 
#endif 

#define IV_SECURE "secure." IV_DOMAIN 
#define IV_MOBILE "m." IV_DOMAIN 
6

Essayez d'utiliser l'opérateur ##

#define IV_SECURE secure.##IV_DOMAIN 
+1

Cela ne résout pas le problème "". – mouviciel

+0

Je pense que le PO demandait à propos de la concaténation de chaînes (#), pas de collage de jetons (##). Cependant, je n'ai pas baissé le ton. La question est un peu ambiguë. –

+0

Le collage de jetons ('##') ou le chaînage ('#') ne fonctionneront pas sans beaucoup d'autres choses. –

8

Les chaînes qui sont ensuite ensemble sont combinés par le compilateur C.

#define DOMAIN "example.com" 
#define SUBDOMAIN "test." DOMAIN 
const char *asCString = SUBDOMAIN; 
NSString *asNSString = @SUBDOMAIN; 
5

Ce dont vous avez besoin, ce sont les opérateurs # et ##, et la concaténation automatique des chaînes.

L'opérateur de prétraitement # transforme le macro-paramètre en chaîne. L'opérateur ## colle deux jetons (tels que les paramètres de macro) ensemble.

La possibilité qui vient à l'esprit pour moi est

#define IV_DOMAIN domain.org 
#define IV_SECURE(DOMAIN) "secure." #DOMAIN 

qui devrait changer IV_SECURE à

#define IV_SECURE "secure." "domain.org" 

qui concaténer automatiquement « secure.domain.org » (en supposant que les phases de traduction sont les mêmes en C que C++).

UNE AUTRE ÉDITION: S'il vous plaît, s'il vous plaît, lisez les commentaires, qui montrent comment j'ai réussi à être confus. Gardez à l'esprit que je suis complètement expérimenté en C, bien que peut-être un peu rouillé. Je supprimerais cette réponse, mais j'ai pensé que je la laisserais comme un exemple de la facilité avec laquelle le préprocesseur C est confus.

+0

## ne colle pas les chaînes, il colle les jetons. Différence subtile mais importante. –

+0

Malheureusement, la version de cpp qui s'exécute dans ma boîte cygwin ne comprend pas l'opérateur # tel que vous le décrivez. – mouviciel

+0

L'opérateur # ne fonctionne que sur les paramètres de la macro. C'est à dire. #define IV_SECURE (DOMAIN) "sécurisé". #DOMAIN Pas beaucoup d'aide dans ce cas. –

3

Comme d'autres l'ont noté, utiliser le collage jeton. Vous devez également savoir que les noms des macros comme

__TESTING 

sont réservés en C (ne savent pas Objective C) pour la mise en œuvre - vous n'êtes pas autorisé à les utiliser dans votre propre code. Les noms réservés sont tous les doubles traits de soulignement et tout début commençant par un trait de soulignement et une majuscule.

+0

L'objectif C est supposé être un surensemble strict de C, et ceci devrait donc être un avis valide. –

19

Il y a deux manières de le faire:

  1. si vous avez affaire à seulement littéraux de chaîne, vous pouvez simplement utiliser il suffit d'utiliser des chaînes - placer une chaîne littérale après l'autre au compilateur de les concaténer .

  2. s'il peut y avoir d'autres choses que des chaînes littérales impliqués (ie., Vous créez de nouveaux identifiants des macros) utilisent le '## "opérateur de coller jeton préprocesseur. Vous auriez probablement besoin aussi d'utiliser le « # » « opérateur stringizing pour faire vos macros dans les chaînes littérales

Un exemple de # 1:.

#ifdef __TESTING 
    #define IV_DOMAIN "domain.org"      //in house testing 
#elif __LIVE_TESTING 
    #define IV_DOMAIN "test.domain.com"   //live testing servers 
#else 
    #define IV_DOMAIN "domain.com"      //production 
#endif 

// Sub-Domain 
#define IV_SECURE "secure." IV_DOMAIN   //secure.domain.org etc 
#define IV_MOBILE "m." IV_DOMAIN 

Et pour autant que l'opérateur de coller jeton va, je ne pense pas que la plupart des réponses qui suggèrent L'utilisation de l'opérateur de préprocesseur de collage de jetons l'a effectivement essayé - il peut être difficile à utiliser.

En utilisant la réponse qui est souvent proposé entraînera une erreur de compilation lorsque vous essayez d'utiliser la IV_SECURE macro, parce que:

#define IV_SECURE "secure."##IV_DOMAIN 

étend à:

"secure"domain.org 

Vous pouvez essayer d'utiliser le ' # ` » opérateur 'stringizing':

#define IV_SECURE "secure." #IV_DOMAIN 

Mais tha Cela ne fonctionnera pas car cela ne fonctionne que sur des arguments de macro - pas n'importe quelle vieille macro. Une chose à savoir lorsque vous utilisez les opérateurs de pré-traitement token-paste ('##') ou de stringizing ('#') est que vous devez utiliser un niveau supplémentaire d'indirection pour qu'ils fonctionnent correctement dans tous les cas.

Si vous ne le faites pas et les éléments transmis à l'opérateur-coller de jeton sont eux-mêmes des macros, vous obtiendrez des résultats qui ne sont probablement pas ce que vous voulez:

#include <stdio.h> 

#define STRINGIFY2(x) #x 
#define STRINGIFY(x) STRINGIFY2(x) 
#define PASTE2(a, b) a##b 
#define PASTE(a, b) PASTE2(a, b) 

#define BAD_PASTE(x,y) x##y 
#define BAD_STRINGIFY(x) #x 

#define SOME_MACRO function_name 

int main() 
{ 
    printf("buggy results:\n"); 
    printf("%s\n", STRINGIFY(BAD_PASTE(SOME_MACRO, __LINE__))); 
    printf("%s\n", BAD_STRINGIFY(BAD_PASTE(SOME_MACRO, __LINE__))); 
    printf("%s\n", BAD_STRINGIFY(PASTE(SOME_MACRO, __LINE__))); 

    printf("\n" "desired result:\n"); 
    printf("%s\n", STRINGIFY(PASTE(SOME_MACRO, __LINE__))); 
} 

La sortie:

buggy results: 
SOME_MACRO__LINE__ 
BAD_PASTE(SOME_MACRO, __LINE__) 
PASTE(SOME_MACRO, __LINE__) 

desired result: 
function_name21 

donc, en utilisant votre IV_DOMAIN d'origine et les macros définit de utilty d'en haut, vous pouvez le faire pour obtenir ce que vous voulez:

// Sub-Domain 
#define IV_SECURE "secure." STRINGIFY(IV_DOMAIN) //secure.domain.org etc 
#define IV_MOBILE "m." STRINGIFY(IV_DOMAIN) 
6

Je vois beaucoup de bonnes et correctes réponses à votre première question, mais aucune à votre seconde, alors voici ceci: Je pense que c'est une idée terrible. Pourquoi devriez-vous reconstruire votre logiciel (en particulier la version finale) juste pour changer le nom du serveur? Aussi, comment saurez-vous quelle version de vos logiciels pointe sur quel serveur? Vous devrez construire un mécanisme pour vérifier à l'exécution. Si c'est pratique sur votre plate-forme, je vous recommande de charger les domaines/URL à partir d'un fichier de configuration. Seule la plus petite des plates-formes embarquées peut ne pas être "pratique" dans ce but :)