2009-12-30 6 views
22

Poiché boost è vietato in un'azienda per cui lavoro, devo implementare la sua funzionalità in puro C++. Ho esaminato le fonti di spinta, ma sembrano essere troppo complesse per capire, almeno per me. So che c'è qualcosa chiamato static_assert() nello standard di C++ 0x, ma mi piacerebbe non usare nessuna caratteristica di C++ 0x.BOOST_STATIC_ASSERT senza boost

+0

fatto chiedi perché non ti è permesso usare boost? –

+3

Nessuno si è preso la briga di costruire il caso in modo che la squadra dell'avvocato ne approvasse l'uso? – AProgrammer

+1

@Gregory Pakosz, dicono perché è troppo complesso :) – Konstantin

risposta

19
template<bool> struct StaticAssert; 
template<> struct StaticAssert<true> {}; 

int main() { 
    StaticAssert< (4>3) >(); //OK 
    StaticAssert< (2+2==5) >(); //ERROR 
} 
+0

+1 abbastanza semplice, ma mi piace avere un messaggio associato all'asserzione –

+0

@Gregory: 'StaticAssert associatedMessage();' –

+1

ci sono luoghi in cui non vuoi/non puoi usare le variabili anche se –

3

Si potrebbe semplicemente copiare la macro dal Boost source file per il proprio codice. Se non hai bisogno di supportare tutti i compilatori Boost supporta puoi semplicemente scegliere la definizione giusta per il tuo compilatore e omettere il resto degli #ifdef s in quel file.

+2

È legale con la licenza di Boost? – gatopeich

23

Un altro trucco (che può essere utilizzato in C) è quello di cercare di costruire una matrice con una dimensione negativo se l'asserzione venga meno

#define ASSERT(cond) int foo[(cond) ? 1 : -1] 

come bonus, si può utilizzare un typedef invece di un oggetto, in modo che sia utilizzabile in più contesti e non lo fa avviene quando si riesce:

#define ASSERT(cond) typedef int foo[(cond) ? 1 : -1] 

infine, costruire un nome con meno possibilità di nome scontro (e riutilizzabili almeno in linee diverse):

#define CAT_(a, b) a ## b 
#define CAT(a, b) CAT_(a, b) 
#define ASSERT(cond) typedef int CAT(AsSeRt, __LINE__)[(cond) ? 1 : -1] 
+1

Qualcuno potrebbe spiegare perché sono necessarie due macro CAT? Che problema stai cercando di evitare? Grazie. – grokus

+6

Se non lo fai, gli argomenti che sono macro (come '__LINE__') non sono espansi. Quindi genererebbe 'AsSeRt__LINE__' invece del desiderato' AsSeRt42'. Sono abbastanza sicuro che ci sia una domanda da qualche parte che spiega questo in dettaglio. – AProgrammer

2

Sto usando il seguente file di intestazione, con il codice di strappato da qualcun altro ...

#ifndef STATIC_ASSERT__H 
#define STATIC_ASSERT__H 

/* ripped from http://www.pixelbeat.org/programming/gcc/static_assert.html */ 

#define ASSERT_CONCAT_(a, b) a##b 
#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b) 
/* These can't be used after statements in c89. */ 
#ifdef __COUNTER__ 
    /* microsoft */ 
    #define STATIC_ASSERT(e) enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) } 
#else 
    /* This can't be used twice on the same line so ensure if using in headers 
    * that the headers are not included twice (by wrapping in #ifndef...#endif) 
    * Note it doesn't cause an issue when used on same line of separate modules 
    * compiled with gcc -combine -fwhole-program. */ 
    #define STATIC_ASSERT(e) enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) } 
#endif 

/* http://msdn.microsoft.com/en-us/library/ms679289(VS.85).aspx */ 
#ifndef C_ASSERT 
#define C_ASSERT(e) STATIC_ASSERT(e) 
#endif 

#endif 
16

Qui è la mia propria implementazione di asserzioni statici estratte dalla mia base di codice: Pre-C++11 Static Assertions Without Boost.

Usage:

STATIC_ASSERT(expression, message);

Quando il test di asserzione statica fallisce, viene generato un messaggio di errore del compilatore che contiene in qualche modo la STATIC_ASSERTION_FAILED_AT_LINE_xxx_message.

message deve essere un identificatore valido C++, come no_you_cant_have_a_pony che produrrà un errore di compilazione contenente:

STATIC_ASSERTION_FAILED_AT_LINE_1337_no_you_cant_have_a_pony :)

#define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) 
#define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) 
#define CONCATENATE2(arg1, arg2) arg1##arg2 

/** 
* Usage: 
* 
* <code>STATIC_ASSERT(expression, message)</code> 
* 
* When the static assertion test fails, a compiler error message that somehow 
* contains the "STATIC_ASSERTION_FAILED_AT_LINE_xxx_message" is generated. 
* 
* /!\ message has to be a valid C++ identifier, that is to say it must not 
* contain space characters, cannot start with a digit, etc. 
* 
* STATIC_ASSERT(true, this_message_will_never_be_displayed); 
*/ 

#define STATIC_ASSERT(expression, message)\ 
    struct CONCATENATE(__static_assertion_at_line_, __LINE__)\ 
    {\ 
    implementation::StaticAssertion<static_cast<bool>((expression))> CONCATENATE(CONCATENATE(CONCATENATE(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__), _), message);\ 
    };\ 
    typedef implementation::StaticAssertionTest<sizeof(CONCATENATE(__static_assertion_at_line_, __LINE__))> CONCATENATE(__static_assertion_test_at_line_, __LINE__) 

    // note that we wrap the non existing type inside a struct to avoid warning 
    // messages about unused variables when static assertions are used at function 
    // scope 
    // the use of sizeof makes sure the assertion error is not ignored by SFINAE 

namespace implementation { 

    template <bool> 
    struct StaticAssertion; 

    template <> 
    struct StaticAssertion<true> 
    { 
    }; // StaticAssertion<true> 

    template<int i> 
    struct StaticAssertionTest 
    { 
    }; // StaticAssertionTest<int> 

} // namespace implementation 


STATIC_ASSERT(true, ok); 
STATIC_ASSERT(false, ko); 

int main() 
{ 
    return 0; 
} 
+6

+1 per "no_you_cant_have_a_pony" –

+2

Molto bello! Questa è una soluzione completa (e una buona alternativa all'implementazione di boost se vuoi evitare l'auto-boost) +1 – Samaursa

3

Credo che questo dovrebbe funzionare:

template<bool> struct CompileTimeAssert; 
template<> struct CompileTimeAssert<true>{}; 
#define STATIC_ASSERT(e) (CompileTimeAssert <(e) != 0>())