Unshielded comma in macro expansion

C

C. Comren

Hi,

why isn't

#define a 1,2
#define b(u,v) u+v

printf("%d\n", b(a));

working?

Thanks,
C. Comren
 
E

Eric Sosman

Hi,

why isn't

#define a 1,2
#define b(u,v) u+v

printf("%d\n", b(a));

working?

Aside: When asking a "Why doesn't it work?" question, it's
always a good idea to explain what "to work" means. "It doesn't
work" amounts to "It doesn't do as I expected," and we're left to
guess about what you expected.

In the case at hand, my guess is that you expected b(a) to
behave like b(1,2). And the reason it doesn't behave that way is
that macros in b's argument list aren't expanded when b is invoked,
but only when (and if) they appear in b's expansion. That is,
the sequence of operation is something like

- Recognize b as a macro invocation
- Gather the arguments (here's where your code goes awry,
because there's only one argument to match two parameters)
- Expand b
- Check whether the expansion contains further macros that
also need expanding

To illustrate this, let's try a different bit of code:

#define b(u,v) u+v
#define x 42
#define y 11
printf ("%d\n", b(x,y));

In the final line, b is recognized as a macro name, and the two
arguments x,y are made to correspond to the parameters u,v. Then
b expands, producing (this is the crux) x+y and *not* 42+11. It
is only *after* x+y is produced that x and y get recognized as
macros, and are themselves expanded to 42 and 11.
 
C

C. Comren

Thank you very much for your answers.
     Aside: When asking a "Why doesn't it work?" question, it's
always a good idea to explain what "to work" means.  "It doesn't
work" amounts to "It doesn't do as I expected," and we're left to
guess about what you expected.

I will try.
     In the case at hand, my guess is that you expected b(a) to
behave like b(1,2).  And the reason it doesn't behave that way is
that macros in b's argument list aren't expanded when b is invoked,
but only when (and if) they appear in b's expansion.

Can I somehow change the expression b(a) to mean b(1,2)?

I want to expand a struct into two parameters with a macro. E.g.,

#define VEC_TO_ARG(v) v->data,v->len

#define at(d,n,i) d;
#define equal(d1,n1,d2,n2) (n1 == n2 && memcmp(d1,d2,n1) == 0)
void process(int *d, size_t n);

at(VEC_TO_ARG(v),10) = 1;
if (!equal(VEC_TO_ARG(v), VEC_TO_ARG(w)))
process(VEC_TO_ARG(v));

The function "process" is called, but I do not see how I must modify
the definition of "at" and "equal" such that

"at(VEC_TO_ARG(v),10)"

expands to

"at(v->data, v->len, 10)".

Thanks again for your help,
C. Comren
 
E

Eric Sosman

[Context: `b(u,v)' is a two-parameter function-like macro; `a'
is an object-like macro expanding to `1,2'.]
>
In the case at hand, my guess is that you expected b(a) to
behave like b(1,2). And the reason it doesn't behave that way is
that macros in b's argument list aren't expanded when b is invoked,
but only when (and if) they appear in b's expansion.

Can I somehow change the expression b(a) to mean b(1,2)?

Not with C's preprocessor. `b' is a macro of two parameters;
you need to provide two arguments when you invoke it.
I want to expand a struct into two parameters with a macro. E.g.,

#define VEC_TO_ARG(v) v->data,v->len

#define at(d,n,i) d;


You probably don't want that semicolon. And what's the
point of the unused `n' parameter? (For that matter, what's
the point of all this Rube Goldberg machinery, just to do a
simple array-index operation?)
#define equal(d1,n1,d2,n2) (n1 == n2&& memcmp(d1,d2,n1) == 0)
void process(int *d, size_t n);

at(VEC_TO_ARG(v),10) = 1;

Okay, you *definitely* don't want the semicolon. And you're
trying to invoke a three-parameter macro with two arguments -- but
since the second parameter never gets used, why beat your head
against the wall?
if (!equal(VEC_TO_ARG(v), VEC_TO_ARG(w)))

Four parameters, two arguments -- but I bet you knew that ...
process(VEC_TO_ARG(v));

This one's fine, because `process' is *not* a macro. The
argument list is processed long after the preprocessor has finished,
and by that time your two expressions and their comma are in place
and ready to go.
The function "process" is called, but I do not see how I must modify
the definition of "at" and "equal" such that

"at(VEC_TO_ARG(v),10)"

expands to

"at(v->data, v->len, 10)".

I can't think of a way to do it. On the other hand, I can't
understand why you want to do it in the first place. What are you
trying to accomplish with all this running around? Not knowing your
purpose, all I can say is this seems like pure obfuscation to me.
 
C

C. Comren

     I can't think of a way to do it.  On the other hand, I can't
understand why you want to do it in the first place.  What are you
trying to accomplish with all this running around?

The purpose is to simplify notation for a type-generic macro. E.g.

#define STRING_TO_ARG(s) [[some compiler dependent if-else to expand
depending on type]]
#define beginswith_(s1,n1,s2,n2) (n1 >= n2 && memcmp(s1,s2,n2) == 0)
#define beginswith(s,t) beginswith_(STRING_TO_ARG(s),STRING_TO_ARG(t))

