0

This question is a follow-on from https://stackoverflow.com/a/5365786/383306.

#define _DEFINE_REF_INTERNAL2(id, ...)

#define _DEFINE_REF_INTERNAL1(id)

#define _VA_NARGS_2_IMPL(_1, _2, N, ...) N
#define _VA_NARGS_2(...) _VA_NARGS_2_IMPL(__VA_ARGS__, 2, 1)
#define _DEFINE_REF_IMPL2(count, ...) _DEFINE_REF_INTERNAL ## count (__VA_ARGS__)
#define _DEFINE_REF_IMPL(count, ...) _DEFINE_REF_IMPL2(count, __VA_ARGS__)
#define DEFINE_REF(...) _DEFINE_REF_IMPL(_VA_NARGS_2(__VA_ARGS__), __VA_ARGS__)

DEFINE_REF(MyClass, typename... Args, Args...); 
// error: ‘_DEFINE_REF_INTERNALArgs’ does not name a type
DEFINE_REF(MyClass, typename T, T); // this is OK

How do I make the macro trick work when passing ellipsis as part of an argument?

Community
  • 1
  • 1
Zach Saw
  • 4,308
  • 3
  • 33
  • 49

1 Answers1

1

The problem isn't with the ellipsis. The problem is that you are passing three arguments in __VA_ARGS__ to DEFINE_REF, but _VA_NARGS_2 only handles up to two arguments.

Once you fix that, the program (I believe) exhibits the desired behavior. gcc 4.7.2 and Clang 3.2 both transform this:

#define DEFINE_REF_INTERNAL3(arg0, arg1, arg2) [arg0] [arg1] [arg2]

#define VA_NARGS_(_1, _2, _3, N, ...) N
#define VA_NARGS(...) VA_NARGS_(__VA_ARGS__, 3, 2, 1)

#define DEFINE_REF_IMPL_(count, ...) DEFINE_REF_INTERNAL ## count(__VA_ARGS__)
#define DEFINE_REF_IMPL(count, ...) DEFINE_REF_IMPL_(count, __VA_ARGS__)

#define DEFINE_REF(...) DEFINE_REF_IMPL(VA_NARGS(__VA_ARGS__), __VA_ARGS__)

DEFINE_REF(MyClass, typename... Args, Args...); 
DEFINE_REF(MyClass, typename T,       T      );

into this:

[MyClass] [typename... Args] [Args...];
[MyClass] [typename T] [T];

(Also note that names beginning with an underscore followed by a capital letter are reserved for the implementation. You may not use such names for your own macros.)


If you are targeting Visual C++, you will need a barrel of indirection to make this work, as it does not correctly replace macros before rescanning in all cases. The following will work with Visual C++ (This solution is also conforming and works with gcc and Clang as well):

#define DEFINE_REF_INTERNAL3(id, arg0, arg1) id [arg0] [arg1]

#define CONCATENATE_(x, y) x ## y
#define CONCATENATE(x, y) CONCATENATE_(x, y)

#define VA_NARGS1(_1, _2, _3, N, ...) N
#define VA_NARGS0(x) VA_NARGS1 x
#define VA_NARGS(...) VA_NARGS0((__VA_ARGS__, 3, 2, 1))

#define DEFINE_REF_IMPL1(macro, pargs) macro pargs
#define DEFINE_REF_IMPL0(count, ...) \
    DEFINE_REF_IMPL1(CONCATENATE(DEFINE_REF_INTERNAL, count), (__VA_ARGS__))
#define DEFINE_REF_IMPL(count, ...) DEFINE_REF_IMPL0(count, __VA_ARGS__)

#define DEFINE_REF(...) DEFINE_REF_IMPL(VA_NARGS(__VA_ARGS__), __VA_ARGS__)
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • RE: comment about underscore + cap letter. Specifically, what is 'the implementation'? – Zach Saw Dec 14 '12 at 01:18
  • 1
    Generally, _the implementation_ is the compiler, the Standard Library, and perhaps some platform libraries (like parts of the OS). There's no clear division between what is and is not part of _the implementation_, but most code is not _the implementation_. – James McNellis Dec 14 '12 at 01:42
  • Ah mine falls perfectly in that compiler / std lib category. So it would've been wrong for me to pollute the global namespace. – Zach Saw Dec 14 '12 at 01:45
  • I now face another problem where I have multiple template typenames -- `DEFINE_REF(MyClass, (typename T, typename U), (T, U))` but how do I get rid of the additional `()` when the macro gets expanded? (should this be another SO question?) – Zach Saw Dec 14 '12 at 02:10
  • 1
    If you require that _all_ uses of `DEFINE_REF` have parenthesized arguments (i.e., that you require `DEFINE_REF(MyClass, (T), (T))`, even if there is only one argument), then you can use `#define EAT_YUMMY_PARENTHESES_(x) x` and `#define EAT_YUMMY_PARENTHESES(x) EAT_YUMMY_PARENTHESES_ x`. This is the same trick as is used in the `VA_NARGS0` in the Visual C++-compatible code I presented. – James McNellis Dec 14 '12 at 02:19