C++ 格式宏/内联 ostringstream
我正在尝试编写一个宏,允许我执行以下操作:FORMAT(a << "b" << c << d)
,结果将是一个字符串——与创建 ostringstream、插入 a...d
并返回 .str()
相同。 类似于:
string f(){
ostringstream o;
o << a << "b" << c << d;
return o.str()
}
本质上,FORMAT(a << "b" << c << d) == f()
。
首先,我尝试过:
1: #define FORMAT(items) \
((std::ostringstream&)(std::ostringstream() << items)).str()
如果第一个项目是 C 字符串 (const char *
),它将以十六进制打印该字符串的地址,接下来的项目将正常打印。 如果第一项是 std::string
,它将无法编译(没有匹配的运算符 <<
)。
这:
2: #define FORMAT(items) \
((std::ostringstream&)(std::ostringstream() << 0 << '\b' << items)).str()
给出了看起来正确的输出,但是 0
和 \b
当然存在于字符串中。
以下似乎可以工作,但编译时会出现警告(获取临时地址):
3: #define FORMAT(items) \
((std::ostringstream&)(*((std::ostream*)(&std::ostringstream())) << items)).str()
有谁知道为什么 1 打印 c 字符串的地址并且无法使用 std::string 进行编译? 1和3本质上不是一样的吗?
我怀疑 C++0x 可变参数模板将使 format(a, "b", c, d)
成为可能。 但现在有办法解决这个问题吗?
I'm trying to write a macro that would allow me to do something like: FORMAT(a << "b" << c << d)
, and the result would be a string -- the same as creating an ostringstream, inserting a...d
, and returning .str()
. Something like:
string f(){
ostringstream o;
o << a << "b" << c << d;
return o.str()
}
Essentially, FORMAT(a << "b" << c << d) == f()
.
First, I tried:
1: #define FORMAT(items) \
((std::ostringstream&)(std::ostringstream() << items)).str()
If the very first item is a C string (const char *
), it will print the address of the string in hex, and the next items will print fine. If the very first item is an std::string
, it will fail to compile (no matching operator <<
).
This:
2: #define FORMAT(items) \
((std::ostringstream&)(std::ostringstream() << 0 << '\b' << items)).str()
gives what seems like the right output, but the 0
and \b
are present in the string of course.
The following seems to work, but compiles with warnings (taking address of temporary):
3: #define FORMAT(items) \
((std::ostringstream&)(*((std::ostream*)(&std::ostringstream())) << items)).str()
Does anyone know why 1 prints the address of the c-string and fails to compile with the std::string
? Aren't 1 and 3 essentially the same?
I suspect that C++0x variadic templates will make format(a, "b", c, d)
possible. But is there a way to solve this now?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
你们都已经很清楚这一点了。 但遵循起来有点困难。 因此,让我尝试总结一下您所说的内容...
这里的困难是:
我们正在使用临时的
ostringstream
对象,因此获取地址是禁忌的。< /p>因为它是临时的,所以我们无法通过转换简单地转换为
ostream
对象。因为它是
构造函数[显然]和
str()
都是类ostringstream
方法。(是的,我们需要使用
.str()
。直接使用ostringstream
对象最终会调用ios::operator void*()
,返回类似指针的好/坏值,而不是字符串对象。)operator<<(...)
既作为继承的ostream
方法也存在全局函数。 在所有情况下,它都会返回一个ostream&
引用。这里的
ostringstream()<<"foo"
的选择是继承方法ostream::operator<<(void* )
和全局函数运算符<<(ostream&,const char*)
。 继承的 ostream::operator<<(void* ) 胜出,因为我们无法转换为ostream
对象引用来调用全局函数。 [向coppro致敬!]因此,为了实现这一目标,我们需要:
ostringstream
。ostream
。ostringstream
。str()
。分配:
ostringstream()
。转换:有多种选择。 其他人建议:
ostringstream() << std::dec // 感谢 *cadabra*
或者我们可以使用:
ostringstream() 。 findp( 0, ios_base::cur )
ostringstream() 。 write( "", 0 )
ostringstream() 。 刷新()
ostringstream() << 刷新
ostringstream() << nounitbuf
ostringstream() << unitbuf
ostringstream() << noshowpos
#include
] 参考:请参阅此网页下方 1/3 处的“插入带格式的数据”。我们不能使用:
运算符<<( ostringstream(), "" )
(ostream &) ostringstream()
附加: 现在就简单了。
转换回来:我们可以只使用
(ostringstream&)
。 但dynamic_cast
会更安全。 万一dynamic_cast
返回NULL
(不应该),下面的.str()
将触发 coredump。调用
str()
:猜猜。把它们放在一起。
参考文献:
ostringstream
ostream::operator<<()
。
You've all pretty much nailed this already. But it's a little challenging to follow. So let me take a stab at summarizing what you've said...
That difficulties here are that:
We are playing with a temporary
ostringstream
object, so taking addresses is contra-indicated.Because it's a temporary, we cannot trivially convert to an
ostream
object through casting.Both the constructor [obviously] and
str()
are classostringstream
methods.(Yes, we need to use
.str()
. Using theostringstream
object directly would wind up invokingios::operator void*()
, returning a pointer-like good/bad value and not a string object.)operator<<(...)
exists as both inheritedostream
methods and global functions. In all cases it returns anostream&
reference.The choices here for
ostringstream()<<"foo"
are the inherited methodostream::operator<<(void* )
and the global functionoperator<<(ostream&,const char* )
. The inheritedostream::operator<<(void* )
wins out because we can't convert to anostream
object reference to invoke the global function. [Kudos to coppro!]So, to pull this off, we need to:
ostringstream
.ostream
.ostringstream
.str()
.Allocating:
ostringstream()
.Converting: There are several choices. Others have suggested:
ostringstream() << std::string() // Kudos to *David Norman*
ostringstream() << std::dec // Kudos to *cadabra*
Or we could use:
ostringstream() . seekp( 0, ios_base::cur )
ostringstream() . write( "", 0 )
ostringstream() . flush()
ostringstream() << flush
ostringstream() << nounitbuf
ostringstream() << unitbuf
ostringstream() << noshowpos
#include <iomanip>
] Reference: See "Insert data with format" 1/3 of the way down on this webpage.We cannot use:
operator<<( ostringstream(), "" )
(ostream &) ostringstream()
Appending: Straightforward now.
Converting back: We could just use
(ostringstream&)
. But adynamic_cast
would be safer. In the unlikely eventdynamic_cast
returnedNULL
(it shouldn't), the following.str()
will trigger a coredump.Invoking
str()
: Guess.Putting it all together.
References:
ostringstream
ostream::operator<<()
.
这是我使用的。 这一切都适合头文件中一个整洁的类定义。
更新:由于litb,对代码进行了重大改进。
下面是它的使用方法:
Here is what I use. It all fits into one tidy class definition in a header file.
update: major improvement to the code thanks to litb.
Here is how it is used:
您遇到的问题与
operator << (ostream&, char*)
不是 ostream 的成员,并且您的临时 ostream 实例无法绑定到非const
引用。 相反,它选择了void*
重载,它是 ostream 的成员,因此没有该限制。最好的(但不是最简单或最优雅的,无论如何想象!)是使用 Boost 预处理器生成大量函数重载,每个函数重载都在大量对象上进行模板化(包括已被省略并假设
using namespace std;
):不能保证它能准确工作(未经测试就编写了它),但这基本上就是这个想法。 然后,您可以调用
BOOST_PP_REPEAT(N, MAKE_FORMAT, ())
创建一系列最多包含 N 个参数的函数,这些函数将根据您的需要格式化字符串(将 N 替换为选择的整数。更高的值可能会对编译时间产生负面影响)。 在您获得带有可变参数模板的编译器之前,这应该足够了。 您应该阅读 boost 预处理器文档,它对于此类事情具有非常强大的功能。 (在调用BOOST_PP_REPEAT
调用生成函数后,您可以随后#undef
宏)The problem you are having is related to the fact that
operator << (ostream&, char*)
is not a member of ostream, and your temporary ostream instance cannot bind to a non-const
reference. Instead, it picks thevoid*
overload, which is a member of ostream, and thus doesn't have that restriction.The best (but not easiest or most elegant, by any stretch of imagination!) would be to use the Boost Preprocessor to generate a large number of function overloads, each templated on a large number of objects (includes have been omitted and assuming
using namespace std;
):It's not guaranteed to work exactly (wrote it without testing), but that's basically the idea. You then call
BOOST_PP_REPEAT(N, MAKE_FORMAT, ())
to create a series of functions taking up to N parameters that will format your string as you want to (replace N with the integer of choice. Higher values may negatively affect compile times). This should suffice until you get a compiler with variadic templates. You should read the boost preprocessor documentation, it has very powerful features for things like this. (you can subsequently#undef
the macros, after calling theBOOST_PP_REPEAT
invocation to generate the functions)这是一个像 cadabra 一样的答案,不会干扰 ostream 状态:
我相信 coppro 答案的第一段描述了为什么事情会这样。
Here's an answer like cadabra's that doesn't mess with the ostream state:
I believe the first paragraph of coppro's answer describes why things behave this way.
这是一个可行的解决方案:
我不太理解第一个参数的行为。
Here's a working solution:
I don't quite understand the behavior of the first argument.
当我采用 mrree 的解决方案(标记为“首选”的解决方案、解释精美的解决方案以及非常适合 G++ 的解决方案)时,我遇到了 MSVC++ 的问题:使用此宏构建的所有字符串最终都为空。
几个小时后(我花了很多时间挠头并在这里问一个“重新加载”问题),我发现seekp()调用是罪魁祸首。 我不确定 MSVC++ 的做法有何不同,但
用cadabra 的
MSVC++ 的作品替换也是如此。
When I took mrree's solution (the one marked "preferred", the one beautifully explained, and the one working perfectly for G++), I ran into problems with MSVC++: All strings built with this macro ended up empty.
Hours (and lots of scratching my head and asking a "reloaded" question here) later, I found out that the seekp() call was the culprit. I am not sure what MSVC++ does differently with that, but replacing
with cadabra's
works for MSVC++, too.
为什么不直接使用函数而不是宏呢?
Why not just use a function instead of a macro?