compile-time assertions

M

Mark

Hello

have found a nice C-language trick on a Web. It implements a compile time
'asserts':

#define COMPILE_TIME_ASSERT(cond, msg) char msg[(cond) ? 1 : -1]

int main(void)
{
int p = 1;
COMPILE_TIME_ASSERT(p > 2, data);
return 0;
}

Unfortunately, it doesn't act as I thought it would. I expected a compiler
(gcc 4.1.1) emits error or at least warning about negative array index, but
it didn't (in both C89 and C99 modes). But it doesn complain if I explicitly
declare an array with negative index:

int k[-1]; /* error: size of array 'k' is negative */

Is the compile-time assertion method not portable ?
 
B

Ben Bacarisse

Mark said:
have found a nice C-language trick on a Web. It implements a compile
time 'asserts':

#define COMPILE_TIME_ASSERT(cond, msg) char msg[(cond) ? 1 : -1]

Neat tricks on the web often miss corner cases.
int main(void)
{
int p = 1;
COMPILE_TIME_ASSERT(p > 2, data);
return 0;
}

Unfortunately, it doesn't act as I thought it would. I expected a
compiler (gcc 4.1.1) emits error or at least warning about negative
array index, but it didn't (in both C89 and C99 modes). But it doesn
complain if I explicitly declare an array with negative index:

int k[-1]; /* error: size of array 'k' is negative */

Is the compile-time assertion method not portable ?

p > 2 is not a constant expression evaluated at compile time. This
"assert" only works for constant expressions.

You should have got a message from a conforming C89 compiler. If you
did not, send a bug report!

In C99, it is permitted to have an array whose size is determined at
run-time. It would have been better if the author had used a method
that fails when the expression can't be evaluated a compile time, even
in C99. For example, by using a bit-field width rather than an array
size:

#define COMPILE_TIME_ASSERT(cond, msg) \
struct assert_struct { unsigned msg : (cond) ? 1 : -1; }

but I suspect there are other improvements one can make as well.

These things are not that common because there are comparatively few
things that can be tested this way that can't be done using #if and
#error. The main use I've seen is asserting properties involving
sizeof.
 
B

Ben Pfaff

Mark said:
have found a nice C-language trick on a Web. It implements a compile
time 'asserts':

#define COMPILE_TIME_ASSERT(cond, msg) char msg[(cond) ? 1 : -1]

int main(void)
{
int p = 1;
COMPILE_TIME_ASSERT(p > 2, data);
return 0;
}

Unfortunately, it doesn't act as I thought it would. I expected a
compiler (gcc 4.1.1) emits error or at least warning about negative
array index, but it didn't (in both C89 and C99 modes).

The problem here is that your expression "p > 2" is not a
constant expression, because "p" is a variable, so that its value
can only be evaluated at runtime. (It doesn't matter that, in
fact, its value is always the same at runtime. The C language
does not consider the value of any variable to be a constant
expression.)

In C89, specifying a non-constant array size is in itself a
constraint violation. But in C99, and under GCC in its default
C89-plus-extensions mode, non-constant array sizes are allowed.
They just get evaluated at runtime instead (and cause an error at
that point). So that's what you have happening.

Try an actual constant expression in place of "p > 2" instead,
e.g. "1 > 2".
 
S

Seebs

have found a nice C-language trick on a Web. It implements a compile time
'asserts':
#define COMPILE_TIME_ASSERT(cond, msg) char msg[(cond) ? 1 : -1]

No, it doesn't. :)
int main(void)
{
int p = 1;
COMPILE_TIME_ASSERT(p > 2, data);
return 0;
}
Unfortunately, it doesn't act as I thought it would. I expected a compiler
(gcc 4.1.1) emits error or at least warning about negative array index, but
it didn't (in both C89 and C99 modes). But it doesn complain if I explicitly
declare an array with negative index:
int k[-1]; /* error: size of array 'k' is negative */
Is the compile-time assertion method not portable ?

It isn't.

