2012-02-02 91 views
1

我写了一个函数,它试图通过返回一个std :: string而不是写入用户提供的char*来自动分配sprintf。 (请,没有答案,建议输入输出流或Boost.Format库或朋友 - 我知道他们的存在,我在其他情况下不使用它们,但有一个要求,对于这种特殊情况下)std :: string和stdarg.h

std::string FormatString(const std::string& format, va_list argList) 
{ 
    char smallBuffer[500], *text = smallBuffer; 
    int length = _countof(smallBuffer); 

    // MSVC is not C99 conformant, so its vsnprintf returns -1 
    // on insufficient buffer space 
    int outputSize = _vsnprintf(text, length, format.c_str(), argList); 
    while (outputSize < 0 && errno == ERANGE && length > 0) 
    { 
     length <<= 1; 
     if (text != smallBuffer) { delete[] text; } 
     text = new char[length]; 
     outputSize = _vsnprintf(text, length, format.c_str(), argList); 
    } 
    if (outputSize < 0) 
    { 
     throw std::runtime_error("Failed to format string."); 
    } 

    std::string ret(text); 
    if (text != smallBuffer) 
    { 
     delete[] text; 
    } 
    return ret; 
} 

std::string FormatString(const std::string& format, ...) 
{ 
    va_list argList; 
    va_start(argList, format); 

    std::string result; 
    try 
    { 
     result = FormatString(format, argList); 
    } 
    catch(...) 
    { 
     va_end(argList); 
     throw; 
    } 
    va_end(argList); 

    return result; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    int foo = 1234; 
    std::string bar = "BlaBla"; 
    std::cout << FormatString("%i (%s)", foo, bar.c_str()) << std::endl; 
    return 0; 
} 

(是的,我看到了将C格式的字符串管道化为C++ iostream的讽刺意味,这只是测试代码。)

不幸的是,使用VS2008时,它在内核的内部深处崩溃,显然是因为它读取了错误的参数在va_list之外(根据调试器,在va_start之后它指向紧接在“真实”第一参数之前的四字节空序列)。

特别值得注意的是,如果在可变参数函数中我将const std::string& format更改为std::string format(即按值传递),它可以正常工作;当然,如果我将其更改为const char *,也是如此。

这是某种编译器错误,还是不合法使用va_list参考参数?

+0

[是否有使用参考参数的可变参数](http://stackoverflow.com/questions/222195/are-there-gotchas-using-varargs-with-reference-parameters)。 – 2012-02-03 00:12:06

+0

谢谢,这里也有一个很好的解释。我的预查询搜索中没有出现这种耻辱。 :) – Miral 2012-02-03 00:16:43

回答

1

我觉得如果你想传递一个参考,你是不走运的。这是C++ 2011标准在18.10 [support.runtime]第3段中对此主题的说明:

ISO C将第二个参数放在头中的va_start()宏的限制中这个国际标准不同。参数parmN是函数定义的变量参数列表中的最右边参数的标识符(紧接在...之前的那个参数)230如果参数parmN用函数,数组或引用类型声明,或者与类型与传递没有参数的参数时导致的类型不兼容,则行为是未定义的。

+0

谢谢,这就是我怀疑的。虽然我不确定“传递参数时没有参数的结果类型”是什么意思。 – Miral 2012-02-03 00:12:46

+0

我认为这是指您传递的参数没有参数名称,即那些在变量参数列表中传递的参数。由于存在一些类型转换(例如'float'到'double'),这实质上是指您尝试传递某些不能通过可变长度参数列表的情况。 – 2012-02-03 00:24:23

相关问题