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
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."
<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/
/* 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! :)
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/
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:
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.
>
> 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
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.
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
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"
> 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.