3

Is there any way to write a macro like this:

#define G(x1, x2, ... , xn) f(x1), f(x2), ... , f(xn)

Or do I need to define this for each individual n?

C++0x answers are ok.

Edit: I'm asking how to create a macro of this form, not a macro that takes a variable number of arguments generally.

Goal:: So I can do something like the following:

#define MAKE_TUPLE(x1, x2, ... , xn) mytuple<decltype((x1)), decltype((x2)), ... , decltype((xn))>{x1, x2, ... , xn}

So this mytuple can be created without and moves and copies, nor unnecessary references to temporaries which could have been created in place using aggregate construction.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Clinton
  • 22,361
  • 15
  • 67
  • 163
  • If you use any features of C++0x then it's not a macro. – user541686 Jun 09 '11 at 02:49
  • Are you asking [whether variadic macros exist at all](http://stackoverflow.com/questions/679979/c-c-how-to-make-a-variadic-macro-variable-number-of-arguments)? Or are you asking [how to use each argument separately](http://stackoverflow.com/questions/4750688/how-to-single-out-the-first-parameter-sent-to-a-macro-taking-only-a-variadic-para)? – Rob Kennedy Jun 09 '11 at 02:53
  • @Mehrdad: Are there no changes to macros in C++0x? If so I'll remove the tag. – Clinton Jun 09 '11 at 02:54
  • @Rob Kennedy: How to use each argument separately. – Clinton Jun 09 '11 at 02:55
  • @Mehrdad: I'm not sure I understand. @Clinton: Only C99 and C++0x support variadic macros, C++03 only supports it through extension. – GManNickG Jun 09 '11 at 02:56
  • 2
    @Clinton: [What for?](http://www.catb.org/esr/faqs/smart-questions.html#goal) Ask about the goal, not the step. (Does it really need to be a macro? C *or* C++, not both.) – GManNickG Jun 09 '11 at 02:57
  • @GMan: I've added the goal to the question now. – Clinton Jun 09 '11 at 03:08
  • @Clinton: You should just use `std::make_tuple`. Your worries about copies and temporaries are unjustified. – GManNickG Jun 09 '11 at 03:30
  • I agree with @GMan's advice, unless your types are for some reason extremely expensive to move. – James McNellis Jun 09 '11 at 03:38
  • @James McNellis: What if they are? I'm intending to make a generic named parameter library where you can store parameter packs for later use (as boost::parameter seems only intended for passing parameters, as storing boost::parameter packs causes references to temporary issues). Passing by my parameter pack ideally should be as close to as fast as ordinary argument passing as possible. I imagine these parameter packs could get reasonably large. – Clinton Jun 09 '11 at 03:44
  • 2
    @Clinton: *I'm intending to make a generic named parameter library* -- if you start inroducing macros like that, nobody will use that library. – Gene Bushuyev Jun 09 '11 at 05:03

3 Answers3

5

If you are willing to use a slightly clumsy syntax, then you can use Boost.Preprocessor's sequences:

#include <boost/preprocessor.hpp>

#define G(args) BOOST_PP_SEQ_FOR_EACH_I(G_GENF, x, args)
#define G_GENF(r, data, i, elem) \
    BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(i, 0)) f(elem)

Usage:

G((a))
G((b)(c))
G((d)(e)(f))

Result:

f(a)
f(b) , f(c)
f(d) , f(e) , f(f)

If you do want the G(a, b, c) syntax, then because macro replacement is not recursive, I think you'll likely need one macro per number of arguments that you are going to pass. You can still delegate to these macros from a single macro used elsewhere in your source, though. Consider:

// Utility for counting the number of args in the __VA_ARGS__ pack:
#define PP_NARGS(...) PP_NARGS2(__VA_ARGS__, PP_NARGS_COUNT())
#define PP_NARGS2(...) PP_NARGS_IMPL(__VA_ARGS__)
#define PP_NARGS_IMPL(x1, x2, x3, N, ...) N
#define PP_NARGS_COUNT() 3, 2, 1, 0, ERROR

// Macros to delegate to concrete, defined-arity implementations:
#define XF(count, ...) XF_IMPL (count, __VA_ARGS__)
#define XF_IMPL(count, ...) XF_ ## count (__VA_ARGS__)

// Defined-arity implementations:
#define XF_1(x1)         f(x1)
#define XF_2(x1, x2)     f(x1), f(x2)
#define XF_3(x1, x2, x3) f(x1), f(x2), f(x3)

// Delegation macro; this is the only macro you need to call from elsewhere:
#define G(...) XF(PP_NARGS(__VA_ARGS__), __VA_ARGS__)    

Usage:

G(a)
G(b, c)
G(d, e, f)

Result:

f(a)
f(b), f(c)
f(d), f(e), f(f)

This could certainly be further generalized, and some preprocessor utilities library might already have some facility to do this, but this demonstrates how it can be done. (I'm not familiar with any C99/C++0x preprocessor libraries, so I can't recommend one.)

James McNellis
  • 348,265
  • 75
  • 913
  • 977
5

Goal: [stripped the description of the again wrong approach]. So this mytuple can be created without and moves and copies, nor unnecessary references to temporaries which could have been created in place using aggregate construction.

You made your requirements more precise in a comment

Passing by my parameter pack ideally should be as close to as fast as ordinary argument passing as possible. I imagine these parameter packs could get reasonably large.

You've been on the complete wrong path. The following is not a macro, creates no moves or copies (not more than a macro) and has no unnecessary references to temporaries (not sure what the last part of your requirements means, but I think we can clarify that iteratively). It's as fast as "ordinary argument passing", since it's doing nothing else.

template<typename ...T>
mytuple<T...> make_tuple(T&&... t) {
  return mytuple<T...>{std::forward<T>(t)...};
}

This has different behavior than the sequence of decltype expressions you give, with regard to argument type X

  • Prvalues are forwarded to the tuple as as X
  • Xvalues are forwarded to the tuple as X
  • Lvalues are forwarded to the tuple as X&

Your macro behaves different

  • Prvalues are forwarded to the tuple as as X
  • Xvalues are forwarded to the tuple as X&&
  • Lvalues are forwarded to the tuple as X&

Note the middle part - you are prone to dangling references, unless the lifetime of your tuple is always less than the lifetime of all the arguments, but then storing prvalues as X creates an unnecessary copy. Use mytuple<T&&...> in my code for always using references, as follows

  • Prvalues are forwarded to the tuple as as X&&
  • Xvalues are forwarded to the tuple as X&&
  • Lvalues are forwarded to the tuple as X&
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
0

As I recall the Boost Preprocessor library can do this, even for C++98.

However, macros are evil and best avoided.

Think instead about doing things like

Type const values[] = {x1, x2, x3, ... xn};

for( int i = 0;  i < countOf( values );  ++i )
{
    f( values[i] );
}

Cheers & hth.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331