Boost logo

Boost Users :

Subject: [Boost-users] [preprocessor] BOOST_PP_VARIADIC_SIZE() and g++ in 1.52
From: Nat Linden (nat_at_[hidden])
Date: 2012-12-11 10:19:02


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;
}


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net