Frage

Ich mag eine „behaupten“ implementieren, die zur Laufzeit als Fehler im Fehlerfall Kompilation, eher verhindert.

ich zur Zeit ein wie folgt definiert haben, die großen Werke, die aber erhöht die Größe der Binärdateien.

#define MY_COMPILER_ASSERT(EXPRESSION) switch (0) {case 0: case (EXPRESSION):;}

Beispielcode (die kompilieren fehlschlägt).

#define DEFINE_A 1
#define DEFINE_B 1
MY_COMPILER_ASSERT(DEFINE_A == DEFINE_B);

Wie kann ich dies implementieren, so dass es keinen Code generieren (um die Größe der Binärdateien erzeugt zu minimieren)?

War es hilfreich?

Lösung

Eine Kompilierung-Assertion in reinem Standard-C möglich ist, und ein wenig Präprozessor Tricks macht seine Verwendung Look genauso sauber wie die Laufzeit Nutzung von assert().

Der Schlüssel Trick ist, ein Konstrukt zu finden, die bei der Kompilierung ausgewertet werden können und einen Fehler für einige Werte verursachen kann. Eine Antwort ist die Deklaration eines Arrays kann nicht eine negative Größe haben. ein typedef verhindert, dass die Zuweisung von Speicherplatz auf Erfolg, und bewahrt den Fehler bei einem Fehler.

Die Fehlermeldung selbst wird kryptisch zu Deklaration einer negativen Größe beziehen (GCC sagt „Größe der Arrays foo negativ“), so dass Sie einen Namen für den Array-Typen auswählen sollen, die Hinweise, dass dieser Fehler wirklich eine Behauptung Prüfung ist.

Ein weiteres Problem zu handhaben ist, dass es nur möglich ist, um einen bestimmten Typ Namen einmal in jeder Übersetzungseinheit typedef. So hat das Makro für jede Nutzung ordnen einen einzigartiger Art-Namen zu erhalten zu erklären.

Meine übliche Lösung ist, zu verlangen, dass der Makro zwei Parameter. Die erste ist die Bedingung zu behaupten, wahr ist, und der zweite Teil des Typnamen hinter den Kulissen erklärt. Die Antwort von Sockel Hinweisen auf Verwendung token einfügen und den __LINE__ vordefiniert Makro möglicherweise einen eindeutigen Namen zu bilden, ohne ein zusätzliches Argument erforderlich ist.

Leider, wenn die Behauptung Prüfung in einer Datei enthalten ist, kann es immer noch mit einem Scheck in der gleichen Zeilennummer in einem zweiten kollidieren Datei enthielt oder in dieser Zeilennummer in der Hauptquelldatei. Wir konnten Papier über die durch das Makro __FILE__ verwenden, aber es wird definiert, um ein String-Konstante und dort zu sein, ist kein Präprozessor Trick, der ein String-Konstante wieder in einem Teil eines Bezeichnernamen drehen kann; nicht zu erwähnen, dass die Namen rechtliche Datei Zeichen enthalten, die nicht legal Teile einer Kennung sind.

Also, ich würde das folgende Codefragment vorschlagen:

/** A compile time assertion check.
 *
 *  Validate at compile time that the predicate is true without
 *  generating code. This can be used at any point in a source file
 *  where typedef is legal.
 *
 *  On success, compilation proceeds normally.
 *
 *  On failure, attempts to typedef an array type of negative size. The
 *  offending line will look like
 *      typedef assertion_failed_file_h_42[-1]
 *  where file is the content of the second parameter which should
 *  typically be related in some obvious way to the containing file
 *  name, 42 is the line number in the file on which the assertion
 *  appears, and -1 is the result of a calculation based on the
 *  predicate failing.
 *
 *  \param predicate The predicate to test. It must evaluate to
 *  something that can be coerced to a normal C boolean.
 *
 *  \param file A sequence of legal identifier characters that should
 *  uniquely identify the source file in which this condition appears.
 */
#define CASSERT(predicate, file) _impl_CASSERT_LINE(predicate,__LINE__,file)

