2009-12-30 16 views
22

Como el impulso está prohibido en una empresa para la que trabajo, necesito implementar su funcionalidad en C++ puro. He buscado fuentes de impulso, pero parecen ser demasiado complejas para comprenderlas, al menos para mí. Sé que hay algo llamado static_assert() en el estándar C++ 0x, pero me gustaría no utilizar ninguna característica C++ 0x.BOOST_STATIC_ASSERT sin impulso

+0

hicieron preguntas por qué no puedes usar boost –

+3

¿Nadie se tomó el trabajo de construir el caso para que el equipo del abogado diera su aprobación para su uso? – AProgrammer

+1

@Gregory Pakosz, dicen porque es demasiado complejo :) – Konstantin

Respuesta

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

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

+1 lo suficientemente simple, pero me gusta tener un mensaje asociado con la afirmación –

+0

@Gregory: 'StaticAssert < (2+2==5) > asociadoMensaje();' –

+1

hay lugares donde no desea/no puede usar variables aunque –

3

Simplemente puede copiar la macro del Boost source file en su propio código. Si no necesita admitir todos los compiladores compatibles con Boost, puede elegir la definición correcta para su compilador y omitir el resto de #ifdef en ese archivo.

+2

no ignore el error de afirmación. ¿Es eso legal con la licencia de Boost? – gatopeich

23

Otro truco (que puede ser usado en C) es tratar de construir una matriz con un tamaño negativo si la aserción falle:

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

como un bono, usted puede utilizar un typedef en lugar de una objeto, de modo que sea utilizable en más contextos y no lo hace, tendrá lugar cuando tenga éxito:

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

finalmente, construir un nombre con menos posibilidades de conflicto de nombres (y reutilizables, al menos, en diferentes líneas):

#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

¿Podría alguien explicar por qué son necesarias dos macros CAT? ¿Qué problema estás tratando de evitar? Gracias. – grokus

+6

Si no lo hace, los argumentos que son macros (como '__LINE__') no se expanden. Entonces generaría 'AsSeRt__LINE__' en lugar del' AsSeRt42' deseado. Estoy bastante seguro de que hay una pregunta en algún lugar explicando esto en detalle. – AProgrammer

2

Estoy utilizando el siguiente archivo de cabecera, con el código arrancado de otra persona ...

#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

Aquí es mi propia implementación de las afirmaciones estáticas extraídos de mi base de código: Pre-C++11 Static Assertions Without Boost.

Uso:

STATIC_ASSERT(expression, message);

Cuando falla la prueba de la afirmación estática, se genera un mensaje de error del compilador que de algún modo contiene el STATIC_ASSERTION_FAILED_AT_LINE_xxx_message.

message tiene que ser un identificador válido C++, como no_you_cant_have_a_pony que producirá un error de compilación que contiene:

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 para "no_you_cant_have_a_pony" –

+2

¡Muy bonito! Esta es una solución completa (y una buena alternativa para la implementación de boost si quieres evitar el impulso) +1 – Samaursa

3

Creo que esto debería funcionar:

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