incr MACRO

W

William Pursell

I would like to initialize an array of structs,
one of whose members is an integer. Some of the
elements will get an explicit initializer for the
int, some will take the 0. The value is a monotonically
increasing counter, starting at 1. For example:

struct foo t[] = {
{ "apple", 1 },
{ "banana" },
{ "cherry", 2 },
{ "drape", 3 },
{ NULL }
};


I don't want to hard code the integer values, and
would rather do something like:

struct foo t[] = {
{ "apple", VALUE },
INCR( VALUE )
{ "banana" },
{ "cherry", VALUE },
INCR( VALUE )
{ "drape", VALUE },
{ NULL }
};


I can't see how to make the preprocessor do this
for me. Any ideas on how to accomplish this would
be greatly appreciated.

(For the curious, the structure I'm initializing
is a struct argp_option, and I'm trying to initialize
the group element to get things like --do-this
--no-do-this to be grouped together . Each time I add
an option to the option list, Everything below it
needs to have the value changed. This is easy enough,
but seems unecessary.)
 
E

Eric Sosman

William said:
I would like to initialize an array of structs,
one of whose members is an integer. Some of the
elements will get an explicit initializer for the
int, some will take the 0. The value is a monotonically
increasing counter, starting at 1. For example:

struct foo t[] = {
{ "apple", 1 },
{ "banana" },
{ "cherry", 2 },
{ "drape", 3 },
{ NULL }
};


I don't want to hard code the integer values, and
would rather do something like:

struct foo t[] = {
{ "apple", VALUE },
INCR( VALUE )
{ "banana" },
{ "cherry", VALUE },
INCR( VALUE )
{ "drape", VALUE },
{ NULL }
};


I can't see how to make the preprocessor do this
for me. Any ideas on how to accomplish this would
be greatly appreciated.

Must the numbers be consecutive from 1, or is it
enough that they just increase? Horrible hack:

struct foo t[] = {
{ "apple", __LINE__ },
{ "banana" },
{ "cherry", __LINE__ ),
{ "damson", __LINE__ },
{ NULL }
};
(For the curious, the structure I'm initializing
is a struct argp_option, and I'm trying to initialize
the group element to get things like --do-this
--no-do-this to be grouped together . Each time I add
an option to the option list, Everything below it
needs to have the value changed. This is easy enough,
but seems unecessary.)

Maybe you could use an enum to provide the auto-
incrementing constants?

enum { APPLE=1, CHERRY, DAMSON };
struct foo t[] = {
{ "apple", APPLE },
{ "banana" },
{ "cherry", CHERRY ),
{ "damson", DAMSON },
{ NULL }
};

Insertions would then be fairly simple:

enum { APPLE=1, AVOCADO, CHERRY, DAMSON };
struct foo t[] = {
{ "apple", APPLE },
{ "avocado", AVOCADO },
{ "banana" },
{ "cherry", CHERRY ),
{ "damson", DAMSON },
{ NULL }
};

If you need something still more elaborate, it may
be time to invent a "helper program" that spits out the
C source, perhaps under control of a "little language."
 
M

Micah Cowan

William said:
I would like to initialize an array of structs,
one of whose members is an integer. Some of the
elements will get an explicit initializer for the
int, some will take the 0. The value is a monotonically
increasing counter, starting at 1.

I can't see how to make the preprocessor do this
for me. Any ideas on how to accomplish this would
be greatly appreciated.

You're right: you can't.

One solution might be to use dummy values (say, all 0):

struct foo t[] = {
{ "apple" },
{ "banana" },
{ "cherry" },
{ "drape" },
{ NULL }
};

And then, in some initialization code, initialize the keys iteratively:

struct foo *tp;
int i;

for (tp=&t[0], i=0; tp->name != NULL; ++tp, ++i) {
tp->key = i;
}

Assuming the first two members of the struct are named name and key,
respectively (following the glibc definition for struct argp_option).

