Aserción estática en C

Respuestas:

90

El estándar C11 agrega la _Static_assertpalabra clave.

Esto se implementa desde gcc-4.6 :

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

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

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

emsr
fuente
4
[... parece ser implementado por gcc, por clang ...] Puede ser más asertivo que eso ;-) _Static_assertes parte del estándar C11 y cualquier compilador que soporte C11, lo tendrá.
PP
1
¿Se puede usar en el ámbito del archivo (fuera de cualquier función)? Porque obtengo la error: expected declaration specifiers or '...' before 'sizeof'línea static_assert( sizeof(int) == sizeof(long int), "Error!); (estoy usando C, no C ++ por cierto)
user10607
@ user10607 Me sorprende que esto no funcione. Espera, te falta una cita al final de la cadena de error. Pon eso y vuelve. Esto me funciona en gcc-4.9: _Static_assert( sizeof(int) == sizeof(long int), "Error!");En mi macine aparece el error.
emsr
Tengo gcc 4.8.2 en Ubuntu. La cita que faltaba era un error tipográfico de comentario (lo tenía en el código). Esta es la primera línea de un archivo después de incluir un par de encabezados. El compilador me da dos errores exactamente iguales: error: expected declaration specifiers or '...' before 'sizeof'Y error: expected declaration specifiers or '...' before string constant(se refiere a la "Error!"cadena) (también: estoy compilando con -std = c11. Al poner la declaración dentro de una función, todo funciona bien (falla y tiene éxito como se esperaba))
user10607
2
@ user10607 También tuve que especificar -std = gnu11 en la línea de comando. Estoy realmente sorprendido de que haya una diferencia entre 4.8 y 4.8. Tengo una fuente con solo una línea. También utilicé el estándar C, _Static_assertno el C ++ ish static_assert. Necesita `#include <assert.h> para obtener la macro static_assert.
emsr
93

Esto funciona en el ámbito de función y no función (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 aserción de tiempo de compilación no puede coincidir, GCC genera un mensaje casi inteligible 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 static_assert_...nombre)

  3. En lugar de un ternario, esto también podría usarse, #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]lo que funciona incluso en el compilador oxidado viejo cc65 (para la cpu 6502).

ACTUALIZACIÓN: En aras de la integridad, 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); 
}

ACTUALIZACIÓN2: código específico de GCC

GCC 4.3 (supongo) introdujo los atributos de función "error" y "advertencia". Si una llamada a una función con ese atributo no se pudo eliminar mediante la eliminación del código muerto (u otras medidas), se genera un error o advertencia. Esto se puede utilizar para hacer afirmaciones en tiempo de compilación con descripciones de fallas definidas por el usuario. Queda por determinar cómo se pueden usar en el ámbito del espacio de nombres sin recurrir a una función ficticia:

#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
Mainframe nórdico
fuente
1
En Visual Studio, solo dice "
Subíndice
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 + específica): esta es muy poderosa, ya que puede verificar cualquier cosa que el optimizador pueda descubrir, pero falla si la optimización no está habilitada. -OgSin embargo, el nivel mínimo de optimización ( ) puede ser suficiente para que esto funcione y no debería interferir con la depuración. Se puede considerar hacer que la aserción estática sea una aserción sin operación o en tiempo de ejecución si __OPTIMIZE__(y __GNUC__) no está definido.
Søren Løvborg
En el fragmento de código con la versión LINE (ACTUALIZACIÓN: en aras de la integridad, aquí está la versión con `LINE), al compilar, se produce un error en la línea (STATIC_ASSERT (X, static_assertion_at_line _ ## L)), que se puede corregir agregando uno más nivel como el siguiente: #define COMPILE_TIME_ASSERT4 (X, L) static_assert (X, # L); #define COMPILE_TIME_ASSERT3 (X, L) COMPILE_TIME_ASSERT3 (X, "" Aserción en: ## L "");
domingo
Utilizo algo similar a la __LINE__versión en gcc 4.1.1 ... ¡con molestias ocasionales cuando dos encabezados diferentes tienen uno en la misma línea numerada!
MM
10

cl

Sé que la pregunta menciona explícitamente gcc, pero solo para completar, aquí hay un ajuste para los compiladores de Microsoft.

El uso de la matriz typedef de tamaño negativo no persuade a cl a escupir un error decente. Solo dice error C2118: negative subscript. Un campo de bits de ancho cero se comporta mejor a este respecto. Dado que esto implica la eliminación de tipos de una estructura, realmente 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)

debajo clda:

error C2149: 'static_assertion_failed_use_another_compiler_luke': el campo de bit con nombre no puede tener un ancho cero

Gcc también da un mensaje inteligible:

error: ancho cero para el campo de bits 'static_assertion_failed_use_another_compiler_luke'

bobbogo
fuente
4

De Wikipedia :

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

COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
Tyler
fuente
15
Sería mejor si se vincule a la fuente verdadera: jaggersoft.com/pubs/CVu11_3.html
Matt Joiner
No funciona en gcc 4.6 - dice "la etiqueta del caso no se reduce a una constante entera". Tiene un punto.
Liosan
Probablemente ambos ya se hayan movido mucho, pero terminé escribiendo el mío (ver mi respuesta ). Usé tu enlace @MattJoiner para ayudarme
Hashbrown
Y si puede molestarse, avíseme si funciona para usted, @Liosan. Recién comencé a profundizar en C ++, así que llegué tarde a la fiesta
Hashbrown
En cuanto a Visual C ++, tiene static_assert integrado desde la versión 2010 y funciona tanto en los modos c ++ como c. Sin embargo, no tiene incorporado el c99 _Static_assert.
ddbug
3

Yo no recomendaría el uso de la solución con un typedef:

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

typedefNO se garantiza que la declaración de matriz con la palabra clave sea evaluada en tiempo de compilación. Por ejemplo, se compilará el siguiente código en el alcance del bloque:

int invalid_value = 0;
STATIC_ASSERT(invalid_value, this_should_fail_at_compile_time_but_will_not);

Recomendaría esto en su lugar (en C99):

#define STATIC_ASSERT(COND,MSG) static int static_assertion_##MSG[(COND)?1:-1]

Debido a la staticpalabra clave, la matriz se definirá en tiempo de compilación. Tenga en cuenta que esta aserción solo funcionará con los CONDque se evalúen en tiempo de compilación. No funcionará (es decir, la compilación fallará) con condiciones basadas en valores en la memoria, como los valores asignados a las variables.

FredFredFredFred
fuente
4
Si bien esto funcionaría, también aumentaría sus requisitos de memoria.
sherrellbc
1
error: 'static_assertion_INVALID_CHAR_SIZE' definido pero no utilizado [-Werror = variable-no utilizada]
Alex
2

Si usa la macro STATIC_ASSERT () con __LINE__, es posible evitar conflictos de números de línea entre una entrada en un archivo .cy una entrada diferente en un archivo de encabezado al incluir __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]
BrentNZ
fuente
1

