Macro redefinition

T

Tor Rustad

I'm implementing a parser, where there is a lot of different data
elements defined. Now instead of hand-coding X defines, and the massive
number of lines needed for table initialization, I decided to generate
this source instead.


So, I basically used this trick:

void gen_source_function(FILE *out)
{
#define EXPAND_DEF(num, name) generate_source_1(out, num, #name)
#include "iso8583_defs"

#define EXPAND_DEF(num, name) generate_source_2(out, num, #name)
#include "iso8583_defs"

}

where the "iso8583_defs" file, did contain a lot of lines like this:

EXPAND_DEF(1, FIELD_NAME);
EXPAND_DEF(2, ANOTHER_FIELD_NAME);


My question is simply, is the above C code allowed?
 
B

Ben Pfaff

Tor Rustad said:
void gen_source_function(FILE *out)
{
#define EXPAND_DEF(num, name) generate_source_1(out, num, #name)
#include "iso8583_defs"

#define EXPAND_DEF(num, name) generate_source_2(out, num, #name)
#include "iso8583_defs"

}

where the "iso8583_defs" file, did contain a lot of lines like this:

EXPAND_DEF(1, FIELD_NAME);
EXPAND_DEF(2, ANOTHER_FIELD_NAME);


My question is simply, is the above C code allowed?

Sure. Lots of code uses this trick to avoid redundancy.

If you don't like the idea of using an include file for this, you
can also use a macro:

void gen_source_function(FILE *out)
{
#define ISO8583_DEFS \
EXPAND_DEF(1, FIELD_NAME); \
EXPAND_DEF(2, ANOTHER_FIELD_NAME);

#define EXPAND_DEF(num, name) generate_source_1(out, num, #name)
ISO8583_DEFS

#define EXPAND_DEF(num, name) generate_source_2(out, num, #name)
ISO8583_DEFS
}

I think it's a toss-up which is better. Neither is pretty, both
are functional.
 
D

Dave Hansen

I'm implementing a parser, where there is a lot of different data
elements defined. Now instead of hand-coding X defines, and the massive
number of lines needed for table initialization, I decided to generate
this source instead.

So, I basically used this trick:

void gen_source_function(FILE *out)
{
#define EXPAND_DEF(num, name) generate_source_1(out, num, #name)
#include "iso8583_defs"

#define EXPAND_DEF(num, name) generate_source_2(out, num, #name)
#include "iso8583_defs"

}

where the "iso8583_defs" file, did contain a lot of lines like this:

EXPAND_DEF(1, FIELD_NAME);
EXPAND_DEF(2, ANOTHER_FIELD_NAME);

My question is simply, is the above C code allowed?

It's a pretty common idiom in the code I work with. One caveat: don't
forget to #undef EXPAND_DEF before redefining it.

Regards,

-=Dave
 
T

Tor Rustad

Ben said:
Sure. Lots of code uses this trick to avoid redundancy.

Yup, that's the idea.
If you don't like the idea of using an include file for this, you
can also use a macro:

void gen_source_function(FILE *out)
{
#define ISO8583_DEFS \
EXPAND_DEF(1, FIELD_NAME); \
EXPAND_DEF(2, ANOTHER_FIELD_NAME);

#define EXPAND_DEF(num, name) generate_source_1(out, num, #name)
ISO8583_DEFS

#define EXPAND_DEF(num, name) generate_source_2(out, num, #name)
ISO8583_DEFS
}

Interesting, I haven't seen this solution before.

However, there is a translation limit of "509 characters in a logical
source line", and isn't logical source lines made up after line-splicing
your ISO8583_DEFS above?
 
T

Tor Rustad

Dave Hansen wrote:

It's a pretty common idiom in the code I work with. One caveat: don't
forget to #undef EXPAND_DEF before redefining it.

Is really the #undef needed in this case? This is a function-like macro,
and the re-definition had identical argument list.
 
K

Keith Thompson

Tor Rustad said:
Dave Hansen wrote:



Is really the #undef needed in this case? This is a function-like
macro, and the re-definition had identical argument list.

C99 6.10.3p2:

