我正在使用一个用C++实现的开源UNIX工具,我需要更改一些代码才能让它做我想做的事情.我想做出尽可能小的改变,希望上传接受我的补丁.可以在标准C++中实现并且不创建更多外部依赖性的解决方案是首选.
这是我的问题.我有一个C++类 - 我们称之为"A" - 目前使用fprintf()将其格式严格的数据结构打印到文件指针.在其print函数中,它还递归调用几个成员类的相同定义的打印函数("B"是一个例子).还有另一个C类,它有一个成员std :: string"foo",需要设置为A实例的print()结果.把它想象成A的to_str()成员函数.
在伪代码中:
class A { public: ... void print(FILE* f); B b; ... }; ... void A::print(FILE *f) { std::string s = "stuff"; fprintf(f, "some %s", s); b.print(f); } class C { ... std::string foo; bool set_foo(std::str); ... } ... A a = new A(); C c = new C(); ... // wish i knew how to write A's to_str() c.set_foo(a.to_str());
我应该提到C是相当稳定的,但是A和B(以及A的其余部分)处于不稳定的状态,因此所需的代码变化越少越好.还需要保留当前的打印(FILE*F)界面.我已经考虑了几种实现A :: to_str()的方法,每种方法都有优点和缺点:
将对fprintf()的调用更改为sprintf()
我不必重写任何格式字符串
print()可以重新实现为:fprint(f,this.to_str());
但是我需要手动分配char [] s,合并很多c字符串,最后将字符数组转换为std :: string
尝试在字符串流中捕获a.print()的结果
我必须将所有格式字符串转换为<<输出格式.有数百个fprintf()要转换: - {
print()必须重写,因为我不知道从UNIX文件句柄创建输出流的标准方法(尽管这个人说它可能是可能的).
使用Boost的字符串格式库
更多外部依赖项.呸.
格式的语法与printf()的不同之处在于令人烦恼:
printf(format_str,args) - > cout << boost :: format(format_str)%arg1%arg2%etc等
使用Qt的QString :: asprintf()
不同的外部依赖.
那么,我已经用尽了所有可能的选择吗?如果是这样,你认为哪个是我最好的选择?如果没有,我忽略了什么?
谢谢.
这是我喜欢的功能,它使功能与'sprintf'相同,但返回一个std :: string,并且不受缓冲区溢出问题的影响.这段代码是我正在编写的开源项目的一部分(BSD许可证),所以每个人都可以随意使用它.
#include#include #include #include 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. size_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); // NB. On Windows, vsnprintf returns -1 if the string didn't fit the // buffer. On Linux & OSX, it returns the length it would have needed. if (needed <= size && needed >= 0) { // 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::vector buf; size = needed; buf.resize (size); needed = vsnprintf (&buf[0], size, fmt, apcopy); return std::string (&buf[0]); } }
编辑:当我编写这段代码时,我不知道这需要C99一致性,并且Windows(以及较旧的glibc)具有不同的vsnprintf行为,其中它返回-1表示失败,而不是确定多少空间的度量需要.这是我修改后的代码,大家可以仔细查看一下,如果你认为没问题,我会再次编辑,以便列出唯一的费用:
std::string Strutil::vformat (const char *fmt, va_list ap) { // Allocate a buffer on the stack that's big enough for us almost // all the time. Be prepared to allocate dynamically if it doesn't fit. size_t size = 1024; char stackbuf[1024]; std::vectordynamicbuf; char *buf = &stackbuf[0]; va_list ap_copy; while (1) { // Try to vsnprintf into our buffer. va_copy(ap_copy, ap); int needed = vsnprintf (buf, size, fmt, ap); va_end(ap_copy); // NB. C99 (which modern Linux and OS X follow) says vsnprintf // failure returns the length it would have needed. But older // glibc and current Windows return -1 for failure, i.e., not // telling us how much was needed. if (needed <= (int)size && needed >= 0) { // It fit fine so we're done. return std::string (buf, (size_t) needed); } // vsnprintf reported that it wanted to write more characters // than we allotted. So try again using a dynamic buffer. This // doesn't happen very often if we chose our initial size well. size = (needed > 0) ? (needed+1) : (size*2); dynamicbuf.resize (size); buf = &dynamicbuf[0]; } }
我正在使用#3:提升字符串格式库 - 但我不得不承认我对格式规范的差异从来没有任何问题.
对我来说就像一个魅力 - 外部依赖可能更糟(一个非常稳定的库)
编辑:添加一个例子如何使用boost :: format而不是printf:
sprintf(buffer, "This is a string with some %s and %d numbers", "strings", 42);
使用boost :: format库会是这样的:
string = boost::str(boost::format("This is a string with some %s and %d numbers") %"strings" %42);
希望这有助于澄清boost :: format的用法
我在4或5个应用程序中使用boost :: format作为sprintf/printf替换(将格式化的字符串写入文件,或将自定义输出写入日志文件),并且从未遇到格式差异问题.可能有一些(或多或少模糊)格式说明符不同 - 但我从来没有遇到过问题.
相比之下,我有一些格式规范,我不能真正用流(我记得)