La forma clásica es usar una matriz:

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

Funciona porque si la aserción es verdadera, la matriz tiene un tamaño 1 y es válida, pero si es falsa, 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 aserción.

Paolo.Bolzoni
fuente
Resumir esto en una #define STATIC_ASSERT()macro de tipo genérico y proporcionar ejemplos más genéricos y la salida del compilador de muestra a partir de sus ejemplos genéricos STATIC_ASSERT()le daría muchos más votos positivos y haría que esta técnica tuviera más sentido, creo.
Gabriel Staples
No estoy de acuerdo El compilador ve macros de pensamiento y da un mensaje más confuso.
Paolo.Bolzoni
1

De Perl, específicamente la perl.hlínea 3455 ( <assert.h>se incluye de antemano):

/* STATIC_ASSERT_DECL/STATIC_ASSERT_STMT are like assert(), but for compile
   time invariants. That is, their argument must be a constant expression that
   can be verified by the compiler. This expression can contain anything that's
   known to the compiler, e.g. #define constants, enums, or sizeof (...). If
   the expression evaluates to 0, compilation fails.
   Because they generate no runtime code (i.e.  their use is "free"), they're
   always active, even under non-DEBUGGING builds.
   STATIC_ASSERT_DECL expands to a declaration and is suitable for use at
   file scope (outside of any function).
   STATIC_ASSERT_STMT expands to a statement and is suitable for use inside a
   function.
*/
#if (defined(static_assert) || (defined(__cplusplus) && __cplusplus >= 201103L)) && (!defined(__IBMC__) || __IBMC__ >= 1210)
/* static_assert is a macro defined in <assert.h> in C11 or a compiler
   builtin in C++11.  But IBM XL C V11 does not support _Static_assert, no
   matter what <assert.h> says.
*/
#  define STATIC_ASSERT_DECL(COND) static_assert(COND, #COND)
#else
/* We use a bit-field instead of an array because gcc accepts
   'typedef char x[n]' where n is not a compile-time constant.
   We want to enforce constantness.
*/
#  define STATIC_ASSERT_2(COND, SUFFIX) \
    typedef struct { \
        unsigned int _static_assertion_failed_##SUFFIX : (COND) ? 1 : -1; \
    } _static_assertion_failed_##SUFFIX PERL_UNUSED_DECL
