C++11 C++;仅包含std::string的变量参数

C++11 C++;仅包含std::string的变量参数,c++11,stdstring,variadic-functions,printf,C++11,Stdstring,Variadic Functions,Printf,我正在尝试创建一个函数,该函数接受数量可变的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 <<

我正在尝试创建一个函数,该函数接受数量可变的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;
}
应该打印
您好,先生

t.formatLine("Hello %s %s! How %s you today?", "good", "sir", "are");
应该打印
你好,好先生!你今天好吗?

甚至可以将
va_list
vsprintf
仅与
std::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;
}
void测试::formatLine(字符串格式,…)
{
字符缓冲区[256];
va_列表参数;
va_开始(参数,格式);
vsprintf_s(缓冲区,format.c_str(),args);
va_端(args);

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

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


您可以使用
vector
作为缓冲区保持器(很容易调整大小)。问题是,无法从
vsprintf
中获得所需的缓冲区大小。一种方法是分配一些初始缓冲区,然后调用
vsnprintf
(注意“n”)每次函数说缓冲区太小时,重复将缓冲区大小加倍。

生产质量答案

#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; }
#包括
#包括
#包括
//至少需要C++11
常量std::string vFormat(常量std::string sFormat,…){
const char*const zcFormat=sFormat.c_str();
//初始化变量参数数组的使用
va_列表变量;
va_启动(vaArgs,sFormat);
//从的副本可靠地获取大小
//变量参数数组
//和功能可靠的通话
//模仿格式
va_列表vaCopy;
复印件(真空复印件、真空复印件);
const int-iLen=std::vsnprintf(NULL,0,zcopy格式);
va_end(vaCopy);
//返回带格式的字符串,不带
//内存管理不善的风险
//而且不需要任何编译器
//或特定于平台的行为
std::向量zc(iLen+1);
std::vsnprintf(zc.data(),zc.size(),zcFormat,vaArgs);
va_端(vaArgs);
返回std::string(zc.data(),zc.size());}
#包括
#包括
#包括
//使用示范
int main(){
std::time\u t=std::time(nullptr);
INTI1=11;INTI2=22;INTI3=33;
标准:cerr

只允许将普通类型传递给
std::string
不是这样的类型。@Xeo但他在示例中只传递普通类型:)当然,你是对的,如果你将示例更改为
t.formatLine(“您好%s!”,std::string(“先生”);
你的“工作示例”只要生成的字符串可以容纳256个字符,就可以使用。此外,它使用的是Microsoft特定的、不可移植的
vsprintf_s
函数;这可能是您关心的问题,也可能不是您关心的问题。为什么
buf.size()+strlen(format.c_str())
?这没有任何意义。我建议您将
\u TRUNCATE
作为
vsnprintf\u s
的第三个参数传递。当然,如果生成的字符串长度超过256个字符,您仍然会遇到问题。出于好奇,我正在尝试将生成的字符串大小设置为动态-256应该已经足够了。不可移植的函数不是一个问题。你能给我一个在这种情况下如何使用vector的例子吗?我只是不知道如何将它与vsnprintf()一起使用。
vector buf(1024);vsnprintf(&buf[0],buf.size(),format.c_str(),args)
。调整缓冲区大小留给读者作为练习。@phew
vsnprint
是正确的想法,但您不必重复调用它。生成
buffer
a
vector
并调用
vsnprint(&buffer[0],0,format.c_str(),args);
。如果返回值非负,则调用
buffer.resize(retval+1);
然后
vsnprintf(&buffer[0],retval+1,format.c_str(),args)
@Praetorian:有趣。我刚才说,
vsnprintf
如果成功返回写入的字符数,当缓冲区太小时返回-1。我现在检查了C99标准,它说
vsnprintf
返回所需的缓冲区大小,因此可以用来提前测量它。我想er(但我懒得检查)MSVC实现是否不一致,或者文档是否错误。@Praetorian:另外,根据C99,如果第二个参数为0,则第一个参数可能是
NULL
,因此可以执行
vsnprint(NULL,0,format.c_str(),args);
。这消除了执行
&buf[0]的顾虑
在零大小的向量上。此外,不应将相同的
va_列表
值两次传递给两个
vsnprintf
调用;每7.15p3,值在第一次调用后变得不确定。必须再次使用
va_start
或事先使用
va_copy
进行复制。
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;
}
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;
}
#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; }