Help with 2-level macro expansion

S

Srinivas Mudireddy

Hi,
We have bunch of message levels and depending on whether that level is
turned on, messages of that level are printed. For example, we have
levels like MSG_LOW, MSG_MED etc.

We want to provide a wrapper API on top of this to print various kinds
of messages and map the wrapper API to either MSG_LOW or MSG_MED. For
example, our wrapper API have macros such as
MSG_FUNCTION_ENTRY, MSG_FUNCTION_EXIT, MSG_STATE_CHANGE,
MSG_INVALID_INPUT etc. Whenever a function is called, we call
MSG_FUNCTION_ENTRY macro. This shields the programmer from whether
this kind of messages is mapped to LOW or MED and it allows us to
configure what kind of messages we want to print in various flavors of
a build. In debug build, we might map MSG_FUNCTION_ENTRY to MSG_MED
and in production build, we might map it to MSG_LOW.

I wrote following macros to get this functionality.

#define IS_MSG_LOW_ON 0
#define IS_MSG_MED_ON 1

#define MSG_FUNCTION_ENTRY_LEVEL MSG_MED
#define MSG_FUNCTION_EXIT_LEVEL MSG_MED

#define MSG_FUNCTION_ENTRY(fmt_string, x, y, z) \
#if (IS_ ## MSG_FUNCTION_ENTRY_LEVEL_ ## ON == 1) \
printf(fmt_string, x, y, z);

But this is not working as IS_ ## MSG_FUNCTION_ENTRY_LEVEL_ ## ON is
translated to IS_MSG_FUNCTION_ENTRY_LEVEL_ON instead of IS_MSG_MED_ON.
So how do I get this to work?

Thx,
Srinivas
 
P

Peter Nilsson

Srinivas said:
...I wrote following macros to get this functionality.

#define IS_MSG_LOW_ON 0
#define IS_MSG_MED_ON 1

#define MSG_FUNCTION_ENTRY_LEVEL MSG_MED
#define MSG_FUNCTION_EXIT_LEVEL MSG_MED

#define MSG_FUNCTION_ENTRY(fmt_string, x, y, z) \
#if (IS_ ## MSG_FUNCTION_ENTRY_LEVEL_ ## ON == 1) \
printf(fmt_string, x, y, z);

But this is not working...

See...

http://www.c-faq.com/cpp/index.html
 
B

Ben Bacarisse

This seems not have been answered, so I'll have a go...
We want to provide a wrapper API on top of this to print various kinds
of messages and map the wrapper API to either MSG_LOW or MSG_MED.
I wrote following macros to get this functionality.

#define IS_MSG_LOW_ON 0
#define IS_MSG_MED_ON 1

#define MSG_FUNCTION_ENTRY_LEVEL MSG_MED
#define MSG_FUNCTION_EXIT_LEVEL MSG_MED

#define MSG_FUNCTION_ENTRY(fmt_string, x, y, z) \
#if (IS_ ## MSG_FUNCTION_ENTRY_LEVEL_ ## ON == 1) \
printf(fmt_string, x, y, z);

But this is not working as IS_ ## MSG_FUNCTION_ENTRY_LEVEL_ ## ON is
translated to IS_MSG_FUNCTION_ENTRY_LEVEL_ON instead of IS_MSG_MED_ON.
So how do I get this to work?

Three things need to be sorted. First, a typo:

#if (IS_ ## MSG_FUNCTION_ENTRY_LEVEL ## _ON == 1) \

(note the _ON). Second, you will never be able to generate a
pre-processing directive (in this case a #if) using a macro. You
just can't. If you can live with the macro expanding to an "if"
(which will in all likelyhood be optimised away) then you need to sort
out the "real" problem. To get MSG_FUNCTION_ENTRY_LEVEL expanded
rather than literally token pasted, you need another round of macro
expansion:

#define XP(s) PASTE(s)
#define PASTE(s) IS_ ## s ## _ON

#define MSG_FUNCTION_ENTRY(fmt_string, x, y, z) \
if (XP(MSG_FUNCTION_ENTRY_LEVEL) == 1) \
printf(fmt_string, x, y, z)

(You might dream up better names).

Finally, if you have C99's variadic macros, you don't need to have
three fixed arguments to printf.

#define MSG_FUNCTION_ENTRY(fmt_string, ...) \
if (XP(MSG_FUNCTION_ENTRY_LEVEL) == 1) \
printf(fmt_string, __VA_ARGS__)

If you don't have that, you can consider the "double parentheses"
trick:

#define MSG_FUNCTION_ENTRY(args) \
if (XP(MSG_FUNCTION_ENTRY_LEVEL) == 1) \
printf args

where you use it like this:

MSG_FUNCTION_ENTRY(("A message\n"));
MSG_FUNCTION_ENTRY(("An int %d\n", x));

Note, also, that I have removed the trailing ; from the macro. I
prefer to have it added at the invocation.

I hope that is useful.
 
S

Srinivas Mudireddy

This seems not have been answered, so I'll have a go...











Three things need to be sorted. First, a typo:

#if (IS_ ## MSG_FUNCTION_ENTRY_LEVEL ## _ON == 1) \

(note the _ON). Second, you will never be able to generate a
pre-processing directive (in this case a #if) using a macro. You
just can't. If you can live with the macro expanding to an "if"
(which will in all likelyhood be optimised away) then you need to sort
out the "real" problem. To get MSG_FUNCTION_ENTRY_LEVEL expanded
rather than literally token pasted, you need another round of macro
expansion:

#define XP(s) PASTE(s)
#define PASTE(s) IS_ ## s ## _ON

#define MSG_FUNCTION_ENTRY(fmt_string, x, y, z) \
if (XP(MSG_FUNCTION_ENTRY_LEVEL) == 1) \
printf(fmt_string, x, y, z)

(You might dream up better names).

Finally, if you have C99's variadic macros, you don't need to have
three fixed arguments to printf.

#define MSG_FUNCTION_ENTRY(fmt_string, ...) \
if (XP(MSG_FUNCTION_ENTRY_LEVEL) == 1) \
printf(fmt_string, __VA_ARGS__)

If you don't have that, you can consider the "double parentheses"
trick:

#define MSG_FUNCTION_ENTRY(args) \
if (XP(MSG_FUNCTION_ENTRY_LEVEL) == 1) \
printf args

where you use it like this:

MSG_FUNCTION_ENTRY(("A message\n"));
MSG_FUNCTION_ENTRY(("An int %d\n", x));

Note, also, that I have removed the trailing ; from the macro. I
prefer to have it added at the invocation.

I hope that is useful.

Thx for the help Ben.
 
P

Peter Nilsson

Ben said:
This seems not have been answered, so I'll have a go...

I directed the OP to the FAQ chapter on preprocessing.
Three things need to be sorted. First, a typo:

#if (IS_ ## MSG_FUNCTION_ENTRY_LEVEL ## _ON == 1) \

(note the _ON).

Note the LEVEL_. Also note that _ON is a reserved (for any use)
identifier.

<snip>
 
B

Ben Bacarisse

Peter Nilsson said:
I directed the OP to the FAQ chapter on preprocessing.


Note the LEVEL_.

Yes, I changed both halves but only pointed one out. Presumably you
are suggesting the original form was deliberate (and the real error
was not using MSG_FUNCTION_ENTRY_LEVEL_ earlier). The reason being to
avoid:
Also note that _ON is a reserved (for any use)
identifier.

Ouch! Good catch. However, does it matter in this case? Isn't _ON
is only a preprocessing token and never gets to be real grown up
identifier.
 

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,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top