#  define STATIC_ASSERT_1(COND, SUFFIX) STATIC_ASSERT_2(COND, SUFFIX)
#  define STATIC_ASSERT_DECL(COND)    STATIC_ASSERT_1(COND, __LINE__)
#endif
/* We need this wrapper even in C11 because 'case X: static_assert(...);' is an
   error (static_assert is a declaration, and only statements can have labels).
*/
#define STATIC_ASSERT_STMT(COND)      STMT_START { STATIC_ASSERT_DECL(COND); } STMT_END

Si static_assertestá disponible (desde <assert.h>), se utiliza. De lo contrario, si la condición es falsa, se declara un campo de bits con un tamaño negativo, lo que hace que la compilación falle.

STMT_START/ STMT_ENDson macros que se expanden a do/ while (0), respectivamente.

melpomene
fuente
1

Porque:

  1. _Static_assert() ahora está definido en gcc para todas las versiones de C, y
  2. static_assert() está definido en C ++ 11 y posteriores

La siguiente macro simple para, STATIC_ASSERT()por lo tanto, funciona en:

  1. C ++:
    1. C ++ 11 ( g++ -std=c++11) o posterior
  2. C:
    1. gcc -std=c90
    2. gcc -std=c99
    3. gcc -std=c11
    4. gcc (sin std especificado)

Defina STATIC_ASSERTcomo sigue:

/* For C++: */
#ifdef __cplusplus
    #ifndef _Static_assert
        #define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
    #endif
#endif
/* Now for gcc (C) (and C++, given the define above): */
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")

Ahora úsalo:

STATIC_ASSERT(1 > 2); // Output will look like: error: static assertion failed: "(1 > 2) failed" 

Ejemplos:

Probado en Ubuntu usando gcc 4.8.4:

Ejemplo 1: buen gccresultado (es decir, los STATIC_ASSERT()códigos funcionan, pero la condición era falsa, lo que provocó una afirmación en tiempo de compilación):

$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: En la función 'main'
static_assert.c: 78: 38: error: la aserción estática falló: "(1> 2) falló"
#define STATIC_ASSERT (test_for_true ) _Static_assert ((prueba_para_verdadero), "(" #prueba_para_verdadero ") falló")
^
static_assert.c: 88: 5: nota: en expansión de macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Ejemplo 2: buen g++ -std=c++11resultado (es decir, los STATIC_ASSERT()códigos funcionan, pero la condición era falsa, lo que provocó una afirmación en tiempo de compilación):