An identifier currently defined as an object-like macro shall not
be redefined by another #define preprocessing directive unless the
second definition is an object-like macro definition and the two
replacement lists are identical. Likewise, an identifier currently
defined as a function-like macro shall not be redefined by another
#define preprocessing directive unless the second definition is a
function-like macro definition that has the same number and
spelling of parameters, and the two replacement lists are
identical.

This is a constraint.
 
B

Ben Pfaff

Tor Rustad said:
However, there is a translation limit of "509 characters in a logical
source line", and isn't logical source lines made up after
line-splicing your ISO8583_DEFS above?

Interesting point. I've not run into an implementation (yet)
that has such a stringent limit. I notice that C99 expanded the
limit to 4095 characters.
 
R

Richard Tobin

Tor Rustad said:
Keith, I don't get your point. Doesn't the

"unless the second definition is a function-like macro definition that
has the same number and spelling of parameters, and the two replacement
lists are identical"

apply?

The replacement lists were different. One had generate_source_1,
the other generate_source_2. There wouldn't have been much point
if they were the same.

-- Richard
 
T

Tor Rustad

Keith said:
C99 6.10.3p2:

An identifier currently defined as an object-like macro shall not
be redefined by another #define preprocessing directive unless the
second definition is an object-like macro definition and the two
replacement lists are identical. Likewise, an identifier currently
defined as a function-like macro shall not be redefined by another
#define preprocessing directive unless the second definition is a
function-like macro definition that has the same number and
spelling of parameters, and the two replacement lists are
identical.

This is a constraint.

Keith, I don't get your point. Doesn't the

"unless the second definition is a function-like macro definition that
has the same number and spelling of parameters, and the two replacement
lists are identical"

apply?
 
T

Tor Rustad

Ben said:
Interesting point. I've not run into an implementation (yet)
that has such a stringent limit. I notice that C99 expanded the
limit to 4095 characters.

In my case, there will be _at least_ 128 lines to splice, each line on
average ca. 50 characters long, in total 6400 characters minimum.
 
B

Ben Pfaff

Tor Rustad said:
Doesn't the

"unless the second definition is a function-like macro definition that
has the same number and spelling of parameters, and the two
replacement lists are identical"

apply?

Your replacement lists are different: the first has
generate_source_1, the second has generate_source_2.
 
B

Ben Pfaff

Tor Rustad said:
In my case, there will be _at least_ 128 lines to splice, each line on
average ca. 50 characters long, in total 6400 characters minimum.

Then you'd definitely be better off an include file.
 
I

Ian Collins

Tor said:
I'm implementing a parser, where there is a lot of different data
elements defined. Now instead of hand-coding X defines, and the massive
number of lines needed for table initialization, I decided to generate
this source instead.


So, I basically used this trick:

void gen_source_function(FILE *out)
{
#define EXPAND_DEF(num, name) generate_source_1(out, num, #name)
#include "iso8583_defs"

#define EXPAND_DEF(num, name) generate_source_2(out, num, #name)
#include "iso8583_defs"

}

where the "iso8583_defs" file, did contain a lot of lines like this:

EXPAND_DEF(1, FIELD_NAME);
EXPAND_DEF(2, ANOTHER_FIELD_NAME);

My question is simply, is the above C code allowed?
With the require #unded as explained else thread, it is quite common.

One project I worked on used it extensively, but with hind site, we
found the code hard to maintain, so we dropped the technique and used
code generation from XML instead.
 
T

Tor Rustad

Ian said:
Tor said:
I'm implementing a parser, where there is a lot of different data
elements defined. Now instead of hand-coding X defines, and the massive
number of lines needed for table initialization, I decided to generate
this source instead.
[...]

One project I worked on used it extensively, but with hind site, we
found the code hard to maintain, so we dropped the technique and used
code generation from XML instead.

For now, I only need to parse messages of the ISO 8583 1993 standard,
one future complication could be to handle the 1987 and 2001 standards
as well. Even so, I can't see how the parser code could become hard to
maintain. The source generator program is tiny, it's input table is "small".

In a more complex case, XML could perhaps give something, but in this
case I would rather write an ASN.1 specification and have the C code
generated by an ASN.1 compiler.


Generally, I'm *sick* of all that XML wasting HD space and network
bandwidth these days... *yuck*
 
I

Ian Collins

