VanLUG Email Archive

Traduisez - Übersetzen - Traduzca - Traduza - Tradurre - Translate

VanLUG Mailing List
problem/puzzle for C++ experts

New Message Reply About this list Date view Thread view Subject view Author view

Dave Martindale (davem@cs.ubc.ca)
Mon, 19 Mar 2001 23:40:09 -0800


I use a large C++ library called LEDA. It compiled and ran OK using
g++ 2.95, but parts of it will not compile under 2.96. The problem is
in the handling of a particular use of the "##" token pasting operator
in the preprocessor. Here is a simplified example of the code that
causes the problem:

=======================================================================
#define _E1FMT0(arg)
#define _E1FMT1(arg) arg
#define _E1FMT2(arg) arg##1 , arg##2
#define _E1FMT3(arg) _E1FMT2(##arg) , arg##3
#define _E1FMT4(arg) _E1FMT3(##arg) , arg##4

#define _DECLARE_BASE_RECEIVER(name,ctypes,types) \
template<##ctypes> class name; \
template<##ctypes> \
class BASE_RECEIVER_##name : public base_receiver { \
  friend class name <##types>; \
  typedef name <##types> event; \
  typedef BASE_RECEIVER_##name <##types> receiver; \
  event* _e; \
  list_item _it; \
  public : \
  BASE_RECEIVER_##name() : _e(0) {} \
  inline ~BASE_RECEIVER_##name(); \
  base_event* get_event() const { return (base_event*)_e; } \
  virtual void notify(types) = 0; \
};

