2010-08-02 6 views

Respuesta

52

C-1x añade la palabra clave _Static_assert.

Esto parece ser implementado en gcc-4.6:

_Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */ 

La primera ranura necesita ser una expresión constante integral. La segunda ranura es una cadena constante literal que puede ser larga (_Static_assert(0, L"assertion of doom!")).

Debo señalar que esto también se implementa en las versiones recientes de clang.

+1

* [... parece ser implementado por gcc, por clang ...] * Puede ser más * asertivo * que eso ;-) '_Static_assert' es parte del estándar C11 y cualquier compilador que admita C11, tendrá eso. –

+0

¿Se puede utilizar esto en el alcance del archivo (fuera de cualquier función)?Porque obtengo 'error: especificadores de declaración esperados o '...' antes de 'sizeof'' para la línea' static_assert (sizeof (int) == sizeof (long int), "¡Error!);' (Estoy usando C no C++ por cierto) – user10607

+0

@ user10607 Me sorprende que esto no funcione ... Espere, le falta una cita al final de su cadena de error. Póngala y vuélvala. Esto funciona para mí en gcc-4.9: '_Static_assert (sizeof (int) == sizeof (long int)," ¡Error! ");' En mi macine recibo el error. – emsr

4

De Wikipedia:

#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;} 

COMPILE_TIME_ASSERT(BOOLEAN CONDITION); 
+7

No funciona fuera de funciones. –

+13

Sería mejor si se vinculara con la fuente verdadera: http://www.jaggersoft.com/pubs/CVu11_3.html –

+0

No funciona en gcc 4.6 - dice "la etiqueta de la caja no se reduce a una constante entera" . Tiene un punto. – Liosan

70

Esto funciona en la función y el alcance de no funcionamiento (pero no dentro de estructuras, uniones).

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1] 

STATIC_ASSERT(1,this_should_be_true); 

int main() 
{ 
STATIC_ASSERT(1,this_should_be_true); 
} 
  1. Si la afirmación tiempo de compilación no podía ser igualada, a continuación, un mensaje de casi inteligible se genera por GCC sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative

  2. La macro podría o debería cambiarse para generar un nombre único para el typedef (es decir, concatenar __LINE__ al final del nombre static_assert_...)

  3. en lugar de un ternario, esto podría ser utilizado, así #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1] que pasa a w ork incluso en el compilador olde cc65 oxidado (para la CPU 6502).

ACTUALIZACIÓN: Para completarlo, aquí está la versión con __LINE__

#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1] 
// token pasting madness: 
#define COMPILE_TIME_ASSERT3(X,L) STATIC_ASSERT(X,static_assertion_at_line_##L) 
#define COMPILE_TIME_ASSERT2(X,L) COMPILE_TIME_ASSERT3(X,L) 
#define COMPILE_TIME_ASSERT(X) COMPILE_TIME_ASSERT2(X,__LINE__) 

COMPILE_TIME_ASSERT(sizeof(long)==8); 
int main() 
{ 
    COMPILE_TIME_ASSERT(sizeof(int)==4); 
} 

Update2: código específico GCC

GCC 4.3 (supongo) presentó el "error" y atributos de función de "advertencia". Si una llamada a una función con ese atributo no puede eliminarse mediante la eliminación del código muerto (u otras medidas), se genera un error o advertencia. Esto se puede usar para hacer que el tiempo de compilación se base en descripciones de fallas definidas por el usuario. Queda por determinar la forma en que se pueden utilizar en su alcance espacio de nombres sin tener que recurrir a una función dummy:

#define CTC(X) ({ extern int __attribute__((error("assertion failure: '" #X "' not true"))) compile_time_check(); ((X)?0:compile_time_check()),0; }) 

// never to be called.  
static void my_constraints() 
{ 
CTC(sizeof(long)==8); 
CTC(sizeof(int)==4); 
} 

int main() 
{ 
} 

Y así es como se ve:

$ gcc-mp-4.5 -m32 sas.c 
sas.c: In function 'myc': 
sas.c:7:1: error: call to 'compile_time_check' declared with attribute error: assertion failure: `sizeof(int)==4` not true 
+1

En Visual Studio solo dice "Subíndice negativo", sin mencionar el nombre de la variable ... – szx

+0

Mainframe nórdico: la opción 3 en su respuesta no funciona en clang. – Elazar

+1

Con respecto a la última solución (GCC 4.3 +): Esto es muy poderoso, ya que puede verificar cualquier cosa que el optimizador pueda descubrir, pero falla si la optimización no está habilitada. El nivel mínimo de optimización ('-Og') a menudo puede ser suficiente para que esto funcione, y no debe interferir con la depuración. Uno puede considerar hacer la afirmación estática como no operativa o de tiempo de ejecución si "__OPTIMIZE__' (y' __GNUC__') no está definido. –

11

cl

sé la pregunta menciona explícitamente gcc, pero para completarlo aquí hay una modificación para los compiladores de Microsoft.

Usando la matriz de tamaño negativo typedef no persuade cl para escupir un error decente. Simplemente dice error C2118: negative subscript. Un campo de bits de ancho cero tiene mejores resultados a este respecto. Como esto implica deshacerse de una estructura, necesitamos usar nombres de tipos únicos. __LINE__ no corta la mostaza — es posible tener un COMPILE_TIME_ASSERT() en la misma línea en un encabezado y un archivo fuente, y su compilación se romperá. __COUNTER__ viene al rescate (y ha estado en gcc desde 4.3).

#define CTASTR2(pre,post) pre ## post 
#define CTASTR(pre,post) CTASTR2(pre,post) 
#define STATIC_ASSERT(cond,msg) \ 
    typedef struct { int CTASTR(static_assertion_failed_,msg) : !!(cond); } \ 
     CTASTR(static_assertion_failed_,__COUNTER__) 