#define _impl_PASTE(a,b) a##b
#define _impl_CASSERT_LINE(predicate, line, file) \
    typedef char _impl_PASTE(assertion_failed_##file##_,line)[2*!!(predicate)-1];

Eine typische Anwendung etwas sein könnte:

#include "CAssert.h"
...
struct foo { 
    ...  /* 76 bytes of members */
};
CASSERT(sizeof(struct foo) == 76, demo_c);

In GCC, ein Assertionsfehler würde wie folgt aussehen:

$ gcc -c demo.c
demo.c:32: error: size of array `assertion_failed_demo_c_32' is negative
$

Andere Tipps

Das folgende COMPILER_VERIFY(exp) Makro funktioniert ziemlich gut.

// combine arguments (after expanding arguments)
#define GLUE(a,b) __GLUE(a,b)
#define __GLUE(a,b) a ## b

#define CVERIFY(expr, msg) typedef char GLUE (compiler_verify_, msg) [(expr) ? (+1) : (-1)]

#define COMPILER_VERIFY(exp) CVERIFY (exp, __LINE__)

Es funktioniert sowohl für C und C ++ und kann überall ein typedef erlaubt würde verwendet werden. Ist der Ausdruck wahr ist, erzeugt er ein typedef für eine Anordnung von 1 Zeichen (das harmlos ist). Wenn der Ausdruck falsch ist, erzeugt er ein typedef für eine Anordnung von -1 Zeichen, die in der Regel in einer Fehlermeldung resultieren. Der Ausdruck als arugment gegeben kann alles, was zu einer Kompilierung-Konstanten ausgewertet wird (so Ausdrücke mit sizeof () funktioniert). Dies macht es viel flexibler als

#if (expr)
#error
#endif

, wo Sie Ausdrücke beschränkt, die durch den Präprozessor ausgewertet werden kann.

Wenn Ihr Compiler eine Präprozessormakro wie DEBUG oder NDEBUG setzt man so etwas wie dieses machen kann (sonst könnte man dies in einem Makefile einrichten):

#ifdef DEBUG
#define MY_COMPILER_ASSERT(EXPRESSION)   switch (0) {case 0: case (EXPRESSION):;}
#else
#define MY_COMPILER_ASSERT(EXPRESSION)
#endif

Dann Compiler behauptet nur für Debug-Builds.

Die beste Zuschreibung, die ich auf statische Behauptungen in C finden könnte, ist unter pixelbeat . Beachten Sie, dass statische Behauptungen C ++ 0X hinzugefügt werden, und es kann in zu C1X machen, aber das ist nicht für eine Weile los sein. Ich weiß nicht, ob die Makros in dem Link Ich habe die Größe Ihrer Binärdateien erhöhen. Ich würde vermuten, sie würden nicht, zumindest wenn man auf einem vernünftigen Niveau der Optimierung kompilieren, aber die Leistung kann variieren.

Ich weiß, dass Sie in C interessiert sind, aber einen Blick auf boost der C ++ nehmen static_assert . (Übrigens, dies wahrscheinlich ist in C ++ 1x verfügbar werden.)

Wir haben getan etwas ähnliches, wieder für C ++:

#define COMPILER_ASSERT(expr)  enum { ARG_JOIN(CompilerAssertAtLine, __LINE__) = sizeof( char[(expr) ? +1 : -1] ) }

Dies funktioniert nur in C ++, offenbar. dieser Artikel einen Weg für die Verwendung in C zu modifizieren diskutiert.

Wie Leander sagte statische Behauptungen C ++ 11 hinzugefügt, und jetzt haben sie.

static_assert(exp, message)

Zum Beispiel

#include "myfile.hpp"

static_assert(sizeof(MyClass) == 16, "MyClass is not 16 bytes!")

void doStuff(MyClass object) { }

Sehen Sie die cppreference auf sie.

Wenn Sie Ihre endgültigen Binärdateien kompilieren, definieren MY_COMPILER_ASSERT leer zu sein, so daß sein Ausgang im Ergebnis nicht enthalten ist. Nur definiert es die Art und Weise haben Sie es für das Debuggen.

Aber wirklich, Sie gehen zu können, nicht jede Behauptung auf diese Weise zu fangen. Manche sind nur keinen Sinn bei der Kompilierung machen (wie die Behauptung, dass ein Wert nicht null ist). Alles, was Sie tun können, ist die Werte anderer #defines zu überprüfen. Ich bin nicht wirklich sicher, warum würden Sie das tun wollen.

Mit ‚#error‘ ist eine gültige Präprozessordefinition der Kompilierung auf den meisten Compiler zu stoppen verursacht. Sie können es so einfach tun, zum Beispiel, Kompilation im Debug zu verhindern:


#ifdef DEBUG
#error Please don't compile now
#endif

Ich fand dies die am wenigsten verwirrend Fehlermeldung für GCC zu geben. Alles hatte sonst etwas Suffix über eine negative Größe oder eine andere verwirrende Sache:

#define STATIC_ASSERT(expr, msg)   \
typedef char ______Assertion_Failed_____##msg[1];  __unused \
typedef char ______Assertion_Failed_____##msg[(expr)?1:2] __unused

Beispiel für die Verwendung:

 unsigned char testvar;
 STATIC_ASSERT(sizeof(testvar) >= 8, testvar_is_too_small);

Und die Fehlermeldung in gcc (ARM / GNU C Compiler: 6.3.1):

conflicting types for '______Assertion_Failed_____testvar_is_too_small'

Nun, könnten Sie die static asserts in der Boost-Bibliothek .

Was ich glaube, dass sie es tun, ist ein Array zu definieren.

 #define MY_COMPILER_ASSERT(EXPRESSION) char x[(EXPRESSION)];

Wenn Ausdruck wahr ist, es definiert char x[1];, was in Ordnung ist. Wenn false, definiert es char x[0];, die illegal ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top