From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2003-04-30 22:46:53


Rene Rivera wrote:
>> The only problem tokens are , ( ) # and ## (as well as %: and %:%:).
>> Everything else is fine. The problems with those tokens arise only
>> from the fact they have special meaning to the syntactic/semantic
>> model of the preprocessor. As I said, we can work around this
>> problem, but the workaround is not local--i.e. it makes it necessary
>> to apply the workaround globally an any situation where you might
>> want to pass one of those tokens.
>
> I don't see what difference the problem tokens are. If the
> preprocessor
> knows to not parse a specific token sequence you tell it how can it
> be a problem?

Well, how about this:

#define STR(x) STR_(x)
#define STR_(x) #x

STR( + ) // okay: "+"
STR( ( ) // error!
STR( , ) // error!

It is impossible to stringize the comma or one of the parentheses by itself.
Best you can do is this:

STR( () ) // okay: "()"
STR( (,) ) // okay: "(,)"

...which is not what is wanted.

>> #define COMMA() ,
>
> Or define it as:
>
> #define COMMA #escape ","

Yes, or just use __comma__ as Vesa suggested. :)

>> And then pass it through other macros without invoking it:
>>
>> MACRO( COMMA )
>
> No need if defined as above.

Yes, same as __comma__ also. The point being that you only need __comma__,
__lparen__, and __rparen__. You don't need it for anything else.

> You could do:
>
> MACRO( #escape "+" )

Yes, but want we want is:

MACRO(+)
MACRO(__comma__)

For the common case where the comma or parentheses aren't needed for further
expansion. If token-pasting was well-defined for unrelated tokens, you could
force the invocation early in a general fashion if desired.

>> Because you'd get this:
>>
>> R(0) +() R(1) +() R(2)
>
> And would get the desired:
>
> R(0) + R(1) + R(2)
>
>> So, you have to either 1) make a PLUS() macro or 2) use some type of
>> invocable identity macro:
>>
>> #define COMMA() ,
>>
>> #define EMPTY()
>> #define IDENTITY(x) x EMPTY
>>
>> MACRO( COMMA ) // R(0) , R(1) , R(2)
>> MACRO( EMPTY ) // R(0) R(1) R(2)
>> MACRO( IDENTITY(+) ) // R(0) + R(1) + R(2)
>> MACRO( IDENTITY(;) ) // R(0) ; R(1) ; R(2)
>
> MACRO( #escape "," ) // R(0) , R(1) , R(2)
> MACRO( #escape " " ) // R(0) R(1) R(2)
> MACRO( #escape "+" ) // R(0) + R(1) + R(2)
> MACRO( #escape ";" ) // R(0) ; R(1) ; R(2)

MACRO(__comma__)
MACRO() // placemarkers from C99
MACRO(+)
MACRO(;)

>> Etc.
>>
>> In other words, the "fix" takes over everything that isn't the
>> problem to begin with. I.e. it is intrusive. There are other
>> options, of course, but all
> of
>> them have some type of intrusive drawback--for instance what if you
>> wanted "((" as a delineator...
>>
>> Late-bound expansion for __comma__, __lparen__, and __rparen__ would
>> *partially* solve the problem. However, it would not fix the
>> problem completely. For instance, if the above result was used as
>> arguments to an other macro:
>>
>> #define OTHER(x) OTHER_I x
> Did you mean #define OTHER(x) OTHER_I(x) ???

Yes, sorry.

>> #define OTHER_I(a, b, c) a b c
>>
>> OTHER( MACRO(__comma__) ) // error
>>
>> The same caveat applies to parentheses. They permanently become
>> "non-operators" to the preprocessor which can be a major limitation.
>
> Ahh (after a few minutes of contemplation ;-)... I see that problem.
> Basically there is no general point of macro replacement that will
> solve it in all cases. Currently it's "replace asap", and with the
> __comma__ & #escape it's "replace last".

(I should say, BTW, that given a "strict" preprocessor, I can cause a delay in a
macro's invocation through virtually any number of rescannings. Meaning that,
if I can count the number of rescans for a given argument (which I can also do),
I can delay the invocation of COMMA all the way through to the correct point and
have it expand automatically. However, that is dangerous and ridiculously
fragile.)

> OK, so here's another idea. Let the macro writer/user specify where
> the replacement point is. With lets say an #eval command. The problem
> example above could then be implemented as:
>
> #define OTHER(x) OTHER_I( #eval x )
>
> Or for the user...
>
> OTHER( #eval MACRO(#escape ",") )

The whole point, IMO, is to avoid the need for the intrusive syntax in as many
places as possible--both in the implementation and in the interface. __comma__,
__lparen__, and __rparen__ are the only useful ones--unless you mean to say that
#escape "..." can include arbitrary macros expansions etc. that are delayed
until the last possible moment?

Paul Mensonides