Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Yikes! We forgot to encode an iteraction between C++ and C varargs. (va_start doesn't work with pack expansions.)

159 views
Skip to first unread message

Daryle Walker

unread,
May 16, 2013, 9:03:18 AM5/16/13
to
[I can't figure out how to post this both here and comp.std.c++, so I
have to multi-post later. Unless a moderator here can do it.]

{ My experience suggests it's best to avoid cross-posting to moderated
groups -mod/we }

This is based on a recent Stack Overflow post at:
<http://stackoverflow.com/q/16563797/1010226>. As a commenter to my
query indirectly mentioned, we can do this:

#include <cstdio>
#include <string>

template< typename T >
T const &printf_helper( T const &x )
{ return x; }

char const *printf_helper( std::string const &x )
{ return x.c_str(); }

template< typename ... Req, typename ... Given >
int wrap_printf( int (*fn)( Req... ... ), Given ... args )
{ return fn( printf_helper( args ) ... ); }

int main() {
wrap_printf( &std::printf, "Hello %s\n", std::string( "world!" ) );
wrap_printf( &std::fprintf, stderr, std::string( "Error %d" ), 5 );
}

channel C++ varargs into C varargs. But how do we go the other way?
How do we use the va_* stuff for C varargs when the next-to-last
argument is a C++ vararg? My limited testing concludes that it DOES
NOT work.

template < typename ...Args >
int my_func2( short x, std::va_list &aa, Args &&...a );

template < typename ...Args >
int my_func( short x, Args &&...a, ... )
{
std::va_list args2;
va_start( args2, a... );
//...
}

I used GCC 4.8 from a compiling website. I initially hoped that I
didn't need to use the "..." after the "a," but I had to. The pack
expansion reacted badly with the "va_start" macro. The code worked
correctly when "sizeof...(a)" was exactly one. It gave errors when
"a" was longer than one AND when it was zero-length. Also, when "a"
was zero-length, I could plug in "x" instead of "a..." and it worked.

What I expect to happen:

1. I can use "a" as the second parameter of "va_start" without the
"...". If that is not possible (since that usage is currently illegal
everywhere), then "va_start" has to work when "a..." is given. I
don't know how all this works, but maybe the C++ version of "va_start"
needs to be a variadic macro?

2. I cannot use "x" instead when "a..." is empty, since the two cases
can't co-exist because it has to be hard-coded. (Can the planned
"static_if" do it?) In the worse case scenario, the compiler will
secretly switch in "x" for "a..." if it detects that "a..." is empty;
the programmer doesn't have to type in anything different.

A fix:

Change the footnote (currently #227 in the C++11 standard) for Section
18.10 [support.runtime], paragraph 3 to:

227) Note that va_start is required to work as specified even if unary
operator& is overloaded for the type of parmN, or parmN is a pack
expansion, or both.

--
Daryle W.


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

SG

unread,
May 16, 2013, 1:02:08 PM5/16/13
to
On May 16, 3:03 pm, Daryle Walker wrote:
> [...] we can do this:
> [...]
> channel C++ varargs into C varargs. But how do we go the other way?
> How do we use the va_* stuff for C varargs when the next-to-last
> argument is a C++ vararg? My limited testing concludes that it DOES
> NOT work.

It can't work. The type information is already lost and only available
at runtime as part of the format string (assuming the programmer did
not make a type error). That's just too late to feed a variadic
function template -- unless you want the compiler to create a LOT of
function template instances in advance for every possible situation
you *could* run into at *runtime* up to a certain maximum number of
parameters. But that would be incredibly wasteful ("template bloat
explosion").

SG


--

Daniel Krügler

unread,
May 16, 2013, 1:05:45 PM5/16/13
to
On 2013-05-16 15:03, Daryle Walker wrote:
> This is based on a recent Stack Overflow post at:
> <http://stackoverflow.com/q/16563797/1010226>. As a commenter to my
> query indirectly mentioned, we can do this:

[..]

> channel C++ varargs into C varargs. But how do we go the other way?
> How do we use the va_* stuff for C varargs when the next-to-last
> argument is a C++ vararg? My limited testing concludes that it DOES
> NOT work.
>
> template < typename ...Args >
> int my_func2( short x, std::va_list &aa, Args &&...a );
>
> template < typename ...Args >
> int my_func( short x, Args &&...a, ... )
> {
> std::va_list args2;
> va_start( args2, a... );
> //...
> }

Yes, this cannot work. The code in addition invokes undefined
behaviour for a non.empty pack expansion, because you are using a
parameter of reference type before the ellipses parameter.
Instead of requiring that the library solves it for you, why not
changing your signature to

template < typename ...Args, typename Last >
int my_func(short x, Args&&...a, Last last, ...)
{
std::va_list args2;
va_start(args2, last);
//...
}

It ensures that you have isolated the last parameter and that it's not
of reference type.

HTH & Greetings from Bremen,

Daniel Krügler


--
0 new messages