2009-04-28 12 views
24

Alternatywne Tytuły (aby wspomóc wyszukiwanie)C makr do tworzenia ciągów

  • Konwersja tokenu preprocesora na ciąg
  • Jak zrobić łańcuch char od wartości danego C makra?

oryginalne pytanie

Chciałbym użyć C#define zbudować dosłowne ciągi w czasie kompilacji.

Ciąg są domenami, które zmieniają dla debugowania, wydania itd

Chciałbym niektóre niektóre rzeczy jak poniżej:

#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" 

Ale preprocesor nie robi nic w „”

ocenić
  1. Czy istnieje sposób obejścia tego?
  2. Czy to nawet dobry pomysł?
+0

Możliwy duplikat [Konwersja tokena preprocesora na ciąg] (http://stackoverflow.com/questions/240353/convert-a-procesor -token-a-string) –

Odpowiedz

31

W języku C litery literowe są łączone automatycznie. Na przykład,

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

s1 i s2 są takie same ciąg.

Tak, dla swojego problemu, odpowiedź (bez tokena wklejania) jest

#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

Spróbuj pomocą operatora ##

#define IV_SECURE secure.##IV_DOMAIN 
+1

To nie rozwiązuje problemu "". – mouviciel

+0

Myślę, że OP było pytanie o konkatenacji ciąg (#), a nie token wklejanie (##). Nie padłem jednak na podłogę. Pytanie jest trochę niejednoznaczne. –

+0

Wklejenie znacznika ('##') lub przeciągnięcie ('#') nie będzie działało bez dodatkowych elementów. –

8

łańcuchów, które są następnie ze sobą są połączone przez kompilator C.

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

Potrzebne są operatory # i ## oraz automatyczne łączenie ciągów.

# operator przetwarzania wstępnego zmienia parametr makro na ciąg znaków. Operator ## wkleja razem dwa tokeny (na przykład parametry makro).

Możliwość, że przychodzi na myśl jest dla mnie

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

który powinien zmienić IV_SECURE do

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

który automatycznie złączyć do „secure.domain.org” (zakładając fazy tłumaczenia są takie same w C jak C++).

KOLEJNA EDYCJA: Proszę, przeczytaj komentarze, które pokazują, w jaki sposób udało mi się pomylić. Pamiętaj, że mam duże doświadczenie w C, chociaż być może odrobinę zardzewiały. Chciałbym usunąć tę odpowiedź, ale pomyślałem, że zostawię to jako przykład, jak łatwo jest pomylić się z preprocesorem C.

+0

## nie wkleja ciągów, wkleja tokeny. Subtelna, ale ważna różnica. –

+0

Niestety, wersja cpp działająca w moim polu cygwin nie rozumie operatora #, jak go opisujesz. – mouviciel

+0

Operator # działa tylko w przypadku parametrów makro. To znaczy. #define IV_SECURE (DOMAIN) "secure." #DOMAIN Nie ma zbytniej pomocy w tym przypadku. –

3

jak inni to zauważyli, użyj tokenu wklejanie. Należy również pamiętać, że nazwy makr jak

__TESTING 

są zarezerwowane w C (nie wiem o Objective C) do realizacji - nie wolno ich używać w swoim własnym kodzie. Zastrzeżone nazwy to cokolwiek zawierającego podwójne podkreślenia i wszystko zaczynające się od podkreślenia i wielkiej litery.

+0

Cel C ma być ścisłym nadzbiorem C, więc powinno to być ważne. –

19

Istnieje kilka sposobów, aby to zrobić:

  1. jeśli masz do czynienia z zaledwie napisowych, można po prostu użyć po prostu użyć ciągi - umieszczenie jeden ciąg dosłowne po drugim powoduje kompilator je złączyć .

  2. czy mogą istnieć inne rzeczy niż napisowych zaangażowanych (tj., Tworzysz nowe identyfikatory z makrami) używać '## "tokenu preprocesora operatora tapetowania. Wy pewnie też trzeba użyć«#» „stringizing operatorowi dokonać makra do dosłownych ciągów

przykładem # 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 

I o ile token operator wklejanie idzie, nie sądzę, że większość odpowiedzi, które sugerują z użyciem procesora wklejającego token wypróbował go - może być trudny w użyciu.

Korzystanie odpowiedź, która jest często sugerowane spowoduje błąd kompilatora podczas próby korzystania z IV_SECURE makro, ponieważ:

#define IV_SECURE "secure."##IV_DOMAIN 

rozszerza się:

"secure"domain.org 

Możecie spróbować używać operatora naciągania ' # '' 'stringizing':

#define IV_SECURE "secure." #IV_DOMAIN 

Ale tha t nie działa, ponieważ działa tylko na argumentach makr - nie tylko na starym makrze.

Należy pamiętać o tym, że podczas korzystania z operatorów przetwarzania wstępnego token-paste ('##') lub szeregowych ('#') konieczne jest stosowanie dodatkowego poziomu niezależności, aby działały poprawnie we wszystkich przypadkach.

Jeśli tego nie zrobi i przedmioty przekazywane do operatora token-wklejania są makra się, dostaniesz wyniki, które są prawdopodobnie nie co chcesz:

#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__))); 
} 

Wyjście:

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

desired result: 
function_name21 

Więc używając oryginalnych IV_DOMAIN definiuje i makr utilty z góry, można to zrobić, aby uzyskać to, co chcesz:

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

Widzę wiele dobrych i poprawnych odpowiedzi na twoje pierwsze pytanie, ale nie na twoją drugą, więc oto jest to: Myślę, że to okropny pomysł. Dlaczego powinieneś przebudować swoje oprogramowanie (szczególnie wersję wydań) tylko po to, aby zmienić nazwę serwera? Ponadto, skąd będziesz wiedzieć, która wersja twojego oprogramowania wskazuje na którym serwerze? Będziesz musiał wbudować mechanizm do sprawdzania w czasie wykonywania. Jeśli jest to w ogóle praktyczne na twojej platformie, polecam załadować domeny/adresy URL z pliku konfiguracyjnego. Tylko najmniejsza z wbudowanych platform może nie być "praktyczna" do tego celu :)