basic help with counting?

A

Ark

Hello NG,
I want a macro to count the number of tokens in a comma-separated list.
I want it to yield a constant integer expression.

I do something like
#define VERBATIM(...) __VA_ARGS__ //not quite verbatim but OK
#define COUNT(...) \
VERBATIM(sizeof(struct {char VERBATIM(__VA_ARGS__);}))
#define COUNT1(...) \
offsetof(VERBATIM(struct {char VERBATIM(__VA_ARGS__), endmark;}), endmark)

[Of course, VERBATIM is used to package multi-token argument as one
macro parameter.]

The evaluation compiles fine:
const int x = COUNT(a, b, c);

However the conditional fails:
#if COUNT(a, b, c) != 3
....

Here are IAR EWARM 5.30 errors
Error[Pe193]: zero used for undefined preprocessing identifier count.c 13
Error[Pe059]: function call is not allowed in a constant expression
count.c 13
Error[Pe193]: zero used for undefined preprocessing identifier count.c 13
Error[Pe018]: expected a ")" count.c 13

And even more errors with
#if COUNT1(a, b, c) != 3
....

IOW, neither COUNT nor COUNT1 yield a constant integer expression wanted
by #if. Compiler diagnostic is less than helpful.
What am I doing wrong?

Another question:
Is there a trick to make COUNT() and/or COUNT1() yield 0?
 
K

Kaz Kylheku

However the conditional fails:
#if COUNT(a, b, c) != 3

The preprocessing stages in C (a.k.a. "the preprocessor") do not know
anything about types.

Stuff like:

#if sizeof (struct foo) != 3

is impossible. Preprocessor expressions are just integer math, e.g:

#if PROGRAM_VERSION > 3

If PROGRAM_VERSION is not defined, it is replaced with 0.
 
B

Ben Bacarisse

Ark said:
I want a macro to count the number of tokens in a comma-separated
list. I want it to yield a constant integer expression.

I do something like
#define VERBATIM(...) __VA_ARGS__ //not quite verbatim but OK
#define COUNT(...) \
VERBATIM(sizeof(struct {char VERBATIM(__VA_ARGS__);}))
#define COUNT1(...) \
offsetof(VERBATIM(struct {char VERBATIM(__VA_ARGS__), endmark;}), endmark)

[Of course, VERBATIM is used to package multi-token argument as one
macro parameter.]

The evaluation compiles fine:
const int x = COUNT(a, b, c);

However the conditional fails:
#if COUNT(a, b, c) != 3
...
And even more errors with
#if COUNT1(a, b, c) != 3
...

IOW, neither COUNT nor COUNT1 yield a constant integer expression
wanted by #if. Compiler diagnostic is less than helpful.
What am I doing wrong?

C is defined in terms of a number of translation phases (eight in all).
Most deal with lexical matters (continuation lines, comments, finding
tokens, and so on). Preprocessing happens in phase 4 -- long before the
main work of translating the code that happens in phase 7.

Because of this, there is a special definition for constant expressions
used in #if directives. The main difference is that all identifiers
that are left over after macro expansion are replaced with 0. This
applies even to identifiers that would otherwise be keywords. struct,
char and sizeof are all simply replaced by 0 when your macros are used
after #if.
Another question:
Is there a trick to make COUNT() and/or COUNT1() yield 0?

Sorry, but I can't answer this. There probably is a way -- there is an
honourable tradition of amazing macro trickery -- but, from a purely
practical point of view, it is often better to use some other
preprocessor. This can limit portability, but it's often the way the go
to get the job done.
 
S

Stefan Ram

Ark said:
#if COUNT(a, b, c) != 3

#define COUNT(...) SELECT_POSITION_4_OF(__VA_ARGS__,LIST43210())
#define SELECT_POSITION_4_OF(...) SELECT_POSITION_4(__VA_ARGS__)
#define SELECT_POSITION_4(dummy0,dummy1,dummy2,dummy3,x,...) x
#define LIST43210() 4,3,2,1,0

#if COUNT(A)!=1
#error
#endif

#if COUNT(A,B)!=2
#error
#endif

#if COUNT(A,B,C)!=3
#error
#endif
 
A

Ark