#define _DECLARE_EVENT_SYSTEM(name,ctypes,types) \
  _DECLARE_BASE_RECEIVER ( name, ##ctypes, ##types )

#define DECLARE_EVENT_SYSTEM(name,argc) \
  _DECLARE_EVENT_SYSTEM( name, \
                         _E1FMT##argc(class argt), \
                         _E1FMT##argc(argt) )

DECLARE_EVENT_SYSTEM(EVENT1,1) // makes EVENT1<type> available
DECLARE_EVENT_SYSTEM(EVENT2,2) // makes EVENT2<type1,type2> available ...
DECLARE_EVENT_SYSTEM(EVENT3,3)
DECLARE_EVENT_SYSTEM(EVENT4,4)
=======================================================================

When I do "g++ -E demo.cc >out" it works fine if the compiler is 2.95.
Using "gcc version 2.96 20000731 (Red Hat Linux 7.0)", I get a batch of
errors:

demo.cc:5:29: warning: nothing can be pasted after this token
demo.cc:6:29: warning: nothing can be pasted after this token
demo.cc:7:29: warning: nothing can be pasted after this token
demo.cc:35:30: warning: pasting would not give a valid preprocessing token
demo.cc:35:30: warning: pasting would not give a valid preprocessing token
demo.cc:35:30: warning: pasting would not give a valid preprocessing token
demo.cc:35:30: warning: pasting would not give a valid preprocessing token
demo.cc:35:30: warning: pasting would not give a valid preprocessing token
demo.cc:35:30: warning: pasting would not give a valid preprocessing token
demo.cc:35:30: warning: pasting would not give a valid preprocessing token
demo.cc:36:30: warning: pasting would not give a valid preprocessing token
demo.cc:36:30: warning: pasting would not give a valid preprocessing token
demo.cc:36:30: too many arguments for macro "_DECLARE_BASE_RECEIVER"
demo.cc:37:30: warning: pasting would not give a valid preprocessing token
demo.cc:37:30: warning: pasting would not give a valid preprocessing token
demo.cc:37:30: too many arguments for macro "_DECLARE_BASE_RECEIVER"
demo.cc:38:30: warning: pasting would not give a valid preprocessing token
demo.cc:38:30: warning: pasting would not give a valid preprocessing token
demo.cc:38:30: too many arguments for macro "_DECLARE_BASE_RECEIVER"

Now, the first problem in all this is that the LEDA code uses the "##"
operator as an effectively *unary* operator in many places in the code,
as well as its usual action as a token-pasting *binary* operator.
Gcc 2.95 doesn't complain about either the normal or the weird use, and
just compiles the code as desired. Even the C++ reference manual (2nd
edition) isn't too clear on what should happen with a unary ##, but in
fact this code must work with most existing C++ compilers, because LEDA
is distributed for use with multiple compilers.

Now, gcc 2.96 generates a bunch of warning messages for these unary uses
of ## - but the presense of the warning doesn't seem to be a problem.
Or I can edit out all of the unary uses of ##, and that gets rid of
all the warnings. However, the "too many arguments" error messages
remain. And, in fact, I've introduced a real problem - the modified
code no longer compiles under 2.95 either. It also gets "too many
arguments" there.

It turns out that the unary ## use in the following line is critical:

  _DECLARE_BASE_RECEIVER ( name, ##ctypes, ##types )

Under 2.95, the effect of this is to replace the second and third
arguments to _DECLARE_BASE_RECEIVER with their values, which are
references to the _E1FMTx macros. The E1FMTx macro is expanded into
a list of one or more comma-separated names. This entire list of
names is then NOT rescanned for commas, and the entire list is passed
to _DECLARE_BASE_RECEIVER as a single argument!

Without the ## operator, the list generated by expanding the _E1FMTx
macro gets rescanned before it's passed to _DECLARE_BASE_RECEIVER,
which causes multiple list members to be turned into multiple arguments,
and this causes the "too many arguments" error message.

But with those two uses of unary-##, this code seems to work under 2.95.
This suggests that all of the *other* uses of unary ## are actually
not needed, at least with gcc.

With gcc 2.96, the rescan *always* happens, and I get the error with
or without the use of ##.

So, I have two questions:

- Is this use of "unary ##" to prevent rescanning, and to pass a whole
  list as a single argument to a macro, actually sanctioned by the
  C++ standard, and it's simply a bug in gcc 2.96? Or is this an
  accidental side effect of the way most preprocessors work, not actually
  guaranteed to work at all, and thus gcc 2.96 is entitled to ignore it
  the way it does?

- How can I get this working again? The use of ## to pass a list as a
  single argument is pretty fundamental to the way the code is set up
  now. If gcc is just buggy, presumably it will get fixed, and in the
  meantime I can go back to 2.95. But if this really is unportable
  code, how can it be fixed?

If you want to try this on your own system, here is a version of the
code without all the unnecessary uses of ##. This still does seem to
work on 2.95, but not on 2.96.

=======================================================================
#define _E1FMT0(arg)
#define _E1FMT1(arg) arg
#define _E1FMT2(arg) arg##1 , arg##2
#define _E1FMT3(arg) _E1FMT2(arg) , arg##3
#define _E1FMT4(arg) _E1FMT3(arg) , arg##4

#define _DECLARE_BASE_RECEIVER(name,ctypes,types) \
template<ctypes> class name; \
template<ctypes> \
class BASE_RECEIVER_##name : public base_receiver { \
  friend class name <types>; \
  typedef name <types> event; \
  typedef BASE_RECEIVER_##name <types> receiver; \
  event* _e; \
  list_item _it; \
  public : \
  BASE_RECEIVER_##name() : _e(0) {} \
  inline ~BASE_RECEIVER_##name(); \
  base_event* get_event() const { return (base_event*)_e; } \
  virtual void notify(types) = 0; \
};

#define _DECLARE_EVENT_SYSTEM(name,ctypes,types) \
  _DECLARE_BASE_RECEIVER ( name, ##ctypes, ##types )

#define DECLARE_EVENT_SYSTEM(name,argc) \
  _DECLARE_EVENT_SYSTEM( name, \
                         _E1FMT##argc(class argt), \
                         _E1FMT##argc(argt) )

DECLARE_EVENT_SYSTEM(EVENT1,1) // makes EVENT1<type> available
DECLARE_EVENT_SYSTEM(EVENT2,2) // makes EVENT2<type1,type2> available ...
DECLARE_EVENT_SYSTEM(EVENT3,3)
DECLARE_EVENT_SYSTEM(EVENT4,4)
=======================================================================

        Dave

--
This message came to you via the Vancouver Linux Users Group mailing list.
For unsubscription instructions do not email the list, but rather send mail
to <vanlug-request@gweep.bc.ca>.


New Message Reply About this list Date view Thread view Subject view Author view

This archive was generated by hypermail 2.0b3 on Tue 03 Jul 2001 - 19:14:50