Or rather, it isn't for things involving non-constant expressions. There's
various hacks like this, and they have a common trait which can come back
and bite you: They are nearly always the wrong tool for the job. Depending
on the compiler, even if you got a variant of this working, you might still
get totally useless error messages.

Consider that the diagnostic message you get will never, ever, have anything
to do with whether or not p is greater than 2. That makes it pretty useless.

Anyway, what's happening here is probably: gcc sees the reference to a
variable, concludes that this is not a constant expression, and hands it off
to the variable-length-array support to generate code to perform the
calculation at runtime.

You simply can't portably make an "assertion" that refers to the value of
a variable, because variables, well, vary -- they don't have a fixed value
at compilation time. (Even in cases where you actually know they ought to,
they're still not constants.)

-s
 
M

Mark

Ben Bacarisse wrote:
[skip]

To be clear the article I've read about his method is
http://www.embedded.com/columns/programmingpointers/164900888?printable=true
p > 2 is not a constant expression evaluated at compile time. This
"assert" only works for constant expressions.

You should have got a message from a conforming C89 compiler. If you
did not, send a bug report!

Yes, gcc invoked with '-std=c89' generates such warning: "ISO C90 forbids
variable-size array 'data'".
In C99, it is permitted to have an array whose size is determined at
run-time. It would have been better if the author had used a method
that fails when the expression can't be evaluated a compile time, even
in C99.

I also thought about this after having read the article, and the author
didn't mention anywhere his method is only valid for C90 compilers.
For example, by using a bit-field width rather than an array
size:

#define COMPILE_TIME_ASSERT(cond, msg) \
struct assert_struct { unsigned msg : (cond) ? 1 : -1; }

but I suspect there are other improvements one can make as well.

Hmm... It doesn't compile neither for C90 nor for C99 with the message
"error: bit-field 'data' width not an integer constant". Obviously the same
reason as was with non-constant array size?

#define COMPILE_TIME_ASSERT(cond, msg) \
struct assert_struct { unsigned msg : (cond) ? 1 : -1; }

int main(void)
{
int p = 1;
COMPILE_TIME_ASSERT(p > 2, data); /* with 4 >2 code compiles, with 1 >
2 don't */
return 0;
}
These things are not that common because there are comparatively few
things that can be tested this way that can't be done using #if and
#error. The main use I've seen is asserting properties involving
sizeof.

Could you post here some examples or web links?
 
O

Old Wolf

Hello

have found a nice C-language trick on a Web. It implements a compile time
'asserts':

#define COMPILE_TIME_ASSERT(cond, msg) char msg[(cond) ? 1 : -1]

int main(void)
{
    int p = 1;
    COMPILE_TIME_ASSERT(p > 2, data);
    return 0;

This isn't a compile-time check -- in C99 it
attempts to declare a VLA, since you give an
array whose parameter isn't a compile-time
constant.

The usual idiom for this would be
#define COMPILE_TIME_ASSERT(cond, msg) typedef char msg[(cond) ? 1 :
-1]

which (I think!) avoids your situation, since a VLA
can't be typedef'd.

Of course this doesn't help with what you
actually want to do, which is to check p.
This isn't possible at compile-time, since
p is not a compile-time constant, as mentioned
previously. :(
 
B

Ben Bacarisse

Mark said:
Ben Bacarisse wrote:
[skip]

To be clear the article I've read about his method is
http://www.embedded.com/columns/programmingpointers/164900888?printable=true
p > 2 is not a constant expression evaluated at compile time. This
"assert" only works for constant expressions.

You should have got a message from a conforming C89 compiler. If you
did not, send a bug report!

Yes, gcc invoked with '-std=c89' generates such warning: "ISO C90
forbids variable-size array 'data'".
In C99, it is permitted to have an array whose size is determined at
run-time. It would have been better if the author had used a method
that fails when the expression can't be evaluated a compile time, even
in C99.

I also thought about this after having read the article, and the
author didn't mention anywhere his method is only valid for C90
compilers.
For example, by using a bit-field width rather than an array
size:

#define COMPILE_TIME_ASSERT(cond, msg) \
struct assert_struct { unsigned msg : (cond) ? 1 : -1; }

but I suspect there are other improvements one can make as well.

Hmm... It doesn't compile neither for C90 nor for C99 with the message
"error: bit-field 'data' width not an integer constant".

That's the whole point! The "trick" is to be used in places where
there is a constant expression: p > 2 is not such an expression. The
compiler tells you that your are using this "hack" with the wrong sort
of expression. That's better than the original which was silently
accepted in places where it was wrong to use it.

Note, though, how confusing the messages are. They don't say "you
can't use compile time assert here" -- it's all about something else.
This is why it is a hack, and why it would be nice if C did have such
a thing. There is a strong possibility that C1x will.
Obviously the
same reason as was with non-constant array size?

#define COMPILE_TIME_ASSERT(cond, msg) \
struct assert_struct { unsigned msg : (cond) ? 1 : -1; }

int main(void)
{
int p = 1;
COMPILE_TIME_ASSERT(p > 2, data); /* with 4 >2 code compiles, with
1 > 2 don't */
return 0;
}


Could you post here some examples or web links?

Sorry, no. I don't know any good sources about this. Why do you
think you need it? Good use cases are not common.
 
M

Mark

Ben said:
That's the whole point! The "trick" is to be used in places where
there is a constant expression: p > 2 is not such an expression. The
compiler tells you that your are using this "hack" with the wrong sort
of expression. That's better than the original which was silently
accepted in places where it was wrong to use it.

Sorry, no. I don't know any good sources about this. Why do you
think you need it? Good use cases are not common.

I was thinking, this hacks may be useful to catch different sort of
alignment problems in structures at compile time, using offsetof() or
sizeof(). I think if sizeof operator is evaluated at compile time and
offsetof() is expanded at compile phase too, then the hack would
well-behave.
 
C

Chris M. Thomasson

Mark said:
I was thinking, this hacks may be useful to catch different sort of
alignment problems in structures at compile time, using offsetof() or
sizeof(). I think if sizeof operator is evaluated at compile time and
offsetof() is expanded at compile phase too, then the hack would
well-behave.

http://pastebin.com/f37a23918

`offsetof()' is awesome.
 
B

Ben Bacarisse

Mark said:
I was thinking, this hacks may be useful to catch different sort of
alignment problems in structures at compile time, using offsetof() or
sizeof(). I think if sizeof operator is evaluated at compile time and
offsetof() is expanded at compile phase too, then the hack would
well-behave.

Yes, that's the sort of thing one could use this for.

Of course, there are no "alignment problems in structures" unless
there are other tricks you are playing at the same time! You might
want to see of there is not a way to avoid the alignment issues in the
first place. Sometimes there isn't, so I am not saying you are wrong
to take this route.
 
M

Mark

Old said:
The usual idiom for this would be
#define COMPILE_TIME_ASSERT(cond, msg) typedef char msg[(cond) ? 1 :
-1]

typedef char x[1];

What is the semantics of the new type aliased in such way? As I understand,
the name of array x[1] is converted to a pointer to its first element, so we
should use:

x p;
 
M

Mark

Richard said:
typedef char x[1];

What is the semantics of the new type aliased in such way?

x is now a synonym for the type char[1]. Thus, you can define an array
of 1 char like this:

From 6.7.7 of the standard:
"...in the following declarations:

typedef T type_ident;
type_ident D;

type_ident is defined as a typedef name with the type specified by the
declaration specifiers in T..."

This is what I don't understand: we declared a synonym for the type 'char',
and its name is 'x[1]', but further we use only 'x' to declare objects of
new type. Why is it so?
 
J

James Kuyper

Mark said:
Richard said:
typedef char x[1];

What is the semantics of the new type aliased in such way?

x is now a synonym for the type char[1]. Thus, you can define an array
of 1 char like this:

From 6.7.7 of the standard:
"...in the following declarations:

typedef T type_ident;
type_ident D;

type_ident is defined as a typedef name with the type specified by the
declaration specifiers in T..."

This is what I don't understand: we declared a synonym for the type
'char', and its name is 'x[1]', but further we use only 'x' to declare
objects of new type. Why is it so?

No, x[1] is never a name; it's either an expression or a part of an
array declaration, but a name has to meet the syntax specifications for
an identifier, and '[' or ']' are not allowed in identifiers. In this
case, the name is x; the typedef makes 'x' an alias for is char[1].
 
L

ld

Hello

have found a nice C-language trick on a Web. It implements a compile time
'asserts':

#define COMPILE_TIME_ASSERT(cond, msg) char msg[(cond) ? 1 : -1]

int main(void)
{
    int p = 1;
    COMPILE_TIME_ASSERT(p > 2, data);
    return 0;

}

Unfortunately, it doesn't act as I thought it would. I expected a compiler
(gcc 4.1.1) emits error or at least warning about negative array index, but
it didn't (in both C89 and C99 modes). But it doesn complain if I explicitly
declare an array with negative index:

int k[-1]; /* error: size of array 'k' is negative */

Is the compile-time assertion method not portable ?

why not

enum { error_msg = 1/(cond) };

cond needs to be a constant expression and therefore do a _compile
time_ assert in both c89 and c99 (techniques base on array may lead to
runtime check in c99). If you find a compiler which does compile the
enum value when cond is false, you should do a bug report.

Here is the macro I use in the C Object System

#define COS_STATIC_ASSERT(tag,cond) \
enum { COS_PP_CAT(COS_ERROR__,tag) = 1/(cond) }

where COS_PP_CAT concatenate the tag to COS_ERROR__

example of use to check a hardcoded default value

COS_STATIC_ASSERT(vector_growth_rate_is_too_small , VECTOR_GROWTH_RATE

cheers,

ld.
 
T

Tim Rentsch

Mark said:
Hello

have found a nice C-language trick on a Web. It implements a compile
time 'asserts':

#define COMPILE_TIME_ASSERT(cond, msg) char msg[(cond) ? 1 : -1]

int main(void)
{
int p = 1;
COMPILE_TIME_ASSERT(p > 2, data);
return 0;
}

There are times when having a compile-time assert is useful, but
I wouldn't say this is one of them. Besides the condition not
being a compile time constant, since the condition being tested
is within a function body, a regular assert() can be used
instead:

...
assert( p > 2 );

Sometimes though we would like to have an assertion outside
function bodies (ie, at the top level), or have the assertion
within a function body but insist that the condition be evaluated
at compile time. For such cases, this definition may be a little
better:

#define STATIC_ASSERT(e,what) extern char (what)[ (e) ? 1 : -1 ]

Adding 'extern' allows STATIC_ASSERT() to be used outside
function definitions without reserving any storage for the
meaningless 'what' array, and also ensures that if used inside
function definitions the condition must still be a compile-time
constant even if C99 is used, because 'extern' variable length
arrays are not allowed. (Also, gcc has a -Wnested-externs flag
which can help with tracking down where these assertions are
used inside functions rather than at the top level.)

Side note: it's a good habit to surround macro parameters with
parentheses, even parameters like 'what' which are expected to be
simple identifiers.
 
T

Tim Rentsch

Old Wolf said:
Hello

have found a nice C-language trick on a Web. It implements a compile time
'asserts':

#define COMPILE_TIME_ASSERT(cond, msg) char msg[(cond) ? 1 : -1]

int main(void)
{
int p = 1;
COMPILE_TIME_ASSERT(p > 2, data);
return 0;

This isn't a compile-time check -- in C99 it
attempts to declare a VLA, since you give an
array whose parameter isn't a compile-time
constant.

The usual idiom for this would be
#define COMPILE_TIME_ASSERT(cond, msg) typedef char msg[(cond) ? 1 :
-1]

which (I think!) avoids your situation, since a VLA
can't be typedef'd.

Certainly VLA's can be typedef'ed. Presumably they can't be
at the top level, but in function bodies they can.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top