Preprocessor - can it make conditional decisions

T

Thad Smith

Richard said:
No. But in this particular case, if you just use an ordinary "if",
the compiler will be able to see that the condition is constant and
will probably generate the same code as if you had been able to do it
in the preprocessor. On the other hand, it may well give a warning
for a conditional with a constant condition.

I think that answers the OP well.

I use that technique a fair amount in embedded systems to generalize I/O
port, pin, logic level, shadow RAM requirements, etc. The compiler
evaluates a lot of constant conditionals and expressions, then generates
efficient code. The only bane is the additional warning you mention and
the difficulty of reading some of the hairy macros. To compensate,
their use is well documented.
 
A

Army1987

Yes. The C way to do the above is:

#define PUT_BYTE(const_index, val) \
do { \
if (!const_index) { *(int*)77 = (val); } \
else { *(int *)99 = (val); } \
} while (0)

Any reasons for use the ugly do while (0) trick when there is
a way to write it as an expression? What if I want to use it
as the third expression in the guard of a for loop? IMO, this
(except when necessary) is even worse than using a function.

#define PUT_BYTE(const_index, val) \
( *(int *)((const_index) ? 99 : 77) = (val) )
(Note that it always evaluates each argument exactly once.)
Or
int PUT_BYTE(int const_index, int val)
{
if (const_index) {
return *(int *)99 = val;
} else {
return *(int *)77 = val;
}
Of course, there's no need to use a macro in the first place; just make
it a function. In C99 (or various flavors of C89 with the extension),
you may want to make it an inline function if you're concerned about
overhead.
Right.
 
A

Army1987

Works better if you use the language correctly:

#if const_index == 0
# define PUT_BYTE(const_index, val) (whatever is legal)
#elsif const_index == 1 You meant elif?
# define PUT_BYTE(const_index, val) (something else)
Hmm. If const_index is a macro expanding to 1, the above becomes
# define PUT_BYTE(0, val) (something else)
I think the OP was trying to have a macro in which
PUT_BYTE(0, val) expanded to ((something)(val))
and PUT_BYTE(1, foo) expanded to ((sth_else)(foo))
I don't think it is possible in standard C, but, since the first
argument is known at preprocessing time, one could simplily use
a PUT_BYTE0 macro and a PUT_BYTE1 macro.
 
A

Army1987

Hi Chris!

Here's a nice example that illustrates ({ ... }):


/*********({ ... })**********/
#include <stdio.h>

#define GET_OUT 'q'

#define PRE_COND \
({ c = getchar(); \
putchar(c); \
})
This is not standard C, it is a GNU extension. Try
#define PRE_COND (c = getchar(), putchar(c))
 
M

Marcin Wolcendorf

Army1987 said:
Hmm. If const_index is a macro expanding to 1, the above becomes
# define PUT_BYTE(0, val) (something else)
I think the OP was trying to have a macro in which
PUT_BYTE(0, val) expanded to ((something)(val))
and PUT_BYTE(1, foo) expanded to ((sth_else)(foo))
I don't think it is possible in standard C,

Consider ## operator. It *is* possible. And you don't really want to do
it... ;).

Let's *try* to do it:
#define PUT_BYTE_0 sth
#define PUT_BYTE_1 sth_else