Ahora

STATIC_ASSERT(sizeof(long)==7, use_another_compiler_luke) 

bajo cl da:

error C2149: 'static_assertion_failed_use_another_compiler_luke' : named bit field cannot have zero width

gcc también da un mensaje inteligible:

error: zero width for bit-field ‘static_assertion_failed_use_another_compiler_luke’

0

Para aquellos de ustedes que desean algo realmente básico y portátil, pero no tienen acceso a las funciones de C++ 11, he escrito solo eso.
Utilice STATIC_ASSERT normalmente (puede escribirlo dos veces en la misma función si lo desea) y use GLOBAL_STATIC_ASSERT fuera de las funciones con una frase única como primer parámetro.

#if defined(static_assert) 
# define STATIC_ASSERT static_assert 
# define GLOBAL_STATIC_ASSERT(a, b, c) static_assert(b, c) 
#else 
# define STATIC_ASSERT(pred, explanation); {char assert[1/(pred)];(void)assert;} 
# define GLOBAL_STATIC_ASSERT(unique, pred, explanation); namespace ASSERTATION {char unique[1/(pred)];} 
#endif 

GLOBAL_STATIC_ASSERT(first, 1, "Hi"); 
GLOBAL_STATIC_ASSERT(second, 1, "Hi"); 

int main(int c, char** v) { 
    (void)c; (void)v; 
    STATIC_ASSERT(1 > 0, "yo"); 
    STATIC_ASSERT(1 > 0, "yo"); 
// STATIC_ASSERT(1 > 2, "yo"); //would compile until you uncomment this one 
    return 0; 
} 

Explicación:
En primer lugar se comprueba si tiene la aserción real, lo que definitivamente desea a utilizar si está disponible.
Si no lo hace, obtiene su ındice pred, y lo divide solo. Esto hace dos cosas.
Si es cero, id est, la aserción ha fallado, causará un error de división por cero (la aritmética es forzada porque está tratando de declarar una matriz).
Si no es cero, normaliza el tamaño de la matriz a 1. Entonces, si la afirmación pasó, no le gustaría que fallara de todos modos porque su predicado se evaluó a -1 (inválido), o sea 232442 (pérdida masiva de espacio, IDK si fuera optimizado).
Para STATIC_ASSERT está envuelto en llaves, esto lo convierte en un bloque, que abarca la variable assert, lo que significa que puede escribirlo muchas veces.
También lo arroja al void, que es una forma conocida de deshacerse de las advertencias unused variable.
Para GLOBAL_STATIC_ASSERT, en lugar de estar en un bloque de código, genera un espacio de nombre. Los espacios de nombres están permitidos fuera de las funciones. Se requiere un identificador unique para detener cualquier definición conflictiva si usa esta más de una vez.


trabajado para mí en GCC y VS'12 C++

+1

No hay espacios de nombres en C. – martinkunev

+0

ah, whoops, leyeron mal la pregunta. Parece que vine aquí buscando una respuesta a C++ de todos modos (mirando la última línea de mi respuesta), así que lo dejaré aquí en caso de que otros hagan lo mismo – Hashbrown

1

La forma clásica está utilizando una matriz:

char int_is_4_bytes_assertion[sizeof(int) == 4 ? 1 : -1]; 

Funciona porque si la afirmación es cierta la matriz tiene un tamaño 1 y es válido, pero si es falso, el tamaño de -1 da un error de compilación.

La mayoría de los compiladores mostrarán el nombre de la variable y señalarán la parte derecha del código donde puede dejar comentarios eventuales sobre la afirmación.

0

Esto funciona, con el conjunto de opciones "eliminar sin usar". Puedo usar una función global para verificar los parámetros globales.

// 
#ifndef __sassert_h__ 
#define __sassert_h__ 

#define _cat(x, y) x##y 

#define _sassert(exp, ln) \ 
extern void _cat(ASSERT_WARNING_, ln)(void); \ 
if(!(exp)) \ 
{ \ 
    _cat(ASSERT_WARNING_, ln)(); \ 
} 

#define sassert(exp) _sassert(exp, __LINE__) 

#endif //__sassert_h__ 

//----------------------------------------- 
static bool tab_req_set_relay(char *p_packet) 
{ 
    sassert(TXB_TX_PKT_SIZE < 3000000); 
    sassert(TXB_TX_PKT_SIZE >= 3000000); 
    ... 
} 

//----------------------------------------- 
Building target: ntank_app.elf 
Invoking: Cross ARM C Linker 
arm-none-eabi-gcc ... 
../Sources/host_if/tab_if.c:637: undefined reference to `ASSERT_WARNING_637' 
collect2: error: ld returned 1 exit status 
make: *** [ntank_app.elf] Error 1 
// 
+1

Si funciona, solo lo haría en el fuente de un ejecutable. – Coder

2

Si se utiliza el macro static_assert() con __LINE__, es posible evitar choques número de línea entre una entrada en un fichero .c y una entrada diferente en un archivo de cabecera incluyendo __INCLUDE_LEVEL__.

Por ejemplo:

/* Trickery to create a unique variable name */ 
#define BOOST_JOIN(X, Y)  BOOST_DO_JOIN(X, Y) 
#define BOOST_DO_JOIN(X, Y) BOOST_DO_JOIN2(X, Y) 
#define BOOST_DO_JOIN2(X, Y) X##Y 
#define STATIC_ASSERT(x)  typedef char \ 
     BOOST_JOIN(BOOST_JOIN(level_,__INCLUDE_LEVEL__), \ 
        BOOST_JOIN(_assert_on_line_,__LINE__)) [(x) ? 1 : -1]