$ g ++ -Wall -std = c ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c: En la función 'int main ()'
static_assert.c: 74: 32: error: la aserción estática falló: (1> 2) falló
#define _Static_assert static_assert / * static_assertes parte de C ++ 11 o posterior * /
^
static_assert.c: 78: 38: nota: en la expansión de la macro '_Static_assert'
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true ") falló")
^
static_assert.c: 88: 5: nota: en la expansión de la macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Ejemplo 3: salida de C ++ fallida (es decir, el código de aserción no funciona correctamente, ya que se usa una versión de C ++ anterior a C ++ 11):

$ g ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 88: 5: advertencia: el identificador 'static_assert' es una palabra clave en C ++ 11 [-Wc ++ 0x-compat]
STATIC_ASSERT (1> 2 );
^
static_assert.c: En la función 'int main ()'
static_assert.c: 78: 99: error: 'static_assert' no se declaró en este ámbito
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true " ) falló ")
^
static_assert.c: 88: 5: nota: en expansión de la macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Resultados completos de la prueba aquí:

/*
static_assert.c
- test static asserts in C and C++ using gcc compiler

Gabriel Staples
4 Mar. 2019 

To be posted in:
1. /programming/987684/does-gcc-have-a-built-in-compile-time-assert/987756#987756
2. /programming/3385515/static-assert-in-c/7287341#7287341

To compile & run:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert

-------------
TEST RESULTS:
-------------

1. `_Static_assert(false, "1. that was false");` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             YES
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    YES
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  NO

2. `static_assert(false, "2. that was false");` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             NO
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    NO
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    NO
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    NO
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  YES

3. `STATIC_ASSERT(1 > 2);` works in:
  C:
    gcc -Wall -o static_assert static_assert.c && ./static_assert             YES
    gcc -Wall -std=c90 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c99 -o static_assert static_assert.c && ./static_assert    YES
    gcc -Wall -std=c11 -o static_assert static_assert.c && ./static_assert    YES
  C++:
    g++ -Wall -o static_assert static_assert.c && ./static_assert             NO
    g++ -Wall -std=c++98 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++03 -o static_assert static_assert.c && ./static_assert  NO
    g++ -Wall -std=c++11 -o static_assert static_assert.c && ./static_assert  YES

*/

#include <stdio.h>
#include <stdbool.h>

/* For C++: */
#ifdef __cplusplus
    #ifndef _Static_assert
        #define _Static_assert static_assert /* `static_assert` is part of C++11 or later */
    #endif
#endif
/* Now for gcc (C) (and C++, given the define above): */
#define STATIC_ASSERT(test_for_true) _Static_assert((test_for_true), "(" #test_for_true ") failed")


int main(void)
{
    printf("Hello World\n");

    /*_Static_assert(false, "1. that was false");*/
    /*static_assert(false, "2. that was false");*/

    STATIC_ASSERT(1 > 2);

    return 0;
}

Relacionado:

  1. Use static_assert para verificar los tipos pasados ​​a la macro [mi propia respuesta]
    1. https://en.cppreference.com/w/cpp/types/is_same
    2. https://en.cppreference.com/w/cpp/language/decltype
  2. Use static_assert para verificar los tipos pasados ​​a la macro
  3. Cómo usar la aserción estática en C para verificar los tipos de parámetros pasados ​​a una macro