Another way might be to write a small program to generate the structure,
so that its output can then be compiled, avoiding a small time
expenditure at startup.
(For the curious, the structure I'm initializing
is a struct argp_option

(A GNU-ism, for those not familiar with it.) I haven't used them myself,
as yet.
 
K

Kaz Kylheku

I would like to initialize an array of structs,
one of whose members is an integer.  Some of the
elements will get an explicit initializer for the
int, some will take the 0.  The value is a monotonically
increasing counter, starting at 1.  For example:

struct foo t[] = {
        { "apple", 1 },
        { "banana" },
        { "cherry", 2 },
        { "drape", 3 },
        { NULL }

};

I don't want to hard code the integer values, and
would rather do something like:

struct foo t[] = {
        { "apple", VALUE },
        INCR( VALUE )
        { "banana" },
        { "cherry", VALUE },
        INCR( VALUE )
        { "drape", VALUE },
        { NULL }

};


/* file data.h */
DATA(apple)
DATA(banana)
DATA(cherry)
DATA(grape)



/* file enum.h */
#define DATA(X) X,

/* generate enumeration from data */
enum {
#include "data.h"
}

#undef DATA


/* file struct.h */
#include "enum.h"

#define DATA(X) { #X, X },

/* generate table associating strings with enumeration values */
struct foo t[] = {
#include "data.h"
}

#undef DATA

Good thing that an extra comma is allowed after an enumertor list and
initializer list! :)
 
P

Paul Hsieh

I would like to initialize an array of structs,
one of whose members is an integer. Some of the
elements will get an explicit initializer for the
int, some will take the 0. The value is a monotonically
increasing counter, starting at 1. For example:
struct foo t[] = {
{ "apple", 1 },
{ "banana" },
{ "cherry", 2 },
{ "drape", 3 },
{ NULL }

I don't want to hard code the integer values, and
would rather do something like:
struct foo t[] = {
{ "apple", VALUE },
INCR( VALUE )
{ "banana" },
{ "cherry", VALUE },
INCR( VALUE )
{ "drape", VALUE },
{ NULL }

/* file data.h */
DATA(apple)
DATA(banana)
DATA(cherry)
DATA(grape)

/* file enum.h */
#define DATA(X) X,

/* generate enumeration from data */
enum {
#include "data.h"
}

#undef DATA

/* file struct.h */
#include "enum.h"

#define DATA(X) { #X, X },

/* generate table associating strings with enumeration values */
struct foo t[] = {
#include "data.h"
}

#undef DATA

Good thing that an extra comma is allowed after an enumerator list and
initializer list! :)

Excellent idea, but this has the problem of invading name space.
I.e., you need to defined the symbols apple, banana, cherry and so
on. You also seem to have missed the fact that the OP wants to skip
an increment between banana and cherry. To get around this:

/* file data.h */
#ifndef REPT
#define REPT(x,y) DATA(x)
#endif
#define glue_aux(x,y) x ## y
#define glue(x,y) glue_aux(x,y)
DATA(apple)
DATA(banana)
REPT(cherry, banana)
DATA(grape)
#undef glue
#undef glue_aux

Then:

/* file enum.h */
#define DATA(X) glue(COUNTER_,X),
#define REPT(X,Y) glue(COUNTER_,X) = glue(COUNTER_,Y),
enum counter {
#include "data.h"
};
#undef DATA

/* file struct.h */
#include "enum.h"
#define DATA(X) { #X, glue(COUNTER_,X) },
struct foo {
char * string;
enum counter idx;
} t[] = {
#include "data.h"
};
#undef DATA

Keeping your name space clean while giving you the strings and indexes
as expected.
 
K

Kaz Kylheku

I would like to initialize an array of structs,
one of whose members is an integer.  Some of the
elements will get an explicit initializer for the
int, some will take the 0.  The value is a monotonically
increasing counter, starting at 1.  For example:
struct foo t[] = {
        { "apple", 1 },
        { "banana" },
        { "cherry", 2 },
        { "drape", 3 },
        { NULL }

I don't want to hard code the integer values, and
would rather do something like:
struct foo t[] = {
        { "apple", VALUE },
        INCR( VALUE )
        { "banana" },
        { "cherry", VALUE },
        INCR( VALUE )
        { "drape", VALUE },
        { NULL }

