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%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 Typentype1
undtype2
(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
entsprichtconst int
.
Hier können wir also nach " void
ness" suchen.
__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 gibtexp1
zurück, wennconst_exp
, das ist ein ganzzahliger konstanter Ausdruck, ungleich Null ist. Andernfalls wirdexp2
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 beispielsweiseconst_exp
als wahr ausgewertet wird, wirdexp2
nicht ausgewertet, selbst wenn es Nebenwirkungen hat.Wenn
exp1
zurückgegeben wird, entspricht der Rückgabetyp dem Typexp1
. Wennexp2
zurückgegeben wird, ist sein Rückgabetyp gleich demexp2
.
So __builtin_choose_expr
intrinsic ist so etwas wie ein "statischer Schalter", der zur Kompilierzeit ausgewertet wird.
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.
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
oderexp2
abhängig vom Wert vonconst_exp
) kann immer noch Syntaxfehler erzeugen. Dies kann sich in zukünftigen Revisionen ändern.
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).
Das ist es! Hier ist der vollständige Quellcode auf Ideone: Ссылка
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.
Tags und Links c compile-time types c-preprocessor void