Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

incr MACRO

3 views
Skip to first unread message

William Pursell

unread,
Feb 26, 2008, 3:39:11 PM2/26/08
to
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.)

--
William Pursell

Eric Sosman

unread,
Feb 26, 2008, 4:23:45 PM2/26/08
to

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."

--
Eric....@sun.com

Micah Cowan

unread,
Feb 26, 2008, 4:42:43 PM2/26/08
to
William Pursell wrote:
> 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.

<snip>

> 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.

--
Micah J. Cowan
Programmer, musician, typesetting enthusiast, gamer...
http://micah.cowan.name/

Kaz Kylheku

unread,
Feb 26, 2008, 5:29:03 PM2/26/08
to
On Feb 26, 12:39 pm, William Pursell <bill.purs...@gmail.com> wrote:
> 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! :)

Paul Hsieh

unread,
Feb 26, 2008, 5:57:18 PM2/26/08
to
> 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.

--
Paul Hsieh
http://www.pobox.com/~qed/
http://bstring.sf.net/

Kaz Kylheku

unread,
Feb 26, 2008, 5:59:20 PM2/26/08
to
On Feb 26, 2:29 pm, Kaz Kylheku <kkylh...@gmail.com> wrote:

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:

Kaz Kylheku

unread,
Feb 26, 2008, 6:10:28 PM2/26/08
to
On Feb 26, 2:57 pm, Paul Hsieh <websn...@gmail.com> wrote:
> 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.

William Pursell

unread,
Feb 27, 2008, 12:25:07 AM2/27/08
to
On Feb 26, 9:23 pm, Eric Sosman <Eric.Sos...@sun.com> wrote:
> William Pursell wrote:
> > 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:

>


> 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.

--
William Pursell

William Pursell

unread,
Feb 27, 2008, 12:31:55 AM2/27/08
to
On Feb 26, 9:42 pm, Micah Cowan <mi...@cowan.name> wrote:

> William Pursell wrote:
> > (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.

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.

Eric Sosman

unread,
Feb 27, 2008, 8:12:09 AM2/27/08
to
William Pursell wrote:
> On Feb 26, 9:23 pm, Eric Sosman <Eric.Sos...@sun.com> wrote:
>> William Pursell wrote:
>>> 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:
>
>> 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.

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!

--
Eric Sosman
eso...@ieee-dot-org.invalid

Keith Thompson

unread,
Feb 27, 2008, 12:07:06 PM2/27/08
to

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).

--
Keith Thompson (The_Other_Keith) <ks...@mib.org>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Chris Thomasson

unread,
Feb 27, 2008, 6:11:38 PM2/27/08
to
On Tue, 26 Feb 2008 12:39:11 -0800, William Pursell wrote:

> 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.

0 new messages