Tor said:
Ian said:
Tor said:
I'm implementing a parser, where there is a lot of different data
elements defined. Now instead of hand-coding X defines, and the massive
number of lines needed for table initialization, I decided to generate
this source instead.
[...]

One project I worked on used it extensively, but with hind site, we
found the code hard to maintain, so we dropped the technique and used
code generation from XML instead.

For now, I only need to parse messages of the ISO 8583 1993 standard,
one future complication could be to handle the 1987 and 2001 standards
as well. Even so, I can't see how the parser code could become hard to
maintain. The source generator program is tiny, it's input table is
"small".
It was as much a case of people's dislike of the "macro magic" as much
as anything else. We also ended up with several product using various
part of the database, so a more modular approach was required.
In a more complex case, XML could perhaps give something, but in this
case I would rather write an ASN.1 specification and have the C code
generated by an ASN.1 compiler.
The team also disliked ASN.1! There are more tools to parse, merge and
fiddle about with XML documents.
Generally, I'm *sick* of all that XML wasting HD space and network
bandwidth these days... *yuck*
Me too, I'm down to my last Terabyte!
 
T

Thad Smith

Ben said:
Sure. Lots of code uses this trick to avoid redundancy.

If you don't like the idea of using an include file for this, you
can also use a macro:

void gen_source_function(FILE *out)
{
#define ISO8583_DEFS \
EXPAND_DEF(1, FIELD_NAME); \
EXPAND_DEF(2, ANOTHER_FIELD_NAME);

#define EXPAND_DEF(num, name) generate_source_1(out, num, #name)
ISO8583_DEFS

#define EXPAND_DEF(num, name) generate_source_2(out, num, #name)
ISO8583_DEFS
}

I think it's a toss-up which is better. Neither is pretty, both
are functional.

Here's another way to do it. Include the macro file, as first proposed,
but make it the same as the including file! Here's an example:

//* incex.c - Show example of self inclusion */
#ifdef M1
M1(Sharon)
M1(Robert)
M1(Nancy)
#else

#include <stdio.h>

#define M1(n) n,
enum names {
#include "incex.c" /* include self for M1 list */
N_NAMES
};
#undef M1

#define M1(n) #n,
const char *const namestring[] = {
#include "incex.c" /* include self for M1 list */
""
};
#undef M1

int main (void) {
enum names i;

for (i=Robert; i <= Nancy; i++) {
printf ("%d: %s\n", i, namestring);
}
return 0;
}
#endif
 
G

Gene

With the require #unded as explained else thread, it is quite common.

One project I worked on used it extensively, but with hind site, we
found the code hard to maintain, so we dropped the technique and used
code generation from XML instead.

Yes. I've had a similar experience. "Preprocessor abuse" has often
led me to a place where something that needed to be done was beyond
the cpp to handle. Using python or perl or similar to generate code
is better in the long run. Most recently, I've used TXL (txl.ca) as a
very robust and maintainable way to (among other things) implement a
language extension inside an established language.
 
S

Stephen Sprunk

Tor Rustad said:
Keith, I don't get your point. Doesn't the

"unless the second definition is a function-like macro definition
that has the same number and spelling of parameters, and the
two replacement lists are identical"

apply?

Benign redefinition only exists when you do the same thing twice. For
instance, this is okay:

#define FOO 1
#define FOO 1

This is not:

#define FOO 1
#define FOO 2

In the latter case, you need to #undef FOO before you redefine it.

S
 
T

Tor Rustad

Thad Smith wrote:

Here's another way to do it. Include the macro file, as first proposed,
but make it the same as the including file! Here's an example:

//* incex.c - Show example of self inclusion */
#ifdef M1
M1(Sharon)
M1(Robert)
M1(Nancy)
#else

#include <stdio.h>

#define M1(n) n,
enum names {
#include "incex.c" /* include self for M1 list */
N_NAMES
};
#undef M1

#define M1(n) #n,
const char *const namestring[] = {
#include "incex.c" /* include self for M1 list */
""
};
#undef M1

int main (void) {
enum names i;

for (i=Robert; i <= Nancy; i++) {
printf ("%d: %s\n", i, namestring);
}
return 0;
}
#endif


Ahh.. that was a clever solution! One pass, no need for a
generator program.
 

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,780
Messages
2,569,611
Members
45,277
Latest member
VytoKetoReview

Latest Threads

Top