Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can C++ and C variadic arguments be used together?

Generally, using the C++11 variadic template feature with functions requires the variadic-based function arguments to be the last in the function argument list. There is one exception; they are the next-to-last arguments if there are C-level variadic arguments, which must be dead last.

template < typename ...Args >
int  super_printf( Something x, Args &&...a, ... );

I sometimes randomly think about C++, and I wondered how such a function can be implemented. I first thought about the usual recursive peeling of arguments from a, then I remembered that the C-level varargs don't cascade. I have to turn them to a definitive va_list right away.

template < typename ...Args >
int  super_vaprintf( Something x, std::va_list &aa, Args &&...a );
// Note that "aa" is passed by reference.

template < typename ...Args >
int  super_printf( Something x, Args &&...a, ... )
{
    std::va_list  args2;
    int           result;

    va_start( args2, XXX );  // (A)
    try {
        result = super_vaprintf( x, args2, std::forward<Args>(a)... );
    } catch ( ... ) {
        va_end( args2 );  // (1)
        throw;
    }
    va_end( args2 );  // (2)
    return result;

    // Can (1) and (2) be compacted with RAII using a custom deleter lambda
    // in std::unique_ptr or something?  Remember that "va_end" is a macro!
}

The usual C++ variadic recursive peeling happens in the super_vaprintf call. At line (A), what goes in the place of XXX, "a" or "a..."? What happens if a is empty, does x go there instead? If that last question's true, are we screwed if there's no x; that there's no arguments besides the variadic ones? (And if it's true, how do we conditionalize the code to use x when a is empty, and a otherwise?)

...

I just looked at my copy of the C++11 standard for any assistance here. There doesn't seem to be any. This would prompt a request for the C++ committee to come back to fix this, but I'm not sure that there's any way such a function could be called without the C++ varargs taking everything. Am I wrong; can a function call be made to use both C++ and C varargs? Or is mixing only useful for declarations, in terms of Stupid (Template) Instantiation Tricks?

like image 718
CTMacUser Avatar asked May 15 '13 11:05

CTMacUser


People also ask

How do you use Variadic arguments?

It takes one fixed argument and then any number of arguments can be passed. The variadic function consists of at least one fixed variable and then an ellipsis(…) as the last parameter. This enables access to variadic function arguments. *argN* is the last fixed argument in the variadic function.

Why do we use variadic parameters in Swift?

In Swift, variadic parameters are the special type of parameters available in the function. It is used to accept zero or more values of the same type in the function. It is also used when the input value of the parameter is varied at the time when the function is called.

Is printf variadic function?

Variadic functions are functions (e.g. printf) which take a variable number of arguments. The declaration of a variadic function uses an ellipsis as the last parameter, e.g. int printf(const char* format, ...);.


1 Answers

When you call a function whose last parameter is a pack, all the arguments become part of that pack. There is nothing left for va_args. Your usage of explicit template arguments is misleading because they are not exclusive; they simply precede implicit arguments.

To defeat deduction, you need a reference:

(& super_printf<int, int>) ( 0L, 1, 2, 3, 4, 5 )

This is fairly contrived, but now you have the problem of nothing to pass to va_start.

To provide a reasonable interface to users, just add a parameter between the two lists.

struct va_separator {}; // Empty; ABI may elide allocation.

template < typename ...Args >
int  super_printf( Something x, Args &&...a, va_separator, ... );

This super_printf will need both explicit arguments to define the pack and an explicit separator argument. But you can alternatively provide a public function which receives all its arguments by pack, then finds the separator and forwards to super_printf using an explicit argument list comprising the pack elements before the separator.

like image 193
Potatoswatter Avatar answered Sep 20 '22 15:09

Potatoswatter