Wie überprüfe ich 'typeof' auf den ungültigen Wert zur Kompilierzeit?

8

sagen wir, dass ich C-Makro haben möchte, das auf jedem Typ funktioniert. Ich benutze GCC-Compiler (& gt; = 4.6) und kann GNU99-Makros verwenden.

%Vor%

Verwendung von Makro für TIMER kann zum Beispiel so aussehen

%Vor%

Also muss TIMER den Wert der gegebenen Funktion und die Druckdauer des Laufs zurückgeben. Es gibt ein Problem mit Funktionen, die void return type haben.

Ich kann natürlich zwei Makros wie TIMER_TYPE und TIMER_VOID haben, aber ich möchte einzelne Funktionen mit jedem beliebigen Rückgabewert verwenden.

Danke für Vorschläge.

Bearbeitetes Beispiel für dieses TIMER-Makro

%Vor%     
DinGODzilla 17.09.2012, 04:33
quelle

3 Antworten

10

Was für eine interessante Frage, Kudos!

Nach einigen Experimenten habe ich eine Lösung gefunden, die __builtin_types_compatible_p und __builtin_choose_expr verwendet intrinsics von GCC.

__builtin_types_compatible_p

Zitat GCC-Handbuch:

  

Eingebaute Funktion: int __builtin_types_compatible_p (type1, type2)

     

Sie können die integrierte Funktion __builtin_types_compatible_p verwenden, um festzustellen, ob zwei Typen identisch sind.

     

Diese integrierte Funktion gibt 1 zurück, wenn die unqualifizierten Versionen der Typen type1 und type2 (welche Typen sind, keine Ausdrücke) kompatibel sind, 0 andernfalls. Das Ergebnis dieser integrierten Funktion kann in konstanten Ganzzahlausdrücken verwendet werden.

     

Diese integrierte Funktion ignoriert Qualifikationsmerkmale auf höchster Ebene (z. B. const , volatile ). Beispiel: int entspricht const int .

Hier können wir also nach " void ness" suchen.

%Vor%

__builtin_choose_expr

  

Eingebaute Funktion: type __builtin_choose_expr (const_exp, exp1, exp2)

     

Sie können die integrierte Funktion __builtin_choose_expr verwenden, um Code in Abhängigkeit vom Wert eines konstanten Ausdrucks auszuwerten. Diese integrierte Funktion gibt exp1 zurück, wenn const_exp , das ist ein ganzzahliger konstanter Ausdruck, ungleich Null ist. Andernfalls wird exp2 zurückgegeben.

     

Diese integrierte Funktion entspricht dem Operator ? : in C, mit der Ausnahme, dass der zurückgegebene Ausdruck von den Regeln für die Aktion nicht geändert wird. Außerdem wertet die integrierte Funktion den Ausdruck nicht aus, der nicht ausgewählt wurde . Wenn beispielsweise const_exp als wahr ausgewertet wird, wird exp2 nicht ausgewertet, selbst wenn es Nebenwirkungen hat.

     

Wenn exp1 zurückgegeben wird, entspricht der Rückgabetyp dem Typ exp1 . Wenn exp2 zurückgegeben wird, ist sein Rückgabetyp gleich dem exp2 .

So __builtin_choose_expr intrinsic ist so etwas wie ein "statischer Schalter", der zur Kompilierzeit ausgewertet wird.

Vorbereitung

Ich füge hier nicht dein TIMER -Makro ein, aber ich nehme an, es ist in der Lage, es in zwei Versionen zu teilen: eine für void expr und eine für den Rest. Hier sind nur Stubs, die den Ausdruck auswerten und das Ergebnis desselben Typs ergeben.

%Vor%

Naive Lösung

Nun können wir statisch zwischen zwei Implementierungen wechseln, abhängig vom tatsächlichen Typ des Ausdrucks. Aber in der Tat funktioniert die naive Lösung nicht, siehe unten.

%Vor%

Der Versuch, diesen Code zu kompilieren, der einen ungültigen Ausdruck übergibt, gibt den folgenden Fehler:

%Vor%

Obwohl __DO_VOID ausgewählt wurde, erzeugt __DO Fehler. Dieses Verhalten wird im Handbuch beschrieben:

  

... der unbenutzte Ausdruck ( exp1 oder exp2 abhängig vom Wert von const_exp ) kann immer noch Syntaxfehler erzeugen. Dies kann sich in zukünftigen Revisionen ändern.

Arbeitslösung

Der Trick besteht darin, das ursprüngliche void expr durch einen nicht-void Wert zu ersetzen, um den __DO case kompilieren zu können (was sowieso ein toter Code ist, wenn expr ungültig ist).

%Vor%

Das ist es! Hier ist der vollständige Quellcode auf Ideone: Ссылка

    
Eldar Abusalimov 16.03.2013, 11:37
quelle
2

können Sie die Antwort "das ist nicht wirklich möglich" akzeptieren?

nicht der Teil über das Zurückkehren von einem Makro. aber der Teil über bedingte Prüfung von Ausdruck für seinen Rückgabetyp.

Sie fragen nach etwas wie dem folgenden:

Nehmen wir an, anstelle eines magischen Checks namens "is_expr_type_void (expr)" übergeben Sie einfach zum Zeitpunkt des Aufrufs einfach eine 1 oder eine 0, um in der folgenden Variation Ihres Makros is_void oder! is_void anzuzeigen:

%Vor%

Das kann einfach nicht funktionieren. Der Präprozessor würde in allen Fällen, sowohl für ungültige als auch für nicht leere Funktionsaufrufe, Code für diese Bedingung erstellen. und beide Seiten würden für nicht-void Funktionen gut kompilieren. aber der Compiler würde immer den "else" -Teil der Bedingung ersticken, wenn TIMER mit einer void-Funktion aufgerufen wird ... trotz der Tatsache, dass der Code nie aufgerufen würde.

(Wenn es nun einen wirklich intelligenten Compiler gäbe, der sowohl erkennen könnte, dass es sich um toten Code handelt, als auch um ihn vor dem Kompilieren als Fehler zu dekomprimieren, hätten Sie Glück, aber ich denke nicht gcc 4.6 ist das schlau ...)

das lässt Sie mit was wäre eine bevorzugte Option eines #if (is_void) bedingten innerhalb der # define. aber das ist einfach nicht erlaubt. da diese Antwort darauf hinweist, dass eine ähnliche Frage zur bedingten Vorverarbeitung beantwortet werden soll, ist der Präprozessor nicht turing-complete .

so ... trotz Ihres Wunsches, ein einzelnes Makro zu haben, denke ich, dass Ihre einfachste Antwort darin besteht, eine für void-Funktionen und eine für Funktionen mit Rückgabewerten zu erstellen.

    
john.k.doe 20.09.2012 05:25
quelle
0

Wenn Sie wirklich vom Makro zurückkehren müssen, verwenden Sie stattdessen die Funktion inline .

    
sardok 19.09.2012 11:51
quelle