Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable arguments into lambda expression

Is the following usage of a lambda wrong, fragile, or silly? It works on VC++ 2012, but I am concerned that there are some variable-argument/lambda stack interactions that make this dangerous.

class
ArgumentException : public std::runtime_error
{
public:
   ArgumentException( 
      const char* format_,
      ... )
      : std::runtime_error(
         [&]()
         { 
            char buffer[2048];
            va_list arguments;
            va_start ( arguments, format_ );  
            int writtenCount = vsnprintf( buffer, 2048, format_, arguments );
            va_end ( arguments );
            return std::string(buffer); 
         }() )
   {
   }
};
like image 495
Elpoca Avatar asked Apr 30 '14 16:04

Elpoca


People also ask

Can we declare variable in lambda expression?

A lambda expression can't define any new scope as an anonymous inner class does, so we can't declare a local variable with the same which is already declared in the enclosing scope of a lambda expression. Inside lambda expression, we can't assign any value to some local variable declared outside the lambda expression.

How many arguments can be passed to lambda?

You can use as many arguments as you want in a lambda function, but it can have only one expression. This expression is evaluated and returned as a result.


1 Answers

The C++11 standard explicitly makes va_list and the supporting macros available through <cstdarg> (§18.10, table 37) but makes no attempt to restrict their use or (re)define their meaning, so I'm going to turn to the C standard.

§7.15.1.4 says that:

void va_start(va_list ap, parmN );

The parameter parmN is the identifier of the rightmost parameter in the variable parameter list in the function definition (the one just before the , ...). If the parameter parmN is declared with the register storage class, with a function or array type, or with a type that is not compatible with the type that results after application of the default argument promotions, the behavior is undefined.

In your case, format_ is not an argument (it's a captured variable in your lambda) and the function in which va_start is called is not even the one with the variable parameter list, so you're arguably very much in the realm of undefined behavior. Not to mention that the argument promotion rules of the C language can't deal with reference types, and therefore it can't correctly deal with the fact that format_ is a reference, not a straight-up pointer.

As far as I know, it's syntactically infeasible to use variadic parameters in a constructor's initializer list. (See this guy below who did it.) You could, however, use variadic templates to forward the parameters to a "clean" C-style variadic parameters function:

#include <cstdarg>
#include <cstdio>
#include <string>

std::string stringprintf(const char* format, ...)
{
    char buffer[0x2000];
    va_list ap;
    va_start(ap, format);
    vsnprintf(buffer, sizeof buffer, format, ap);
    va_end(ap);
    return buffer;
}

class ArgumentException : public std::runtime_error
{
public:
    template<typename... T>
    ArgumentException(const char* format, T... arguments)
    : std::runtime_error(stringprintf(format, arguments...))
    { }
};

Also consider using <stdexcept>'s invalid_argument exception subclass.

like image 162
zneak Avatar answered Sep 24 '22 11:09

zneak