wake-up-neo.com

So vergleichen Sie Strings in C-Pre-Prozessor-Direktiven

Ich muss so etwas in C machen. Es funktioniert nur, wenn ich ein Zeichen verwende, aber ich brauche einen String. Wie kann ich das machen?

#define USER "jack" // jack or queen

#if USER == "jack"
#define USER_VS "queen"
#Elif USER == "queen"
#define USER_VS "jack"
#endif
72
frx08

Ich glaube nicht, dass es eine Möglichkeit gibt, String-Vergleiche mit variabler Länge vollständig in Präprozessoranweisungen durchzuführen. Sie könnten vielleicht Folgendes tun:

#define USER_JACK 1
#define USER_QUEEN 2

#define USER USER_JACK 

#if USER == USER_JACK
#define USER_VS USER_QUEEN
#Elif USER == USER_QUEEN
#define USER_VS USER_JACK
#endif

Oder Sie können den Code ein wenig umgestalten und stattdessen C-Code verwenden. 

55
Brian R. Bondy

[UPDATE: 2018.05.03]

[~ # ~] Einschränkung [~ # ~] : Nicht alle Compiler implementieren die C++ 11-Spezifikation auf dieselbe Weise. Der folgende Code funktioniert in dem Compiler, auf dem ich getestet habe, während viele Kommentatoren einen anderen Compiler verwendeten.

Zitat aus Shafik Yaghmours Antwort um: Berechnen der Länge eines C-Strings zur Kompilierungszeit. Ist das wirklich ein Constexpr?

Es ist nicht garantiert, dass konstante Ausdrücke zur Kompilierungszeit ausgewertet werden. Wir haben nur ein nicht normatives Zitat aus dem Entwurf des C++ - Standards, Abschnitt 5.19 Konstante Ausdrücke, das dies jedoch sagt:

[...]> [Hinweis: Konstante Ausdrücke können während der Übersetzung ausgewertet werden. - Endnote]

Dieses Wort can macht den ganzen Unterschied in der Welt.

Also, YMMV zu dieser (oder jeder anderen) Antwort mit constexpr, abhängig von der Interpretation der Spezifikation durch den Compiler-Writer.

[AKTUALISIERT am 31.01.2016]

Da einige meine frühere Antwort nicht mochten, weil sie den gesamten compile time string compare - Aspekt des OP vermieden , indem sie das Ziel ohne Stringvergleiche erreichten, hier ist ein detailliertere Antwort.

Das kannst du nicht! Nicht in C98 oder C99. Nicht einmal in C11. Kein Ausmaß an MAKRO-Manipulationen wird dies ändern.

Die in const-expression Verwendete Definition von #if Lässt keine Zeichenfolgen zu.

Zeichen sind zulässig. Wenn Sie sich also auf Zeichen beschränken, können Sie Folgendes verwenden:

#define JACK 'J'
#define QUEEN 'Q'

#define CHOICE JACK     // or QUEEN, your choice

#if 'J' == CHOICE
#define USER "jack"
#define USER_VS "queen"
#Elif 'Q' == CHOICE
#define USER "queen"
#define USER_VS "jack"
#else
#define USER "anonymous1"
#define USER_VS "anonymous2"
#endif

#pragma message "USER    IS " USER
#pragma message "USER_VS IS " USER_VS

Du kannst! In C++ 11. Wenn Sie eine Hilfsfunktion zur Kompilierungszeit für den Vergleich definieren.

// compares two strings in compile time constant fashion
constexpr int c_strcmp( char const* lhs, char const* rhs )
{
    return (('\0' == lhs[0]) && ('\0' == rhs[0])) ? 0
        :  (lhs[0] != rhs[0]) ? (lhs[0] - rhs[0])
        : c_strcmp( lhs+1, rhs+1 );
}
// some compilers may require ((int)lhs[0] - (int)rhs[0])

#define JACK "jack"
#define QUEEN "queen"

#define USER JACK       // or QUEEN, your choice

#if 0 == c_strcmp( USER, JACK )
#define USER_VS QUEEN
#Elif 0 == c_strcmp( USER, QUEEN )
#define USER_VS JACK
#else
#define USER_VS "unknown"
#endif

#pragma message "USER    IS " USER
#pragma message "USER_VS IS " USER_VS

Letztendlich müssen Sie also die Art und Weise ändern, in der Sie Ihr Ziel erreichen, endgültige Zeichenfolgenwerte für USER und USER_VS Auszuwählen.

Sie können in C99 keine Zeichenfolgenvergleiche für die Kompilierungszeit durchführen, aber Sie können Zeichenfolgen für die Kompilierungszeit auswählen.

Wenn Sie wirklich Zeitstich-Vergleiche kompilieren müssen, müssen Sie zu C++ 11 oder neueren Varianten wechseln, die diese Funktion zulassen.

[ORIGINALE ANTWORT FOLGT]

Versuchen:

#define jack_VS queen
#define queen_VS jack

#define USER jack          // jack    or queen, your choice
#define USER_VS USER##_VS  // jack_VS or queen_VS

// stringify usage: S(USER) or S(USER_VS) when you need the string form.
#define S(U) S_(U)
#define S_(U) #U

UPDATE: Das Einfügen von ANSI-Token ist manchmal weniger als offensichtlich. ;-D

Wenn Sie einem Makro ein einzelnes # Voranstellen, wird es in eine Zeichenfolge mit dem Wert und nicht mit dem bloßen Wert geändert.

Wenn Sie ein doppeltes ## Zwischen zwei Token setzen, werden diese zu einem einzigen Token verkettet.

Das Makro USER_VS Hat also die Erweiterung jack_VS Oder queen_VS, Je nachdem, wie Sie USER eingestellt haben.

Das stringify Makro S(...) verwendet die Makro-Indirektion, sodass der Wert des genannten Makros in einen String umgewandelt wird. anstelle des Namens des Makros.

So wird USER##_VS Zu jack_VS (Oder queen_VS), Je nachdem, wie Sie USER eingestellt haben.

Wenn später das stringify -Makro als S(USER_VS) verwendet wird, wird der Wert von USER_VS (jack_VS In diesem Beispiel) übergeben zu dem Indirektionsschritt S_(jack_VS), der seinen Wert (queen) in einen String "queen" umwandelt.

Wenn Sie USER auf queen setzen, ist das Endergebnis die Zeichenfolge "jack".

Informationen zur Token-Verkettung finden Sie unter: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

Informationen zur Konvertierung von Token-Strings finden Sie unter: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification

[UPDATED 2015.02.15, um einen Tippfehler zu korrigieren.]

20
Jesse Chisholm

Verwenden Sie numerische Werte anstelle von Zeichenfolgen.

Um die Konstanten JACK oder QUEEN in eine Zeichenfolge zu konvertieren, verwenden Sie die Operatoren stringize (und/oder tokenize).

7
Patrick

Folgendes arbeitete für mich mit Klängen. Erlaubt, was als symbolischer Makrowertvergleich angezeigt wird. #error xxx ist nur zu sehen, was der Compiler wirklich macht. Ersetzen katze Definition mit #define Katze (a, b) a ## b bricht Dinge.

#define cat(a,...) cat_impl(a, __VA_ARGS__)
#define cat_impl(a,...) a ## __VA_ARGS__

#define xUSER_jack 0
#define xUSER_queen 1
#define USER_VAL cat(xUSER_,USER)

#define USER jack // jack or queen

#if USER_VAL==xUSER_jack
  #error USER=jack
  #define USER_VS "queen"
#Elif USER_VAL==xUSER_queen
  #error USER=queen
  #define USER_VS "jack"
#endif

Wie oben bereits erwähnt, unterstützt der Präprozessor für ISO-C11 nicht den Stringvergleich. Das Problem der Zuweisung eines Makros mit dem "entgegengesetzten Wert" kann jedoch mit "Token-Einfügen" und "Tabellenzugriff" gelöst werden. Jesses einfache Makrelösung verkettet/stringify schlägt mit gcc 5.4.0 fehl, da die Zeichenfolge vor der Auswertung der Verkettung (gemäß ISO C11) durchgeführt wird. Es kann jedoch behoben werden:

#define P_(user) user ## _VS
#define VS(user) P_ (user)
#define S(U) S_(U)
#define S_(U) #U

#define jack_VS  queen
#define queen_VS jack

S (VS (jack))
S (jack)
S (VS (queen))
S (queen)

#define USER jack          // jack    or queen, your choice
#define USER_VS USER##_VS  // jack_VS or queen_VS
S (USER)
S (USER_VS)

Die erste Zeile (Makro P_()) fügt eine Indirektion hinzu, damit die nächste Zeile (Makro VS()) die Verkettung vor der Stringisierung beenden kann (siehe Warum brauche ich eine doppelte Indirektionsschicht für Makros? ). Die Stringisierungsmakros (S() und S_()) stammen von Jesse.

Die Tabelle (Makros jack_VS und queen_VS), die viel einfacher zu warten ist als die if-then-else-Konstruktion des OP, stammt von Jesse. 

Der nächste vierzeilige Block ruft schließlich die Makros im Funktionsstil auf. Der letzte Block mit vier Zeilen stammt von Jesse.

Wenn Sie den Code in foo.c speichern und den Präprozessor gcc -nostdinc -E foo.c aufrufen, erhalten Sie:

# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "foo.c"
# 9 "foo.c"
"queen"
"jack"
"jack"
"queen"



"jack"
"USER_VS"

Die Ausgabe ist wie erwartet. Die letzte Zeile zeigt, dass das Makro USER_VSnicht vor der Stringisierung erweitert wird.

1
hermannk

Die Antwort von Patrick und von Jesse Chisholm brachte mich dazu, folgendes zu tun:

#define QUEEN 'Q'
#define JACK 'J'

#define CHECK_QUEEN(s) (s==QUEEN?1:0)
#define CHECK_JACK(s) (s==JACK?1:0)

#define USER 'Q'

[... later on in code ...]

#if CHECK_QUEEN(USER)
  compile_queen_func();
#Elif CHECK_JACK(USER)
  compile_jack_func();
#Elif
#error "unknown user"
#endif

Anstelle von #define USER 'Q'#define USER QUEEN sollte auch funktionieren aber wurde nicht getestet funktioniert auch und könnte einfacher zu handhaben sein.

1
benni

Wenn Ihre Zeichenfolgen (wie in Ihrem Fall) Kompilierungszeitkonstanten sind, können Sie den folgenden Trick verwenden:

#define USER_JACK strcmp(USER, "jack")
#define USER_QUEEN strcmp(USER, "queen")
#if $USER_JACK == 0
#define USER_VS USER_QUEEN
#Elif USER_QUEEN == 0
#define USER_VS USER_JACK
#endif

Der Compiler kann das Ergebnis des strcmp im Voraus feststellen und wird das strcmp durch sein Ergebnis ersetzen. Dadurch erhalten Sie ein #Define, das mit den Präprozessoranweisungen verglichen werden kann. Ich weiß nicht, ob es Abweichungen zwischen Compilern/Abhängigkeiten von Compileroptionen gibt, aber bei GCC 4.7.2 hat es für mich funktioniert.

BEARBEITEN: Nach weiteren Untersuchungen sieht es so aus, als sei dies eine Toolchain-Erweiterung, nicht eine GCC-Erweiterung.

0
EpsilonVector
#define USER_IS(c0,c1,c2,c3,c4,c5,c6,c7,c8,c9)\
ch0==c0 && ch1==c1 && ch2==c2 && ch3==c3 && ch4==c4 && ch5==c5 && ch6==c6 && ch7==c7 ;

#define ch0 'j'
#define ch1 'a'
#define ch2 'c'
#define ch3 'k'

#if USER_IS('j','a','c','k',0,0,0,0)
#define USER_VS "queen"
#Elif USER_IS('q','u','e','e','n',0,0,0)
#define USER_VS "jack"
#endif

im Grunde handelt es sich dabei um ein statisches Char-Array mit fester Länge, das manuell initialisiert wurde. _ statt einem statischen Char-Array mit variabler Länge, das automatisch initialisiert wird und immer mit einem abschließenden Null-Char endet

0
yan bellavance