Boost logo

Boost :

From: scleary_at_[hidden]
Date: 2000-02-25 09:14:09


"mark borgerding" wrote:
> john maddock wrote:
>
> > #define BOOST_CT_ASSERT( B ) enum { BOOST_JOIN(_boost_assert_enum_,
> > __LINE__) = sizeof(::boost::ct_assert<(B)>) }
> > //#define BOOST_CT_ASSERT(condition) struct { int : (condition) ? 0
:
> -1; }
> >
> > #define BOOST_DO_JOIN( X, Y ) X ## Y
> > #define BOOST_JOIN( X, Y ) BOOST_DO_JOIN( X, Y )
>
> I am trying to get this to work on VC6, but I keep getting errors that
> seem to suggest that the macro expansion of __LINE__ and the
> token-pasting are happening in reverse order. I installed Visual
> Studio SP3, but it didn't help.
>
> What are you doing differently from me?
>
> I boiled the macros down a bit, and cannot get the following to
compile
> on VC.
>
> #define TEST_DECLARATION int JOIN(unique_,__LINE__ ) = 0
> #define DO_JOIN(X,Y) X##Y
> #define JOIN(X,Y) DO_JOIN(X,Y)
>
> int main()
> {
> TEST_DECLARATION;
> return 0;
> }
>
> It works fine on g++ 2.95. And when I use the /E (preprocess to
> stdout) option for VC, it looks right too:
>
> int main()
> {
> int unique_7 = 0;
> return 0;
> }
>
>
> I am really frustrated by this. Does the C standard dictate which
> order the preprocessor must go? I don't have access to this document
> and the C++ standard doesn't give any hints.
>

I just stole this from John's code. Without really looking at it, I
assumed it was designed to get around this very problem; and after
looking at the Standard (which is hard to understand) and C:ARM (which
isn't), I'm convinced it works for C++, but may not work for C. I
don't have a copy of the actual C standard, so I can't say for sure if
it works for C, but it should work for C++.

Note that the C:ARM I'm referring to is the old C:ARM (Harbison &
Steele), which covers the C language, not C++.

The "##" operator is what's getting us here. After macro replacement,
the text is rescanned for other macros (16.3.4/1, C:ARM 3.3.3).
However, "##" merges the tokens _before_ rescanning for more macros
([16.3.3/3], C:ARM 3.3.9).

The above is true AFAIK for C, and will result in:

(given:)
  #define DO_JOIN(a, b) a ## b
  #define JOIN(a, b) DO_JOIN(a, b)

JOIN(x, __LINE__) // macro scanning finds 'JOIN', and expands
DO_JOIN(x, __LINE__) // macro scanning finds 'DO_JOIN', and expands
x ## __LINE__ // ## is found _before_ macro scanning, and
concacts
x__LINE__ // macro scanning finds nothing, and is done

However, this is NOT true for C++, because the Standard says in
[16.3.1/1] ". . . A parameter in the replacement list, unless preceded
by a # or ## preprocessing token or followed by a ## preprocessing
token. . . is replaced by the corresponding argument after all macros
contained therein have been expanded. . ." This means that the macro
_arguments_ are scanned before substitution (unless they're used with #
or ##).

C:ARM differs here: (3.3.3) ". . . Macro replacement is also not
performed within the actual argument token strings of a function-like
macro call at the time the macro call is being scanned. Macro names
are recognized within actual argument token strings only during the
rescanning of the expansion. . ." Seems to make it very clear that
macro arguments are _not_ scanned before substitution.

Thus, for C++, the following should work:

(given:)
  #define DO_JOIN(a, b) a ## b
  #define JOIN(a, b) DO_JOIN(a, b)

JOIN(x, __LINE__) // macro scanning finds 'JOIN', and expands;
                  // macro argument scanning finds '__LINE__' and
expands
DO_JOIN(x, 13) // macro scanning finds 'DO_JOIN', and expands
x ## 13 // ## is found _before_ macro scanning, and concacts
x13 // macro scanning finds nothing, and is done

Note that this is why the DO_JOIN/JOIN pair are both required; just
"DO_JOIN(x, __LINE__)" will not scan the argument "__LINE__" because
it's used after "##".

So I must conclude that this is Yet Another Shortcoming of Micro$oft :)
 Or, possibly, that M$ is trying to compile it as C code.

Now, I have a question for anyone with the C standard, or for someone
on the C++ committee who remembers making this change (if it was a
change). Would this work for C? C:ARM says no; but it could be wrong,
for all I know. There's no mention in [C.1] (Compatibility: C++ and
ISO C) about this kind of a difference from C.

        -Steve


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk