Greetings old friends.  Long time no see.  I have been off fighting
the spam wars for about the past 10-15 years, but I hope I'll still
be welcome in these parts.

I just dropped in again because I've got a bit of a C programming
difficulty, and if nobody else has a solution, I'd like to propose
what I believe is a modest solution.

First, the problem...

I have quite a lot of code written (my own) wherein I define various
function-like macros to do certain useful things for me.  Some of these
macros invoke yet other macros.  Here's a brief sample (but I have a
lot code containing of this kind of stuff).  Please bear with me, while
I explain.

==============================================================================
...
#define for_named_file_e_0(PATH,MODE,EXPR) \
  __extension__ ({ \
    register char const *const path = (PATH); \
    register char const *const mode = (MODE); \
    ... \
  })

#define for_named_file_e(PATH,MODE,EXPR) \
  __extension__ ({ \
    register char const *const path = (PATH); \
    register char const *const mode = (MODE); \
    ... \
    for_named_file_e_0 (path, mode, (EXPR)); \
  })
...
==============================================================================

Some of you may have run into this same problem (that I'm having) yourselves
in the past, so perhaps some of you already see the problem.

The problem is that in the final expansion of any call to `for_named_file_e',
inside the (nested) expansion of `for_named_file_e_0' we will unintentionally
end up with code that says:

        ...
        register char const *const path = path;
        register char const *const mode = mode;
        ...

Unfortunately, because of C's scoping rules, the run-time effects of these 
two declaration don't have at all the desired effect, i.e. of taking the
values of the original PATH and MODE argments passed in to the outer
`first-level' call (to `for_named_file_e') and assigning those value to
the path_ and mode_ variables that belong to the *inner* macro expansion
(i.e. those of `for_named_file_e_0').

As someone taught me a long long time ago, it is always Good, within macros,
to evaluate those macro arguments that happen to be expressions only once.
This prevents problems like some people experienced way back in the Bad Old
Days (before the language was standardized) when people ended up scratching
their heads over macro calls like:

        ... = tolower (*p++);

When using un-carful early implmentations of C, the above code often caused
the value of `p' to be incremented two or more times, which was almost
always _not_ what was desired or intended.

Following the rule of always evaluating (and then saving) the values of any
of the expression arguments to most function-like macros prevents such
problems.

Unfortunately however, universally obeying that rule can give rise to cases
of the _other_ (scoping related) problem I have illustrated above (via my
two example macro definitions).

I have an idea that I think could help to solve the latter problem, and one
which might also perhaps be useful in other contexts.

I'd like to propose a small enhancement for the GNU preprocessor, i.e.
the addition of a new __MACRO__ pre-defined built-in symbol.  This new
built-in would be analogous to the existing __LINE__ __FILE__ and __FUNCTION__
built-in symbols.  Unlike those, of course, at any point where its value was
referenced, the pre-processor would substitute for the __MACRO__ symbol the
exact text of the name of the ``innermost'' macro currently be expanded by
the preprocessor.  Thus, two example macros might be composed as follows:

        #define foo(ARG1,ARG2) \
          { \
            register int __MACRO__##_macro_arg1 = ARG1; \
            register int __MACRO__##_macro_arg2 = ARG2; \
            foobar = __MACRO__##_macro_arg1 + __MACRO__##_macro_arg2; \
          }

        #define bar(ARG1,ARG2) \
          { \
            register int __MACRO__##_macro_arg1 = ARG1; \
            register int __MACRO__##_macro_arg2 = ARG2; \
            foo (__MACRO__##_macro_arg1, __MACRO__##_macro_arg2); \
          }

In such a case, the following macro call:

        bar (*p++, *q++);

would result in the following code expansion (reformatted for legibility):

        {
          register int bar_macro_arg1 = (*p++);
          register int bar_macro_arg2 = (*q++);
          {
            register int foo_macro_arg1 = bar_macro_arg1;
            register int foo_macro_arg2 = bar_macro_arg2;
            foobar = foo_macro_arg1 + foo_macro_arg2;
          }
        }

This expansion would have the desirable effects of (a) _not_ improperly
incrementing the two pointers, `p' and `q', multiple times (as a side-
effect of the call to `bar') and also (b) using & generating identifiers
(used here as variable names) that are unique to their immediately
enclosing macro definition(s).  This avoids the C/C++ scoping problem noted
above, and it may perhaps also have other salutory effects in other contexts.

Note that the standard preprocessor ## (string concatenation) operator is
defined (by the standard) in such a way that it's evaluation/execution must
necessarily be delayed until macro-expansion time, and that fact suggests
... to me anyway... that there would be a minimum of special hacks needed
in the preprocessor in order to implement this small preprocessor extension.

There is one small case in which some fiddling of the existing preprocessor
rules would be needed however.  In order to make this newly proposed __MACRO__
feature usable in the way intended however, evaluation rules for arguments
of the pre-processor ## operator might have to be adjusted very slightly
(and in a completely backward-compatible manner).

Currently, the ## operator is defined (by the standard) in a way that
causes it to only perform its really special magic _just_ in those cases
where one or another of the (preceeding or following) arguments to the
## operator are in fact parameter identifiers which name some parameter
or another of the current function-like macro.  Thus, the code:

        #defined example(BAR) foo##BAR foo##__LINE__
        example(bar)

produces the macro-expanded text:

        foobar foo__LINE__

It _does not_ generate a version of the macro expansion which would arguably
be more helpful/useful, i.e.:

        foobar foo2

In order to allow the new __MACRO__ built-in symbol to be used as intended,
at the very least that one new preprocessor built-in symbol would have to
be handled specially when it appeared either before or after the ## binary
operator.  In such contexts, it would have to be macro-replaced _before_
being supplied as a operand to the ## operator.

In short, the rule regarding the arguments to binary ## would necessarily
be relaxed ever so slightly, i.e. so as permit macro-replacement (with the
corresponding value), prior to ## evaluation/concatenation of either:

        (a) any named parameter of the current function-like macro, or
        (b) the special new (extension) built-in named `__MACRO__'

This small adjustment should not be difficult.  (And if _I_ were implemeting
this proposal, that's how I would do it.)

Alternatively however, a more generally useful generalization of the pre-
processor ## binary operator might instead be implemented, i.e. one that
doesn't break full backwards compatability for any existing code, but one
with more generally useful semantics.  Such an operator might conceivably
be spelled `@@'.  Assuming that it were in fact spelled that way, and
assuming that it was defined in a way that called for both of its aguments
to be fully macro-expanded _prior_ to being operated upon by the new
concatenation operator (_regardless_ of whether they are named parameters
of the current function-like macro or not), then it would be possible to write

        #defined example(BAR) foo@@BAR foo@@__LINE__
        example(bar)

and thence obtain (from the preprocessor) the following text:

        foobar foo2

The implementation of this possible/plausible additional preprocessor binary
operator is however beyond the scope of what it is my intention to propose,
i.e. a new __MACRO__ built-in with some very slightly augmented semantics
(hack?) for the existing ## operator.  If that latter aspect of this
proposal offends anyway (i.e. due to its special treatment of one special
new built-in symbol) then certainly, I would instead propose a new __MACRO__
built-in, together with a new ``enhanced semactics'' preprocessor concatenation
operator (which, as I say, might possibly be spelled `@@' or perhaps `###').

Comments?

Reply via email to