之前的一个问题显示了打印到字符串的好方法.答案涉及va_copy:
std::string format (const char *fmt, ...); { va_list ap; va_start (ap, fmt); std::string buf = vformat (fmt, ap); va_end (ap); return buf; } std::string vformat (const char *fmt, va_list ap) { // Allocate a buffer on the stack that's big enough for us almost // all the time. s ize_t size = 1024; char buf[size]; // Try to vsnprintf into our buffer. va_list apcopy; va_copy (apcopy, ap); int needed = vsnprintf (&buf[0], size, fmt, ap); if (needed <= size) { // It fit fine the first time, we're done. return std::string (&buf[0]); } else { // vsnprintf reported that it wanted to write more characters // than we allotted. So do a malloc of the right size and try again. // This doesn't happen very often if we chose our initial size // well. std::vectorbuf; size = needed; buf.resize (size); needed = vsnprintf (&buf[0], size, fmt, apcopy); return std::string (&buf[0]); }
}
我遇到的问题是上面的代码没有移植到Visual C++,因为它不提供va_copy(甚至__va_copy).那么,有谁知道如何安全地移植上面的代码?据推测,我需要做一个va_copy副本,因为vsnprintf破坏性地修改了传递的va_list.
你应该能够通过定期任务逃脱:
va_list apcopy = ap;
它在技术上是不可移植和未定义的行为,但它适用于大多数编译器和体系结构.在x86调用约定中,va_list
s只是指向堆栈的指针,可以安全地复制.
对于Windows,您可以自己定义va_copy:
#define va_copy(dest, src) (dest = src)
您可以做的一件事是,如果您不需要该vformat()
函数,请将其实现移动到format()
函数中(未经测试):
#include#include #include #include #include std::string format(const char *fmt, ...) { va_list ap; enum {size = 1024}; // if you want a buffer on the stack for the 99% of the time case // for efficiency or whatever), I suggest something like // STLSoft's auto_buffer<> template. // // http://www.synesis.com.au/software/stlsoft/doc-1.9/classstlsoft_1_1auto__buffer.html // std::vector buf( size); // // where you get a proper vsnprintf() for MSVC is another problem // maybe look at http://www.jhweiss.de/software/snprintf.html // // note that vsnprintf() might use the passed ap with the // va_arg() macro. This would invalidate ap here, so we // we va_end() it here, and have to redo the va_start() // if we want to use it again. From the C standard: // // The object ap may be passed as an argument to // another function; if that function invokes the // va_arg macro with parameter ap, the value of ap // in the calling function is indeterminate and // shall be passed to the va_end macro prior to // any further reference to ap. // // Thanks to Rob Kennedy for pointing that out. // va_start (ap, fmt); int needed = vsnprintf (&buf[0], buf.size(), fmt, ap); va_end( ap); if (needed >= size) { // vsnprintf reported that it wanted to write more characters // than we allotted. So do a malloc of the right size and try again. // This doesn't happen very often if we chose our initial size // well. buf.resize( needed + 1); va_start (ap, fmt); needed = vsnprintf (&buf[0], buf.size(), fmt, ap); va_end( ap); assert( needed < buf.size()); } return std::string( &buf[0]); }