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);
}() )
{
}
};
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.
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.
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 theregister
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With