Then I can write

beginswith(s,"pattern");
beginswith(s,t);

Since "pattern" is a compile-time constant, it is expanded to
"pattern",7. If I make beginswith_ into a function, the expressions
are expanded correctly. However, using a function defeats my intention
of having no overhead.

C. Comren
 
C

C. Comren

Since I am calling beginswith_ always with two arguments, I changed it
to

#define beginswith_(x,y) beginswith__(x,y)
#define beginswith__(s1,n1,s2,n2) (n1 >= n2 && memcmp(s1,s2,n2) == 0)

Does then the macro have to be expanded as follows? Or is it compiler-
dependant?

beginswith_(STRING_TO_ARG(s),STRING_TO_ARG(t))
--> beginswith__(s->data,s->len,t->data,t->len)

C. Comren
 
L

lawrence.jones

C. Comren said:
Since I am calling beginswith_ always with two arguments, I changed it
to

#define beginswith_(x,y) beginswith__(x,y)
#define beginswith__(s1,n1,s2,n2) (n1 >= n2 && memcmp(s1,s2,n2) == 0)

Does then the macro have to be expanded as follows? Or is it compiler-
dependant?

beginswith_(STRING_TO_ARG(s),STRING_TO_ARG(t))
--> beginswith__(s->data,s->len,t->data,t->len)

Yes, adding the extra layer of macro expansion expands the arguments the
way you want (just like adding an extra layer of macro expansion allows
you to stringize the value of a macro rather than its name).
 
E

Eric Sosman

I can't think of a way to do it. On the other hand, I can't
understand why you want to do it in the first place. What are you
trying to accomplish with all this running around?

The purpose is to simplify notation for a type-generic macro. E.g.

#define STRING_TO_ARG(s) [[some compiler dependent if-else to expand
depending on type]]

Looks pretty magical to me. C is a statically-typed language,
by which I mean that every expression's (and sub-expression's) type
is known at compile time and immutable thereafter. There is no run-
time type information, except possibly for extra-linguistic stuff
carried around for the benefit of debuggers and the like (and not
accessible to the C language).

Furthermore, the preprocessor operates before the notion of
"type" even comes into existence. When the preprocessor makes its
transformations on the source, it's dealing with tokens that have
no meanings attached yet. `int' and `printf' and `12.34' are just
lexically-identified globs of goo, not keywords and identifiers and
constants. Thus, the preprocessor can't make tests of types, since
there are no types to test.

