【问题标题】:C++ variable arguments with std::string only仅带有 std::string 的 C++ 变量参数
【发布时间】:2013-09-25 15:29:53
【问题描述】:

我正在尝试创建一个函数,它接受可变数量的 std::string 参数并用它格式化字符串。

例子:

Test::formatLine(const string::format, ...)
{
    const std::string buffer;
va_list args;
va_start(args, format);
vsprintf(buffer.c_str, format.c_str, args);
va_end(args);
cout << buffer << endl;
}

编译这个sn-p错误:

Error   1   error C3867: 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str': function call missing argument list; use '&std::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str' to create a pointer to member

我想要达到的目标:

Test t = Test();
t.formatLine("Hello %s!", "monsieur");

应该打印Hello monsieur!

t.formatLine("Hello %s %s! How %s you today?", "good", "sir", "are");

应该打印Hello good sir! How are you today?

是否可以仅将va_listvsprintfstd::string 一起使用,避免使用char buffer[size]

Igor 建议修复的工作示例(到目前为止),使用缓冲区:

void Test::formatLine(string format, ...)
{
    char buffer[256];
    va_list args;
    va_start(args, format);
    vsprintf_s(buffer, format.c_str(), args);
    va_end(args);
    cout << buffer << endl;
}

使用 Igor Tandetnik 的建议和示例代码,我终于得到了一个不使用 char buffer[size] 的工作示例:

void Test::formatLine(string format, ...)
{
    vector<char> buf(256);
    va_list args;
    va_start(args, format);
    vsnprintf_s(&buf[0], buf.size(), buf.size() + strlen(format.c_str()), format.c_str(), args);
    va_end(args);
    cout << &buf[0] << endl;
}

【问题讨论】:

  • 只允许将琐碎的类型传递给...std::string 没有这种类型。
  • @Xeo 但是他在他的例子中只传递了一些琐碎的类型 :) 当然,你是对的,如果你把例子改成t.formatLine("Hello %s!", std::string("monsieur"));,这将非常失败
  • 只要生成的字符串适合 256 个字符,您的“工作示例”就很好。此外,它使用的是 Microsoft 特定的、不可移植的 vsprintf_s 函数;这可能对您来说是一个问题,也可能不是。
  • 为什么是buf.size() + strlen(format.c_str())?这没有任何意义。我建议你将_TRUNCATE 作为vsnprintf_s 的第三个参数传递。当然,如果结果字符串长于 256 个字符,您仍然会遇到问题。
  • 我正在尝试使生成的字符串大小动态化——出于好奇——现在 256 对我来说应该绰绰有余了。不可移植的功能不是问题。

标签: c++11 stdstring variadic-functions printf


【解决方案1】:

生产质量答案

#include <cstdarg>
#include <string>
#include <vector>

// requires at least C++11
const std::string vFormat(const std::string sFormat, ...) {

    const char * const zcFormat = sFormat.c_str();

    // initialize use of the variable argument array
    va_list vaArgs;
    va_start(vaArgs, sFormat);

    // reliably acquire the size from a copy of
    // the variable argument array
    // and a functionally reliable call
    // to mock the formatting
    va_list vaCopy;
    va_copy(vaCopy, vaArgs);
    const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaCopy);
    va_end(vaCopy);

    // return a formatted string without
    // risking memory mismanagement
    // and without assuming any compiler
    // or platform specific behavior
    std::vector<char> zc(iLen + 1);
    std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
    va_end(vaArgs);
    return std::string(zc.data(), zc.size()); } 

#include <ctime>
#include <iostream>
#include <iomanip>

// demonstration of use
int main() { 

    std::time_t t = std::time(nullptr);
    int i1 = 11; int i2 = 22; int i3 = 33;
    std::cerr
        << std::put_time(std::localtime(& t), "%D %T")
        << vFormat(" [%s]: %s {i1=%d, i2=%d, i3=%d}",
                "DEBUG",
                "Xyz failed",
                i1, i2, i3)
        << std::endl;
    return 0; }

【讨论】:

    【解决方案2】:

    首先是buffer.c_str()format.c_str()(注意括号)。

    第二,vsprintf的第一个参数应该是一个足够大的可修改缓冲区。您正在尝试传递一个 const char* 指向一个只有一个字节大的缓冲区。

    您可以使用vector&lt;char&gt; 作为缓冲区持有人(它很容易调整大小)。问题是,无法从vsprintf 中获得所需的缓冲区大小。一种技术是分配一些初始缓冲区,然后重复调用vsnprintf(注意“n”),每次函数说缓冲区太小时,缓冲区的大小都会加倍。

    【讨论】:

    • 你能给我一个例子来说明如何在这种情况下使用 vector 吗?我只是不知道如何将它与 vsnprintf() 一起使用。
    • vector&lt;char&gt; buf(1024); vsnprintf(&amp;buf[0], buf.size(), format.c_str(), args);。调整缓冲区大小留给读者练习。
    • @phew vsnprintf 是正确的想法,但您不应该重复调用它。将buffer 设为vector&lt;char&gt; 并致电vsnprint(&amp;buffer[0], 0, format.c_str(), args);。如果返回值非负,则调用buffer.resize(retval+1);,然后调用vsnprintf(&amp;buffer[0], retval+1, format.c_str(), args);
    • @Praetorian:很有趣。我去的是MSDN documentation,它指出vsnprintf 如果成功则返回写入的字符数,如果缓冲区太小,则返回-1。我现在检查了 C99 标准,它说vsnprintf 返回所需的缓冲区大小,因此可以用来提前测量它。我想知道(但懒得检查)MSVC 实现是否不符合标准,或者文档是否错误。
    • @Praetorian:此外,根据 C99,如果第二个参数为 0,则第一个参数可能是 NULL,因此可以使用 vsnprint(NULL, 0, format.c_str(), args);。这消除了对零大小向量执行&amp;buf[0] 的担忧。此外,不应将相同的va_list 值两次传递给两个vsnprintf 调用;根据 7.15p3,第一次调用后该值变得不确定。必须再次使用va_start,否则请事先使用va_copy 进行复制。
    猜你喜欢
    • 2015-12-25
    • 1970-01-01
    • 1970-01-01
    • 2022-06-29
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多