  /* file data.h */
  DATA(apple)
  DATA(banana)
  DATA(cherry)
  DATA(grape)

Oops, I missed the requirement about some entries being zero. If the
incrementing counter is allowed to skip, then you can of course do
this:

DATA(apple,1)
DATA(banana,0)
DATA(cherry,1)
DATA(grape,1)
  /* file enum.h */
  #define DATA(X) X,

#define DATA(X, Y) X,
  /* generate enumeration from data */
  enum {
  #include "data.h"
  }

  #undef DATA

  /* file struct.h */
  #include "enum.h"

  #define DATA(X) { #X, X },

#define DATA(X, Y) { #X, (Y) * (X)},

Now, how about that requirement to skip values, so that banana is
zero, and then cherry continues where apple left off.

/* data.h */
DATA(apple,)
DATA(banana,= 0)
DATA(cherry,= apple + 1)
DATA(grape,)

It's not exactly automatic, since you have to program the skips with
these backreferences among the data.

In the enum.h, we define the DATA macro like this:

#define DATA(X, Y) X Y,

In struct.h, we define DATA just like in the original version:

#define DATA(X) { #X, X },
 
K

Kaz Kylheku

Excellent idea, but this has the problem of invading name space.
I.e., you need to defined the symbols apple, banana, cherry and so
on.  You also seem to have missed the fact that the OP wants to skip
an increment between banana and cherry.  To get around this:

   /* file data.h */
   #ifndef REPT
   #define REPT(x,y) DATA(x)
   #endif
   #define glue_aux(x,y) x ## y
   #define glue(x,y) glue_aux(x,y)
   DATA(apple)
   DATA(banana)
   REPT(cherry, banana)
   DATA(grape)

Yes, but unfortunately the value for banana also has to vanish to
zero. So you might want an additional data form ZERO:

ZERO(banana)
REPT(cherry, apple + 1)

The default reduction of ZERO is to just pull out the name, like with
REPT:

#ifndef ZERO
#define ZERO(x, y) DATA(x)
#endif

   #undef glue
   #undef glue_aux

Then:

