Declaração estática em C

86

Qual é a melhor maneira de obter declarações estáticas de tempo de compilação em C (não C ++), com ênfase particular no GCC?

Matt Joiner
fonte

Respostas:

90

O padrão C11 adiciona a _Static_assertpalavra - chave.

Isso é implementado desde gcc-4.6 :

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

O primeiro slot precisa ser uma expressão constante integral. O segundo slot é um literal de string constante que pode ser long ( _Static_assert(0, L"assertion of doom!")).

Devo observar que isso também é implementado em versões recentes do clang.

emsr
fonte
4
[... parece ser implementado pelo gcc, pelo clang ...] Você pode ser mais assertivo que ;-) _Static_asserté parte do padrão C11 e qualquer compilador que suporte C11, terá.
PP de
1
Isso pode ser usado no escopo do arquivo (fora de qualquer função)? Porque eu pego error: expected declaration specifiers or '...' before 'sizeof'linha static_assert( sizeof(int) == sizeof(long int), "Error!); (estou usando C, não C ++, a propósito)
user10607
@ user10607 Estou surpreso que isso não funcione .. Espere, está faltando uma citação no final da string de erro. Coloque isso e volte. Isso funciona para mim no gcc-4.9: _Static_assert( sizeof(int) == sizeof(long int), "Error!");No meu macine, recebo o erro.
emsr
Tenho gcc 4.8.2 no Ubuntu. A citação que faltava era um erro de digitação de comentário (eu tinha no código). Esta é a primeira linha em um arquivo após a inclusão de alguns cabeçalhos. O compilador me dá exatamente os mesmos erros: error: expected declaration specifiers or '...' before 'sizeof'AND error: expected declaration specifiers or '...' before string constant(ele está se referindo à "Error!"string) (também: estou compilando com -std = c11. Ao colocar a declaração dentro de uma função, tudo funciona bem (falha e é bem-sucedido conforme o esperado))
user10607
2
@ user10607 Também tive que especificar -std = gnu11 na linha de comando. Estou realmente surpreso que haja uma diferença entre 4,8 e 4,8. Eu tenho uma fonte com apenas uma linha. Eu também usei o padrão C, _Static_assertnão o C ++ static_assert. Você precisa `#include <assert.h> para obter a macro static_assert.
emsr
93

Isso funciona no escopo funcional e não funcional (mas não dentro de structs, uniões).

#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. Se a declaração de tempo de compilação não puder ser correspondida, uma mensagem quase inteligível é gerada pelo GCC sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative

  2. A macro pode ou deve ser alterada para gerar um nome exclusivo para o typedef (ou seja, concatenar __LINE__no final do static_assert_...nome)

  3. Em vez de um ternário, isso poderia ser usado também, o #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]que funciona até mesmo no antigo compilador cc65 enferrujado (para a CPU 6502).

ATUALIZAÇÃO: para completar, aqui está a versão com__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); 
}

ATUALIZAÇÃO2: código específico do GCC

O GCC 4.3 (eu acho) introduziu os atributos de função "erro" e "aviso". Se uma chamada para uma função com esse atributo não puder ser eliminada por meio da eliminação de código morto (ou outras medidas), um erro ou aviso será gerado. Isso pode ser usado para fazer declarações de tempo de compilação com descrições de falha definidas pelo usuário. Resta determinar como eles podem ser usados ​​no escopo do namespace sem recorrer a uma função fictícia:

#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()
{
}

E é assim que parece:

$ 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
Nordic Mainframe
fonte
1
No Visual Studio, ele diz apenas "Subscrito negativo", sem mencionar o nome da variável ...
szx
Nordic Mainframe - a opção 3 em sua resposta não funciona no clang.
Elazar de
1
Em relação à última solução (específica do GCC 4.3+): Esta é muito poderosa, pois pode verificar qualquer coisa que o otimizador possa descobrir, mas falha se a otimização não estiver habilitada. O nível mínimo de otimização ( -Og) muitas vezes pode ser suficiente para que isso funcione, no entanto, e não deve interferir na depuração. Pode-se considerar fazer a declaração estática em um ambiente autônomo ou em tempo de execução se __OPTIMIZE__(e __GNUC__) não estiver definido.
Søren Løvborg
No trecho de código com a versão LINE (ATUALIZAÇÃO: para fins de completude, aqui está a versão com `LINE), ao compilar, ele apresenta erros na linha (STATIC_ASSERT (X, static_assertion_at_line _ ## L)), que pode ser corrigido adicionando mais um nível como abaixo: #define COMPILE_TIME_ASSERT4 (X, L) static_assert (X, # L); #define COMPILE_TIME_ASSERT3 (X, L) COMPILE_TIME_ASSERT3 (X, "" Asserção em: ## L "");
domingo
Eu uso algo semelhante à __LINE__versão do gcc 4.1.1 ... com incômodo ocasional quando dois cabeçalhos diferentes têm um na mesma linha numerada!
MM de
10

cl

Eu sei que a pergunta menciona explicitamente o gcc, mas apenas para completar, aqui está um ajuste para compiladores da Microsoft.

Usar o typedef de matriz de tamanho negativo não convence cl a cuspir um erro decente. Apenas diz error C2118: negative subscript. Um campo de bits de largura zero se sai melhor nesse aspecto. Como isso envolve o typedeffing de uma estrutura, realmente precisamos usar nomes de tipo exclusivos. __LINE__não corta a mostarda - é possível ter um COMPILE_TIME_ASSERT()na mesma linha em um cabeçalho e um arquivo de origem, e sua compilação será interrompida. __COUNTER__vem em seu auxílio (e está no 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__)

Agora

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

sob cldá:

erro C2149: 'static_assertion_failed_use_another_compiler_luke': campo de bit nomeado não pode ter largura zero

Gcc também dá uma mensagem inteligível:

erro: largura zero para campo de bits 'static_assertion_failed_use_another_compiler_luke'

Bobbogo
fonte
4

Da Wikipedia :

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

COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
Tyler
fonte
15
Seria melhor se você vinculasse a
Matt Joiner
Isso não funciona no gcc 4.6 - diz "o rótulo do caso não se reduz a uma constante inteira". Tem razão.
Liosan
vocês provavelmente já mudaram de ideia agora, mas acabei escrevendo o meu próprio (veja minha resposta ). Usei seu link @MattJoiner para me ajudar
Hashbrown
E se você se incomodar, diga-me se funciona para você, @Liosan. Eu só comecei a me aprofundar em C ++, então cheguei tarde para a festa
Hashbrown
Quanto ao Visual C ++, ele tem static_assert integrado desde a versão 2010 e funciona nos modos c ++ e c. No entanto, ele não possui o c99 _Static_assert integrado.
ddbug
3

Eu NÃO recomendaria usar a solução com typedef:

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

A declaração de array com typedefpalavra-chave NÃO tem garantia de ser avaliada em tempo de compilação. Por exemplo, o seguinte código no escopo do bloco será compilado:

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

Eu recomendaria isso (em C99):

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

Por causa da staticpalavra - chave, o array será definido em tempo de compilação. Observe que esta declaração só funcionará com os CONDque são avaliados em tempo de compilação. Ele não funcionará (ou seja, a compilação falhará) com condições baseadas em valores na memória, como valores atribuídos a variáveis.

FredFredFredFred
fonte
4
Embora isso funcionasse, também aumentaria os requisitos de memória.
Sherrellbc
1
erro: 'static_assertion_INVALID_CHAR_SIZE' definido, mas não usado [-Werror = variável não utilizada]
Alex
2

Se estiver usando a macro STATIC_ASSERT () com __LINE__, é possível evitar conflitos de número de linha entre uma entrada em um arquivo .c e uma entrada diferente em um arquivo de cabeçalho incluindo __INCLUDE_LEVEL__.

Por exemplo :

/* 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
fonte
1

A maneira clássica é usar uma matriz:

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

Funciona porque se a asserção for verdadeira, o array tem tamanho 1 e é válido, mas se for falso, o tamanho -1 dá um erro de compilação.

A maioria dos compiladores mostra o nome da variável e aponta para a parte certa do código onde você pode deixar comentários eventuais sobre a asserção.

Paolo.Bolzoni
fonte
Resumindo isso em uma #define STATIC_ASSERT()macro de tipo genérico e fornecendo mais exemplos genéricos e saída de amostra do compilador de seus exemplos genéricos usando STATIC_ASSERT()daria a você muito mais votos positivos e faria essa técnica fazer mais sentido, eu acho.
Gabriel Staples
Eu não concordo. O compilador vê macros de pensamento e dá uma mensagem mais confusa.
Paolo.Bolzoni
1

De Perl, especificamente a perl.hlinha 3455 ( <assert.h>incluída de antemão):

/* 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

Se static_assertestiver disponível (em <assert.h>), ele será usado. Caso contrário, se a condição for falsa, um campo de bits com tamanho negativo é declarado, o que faz com que a compilação falhe.

STMT_START/ STMT_ENDsão macros que se expandem para do/ while (0), respectivamente.

Melpomene
fonte
1

Porque:

  1. _Static_assert() agora está definido no gcc para todas as versões de C, e
  2. static_assert() é definido em C ++ 11 e posterior

A seguinte macro simples para, STATIC_ASSERT()portanto, funciona em:

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

Defina STATIC_ASSERTo seguinte:

/* 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")

Agora use:

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

Exemplos:

Testado no Ubuntu usando gcc 4.8.4:

Exemplo 1: boa gccsaída (ou seja: os STATIC_ASSERT()códigos funcionam, mas a condição era falsa, causando uma declaração em tempo de compilação):

$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: Na função 'main'
static_assert.c: 78: 38: erro: declaração estática falhou: "(1> 2) falhou"
#define STATIC_ASSERT (test_for_true ) _Static_assert ((test_for_true), "(" #test_for_true ") falhou")
^
static_assert.c: 88: 5: nota: na expansão da macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Exemplo 2: boa g++ -std=c++11saída (ou seja: os STATIC_ASSERT()códigos funcionam, mas a condição era falsa, causando uma declaração em tempo de compilação):

$ g ++ -Wall -std = c ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c: Na função 'int main ()'
static_assert.c: 74: 32: erro: falha de declaração estática: (1> 2)
#define _Static_assert static_assert / * static_asserté parte do C ++ 11 ou posterior * /
^
static_assert.c: 78: 38: observação: na expansão da macro '_Static_assert'
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true ") falhou")
^
static_assert.c: 88: 5: nota: na expansão da macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Exemplo 3: saída em C ++ com falha (ou seja: o código de declaração não funciona corretamente, pois está usando uma versão de C ++ anterior a C ++ 11):

$ g ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 88: 5: warning: identifier 'static_assert' é uma palavra-chave em C ++ 11 [-Wc ++ 0x-compat]
STATIC_ASSERT (1> 2 );
^
static_assert.c: Na função 'int main ()'
static_assert.c: 78: 99: erro: 'static_assert' não foi declarado neste escopo
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true " ) falhou ")
^
static_assert.c: 88: 5: nota: na expansão da macro 'STATIC_ASSERT'
STATIC_ASSERT (1> 2);
^

Resultados completos do teste aqui:

/*
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 os tipos passados ​​para macro [minha própria resposta]
    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 os tipos passados ​​para a macro
  3. Como usar declaração estática em C para verificar os tipos de parâmetros passados ​​para uma macro
Gabriel Staples
fonte
1
Por que tão complicado, quando há uma static_assertmacro assert.h?
Adeus SE
@KamiKaze, estou surpreso com sua pergunta, pois parece que você não leu realmente minha resposta? A segunda linha da minha resposta diz tudo: "static_assert () é definido em C ++ 11 e posterior". Portanto, static_assert()não está disponível em C. Veja aqui também: en.cppreference.com/w/cpp/language/static_assert - mostra que static_assertexiste "(desde C ++ 11)". A beleza da minha resposta é que funciona no C90 do gcc e posterior, bem como em qualquer C ++ 11 e posterior, em vez de apenas no C ++ 11 e posterior, como static_assert(). Além disso, o que há de complicado em minha resposta? São apenas alguns #definesegundos.
Gabriel Staples
static_asserté definido em C desde C11. É uma macro que se expande para _Static_assert. en.cppreference.com/w/c/error/static_assert . Além disso, o contraste com a sua resposta _Static_assertnão está disponível em c99 e c90 no gcc (apenas no gnu99 e gnu90). Isso é compatível com o padrão. Basicamente, você faz muito trabalho extra, que só traz benefícios se compilado com gnu90 e gnu99 e que torna o caso de uso real insignificantemente pequeno.
Adeus SE
> "_Static_assert não está disponível em c99 e c90 no gcc (apenas no gnu99 e gnu90)". Eu vejo o que você quer dizer. É uma extensão gcc, então você está correto. > "Basicamente, você faz muito trabalho extra". Discordo; 2 definições extremamente simples não significam "muito" trabalho extra. Dito isso, entendo o que você quer dizer agora. Ainda acho que o que fiz é útil e agrega valor ao corpo de conhecimento e respostas apresentadas aqui, então não acho que mereça o downvote. Além disso, meu erro ao dizer "C90 e posterior" em vez de "gcc C90 e posterior", ou "g90 e posterior", foi apenas em meu comentário acima, não em minha resposta.
Gabriel Staples
Como era factualmente errado, um voto negativo era justificado. Se você corrigir as afirmações erradas, verificarei a resposta novamente e poderei retirar meu voto negativo. Ainda assim, adicionar esse código se não for necessário (se você não trabalhar com gnu90 e gnu99) não é benéfico para a clareza e adiciona mais desordem. Se você tiver o caso de uso, pode valer a pena. Mas eu me pergunto sobre a raridade do caso de uso de onde a compatibilidade com gnu99 / 90 e c ++ 11 é necessária.
Adeus SE
0

Para aqueles que desejam algo realmente básico e portátil, mas não têm acesso aos recursos do C ++ 11, escrevi exatamente a coisa certa.
Use STATIC_ASSERTnormalmente (você pode escrever duas vezes na mesma função se quiser) e use GLOBAL_STATIC_ASSERTfora das funções com uma frase única como o primeiro 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;
}

Explicação:
Primeiro, ele verifica se você tem a declaração real, que você definitivamente gostaria de usar se estiver disponível.
Se você não fizer isso, ele será preddeclarado pegando seu icate e dividindo-o sozinho. Isso faz duas coisas.
Se for zero, id est, a afirmação falhou, causará um erro de divisão por zero (a aritmética é forçada porque está tentando declarar uma matriz).
Se não for zero, normaliza o tamanho do array para 1. Portanto, se a asserção for aprovada, você não gostaria que ela falhasse de qualquer maneira porque seu predicado foi avaliado como -1(inválido) ou ser 232442(grande perda de espaço, IDK se fosse otimizado).
Por STATIC_ASSERTestar entre colchetes, isso o torna um bloco, que define o escopo da variávelassert, o que significa que você pode escrevê-lo muitas vezes.
Ele também o converte em void, que é uma forma conhecida de se livrar de unused variableavisos.
Pois GLOBAL_STATIC_ASSERT, em vez de estar em um bloco de código, ele gera um namespace. Os namespaces são permitidos fora das funções. Um uniqueidentificador é necessário para interromper quaisquer definições conflitantes se você usar este mais de uma vez.


Trabalhou para mim no GCC e VS'12 C ++

Hashbrown
fonte
2
Não há namespaces em C.
martinkunev
ah, ops, interpretou mal a pergunta. Parece que vim aqui procurando uma resposta para C ++ de qualquer maneira (olhando a última linha da minha resposta), então vou deixá-la aqui para o caso de outros fazerem o mesmo
Hashbrown
0

Isso funciona, com o conjunto de opções "remover não utilizado". Posso usar uma função global para verificar os parâmetros globais.

//
#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
//
user4978854
fonte
1
Se funcionar, só o faria na origem de um executável.
Coder
0

Isso funcionou para alguns gcc antigos. Desculpe ter esquecido qual era a versão:

#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
gaio
fonte