#define COUNT(...) SELECT_POSITION_4_OF(__VA_ARGS__,LIST43210())
#define SELECT_POSITION_4_OF(...) SELECT_POSITION_4(__VA_ARGS__)
#define SELECT_POSITION_4(dummy0,dummy1,dummy2,dummy3,x,...) x
#define LIST43210() 4,3,2,1,0

#if COUNT(A)!=1
#error
#endif

#if COUNT(A,B)!=2
#error
#endif

#if COUNT(A,B,C)!=3
#error
#endif
That's truly excellent! Thank you very much!
(Unfortunately, COUNT() is still a 1, not 0.)
 
A

Ark

Ark said:
I want a macro to count the number of tokens in a comma-separated
list. I want it to yield a constant integer expression.

I do something like
#define VERBATIM(...) __VA_ARGS__ //not quite verbatim but OK
#define COUNT(...) \
VERBATIM(sizeof(struct {char VERBATIM(__VA_ARGS__);}))
#define COUNT1(...) \
offsetof(VERBATIM(struct {char VERBATIM(__VA_ARGS__), endmark;}), endmark)

[Of course, VERBATIM is used to package multi-token argument as one
macro parameter.]

The evaluation compiles fine:
const int x = COUNT(a, b, c);

However the conditional fails:
#if COUNT(a, b, c) != 3
...
And even more errors with
#if COUNT1(a, b, c) != 3
...

IOW, neither COUNT nor COUNT1 yield a constant integer expression
wanted by #if. Compiler diagnostic is less than helpful.
What am I doing wrong?

C is defined in terms of a number of translation phases (eight in all).
Most deal with lexical matters (continuation lines, comments, finding
tokens, and so on). Preprocessing happens in phase 4 -- long before the
main work of translating the code that happens in phase 7.

Because of this, there is a special definition for constant expressions
used in #if directives. The main difference is that all identifiers
that are left over after macro expansion are replaced with 0. This
applies even to identifiers that would otherwise be keywords. struct,
char and sizeof are all simply replaced by 0 when your macros are used
after #if.

Sure. My bad; I should have known better.
Sorry, but I can't answer this. There probably is a way -- there is an
honourable tradition of amazing macro trickery -- but, from a purely
practical point of view, it is often better to use some other
preprocessor. This can limit portability, but it's often the way the go
to get the job done.

I know :( I even wrote my own preprocessor (Unimal) but the team I am on
is willing to use it only when no C native alternative is available.
 
A

Ark

The preprocessing stages in C (a.k.a. "the preprocessor") do not know
anything about types.

Stuff like:

#if sizeof (struct foo) != 3

is impossible. Preprocessor expressions are just integer math, e.g:

#if PROGRAM_VERSION> 3

If PROGRAM_VERSION is not defined, it is replaced with 0.
Thanks, got it.
 
J

Joel C. Salomon

I want a macro to count the number of tokens in a comma-separated list.
I want it to yield a constant integer expression.

Almost six years ago, Laurent Deniau posted PP_NARG to comp.std.c; see
<https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s>.

More recently, Jens Gustedt posted an enhancement that will correctly
count the zero-arguments case; see
<http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/> &
<http://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/>.

--Joel
 
P

Phil Carmody

Joel C. Salomon said:
Almost six years ago, Laurent Deniau posted PP_NARG to comp.std.c; see
<https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s>.

More recently, Jens Gustedt posted an enhancement that will correctly
count the zero-arguments case; see
<http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/> &

All very clever, but I'm not sure I see why 9 lines of NOISE is better
than just

static inline int one_or_two_lazy(int a) { return one_or_two(a, 5); }

Or, horror of horrors, simply provividing the default parameter. Missing
it out is purely syntactic sugar, nothing more.

Phil
 
A

Ark

Almost six years ago, Laurent Deniau posted PP_NARG to comp.std.c; see
<https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s>.

More recently, Jens Gustedt posted an enhancement that will correctly
count the zero-arguments case; see
<http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/> &
<http://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/>.

--Joel
Thanks, Joel, very interesting.
But seems too academic all the same.
From the cost/benefit analysis, it's hard to justify the maintenance
overhead (+bugs, whether in macros or in compilers) of having COUNT()
expand to 0.
If I wanted it VERY badly, I'd go with external preprocessor. For now,
I'll live with COUNT() expand to 1. It's a simplified version, also
proposed by Stefan.
-- Ark
 

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,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top