
I'm trying to use a variadic macro to solve a problem. We use a third-party function I can't modify -- actually a family of functions of arbitrary arity. Before each call to that function, I want to examine and possibly modify its first argument. (So not completely arbitrary arity: there's always at least one argument.) What I'd really like is a variadic function. Unfortunately the compilers we use don't yet support those. We do have cross-platform support for variadic macros, albeit with idiosyncratic syntax. My problem is that I need: intercept(first, second, third) to expand to: targetfunc(transform(first), second, third) but I need: intercept(first) to expand to: targetfunc(transform(first)) // <= no trailing comma This is the role of BOOST_PP_COMMA_IF(). In Visual Studio 2010 I can write BOOST_PP_COMMA_IF(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)), and the code behaves as expected. In g++ 4.1, 4.2 and 4.4, however, my first problem is that BOOST_PP_VARIADICS is not set by default. When I set it explicitly, as permitted by the documentation[0], a call such as intercept("first", "second") (comma required) succeeds, but a call such as intercept("first") (comma forbidden) fails. Using -E and examining the preprocessed output from intercept("first") makes clear that BOOST_PP_COMMA_IF() is expanding to a comma, hence BOOST_PP_VARIADIC_SIZE() is expanding to nonzero. In fact, when I change the definition of the intercept() macro below to: #define intercept(FIRST, ARGS...) \ targetfunc(transform(FIRST) \ , boost::lexical_cast<std::string>(BOOST_PP_VARIADIC_SIZE(ARGS))) I see that BOOST_PP_VARIADIC_SIZE() expands to 1 for intercept("first") as well as for intercept("first", "second"). Is it possible to achieve what I want with my present suite of compilers? Is there some workaround we could use until (at some point in the indefinite future) we've upgraded all our compilers so that even the oldest of them supports variadic functions? [0] http://www.boost.org/doc/libs/1_52_0/libs/preprocessor/doc/topics.html ================ source code follows ================ #include <iostream> #include <string> #include <boost/preprocessor/variadic/size.hpp> #include <boost/preprocessor/punctuation/comma_if.hpp> // no targetfunc(void) void targetfunc(const std::string& first) { std::cout << "targetfunc('" << first << "')\n"; } void targetfunc(const std::string& first, const std::string& second) { std::cout << "targetfunc('" << first << "', '" << second << "')\n"; } // ... assume a family of targetfunc() arities ... // want to implicitly "do something" to targetfunc()'s first arg std::string transform(const std::string& arg) { return "transformed " + arg; } #if defined(_MSC_VER) // anonymous variadic arguments and __VA_ARGS__ keyword #define intercept(FIRST, ...) \ targetfunc(transform(FIRST) \ BOOST_PP_COMMA_IF(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__)) \ __VA_ARGS__) #else // collectively-named variadic arguments #define intercept(FIRST, ARGS...) \ targetfunc(transform(FIRST) \ BOOST_PP_COMMA_IF(BOOST_PP_VARIADIC_SIZE(ARGS)) \ ARGS) #endif int main(int argc, char *argv[]) { std::cout << "BOOST_PP_VARIADICS = " << BOOST_PP_VARIADICS << "\n"; intercept("first", "second"); intercept("first"); // <= this line has trouble with g++ 4.1, 4.2, 4.4 return 0; }