开发者

Variadic macros

开发者 https://www.devze.com 2023-03-11 05:20 出处:网络
Is there any way to write a macr开发者_运维百科o like this: #define G(x1, x2, ... , xn) f(x1), f(x2), ... , f(xn)

Is there any way to write a macr开发者_运维百科o 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.


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.)


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&


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消