The only time when types can be tested -- and I mean "tested"
in a rather weak way -- is in the intermediate period after the
preprocessor finishes and before the program runs. Somewhere in
that span, the compiler recognizes that `12.34' is a `double' and
that `x' is a `whatever x is', so (for example) it knows how and
whether to perform promotions, whether a `+' specifies floating-
point, integer, or pointer addition, and so on. But there's no way
to extend this repertoire of tests into something the code can use,
except maybe by forcing an error message from a type mismatch (e.g.,
calling `sin(stdout)' "tests" whether `stdout' is something that
will convert to a `double').

So I think you're out of luck, unless you really can work magic.
Is "C. Comren" a nom de plume for "Harry Potter?"
#define beginswith_(s1,n1,s2,n2) (n1>= n2&& memcmp(s1,s2,n2) == 0)
#define beginswith(s,t) beginswith_(STRING_TO_ARG(s),STRING_TO_ARG(t))

Then I can write

beginswith(s,"pattern");
beginswith(s,t);

Since "pattern" is a compile-time constant, it is expanded to
"pattern",7. If I make beginswith_ into a function, the expressions
are expanded correctly. However, using a function defeats my intention
of having no overhead.

"No overhead." (Sigh.) Eternal youth would be nice, too,
and perpetual motion, and the trash removal contract for El Dorado.
Perhaps you've done this, but I must ask: Have you *measured* the
"overhead" of a straightforward approach and found it wanting? Or
are you just guessing?
 
C

C. Comren

So I think you're out of luck, unless you really can work magic.

Many compilers implement the typeof operator. Combining that with
GCC's __builtin_types_compatible allows for branching depending on
the type of the argument. In fact, all compilers implementing C99
have to support similar machinery in order to support tgmath.h
Perhaps you've done this, but I must ask: Have you *measured* the
"overhead" of a straightforward approach and found it wanting?  Or
are you just guessing?

For the moment, this is for academic reasons. In a tight loop,
however,
like a time-critical parser, it might have measurable impact. But you
are of course right: the overhead might be negligible.

The main reason for implementing it type-generically is the ability to
change the data type without having to change each and every function
call. For example, we might use a naive algorithm at first and then
measure the impact of using a different one afterwards. I believe that
this were the reasons for the inclusion of tgmath.h into C99.

C. Comren
 
C

C. Comren

Yes, adding the extra layer of macro expansion expands the arguments the
way you want (just like adding an extra layer of macro expansion allows
you to stringize the value of a macro rather than its name).

Thanks.

C. Comren
 
E

Eric Sosman

Many compilers implement the typeof operator. Combining that with
GCC's __builtin_types_compatible allows for branching depending on
the type of the argument. In fact, all compilers implementing C99
have to support similar machinery in order to support tgmath.h

Okay. If you're writing "C-ish dialect specific to one
C-ish compiler" rather than "C," go ahead. It's not an unalloyed
benefit, though: In the pre-ANSI days, that was effectively the
description of *all* C, and the chaos was extraordinary. Trust
me: I was there and I don't want to go back.

By the way, the <tgmath.h> implementation does in fact need
magic, but it does not follow that the magic is accessible to
us muggles. C offers no way to write type-generic functions;
it just provides some as part of the Standard library. The setjmp()
macro is magical, too, and its magic is similarly inaccessible.
For the moment, this is for academic reasons. In a tight loop,
however,
like a time-critical parser, it might have measurable impact. But you
are of course right: the overhead might be negligible.

Eliminating the "overhead" of a function call is a linear
improvement in computation speed (if it's even that much). If
a parser is too slow, it's because of complexity issues that
are quadratic or exponential or even worse, not because of a
few nano-quivers added to each function invocation. That is,
eliminating those nano-quivers (if you can) won't help; if the
parser spends exponential time doing fruitless descents and
backtracks, it'll still do so.

Also, pretty much any time you're talking about a parser
there's some I/O lurking in the background. Even the fastest I/O
is several decimal orders of magnitude slower than the CPU, so a
microscopically faster parser just gets you back to the wait loop
a little sooner.
The main reason for implementing it type-generically is the ability to
change the data type without having to change each and every function
call. For example, we might use a naive algorithm at first and then
measure the impact of using a different one afterwards.

I cannot see how type-shuffling bears on this. Changing
`void naive(MyConcreteType arg)' to `void fancy(MyConcreteType arg)'
requires no magic.
I believe that
this were the reasons for the inclusion of tgmath.h into C99.

Perhaps; I certainly don't know. A good many things went into
C99 for reasons that remain obscure to me.
 
S

sandeep

Eric said:
I can't think of a way to do it. On the other hand, I can't
understand why you want to do it in the first place. What are you
trying to accomplish with all this running around?