   /* file enum.h */
   #define DATA(X) glue(COUNTER_,X),
   #define REPT(X,Y) glue(COUNTER_,X) = glue(COUNTER_,Y),

And then for enums, ZERO of course does this:

#define ZERO(X, Y) glue(COUNTER_,X) = 0,

Very good. I like this idea of variants that can be mapped to a
uniform representation with a default implementation, or to separate
forms where needed.
 
W

William Pursell

Must the numbers be consecutive from 1, or is it
enough that they just increase? Horrible hack:
<snip example using __LINE__>

It is enough that they increase. Using __LINE__
is absolutely brilliant, and not a "Horrible hack"
at all.
Maybe you could use an enum to provide the auto-
incrementing constants?

Also acceptable.
If you need something still more elaborate, it may
be time to invent a "helper program" that spits out the
C source, perhaps under control of a "little language."

This was considered, but rejected on the grounds that
I only insert an option rarely, and am only up to
7 values, so hand editing has been relatively painless
and it wasn't worth the effort. Thanks for the __LINE__
suggestion...I'll run with it.

Also thanks to Kaz and Paul, who also provided some
really good ideas.
 
W

William Pursell

(A GNU-ism, for those not familiar with it.) I haven't used them myself,
as yet.

I highly recommend it. I just implemented my first child parser,
and am using it to give me some standard option parsing
including and a -h option that pipes the help message through
$PAGER, all with one line of code! (I can't believe I never
thought of doing that before.) It's really, really nice,
although (at first appearance) a bit kludgy to use.
 
E

Eric Sosman

William said:
<snip example using __LINE__>

It is enough that they increase. Using __LINE__
is absolutely brilliant, and not a "Horrible hack"
at all.

I'm not sure I merit the brilliancy accolade, but
I *am* sure about calling my suggestion a "horrible hack."
For reference, here it is again:

struct foo t[] = {
{ "apple", __LINE__ },
{ "banana" },
{ "cherry", __LINE__ ),
{ "damson", __LINE__ },
{ NULL }
};

I call it horrible because it's fragile. The typical
C textbook will describe "white space" in source, and will
tell you that all forms of white space are equivalent: spaces,
tabs, comments, newlines, they're all the same. So, let's
take advantage of this equivalence to format the source
more compactly:

struct foo t[] = { { "apple", __LINE__ }, { "banana" },
{ "cherry", __LINE__ ), { "damson", __LINE__ },
{ NULL } };

Oops!
 
K

Keith Thompson

Eric Sosman said:
William said:
<snip example using __LINE__>

It is enough that they increase. Using __LINE__
is absolutely brilliant, and not a "Horrible hack"
at all.

I'm not sure I merit the brilliancy accolade, but
I *am* sure about calling my suggestion a "horrible hack."
For reference, here it is again:

struct foo t[] = {
{ "apple", __LINE__ },
{ "banana" },
{ "cherry", __LINE__ ),
{ "damson", __LINE__ },
{ NULL }
};

I call it horrible because it's fragile. The typical
C textbook will describe "white space" in source, and will
tell you that all forms of white space are equivalent: spaces,
tabs, comments, newlines, they're all the same. So, let's
take advantage of this equivalence to format the source
more compactly:

struct foo t[] = { { "apple", __LINE__ }, { "banana" },
{ "cherry", __LINE__ ), { "damson", __LINE__ },
{ NULL } };

Oops!

I thought it fell only slightly short of brilliance; I find it clever,
though perhaps a little excessively so. (A comment reminding
maintainers not to put two occurrences on the same line would probably
suffice to avoid the problem you describe. Or a check routine, not
called in production mode, could confirm that all the values are
unique.

But when I saw it, I thought of an another potential problem. As the
code is maintained, and lines are added or removed before the
declaration, the values are going to change. If the code is well
written to depend only on the uniqueness of each value, that's not
going to be a problem. But if there's a subtle dependency on the
specific values, it could cause trouble. For example, imagine that
there's an implicit assumption that each value is no more than a
2-digit number. The code could break years later, when an inserted
declaration pushes the last usage of __LINE__ to line 100. That's
probably an unlikely scenario, but it could be fragile in other ways
I haven't thought of.

Then again, ordinary enumerated types have the same problem, so
perhaps I'm being too paranoid (though that's often better than not
being paranoid enough).
 
C

Chris Thomasson

I would like to initialize an array of structs,
one of whose members is an integer. Some of the
elements will get an explicit initializer for the
int, some will take the 0. The value is a monotonically
increasing counter, starting at 1. For example:
[...]

This is not a solution to your problem. I would go with Eric Sosmans
"neat hack" and take Keith Thompson's comment to heart:

http://groups.google.com/group/comp.lang.c/msg/cadb1cc2eef560ee


Anyway, FWIW, here is a simple way to do math solely in the pre-processor:
________________________________________________________________________
#include <stdio.h>


#define PLACE_X(t)t
#define PLACE(t)PLACE_X(t)


#define CONCAT_X(t0, t1)t0##t1
#define CONCAT(t0, t1)CONCAT_X(t0, t1)


#define SELECT_0(t0, t1)t0
#define SELECT_1(t0, t1)t1
#define SELECT(t, ts)CONCAT(SELECT_, t)ts


#define MATHTBL_1()(2, 0)
#define MATHTBL_2()(3, 1)
#define MATHTBL_3()(4, 2)
#define MATHINC(t)SELECT(0, CONCAT(MATHTBL_, t)())
#define MATHDEC(t)SELECT(1, CONCAT(MATHTBL_, t)())


int main(void) {
printf("1 + 1 = %d\n", MATHINC(1));
printf("2 + 1 = %d\n", MATHINC(2));
printf("3 + 1 = %d\n", MATHINC(3));
printf("1 - 1 = %d\n", MATHDEC(1));
printf("2 - 1 = %d\n", MATHDEC(2));
printf("3 - 1 = %d\n", MATHDEC(3));


/*-----------------------------------------------------------*/
puts("\n\n\n______________________________________________\n\
press <ENTER> to exit...");
getchar();
return 0;
}

________________________________________________________________________



The 'MATHTBL_X' and 'SELECT_X' macros can also be extended to
multiplication and division.
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top