Howto check value of constants in a macro

R

Richard Meister

Hi,

I'd like to define several constants and make sure that all of them are
smaller than a given other constant. I thought this could be done by a
simple macro. Something like this:

#define MAX 999

#define DEF_CHECKED_VAL( name, value) #if (value < MAX) \
#define name MAX \
#else \
#define name value \
#endif

So
DEF_CHECKED_VAL(test_1, 33) should expand to #define test_1 33
and
DEF_CHECKED_VAL(test_2, 1000) should yield #define test_2 999


As you probably know (and I learned today) this doesn't work. The most
relevant FAQ to this problems seems to be "10.25 I've got this tricky
preprocessing I want to do and I can't figure out a way to do it."
Unfortunately writing my own preprocessor isn't really an option and I
can't believe that I'm the first one who'd like to implement this
functionality. Is there a well-known solution to my problem, or am I doing
something fundamentally stupid?

Thanks for any help, Rick
 
B

Ben Pfaff

Richard Meister said:
I'd like to define several constants and make sure that all of them are
smaller than a given other constant. I thought this could be done by a
simple macro. Something like this:

#define MAX 999

#define DEF_CHECKED_VAL( name, value) #if (value < MAX) \
#define name MAX \
#else \
#define name value \
#endif

So
DEF_CHECKED_VAL(test_1, 33) should expand to #define test_1 33
and
DEF_CHECKED_VAL(test_2, 1000) should yield #define test_2 999

I think the #if logic above is opposite your examples, so that
the < should be >.

Here is one possible solution:
#define DEF_CHECKED_VAL(name, value) \
enum { name = (value) < MAX ? (value) : MAX };
 
K

Keith Thompson

Ben Pfaff said:
I think the #if logic above is opposite your examples, so that
the < should be >.

Here is one possible solution:
#define DEF_CHECKED_VAL(name, value) \
enum { name = (value) < MAX ? (value) : MAX };

Oooh, that's clever!

If you need something more complex, that can't be expressed with
integer values and constant expressions, you can always implement your
own *simple* preprocessor. There's no need to write a fully
functional C preprocessor, just a program that produces C code as its
output.

For example, given an input like this:

MAX 999
DEF_CHECKED_VAL test_1 33
DEF_CHECKED_VAL test_2 1000

the program could produce the following output:

#define test_1 33
#define test_2 999

In your build procedure, you can run your program and generate a
header file that's then #included by your C source files. Define
whatever syntax and semantics you like for the tranformation; the
simpler it is, the easier it will be to implement.

(Personally, I'd use Perl for something like this, but it can
certainly be done in C.)
 
P

pete

Richard said:
Hi,

I'd like to define several constants and
make sure that all of them are
smaller than a given other constant. I thought this could be done by a
simple macro.

I use the assert macro for that.

#include <assert.h>

void assert(scalar expression);
 
R

Randy Howard

Ben Pfaff wrote
(in article said:
I think the #if logic above is opposite your examples, so that
the < should be >.

Here is one possible solution:
#define DEF_CHECKED_VAL(name, value) \
enum { name = (value) < MAX ? (value) : MAX };

Wow, that is cool. I didn't think it would really work on first
look. :)
 
B

Ben Pfaff

Keith Thompson said:
If you need something more complex, that can't be expressed with
integer values and constant expressions, you can always implement your
own *simple* preprocessor. There's no need to write a fully
functional C preprocessor, just a program that produces C code as its
output.

For example, given an input like this:

MAX 999
DEF_CHECKED_VAL test_1 33
DEF_CHECKED_VAL test_2 1000

the program could produce the following output:

#define test_1 33
#define test_2 999

Even simpler is to write a C program that takes no input to do
it. For example (untested, not compiled, etc.):

#include <stdio.h>

int main (void) {
const int max = 999;

static const struct {
const char *name;
int value;
} *p, values[] = {
{"test_1", 33},
{"test_2", 1000},
};

int n_values = sizeof values / sizeof *values;

for (p = values; p < &values[n_values]; p++) {
int clamped_value = p->value < max ? p->value : max;
printf ("#define %s %d\n", p->name, clamped_value);
}
return 0;
}
 
C

Christian Bau

pete said:
I use the assert macro for that.

#include <assert.h>

void assert(scalar expression);

If you want to check at compile time, proceed as follows:

0 is a "null pointer constant", and 1 isn't.

Therefore "" == 0 is a valid expression and "" == 1 isn't.

Also, sizeof ("" == 0) is a valid expression and sizeof ("" == 1) isn't.


If you have two constants a and b, then ! (a < b) has a value of 0 if a
< b is true and a value of 1 if a < b is false.

sizeof ("" == ! (a < b)) is a valid expression if a < b and it is not a
valid expression if a >= b.

So if you have for example ten constants c0, c1, ..., 9 and you want to
check at compile time that each is less than 10, then all you need to do
is to write a function

static void compiletimechecks (void) {
sizeof ("" == ! (c0 < 10));
sizeof ("" == ! (c1 < 10));
...
sizeof ("" == ! (c9 < 10));
}

Suitable macros could make this more readable.
 
M

Michael Mair

Christian said:
If you want to check at compile time, proceed as follows:

0 is a "null pointer constant", and 1 isn't.

Therefore "" == 0 is a valid expression and "" == 1 isn't.

Also, sizeof ("" == 0) is a valid expression and sizeof ("" == 1) isn't.


If you have two constants a and b, then ! (a < b) has a value of 0 if a
< b is true and a value of 1 if a < b is false.