Gabriel Staples
fuente
1
¿Por qué tan complicado, cuando hay una static_assertmacro assert.h?
Adiós SE
@KamiKaze, estoy sorprendido por tu pregunta, ya que parece que es posible que no hayas leído mi respuesta. La segunda línea de mi respuesta lo dice todo: "static_assert () está definido en C ++ 11 y posteriores". Por lo tanto, static_assert()no está disponible en absoluto en C. Vea aquí también: en.cppreference.com/w/cpp/language/static_assert - muestra que static_assertexiste "(desde C ++ 11)". La belleza de mi respuesta es que funciona en gcc's C90 y posteriores, así como en cualquier C ++ 11 y posteriores, en lugar de solo en C ++ 11 y posteriores, como static_assert(). Además, ¿qué tiene de complicado mi respuesta? Son solo un par de #defines.
Gabriel Staples
static_assertse define en C desde C11. Es una macro que se expande a _Static_assert. en.cppreference.com/w/c/error/static_assert . Además, el contraste con su respuesta _Static_assertno está disponible en c99 y c90 en gcc (solo en gnu99 y gnu90). Esto cumple con el estándar. Básicamente, hace mucho trabajo adicional, que solo aporta beneficios si se compila con gnu90 y gnu99 y que hace que el uso real sea insignificantemente pequeño.
Adiós SE
> "_Static_assert no está disponible en c99 y c90 en gcc (solo en gnu99 y gnu90)". Veo a que te refieres. Es una extensión de gcc, así que tienes razón. > "Básicamente haces mucho trabajo extra". Estoy en desacuerdo; 2 definiciones extremadamente simples no significan "mucho" trabajo extra. Dicho esto, veo lo que quieres decir ahora. Sigo pensando que lo que he hecho es útil y agrega valor al conjunto de conocimientos y respuestas presentados aquí, por lo que no creo que merezca el voto negativo. Además, mi error al decir "C90 y posteriores" en lugar de "gcc C90 y posteriores", o "g90 y posteriores", fue solo en mi comentario anterior, no en mi respuesta.
Gabriel Staples
Como era un error de hecho, se justificó un voto negativo. Si corrige las afirmaciones incorrectas, volveré a comprobar la respuesta y es posible que retire mi voto negativo. Aún así, agregar dicho código si no es necesario (por lo tanto, si no trabaja con gnu90 y gnu99) no es beneficioso para mayor claridad y agrega más desorden. Si tiene el caso de uso, podría valer la pena. Pero me pregunto acerca de la rareza del caso de uso en el que se requiere la compatibilidad de gnu99 / 90 y c ++ 11.
Adiós SE
0

Para aquellos de ustedes que desean algo realmente básico y portátil pero no tienen acceso a las funciones de C ++ 11, escribí exactamente lo que necesita.
Use STATIC_ASSERTnormalmente (puede escribirlo dos veces en la misma función si lo desea) y use GLOBAL_STATIC_ASSERTfuera de 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:
Primero verifica si tiene la aserción real, que definitivamente querrá usar si está disponible.
Si no lo hace, lo afirma obteniendo su predicate y dividiéndolo por sí mismo. 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 en 1. Entonces, si la afirmación pasa, no querrá que falle de todos modos porque su predicado evaluado como -1(inválido), o sea 232442(desperdicio masivo de espacio, IDK si se optimizara).
Porque STATIC_ASSERTestá envuelto entre llaves, esto lo convierte en un bloque, que alcanza la variableassert, lo que significa que puedes escribirlo muchas veces.
También lo lanza a void, que es una forma conocida de deshacerse de las unused variableadvertencias.
Porque GLOBAL_STATIC_ASSERT, en lugar de estar en un bloque de código, genera un espacio de nombres. Los espacios de nombres están permitidos fuera de las funciones. Se uniquerequiere un identificador para detener cualquier definición en conflicto si usa esta más de una vez.


Trabajó para mí en GCC y VS'12 C ++

Hashbrown
fuente
2
No hay espacios de nombres en C.
martinkunev
ah, vaya, malinterpretó 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
0

Esto funciona con la opción "eliminar no utilizados" configurada. 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
//
usuario4978854
fuente
1
Si funciona, solo lo haría en la fuente de un ejecutable.
Codificador
0

Esto funcionó para algunos viejos gcc. Lamento haber olvidado qué versión era:

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

#define _sassert(exp, ln)\
extern char _cat(SASSERT_, ln)[1]; \
extern char _cat(SASSERT_, ln)[exp ? 1 : 2]

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

//
sassert(1 == 2);

//
#148 declaration is incompatible with "char SASSERT_134[1]" (declared at line 134)  main.c  /test/source/controller line 134    C/C++ Problem
arrendajo
fuente