Özellikle GCC'ye vurgu yaparak, C (C ++ değil) derleme zamanı statik iddialarına ulaşmanın en iyi yolu nedir?
Özellikle GCC'ye vurgu yaparak, C (C ++ değil) derleme zamanı statik iddialarına ulaşmanın en iyi yolu nedir?
Yanıtlar:
C11 standardı _Static_assert
anahtar kelimeyi ekler .
Bu, gcc-4.6'dan beri uygulanmaktadır :
_Static_assert (0, "assert1"); /* { dg-error "static assertion failed: \"assert1\"" } */
İlk yuvanın sabit bir integral ifade olması gerekir. İkinci yuva, uzun ( _Static_assert(0, L"assertion of doom!")
) olabilen sabit bir dizedir .
Bunun clang'ın son sürümlerinde de uygulandığına dikkat etmeliyim.
error: expected declaration specifiers or '...' before 'sizeof'
satır için static_assert( sizeof(int) == sizeof(long int), "Error!);
alıyorum (bu arada C ++ değil C kullanıyorum)
_Static_assert( sizeof(int) == sizeof(long int), "Error!");
Macine'imde hatayı alıyorum.
error: expected declaration specifiers or '...' before 'sizeof'
VE error: expected declaration specifiers or '...' before string constant
( "Error!"
dizgeye atıfta bulunuyor ) (ayrıca: -std = c11 ile derliyorum. Bildirimi bir işlevin içine koyarken her şey iyi çalışıyor (beklendiği gibi başarısız oluyor ve başarılı oluyor)
_Static_assert
C ++ ish değil C standardını kullandım static_assert
. Static_assert makrosunu almak için "#include <assert.h> gerekir.
Bu, işlev ve işlev dışı kapsamda çalışır (ancak yapıların, birliklerin içinde değil).
#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);
}
Derleme zamanı beyanı eşleştirilemezse, GCC tarafından neredeyse anlaşılır bir mesaj üretilir. sas.c:4: error: size of array ‘static_assertion_this_should_be_true’ is negative
Makro, typedef için benzersiz bir ad oluşturmak üzere değiştirilebilir veya değiştirilmelidir (yani adın __LINE__
sonunda birleştirin static_assert_...
)
Üçlü bir kod yerine, bu #define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[2*(!!(COND))-1]
paslı olde cc65 (6502 cpu için) derleyicisinde bile işe yarayan kullanılabilir .
GÜNCELLEME:
Tamlık adına, işte sürüm__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: GCC'ye özel kod
GCC 4.3 (sanırım) "hata" ve "uyarı" işlev özelliklerini tanıttı. Bu özniteliğe sahip bir işleve yapılan çağrı, ölü kodun ortadan kaldırılmasıyla (veya diğer önlemlerle) ortadan kaldırılamazsa, bir hata veya uyarı üretilir. Bu, kullanıcı tanımlı hata açıklamaları ile derleme zamanı iddiaları yapmak için kullanılabilir. Kukla bir işleve başvurmadan ad alanı kapsamında nasıl kullanılabileceklerini belirlemeye devam ediyor:
#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()
{
}
Ve şöyle görünüyor:
$ 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
-Og
) bunun çalışması için genellikle yeterli olabilir ve hata ayıklamayı engellememelidir. __OPTIMIZE__
(Ve __GNUC__
) tanımlanmamışsa , statik iddiayı bir işlemsiz veya çalışma zamanı iddiası yapmak düşünülebilir .
__LINE__
Gcc 4.1.1'deki sürüme benzer bir şey kullanıyorum ... iki farklı başlık aynı numaralı satırda bir tane olduğunda ara sıra rahatsızlık duyuyorum !
Sorunun açıkça gcc'den bahsettiğini biliyorum, ancak burada tamlık için Microsoft derleyicileri için bir ince ayar var.
İkna etmiyor typedef olumsuz boyutlu diziyi kullanarak cl iyi hata tükürmek için. Sadece diyor error C2118: negative subscript
. Sıfır genişlikli bir bit alanı bu açıdan daha iyidir. Bu, bir yapının typedeffing'i içerdiğinden, gerçekten benzersiz tür adları kullanmamız gerekir. __LINE__
hardalı kesmez - COMPILE_TIME_ASSERT()
aynı satırda bir başlık ve bir kaynak dosyaya sahip olmak mümkündür ve derlemeniz bozulur. __COUNTER__
kurtarmaya geliyor (ve 4.3'ten beri gcc'de bulunuyor).
#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__)
Şimdi
STATIC_ASSERT(sizeof(long)==7, use_another_compiler_luke)
altında cl
verir:
C2149 hatası: 'static_assertion_failed_use_another_compiler_luke': adlandırılmış bit alanı sıfır genişliğe sahip olamaz
Gcc ayrıca anlaşılır bir mesaj verir:
hata: 'static_assertion_failed_use_another_compiler_luke' bit alanı için sıfır genişlik
Gönderen Vikipedi :
#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
COMPILE_TIME_ASSERT( BOOLEAN CONDITION );
Ben ediyorum DEĞİL bir kullanarak çözüm kullanarak tavsiye typedef
:
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
typedef
Anahtar kelimeli dizi bildiriminin derleme zamanında değerlendirilmesi garanti EDİLMEZ. Örneğin, blok kapsamındaki aşağıdaki kod derlenecektir:
int invalid_value = 0;
STATIC_ASSERT(invalid_value, this_should_fail_at_compile_time_but_will_not);
Bunun yerine bunu tavsiye ederim (C99'da):
#define STATIC_ASSERT(COND,MSG) static int static_assertion_##MSG[(COND)?1:-1]
static
Anahtar sözcük nedeniyle dizi derleme zamanında tanımlanacaktır. Bu iddianın yalnızca COND
derleme zamanında değerlendirilenlerle çalışacağını unutmayın . Değişkenlere atanan değerler gibi bellekteki değerlere dayalı koşullarla çalışmaz (yani derleme başarısız olur).
STATIC_ASSERT () makrosu ile birlikte kullanılıyorsa __LINE__
, bir .c dosyasındaki bir giriş ile bir başlık dosyasındaki farklı bir giriş arasındaki satır numarası çatışmalarını dahil ederek önlemek mümkündür __INCLUDE_LEVEL__
.
Örneğin :
/* 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]
Klasik yöntem bir dizi kullanmaktır:
char int_is_4_bytes_assertion[sizeof(int) == 4 ? 1 : -1];
Bu işe yarar çünkü eğer iddia doğruysa dizinin boyutu 1'dir ve geçerlidir, ancak yanlışsa -1'in boyutu bir derleme hatası verir.
Çoğu derleyici, değişkenin adını gösterecek ve iddia hakkında nihai yorumlar bırakabileceğiniz kodun sağ bölümünü gösterecektir.
#define STATIC_ASSERT()
makroya sarmak ve genel örneklerinizden daha genel örnekler ve örnek derleyici çıktısı STATIC_ASSERT()
sağlamak size çok daha fazla olumlu oy verecek ve bu tekniğin daha mantıklı olacağını düşünüyorum.
Perl'den, özellikle perl.h
3455 numaralı satır ( <assert.h>
önceden dahil edilmiştir):
/* 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
Varsa static_assert
(nereden <assert.h>
), kullanılır. Aksi takdirde, koşul yanlışsa, negatif boyutlu bir bit alanı bildirilir ve bu da derlemenin başarısız olmasına neden olur.
STMT_START
/ STMT_END
Genişleyen makrolardır do
/ while (0)
sırasıyla.
_Static_assert()
artık C'nin tüm sürümleri için gcc'de tanımlanmıştır ve static_assert()
C ++ 11 ve sonrasında tanımlanmıştırSTATIC_ASSERT()
nedenle aşağıdaki basit makro burada çalışır:g++ -std=c++11
) veya üstügcc -std=c90
gcc -std=c99
gcc -std=c11
gcc
(standart belirtilmemiş)Aşağıdaki STATIC_ASSERT
gibi tanımlayın :
/* 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")
Şimdi kullan:
STATIC_ASSERT(1 > 2); // Output will look like: error: static assertion failed: "(1 > 2) failed"
Gcc 4.8.4 kullanılarak Ubuntu'da test edilmiştir:
Örnek 1: iyi gcc
çıktı (yani: STATIC_ASSERT()
kodlar çalışıyor, ancak koşul yanlıştı, derleme zamanı iddiasına neden oluyordu):
$ gcc -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 'main'
işlevinde static_assert.c: 78: 38: error: static assertion fail: "(1> 2) başarısız"
#define STATIC_ASSERT (test_for_true ) _Static_assert ((test_for_true), "(" #test_for_true ") başarısız")
^
static_assert.c: 88: 5: not: 'STATIC_ASSERT'
STATIC_ASSERT (1> 2) makrosunun genişlemesinde ;
^
Örnek 2: iyi g++ -std=c++11
çıktı (yani: STATIC_ASSERT()
kodlar çalışıyor, ancak koşul yanlıştı, derleme zamanı iddiasına neden oluyordu):
$ g ++ -Wall -std = c ++ 11 -o static_assert static_assert.c && ./static_assert
static_assert.c: 'int main ()'
static_assert.c: 74: 32: hata: statik ispat başarısız oldu: (1> 2) başarısız
#define _Static_assert static_assert / *static_assert
C ++ 11 veya sonraki bir sürümün parçasıdır * /
^
static_assert.c: 78: 38: not: '_Static_assert' makrosunun genişletilmesinde
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true ") başarısız")
^
static_assert.c: 88: 5: not: 'STATIC_ASSERT'
STATIC_ASSERT (1> 2) makrosunun genişlemesinde ;
^
Örnek 3: başarısız C ++ çıkışı (yani, C ++ 11'den önce bir C ++ sürümü kullanıldığından, onay kodu hiç düzgün çalışmıyor )
$ g ++ -Wall -o static_assert static_assert.c && ./static_assert
static_assert.c: 88: 5: uyarı: tanımlayıcı 'static_assert' C ++ 11 [-Wc ++ 0x-uyumlu]
STATIC_ASSERT (1> 2 );
^
static_assert.c: 'int main ()'
static_assert.c: 78: 99: hata: 'static_assert' bu kapsamda bildirilmedi
#define STATIC_ASSERT (test_for_true) _Static_assert ((test_for_true), "(" #test_for_true " ) başarısız ")
^
static_assert.c: 88: 5: not: 'STATIC_ASSERT'
STATIC_ASSERT (1> 2) makrosunun genişlemesinde ;
^
/*
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;
}
static_assert
makro varken neden bu kadar karmaşık assert.h
?
static_assert()
C'de hiç mevcut değildir. Ayrıca buraya bakın: en.cppreference.com/w/cpp/language/static_assert - static_assert
"(C ++ 11'den beri)" var olduğunu gösterir. Cevabımın güzelliği, sadece C ++ 11 ve sonraki sürümler yerine gcc'nin C90 ve sonraki sürümlerinde ve ayrıca herhangi bir C ++ 11 ve sonraki sürümlerde çalışmasıdır static_assert()
. Ayrıca, cevabımla ilgili karmaşık olan nedir? Sadece bir çift #define
.
static_assert
C11'den beri C'de tanımlanmıştır. Genişleyen bir makrodur _Static_assert
. en.cppreference.com/w/c/error/static_assert . Ek olarak ve cevabınızın aksine _Static_assert
c99 ve c90'da gcc'de yoktur (sadece gnu99 ve gnu90'da). Bu, standartla uyumludur. Temel olarak, yalnızca gnu90 ve gnu99 ile derlendiğinde fayda sağlayan ve gerçek kullanım alanını önemsiz ölçüde küçük yapan çok fazla ekstra iş yaparsınız.
Gerçekten basit ve taşınabilir bir şey isteyenler, ancak C ++ 11 özelliklerine erişimi olmayanlar için, tam olarak yazdım. Normal olarak
kullanın STATIC_ASSERT
(isterseniz aynı işlevde iki kez yazabilirsiniz) ve GLOBAL_STATIC_ASSERT
ilk parametre olarak benzersiz bir ifade ile işlevlerin dışında kullanın .
#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;
}
Açıklama:
Öncelikle, varsa kesinlikle kullanmak isteyeceğiniz gerçek iddiaya sahip olup olmadığınızı kontrol eder.
Eğer yapmazsanız, pred
icatınızı alıp kendi kendine bölerek iddia eder. Bu iki şey yapar.
Sıfır ise, id est, iddia başarısız oldu, sıfıra bölme hatasına neden olur (aritmetik, bir dizi bildirmeye çalıştığı için zorlanır).
Sıfır değilse, dizi boyutunu olarak normalleştirir 1
. Dolayısıyla, iddia başarılı olsaydı, yükleminiz -1
(geçersiz) olarak değerlendirildiği için başarısız olmasını istemezsiniz veya 232442
(optimize edilmiş olsaydı büyük alan israfı, IDK).
İçin STATIC_ASSERT
bu parantez içinde sarılır, bu değişken kapsamları bir blok yaparassert
yani birçok kez yazabilirsin.
Ayrıca void
, unused variable
uyarılardan kurtulmanın bilinen bir yolu olan yayınlar .
Çünkü GLOBAL_STATIC_ASSERT
, bir kod bloğunun içinde olmak yerine, bir ad alanı oluşturur. Ad alanlarına işlevlerin dışında izin verilir. unique
Bunu bir kereden fazla kullanırsanız, çakışan tanımları durdurmak için bir tanımlayıcı gerekir.
Benim için GCC ve VS'12 C ++ üzerinde çalıştı
Bu, "kullanılmayanları kaldır" seçenek setiyle çalışır. Global parametreleri kontrol etmek için bir global fonksiyon kullanabilirim.
//
#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
//
Bu bazı eski gcc için çalıştı. Maalesef hangi sürüm olduğunu unuttum:
#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
_Static_assert
.