#define PUT_BYTE_EXPANDED(__x, __y) \
((PUT_BYTE_ ## __x)(__y))

#define PUT_BYTE(__x, __y) PUT_BYTE_EXPANDED(__x, __y)

but, since the first
argument is known at preprocessing time, one could simplily use
a PUT_BYTE0 macro and a PUT_BYTE1 macro.

Sometimes you can't. Consider function with parameter in it's name (you
can't use parameter, cause you have no register available) configured at
compile time.

M.

PS. I leave discovering PUT_BYTE_EXPANDED usage reasons as an exercise ;).
M.
 
R

Richard

CBFalconer said:
You are top-posting. Cease this if you want to have answers.

Please do not top-post. Your answer belongs after (or intermixed
with) the quoted material to which you reply, after snipping all
irrelevant material. See the following links:

--
<http://www.catb.org/~esr/faqs/smart-questions.html>
<http://www.caliburn.nl/topposting.html>
<http://www.netmeister.org/news/learn2quote.html>
<http://cfaj.freeshell.org/google/> (taming google)
<http://members.fortunecity.com/nnqweb/> (newusers)

Back in the killfile you go. And get your signatures sorted out before
you lecture other people on posting styles.
 
S

Stephen Sprunk

Army1987 said:
Any reasons for use the ugly do while (0) trick when there is
a way to write it as an expression? What if I want to use it
as the third expression in the guard of a for loop? IMO, this
(except when necessary) is even worse than using a function.

#define PUT_BYTE(const_index, val) \
( *(int *)((const_index) ? 99 : 77) = (val) )

It didn't occur to me to use the trinary operator on the left side of an
assignment expression; that's probably an improvement, though I consider it
less readable and you should end up with the same code generated.
(Note that it always evaluates each argument exactly once.)

It's better in that respect.
Or
int PUT_BYTE(int const_index, int val)
{
if (const_index) {
return *(int *)99 = val;
} else {
return *(int *)77 = val;
}

Evil; functions should not be in upper case.

S
 
A

anon.asdf

Consider ## operator. It *is* possible. And you don't really want to do
it... ;).

Let's *try* to do it:
#define PUT_BYTE_0 sth
#define PUT_BYTE_1 sth_else

#define PUT_BYTE_EXPANDED(__x, __y) \
((PUT_BYTE_ ## __x)(__y))

#define PUT_BYTE(__x, __y) PUT_BYTE_EXPANDED(__x, __y)


Sometimes you can't. Consider function with parameter in it's name (you
can't use parameter, cause you have no register available) configured at
compile time.

M.

PS. I leave discovering PUT_BYTE_EXPANDED usage reasons as an exercise ;).
M.

Hi Marcin!

I've tested it with
/*************/
#include <stdio.h>

#define PUT_BYTE_0(val) printf("0: %d\n", val)
#define PUT_BYTE_1(val) printf("1: %d\n", val)

#define PUT_BYTE(__x, __y) (PUT_BYTE_ ## __x(__y))

int main(void)
{
PUT_BYTE(0, 8);
PUT_BYTE(1, 9);
return 0;
}
/*************/

and it works nicely:

(I don't think we need PUT_BYTE_EXPANDED)

But this is just luck, that it's such a simple example!


How would you do the following:


/************** Example: nice to have *****************/
int regA;
#define MEM_MAP_A &regA;

int regB;
#define MEM_MAP_B &regB;

#define GO(val) \
#preprocessor_META_level_ON \
if (!(val % 3)) \
#machine_code_begin \
*((int *) MEM_MAP_A) = val \
#machine_code_end \
else \
#machine_code_begin \
*((int *) MEM_MAP_B) = val \
#machine_code_end \
#preprocessor_META_level_OFF \

/*
if the preprocessor catches a variable in the meta_level e.g.
{
int my_var;
GO(my_var);
}

then a compiler-error (or rather preprocessor-error) should result!
*/

//In the code I'll have:

{
GO(1);
//...
GO(876);
//...
GO(7987);
//...
GO(3);
//...

}

/************** end *****************/

????????????


It can probably be done, but the header file with it's preprocessor
lines will be huge. Probably just as huge as

/************** Example: bitter reality *****************/
//...
#define GO_1 *((int *) MEM_MAP_B) = 1
#define GO_2 *((int *) MEM_MAP_B) = 2
#define GO_3 *((int *) MEM_MAP_A) = 3
#define GO_4 *((int *) MEM_MAP_B) = 4
#define GO_5 *((int *) MEM_MAP_B) = 5
//...
#define GO_7987 *((int *) MEM_MAP_B) = 7987

//many many lines above!

//In the code I'll have:

{
GO_1;
//...
GO_876;
//...
GO_7987;
//...
GO_3;
//...

}

/************** end *****************/


The reality is that the preprocessor is not turing complete if a
program is preprocessed only once. (Ref: http://www.ioccc.org/2001/herrmann1.hint)

But a Meta-Level would make it turing complete, right? Refer to:
http://groups.google.de/group/comp.lang.c/msg/4d74e79644f7befd?

-Albert
 
A

anon.asdf

That's what the compiler is for.

No! The compiler is there to generate machine code from C.

It should not be clouded by Meta-information like bit-width
calculations etc. all of which belong firmly to the preprocessor.

Though nice things can be done with the preprocessor, it is
unfortunately not powerful enough to allow one to really do things on
a meta-level.

Things like bit-width calculations etc. should really be done on a
Meta-Level, since the output is then a fixed bit-width, or a fixed C-
code segment:

I hate writing things like:

#define AAA 1<<8

int a = AAA;

because how do I know what the compiler is going to do, without
checking the generated asm, or machine code?

I'll rather write:
#define AAA 256 /* 1<<8 */

but I don't like it. This is where a Meta-Level would come in handy:

#define AAA \
#META_level( 1<<8 )

//or

#define BIT_WIDTH 8
#define AAA \
#META_level( 1<<BIT_WIDTH )

The Meta-Level guarantees that this is resolved.

On the otherhand, perhaps I've now just written a lot of junk, since
perhaps every compiler out there will simplify and resolve constants,
constant expressions, conditionals with constants, etc.

I still think my modulo-3 example:
http://groups.google.com/group/comp.lang.c/msg/a889a3452cb74c4c?
is interesting though.

?
-Albert
 
A

anon.asdf

#define PUT_BYTE(const_index, val) \
do { \
if (!const_index) { *(int*)77 = (val); } \
else { *(int *)99 = (val); } \

} while (0)

(Note that assumes val is of type int; modify the cast if it's some other
type. And, of course, that cast is inherently unportable for at least two
reasons.)

Of course, there's no need to use a macro in the first place; just make it a
function. In C99 (or various flavors of C89 with the extension), you may
want to make it an inline function if you're concerned about overhead.

But what if I make it a function which evaluates only constants and
returns only constants. In other words the function is only there
during preprocessing and compiler-resolution and -optimization.

Will an inline function guarantee that no superfluous code-bits will
sit in the generated machine code???

Example:

static inline int go(const int index, int val) {
if(index == 0) {
*((int *)77) = val;
} else {
*((int *)77) = val;
}
}

int main(void)
{
go(0, 1);
go(1, 3);

int a;
// calculations with a...
go(a, 8);

}


If all conditionals in the inline are resolved during preprocessing,
compiler-resolution/optimization, then thats nice.

The machine code should only have one conditional for
go(a, 8)
since it is not known before hand, what a will be

Albert
 
S

Stephen Sprunk

But what if I make it a function which evaluates only constants and
returns only constants. In other words the function is only there
during preprocessing and compiler-resolution and -optimization.

Will an inline function guarantee that no superfluous code-bits will
sit in the generated machine code???

That's the whole point of inlining. Basically, you get all the performance
and overhead savings of using macros with all the safety and (most of the)
flexibility of functions.
Example:

static inline int go(const int index, int val) {
if(index == 0) {
*((int *)77) = val;
} else {
*((int *)77) = val;
}
}

int main(void)
{
go(0, 1);
go(1, 3);

int a;
// calculations with a...
go(a, 8);

}


If all conditionals in the inline are resolved during preprocessing,
compiler-resolution/optimization, then thats nice.

The machine code should only have one conditional for
go(a, 8)
since it is not known before hand, what a will be

Any decent compiler will do that with the appropriate optimizations enabled.
Many will even do it without the function being declared inline, as long as
the function's definition is visible. If your compiler doesn't do this, get
a new one. This has been a standard feature of C compilers ever since C++
made inlining so critical.

S
 
K

Keith Thompson

Stephen Sprunk said:
Any decent compiler will do that with the appropriate optimizations
enabled. Many will even do it without the function being declared
inline, as long as the function's definition is visible. If your
compiler doesn't do this, get a new one. This has been a standard
feature of C compilers ever since C++ made inlining so critical.

For a loose definition of "standard". As I'm sure you know, the C90
standard doesn't define the 'inline' keyword (though it certainly
permits inlining as an optimization). C99 has the 'inline' keyword,
but it's only a hint, similar to 'register'. A conforming but
low-quality C99 compiler could simply ignore it, or a clever compiler
could ignore it and make its own decisions about which functions to
inline.

If you mean that support for inlining (with or without the keyword) is
a common feature of C compilers because many of them share code with
C++ compilers, you may well be right.
 
¬

¬a\\/b

anon wrote in message


That's the whole point of inlining. Basically, you get all the performance
and overhead savings of using macros with all the safety and (most of the)
flexibility of functions.


Any decent compiler will do that with the appropriate optimizations enabled.
Many will even do it without the function being declared inline,

i not agree;
if i declare a big function not inline and the compiler inline it =>
the exe size (or the size of some loop) can become too big for the
good use from cpu
 
A

Army1987

anon wrote in message
static inline int go(const int index, int val) { [snip]
}


If all conditionals in the inline are resolved during preprocessing,
compiler-resolution/optimization, then thats nice.

The machine code should only have one conditional for
go(a, 8)
since it is not known before hand, what a will be

Any decent compiler will do that with the appropriate optimizations enabled.
Many will even do it without the function being declared inline,

i not agree;
if i declare a big function not inline and the compiler inline it =>
the exe size (or the size of some loop) can become too big for the
good use from cpu
Any decent compiler will do that only if it understands that it is
worth the trouble. Also, with some compilers you can choose
whether to optimize for space or for time (e.g -Os for gcc).
 
A

Army1987

No! The compiler is there to generate machine code from C.

It should not be clouded by Meta-information like bit-width
calculations etc. all of which belong firmly to the preprocessor.

Though nice things can be done with the preprocessor, it is
unfortunately not powerful enough to allow one to really do things on
a meta-level.

Things like bit-width calculations etc. should really be done on a
Meta-Level, since the output is then a fixed bit-width, or a fixed C-
code segment:

I hate writing things like:

#define AAA 1<<8
Add parentheses (1<<8) or there might be great surprises when you
use it in a more complicated expression.
int a = AAA;

because how do I know what the compiler is going to do, without
checking the generated asm, or machine code?
1<<8 is a constant integer expression, so it is *very* unlikely to
be actually computed at runtime.
 
P

pete

Hi Chris!

Here's a nice example that illustrates ({ ... }):

/*********({ ... })**********/

What, the comment?
#include <stdio.h>

#define GET_OUT 'q'

#define PRE_COND \
({ c = getchar(); \
putchar(c); \
})

That's no good. That doesn't compile.
Should be:
#define PRE_COND (c = getchar(), putchar(c))
 
C

CBFalconer

Army1987 said:
¬a\/b wrote:
.... snip ...


Any decent compiler will do that only if it understands that it
is worth the trouble. Also, with some compilers you can choose
whether to optimize for space or for time (e.g -Os for gcc).

¬a\/b is a known troll. Ignore it.
 
M

Marcin Wolcendorf

Hi,

Hi Marcin!

I've tested it with
/*************/
#include <stdio.h>

#define PUT_BYTE_0(val) printf("0: %d\n", val)
#define PUT_BYTE_1(val) printf("1: %d\n", val)

#define PUT_BYTE(__x, __y) (PUT_BYTE_ ## __x(__y))

int main(void)
{
PUT_BYTE(0, 8);
PUT_BYTE(1, 9);
return 0;
}
/*************/

and it works nicely:

(I don't think we need PUT_BYTE_EXPANDED)

As long, as 0 and 1 are numbers, not defines. If they are '#define'd,
you'll need expansion.

Regards,

M.W.
 

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

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,111
Latest member
KetoBurn
Top