sizeof ("" == ! (a < b)) is a valid expression if a < b and it is not a
valid expression if a >= b.

So if you have for example ten constants c0, c1, ..., 9 and you want to
check at compile time that each is less than 10, then all you need to do
is to write a function

static void compiletimechecks (void) {
sizeof ("" == ! (c0 < 10));
sizeof ("" == ! (c1 < 10));
...
sizeof ("" == ! (c9 < 10));
}

Suitable macros could make this more readable.

Shiny.
However, I like the
static char test_c0[10-c0];
trick (seen some time ago in clc) as well.

Cheers
Michael
 
C

Christian Bau

Michael Mair said:
Christian said:
If you want to check at compile time, proceed as follows:

0 is a "null pointer constant", and 1 isn't.

Therefore "" == 0 is a valid expression and "" == 1 isn't.

Also, sizeof ("" == 0) is a valid expression and sizeof ("" == 1) isn't.


If you have two constants a and b, then ! (a < b) has a value of 0 if a
< b is true and a value of 1 if a < b is false.

sizeof ("" == ! (a < b)) is a valid expression if a < b and it is not a
valid expression if a >= b.

So if you have for example ten constants c0, c1, ..., 9 and you want to
check at compile time that each is less than 10, then all you need to do
is to write a function

static void compiletimechecks (void) {
sizeof ("" == ! (c0 < 10));
sizeof ("" == ! (c1 < 10));
...
sizeof ("" == ! (c9 < 10));
}

Suitable macros could make this more readable.

Shiny.
However, I like the
static char test_c0[10-c0];
trick (seen some time ago in clc) as well.

That has two problems: You can run into problems when c0 is unsigned
(for example c0 is 11u, 10 - 11u might be 65535u, and you might allocate
a static array of 65535 characters without warning! Second, you will
declare one or more static arrays and have to hope that compiler and
linker optimise them away. The function above is equivalent to

static void compiletimechecks (void) {
sizeof (int);
sizeof (int);
...
sizeof (int);
}

if it compiles at all and hopefully doesn't generate any code.
 
S

S.Tobias

Keith Thompson said:
Oooh, that's clever!

To expand on the method...
I think it's okay when we need just a number, but it won't work
for the situation where we require a constant of a specific type.
Would these always work as intended:
DEF_CHECKED_VAL(test_1, 33u)
DEF_CHECKED_VAL(test_2, 33l)
?

Also, obviously, enum members could not be used in pp directives:
DEF_CHECKED_VAL(test_1, 33)
/*...*/
#if test_1 > 32


My answer to the problem would rather be a simple:
#define MY_MAX 999
#define CK_MY_MAX(x) ((x) < MY_MAX ? (x) : MY_MAX)
#define test_1 CK_MY_MAX(33)
#define test_2 CK_MY_MAX(1000)
There's only one more word to type per each define. It produces
constant expressions, suitable for pp. Is there a situation where
constant integer expression could not be substituted for an integer
constant? (I don't think so.)
 
S

S.Tobias

Why do we need `sizeof'? Wouldn't this be just enough:
"" == ! (c0 < 10);
?
(Actually, I'd probably add the cast:
(void)("" == ! (c0 < 10));
in order to suppress "code has no effect" warnings.)
}

Suitable macros could make this more readable.

Shiny.
However, I like the
static char test_c0[10-c0];
trick (seen some time ago in clc) as well.

In addtion to what Christian has already written (below), gcc accepts
char array[0];
(perhaps not in `-ansi' mode, but we need something practical).
That's why I have always coded this like:
static char test_c0[(c0 < 10) * 2 - 1];
(which has more chances to fail on any compiler if `c0' is not right).
Christian's method might be better, but I havn't checked it yet (I don't
want just warnings, I want the whole compiler to bail out).
That has two problems: You can run into problems when c0 is unsigned
(for example c0 is 11u, 10 - 11u might be 65535u, and you might allocate
a static array of 65535 characters without warning! Second, you will
declare one or more static arrays and have to hope that compiler and
linker optimise them away. The function above is equivalent to
[snip]
 
M

Michael Mair

S.Tobias said:
Why do we need `sizeof'? Wouldn't this be just enough:
"" == ! (c0 < 10);
?
(Actually, I'd probably add the cast:
(void)("" == ! (c0 < 10));
in order to suppress "code has no effect" warnings.)

Hmmm, I'd say because even if the compiler does not
optimise at all,
sizeof ("" == ! (c0 < 10));
will give you
sizeof 0;
if the thing compiles (sprinkle with (void) ad libitum).
However,
"" == !(c0 < 10);
might be carried out otherwise, including memory for each
and every "".

It certainly is unnecessary for every C compiler I worked
with or generated code for, though.
}

Suitable macros could make this more readable.

Shiny.
However, I like the
static char test_c0[10-c0];
trick (seen some time ago in clc) as well.


In addtion to what Christian has already written (below), gcc accepts
char array[0];
(perhaps not in `-ansi' mode, but we need something practical).
That's why I have always coded this like:
static char test_c0[(c0 < 10) * 2 - 1];
(which has more chances to fail on any compiler if `c0' is not right).

Good one :)
Christian's method might be better, but I havn't checked it yet (I don't
want just warnings, I want the whole compiler to bail out).

It certainly is more elegant.


True; your solution is better and safer to use in this respect. I only
used this for checking exact values (by using a pair of these arrays)
at compile time with appropriate casting.


Cheers
Michael
 

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,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top