如何在一个在C ++中接收可变长度数组的函数中传递一个可变长度数组?

时间:2015-02-20 10:58:29

标签: c++ arrays

我找到了一种在C ++中传递可变长度数组的方法。但它失败了'包装'功能在下面的代码中。其实我想在我的项目中包装格式化功能。 我的代码中我做错了什么?

测试代码

#include <iostream>
#include <stdarg.h>
#include <string>

void log(const char* format, ...)
{
    va_list argptr;
    va_start(argptr, format);

    int length = _vscprintf(format, argptr);
    char* buf_ = new char [length + 1];
    int ret = vsnprintf(buf_, 1000, format, argptr);

    if (ret >= 0) {
        std::cout << buf_ << std::endl;
    }

    delete[] buf_;
    va_end(argptr);
}

void wrap(const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    log(format, ap);
    va_end(ap);
}


int main()
{
    log( "direct = %d", 1);
    wrap("wrap   = %d", 1);

    return 0;
}

结果就在这里。

direct = 1
wrap   = 15137088 // what's happen?

3 个答案:

答案 0 :(得分:1)

  

我找到了一种在C ++中传递可变长度数组的方法

这不是一个可变长度的数组,它不是真正惯用的C ++。 ...是一个可变长度参数列表,可在C中使用。

包装日志函数的最简单合理的方法是可变参数模板,它可以简单地写成:

template <typename... Args>
void wrap(const char *format, Args&&... args) {
    log(format, std::forward<Args>(args)...);
}

在日志函数本身中,vsnprintf返回将被写入的字节数,如果它填充缓冲区的话。因此,您始终可以使用乐观缓冲区大小调用一次,并在必要时增大缓冲区:您不需要非标准_vscprintf。这看起来像是:

void log(const char* format, ...)
{
    va_list argptr;
    va_start(argptr, format);

    static const size_t DefaultSize = 200;
    // pick some value that makes sense ^^ here
    char buf[DefaultSize];
    int rv = vsnprintf(buf, DefaultSize, format, argptr);
    if (rv < 0) {
        // we can't return errors with this prototype:
        // should it throw?
        return;
    }
    if (rv >= DefaultSize) {
        vector<char> dynbuf(rv+1);
        rv = vsnprintf(&dynbuf[0], dynbuf.size(), format, argptr);
        std::cout << &dynbuf[0] << std::endl;
    } else {
        std::cout << buf << std::endl;
    }

    va_end(argptr);
}

另请注意wrap知道其所有参数的类型,但在调用C风格的可变参数函数log时,该信息将被丢弃。您可以将Boost.Format视为类型安全的替代方案 - 作为奖励,它将为您管理缓冲区。

答案 1 :(得分:0)

传递一个va_list,其中预期可变数量的争论(x,y,z)是不可行的。

为了实现你想要的,你需要做这样的事情:

void log_args(const char* format, va_list& argptr)
{
    // I'm unsure on this... you may possibly need to make a separate
    // copy of the va_list to pass in to each of _vscprintf and vsnprintf.  
    va_list second;
    va_copy(second, argptr);

    int length = _vscprintf(format, argptr);
    char* buf_ = new char [length + 1];

    int ret = vsnprintf(buf_, 1000, format, second);

    if (ret >= 0) {
        std::cout << buf_ << std::endl;
    }

    delete[] buf_;
}

void log(const char* format, ...)
{
    va_list argptr;
    va_start(argptr, format);    
    log_args(argptr);
    va_end(argptr);
}

void wrap(const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    log_args(format, ap);
    va_end(ap);
}

在这个例子中,'wrap'和'log'看起来是一样的...但我认为你想在你的真正的包装函数中做一些额外的事情,否则你为什么要问这个问题。

答案 2 :(得分:0)

在c ++ 11中,您可以使用variadic templates

#include <iostream>

void tprintf(const char* format) // base function
{
    std::cout << format;
}

template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
{
    for ( ; *format != '\0'; format++ ) {
        if ( *format == '%' ) {
           std::cout << value;
           tprintf(format+1, Fargs...); // recursive call
           return;
        }
        std::cout << *format;
    }
}

int main()
{
    tprintf("direct = %\n", 1);
    tprintf("wrap   = %\n", 1);
    return 0;
}