The purpose is to simplify notation for a type-generic macro. E.g.

#define STRING_TO_ARG(s) [[some compiler dependent if-else to expand
depending on type]]

Looks pretty magical to me. C is a statically-typed language,
by which I mean that every expression's (and sub-expression's) type is
known at compile time and immutable thereafter. There is no run- time
type information, except possibly for extra-linguistic stuff carried
around for the benefit of debuggers and the like (and not accessible to
the C language).

Hello Eric ~~

You are wrong about that.

For runtime polymorphism all that's needed is a vtable. This can be
implemented in C using Structs and Function Pointers (=> Dispatch Table).
An example is GTK+.

Regards ~~
 
E

Eric Sosman

Eric said:
I can't think of a way to do it. On the other hand, I can't
understand why you want to do it in the first place. What are you
trying to accomplish with all this running around?

The purpose is to simplify notation for a type-generic macro. E.g.

#define STRING_TO_ARG(s) [[some compiler dependent if-else to expand
depending on type]]

Looks pretty magical to me. C is a statically-typed language,
by which I mean that every expression's (and sub-expression's) type is
known at compile time and immutable thereafter. There is no run- time
type information, except possibly for extra-linguistic stuff carried
around for the benefit of debuggers and the like (and not accessible to
the C language).

Hello Eric ~~

You are wrong about that.

For runtime polymorphism all that's needed is a vtable. This can be
implemented in C using Structs and Function Pointers (=> Dispatch Table).
An example is GTK+.

s/implemented/simulated/, and I'd agree with you. But in C,
the struct type you refer to is of one and only one type, fixed
at compilation time and not changeable thereafter. If the struct
is of the type `struct vtable' and you access it through any
expression whose type is not `struct vtable', C does not define
the outcome.[*]

In any event, the O.P.'s desire to accomplish his polymorphism
via the preprocessor remains unsatisfied; the preprocessor can't
make use of the values stored in a struct instance.

[*] Okay, there are two exceptions where C *does* define the
behavior, at least in part. First, you can always view any object
instance as an array of bytes -- but you can only "use" the byte-
nature of the array elements; you can't, for example, call a
function via an array of a function pointer's constituent bytes.
Second, there's a special dispensation for structs with a common
initial subsequence that reside in the same union; if any of those
structs is currently stored in the union, its initial elements can
be accessed via any other union member with the same start. Even
so, the thing stored in the union *is* a `struct vtable' and not a
`struct wtable' or a `void(*)(double)'.)
 
P

Peter Nilsson

C. Comren said:
Can I somehow change the expression b(a) to mean b(1,2)?

I want to expand a struct into two parameters with a macro.
E.g.,

#define VEC_TO_ARG(v) v->data,v->len

Better would be...

(v)->data, (v)->len
#define at(d,n,i) d;


Did you really mean that semicolon? Please copy/paste, not
type from memory. [Rather don't _invent_ problems when your
actual problem could be stated explicitly.]

Aside, macro's that don't use an argument can be misleading.
There's no obvious purpose for it in this case.
#define equal(d1,n1,d2,n2) (n1 == n2 && memcmp(d1,d2,n1) == 0)
void process(int *d, size_t n);

at(VEC_TO_ARG(v),10) = 1;
if (!equal(VEC_TO_ARG(v), VEC_TO_ARG(w)))
 process(VEC_TO_ARG(v));

Either do it explicitly...

#define atv(v,i) at((v)->data, (v)->len,i)
#define equalv(v1,v2) \
equal( (v1)->data, (v1)->len, \
(v2)->data, (v2)->len )

atv(v,10) = 1;
if (!equalv(v, w))
process(VEC_TO_ARG(v));

....or use an intermediate macro to force expansion of arguments
prior to expansion of outer macro...

#define apply_2(M, a, b) M(a, b)

#define atv(v, i) apply_2(at, VEC_TO_ARG(v), i)
#define equalv(v, w) \
apply_2(equal, VEC_TO_ARG(v), VEC_TO_ARG(w))
 

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
474,099
Messages
2,570,626
Members
47,237
Latest member
David123

Latest Threads

Top