lambda表达式的变量参数

时间:2014-04-30 16:39:08

标签: c++ exception lambda variadic-functions

lambda的以下用法是错误的,脆弱的还是愚蠢的?它适用于VC ++ 2012,但我担心有一些变量参数/ lambda堆栈交互会使这种情况变得危险。

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); 
         }() )
   {
   }
};

3 个答案:

答案 0 :(得分:4)

C ++ 11标准通过va_list(第18.10节,表37)显式地使<cstdarg>和支持宏可用,但不试图限制它们的使用或(重新)定义它们的含义,所以我要转向C标准。

§7.15.1.4说:

  

void va_start(va_list ap, parmN );

     

参数 parmN 是函数定义中变量参数列表中最右边参数的标识符( , ... 之前的参数)。如果使用 register 存储类声明参数 parmN ,使用函数或数组类型,或者使用与结果类型不兼容的类型在应用默认参数促销后,行为未定义。

在你的情况下,format_不是一个参数(它是你的lambda中捕获的变量),调用va_start的函数甚至不是带有变量参数列表的函数,所以你可以说是在未定义的行为领域。更不用说C语言的参数提升规则不能处理引用类型,因此它无法正确处理format_是引用的事实,而不是直接指针。

据我所知,在构造函数的初始化列表中使用variadic参数在语法上是不可行的。See this guy below who did it.)但是,您可以使用可变参数模板将参数转发给“干净”C风格的可变参数功能:

#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...))
    { }
};

另请考虑使用<stdexcept>的{​​{1}}异常子类。

答案 1 :(得分:2)

我不建议这样做,所以这更像是一种练习,无论是否可以在没有可变参数模板的情况下编写符合要求的实现。我认为这是,虽然它不优雅或漂亮:

#include <stdexcept>
#include <cstdarg>

std::string
helper(const char *fmt, va_list args) {
    char buffer[2048];
    int writtenCount = vsnprintf(buffer, sizeof buffer, fmt, args);
    return std::string(buffer);
}

class ArgumentException : public std::runtime_error {
    public:
       ArgumentException(const char* fmt, ...)
           : std::runtime_error(helper(fmt, (va_start(args, fmt), args))) {
           va_end(args);
       }
    private:
        va_list args;
};

顺便说一下,原来的尝试给了我一个g ++错误,声称我在一个带有固定参数的函数中使用了va_start,这是正确的,我相信。

答案 2 :(得分:0)

为了后人,在这里添加WhozCraig的解决方案:

class ArgumentException : public std::runtime_error
{
private:
    static std::string mkmsg(const char *fmt, ...)
    {
        char buffer[2048];
        va_list args;
        va_start ( args, fmt );
        int writtenCount = vsnprintf( buffer, 2048, fmt, args );
        va_end ( args );
        if (writtenCount<=0)
            *buffer = 0;
        return buffer;
    }

public:
    template<class... Args>
    ArgumentException(const char* fmt, Args&&... args )
        : std::runtime_error( mkmsg(fmt, std::forward<Args>(args)... ) )
    {
    }
};