需要一个宏来从 std::ostringstream 和 << arg 列表创建 std::string
Posted
技术标签:
【中文标题】需要一个宏来从 std::ostringstream 和 << arg 列表创建 std::string【英文标题】:Need a macro to create a std::string from a std::ostringstream and a << arg list 【发布时间】:2012-09-28 21:26:48 【问题描述】:我想编写一个宏,它将 std::ostream& operatorERR_MSG(inputs) std::cout << "ERROR: " << inputs 来重写以工作,但是将输出发送到 std::cout 不是目标,它只是我为示例选择的测试目标.
我正在使用 GCC 4.1.2 (Red Hat 4.1.2-52) 并且升级它不是一个选项。这是我尝试过的一个非常简化的版本:
#include <sstream>
#include <iostream>
#define ERR_MSG(inputs) errMsg(std::ostringstream().str()) // 1
#define ERR_MSG(inputs) errMsg((std::ostringstream()<<inputs).str()) // 2
<aReturnType> errMsg(const std::string& msg) // use with 1 & 2
std::cout << "\nERROR: " << msg << "\n\n";
return <someObjectCreatedBasedOnTheInput>;
#define ERR_MSG(inputs) errMsg(std::ostringstream()<<inputs) // 3
<aReturnType> errMsg(const std::ostringstream& msg) // use with 3
std::cout << "\nERROR: " << msg.str() << "\n\n";
return <someObjectCreatedBasedOnTheInput>;
int main()
ERR_MSG("A number: " << 24 << ", a char: " << 'c' << ", that's all!");
宏#1 编译,但当然只打印消息的“”。宏 2 和 3 均无法编译,并出现以下错误:
#define ERR_MSG(inputs) errMsg((std::ostringstream()<<inputs).str()) // 2
error: ‘struct std::basic_ostream<char, std::char_traits<char> >’ has no member named ‘str’
#define ERR_MSG(inputs) errMsg(std::ostringstream()<<inputs) // 3
no matching function for call to ‘errMsg(std::basic_ostream<char, std::char_traits<char> >&)’
note: candidates are: char* errMsg(const std::string&)
note: char* errMsg(const std::ostringstream&)
我不对如何在没有宏的情况下重写它感兴趣;我自己可以很容易地做到这一点。
=== 更新:=== 我忘了提到,在它的实际用例中,宏调用的函数返回一个对象,宏的调用者可能会使用该对象。这会使无法在单个表达式中实现的任何宏实现无效,其结果是宏调用的函数的返回类型。宏的“什么都不做”实现(用于发布版本)将简单地将空的 std::string 传递给函数,而不管“输入”是什么。很抱歉之前没有提及。
【问题讨论】:
std::ostringstream::operator<<
返回一个std::ostream &
。
嗯。好的。关于如何实现这一目标的任何想法? (使用宏和
我相信在 std::ostream
的某个地方有一个不错的函数,您可以使用 const std::ostream &
重载来打印它。可能是rdbuf
,但我忘记了。
【参考方案1】:
您当前的问题是所有各种operator<<
函数都返回ostream&
,而不是ostringstream&
。你可以用a simple cast解决这个问题:
#define ERR_MSG(inputs) errMsg((static_cast<std::ostringstream&>(std::ostringstream().flush() << inputs)).str())
flush
是必需的,因为 std::ostringstream()
是临时的。因此,您不能在其上调用带有左值引用的函数(即:std::ostream&
)。与大多数 operator<<
变体类似的函数。 flush
调用所做的只是将 this
指针作为左值引用返回。
【讨论】:
"...您不能在其上调用带有左值引用的函数... ...flush
调用所做的只是将this
指针作为左值引用返回。 "嗯。我希望有一天能明白这意味着什么。但似乎如果这就是 flush() 所做的一切,那么同样的事情应该可以通过某种类型的 cast+dereference 组合来完成?
@phonetagger:也许,但flush
更短。 ;)
完成这项工作有两个关键点,首先是从 ostream& 到 ostringstream& 的“简单转换”,其次是 .flush() 调用,它是由“Rob”提供的,带有有趣的角色在他的名字的末尾(在我的浏览器使用的任何字符集中都显示为一个框)。没有这两个,宏就不起作用。 Nicol 最初首先发布了第 1 点,然后 Rob 使用 .flush() 发布,这最终使整个事情顺利进行。我希望我能在你们俩之间分开接受,但是由于 Nicol 向我解释了为什么需要 flush(),所以我会选择这个答案。【参考方案2】:
如果你愿意使用一些GCC extension,你可以在一个块中的宏内声明一个实际的ostringstream,这样.str()方法就可以在不强制转换的情况下使用: p>
#define ERR_MSG(inputs) \
do std::ostringstream _s_; _s_<<inputs;errMsg(_s_.str()); while(false)
演示:http://ideone.com/clone/y56lc
【讨论】:
只要去掉那些外部括号,它就可以在任何编译器上工作! :) 然后你只是定义一个常规块。外部范围是否包含名为_s_
的东西并不重要,因为_s_
的查找总是会找到最里面声明的(可能除非奇怪的Koenig 查找极端情况); _s_
对象在结束时被销毁并清理
!
@j_random_hacker 注意大小写:int _s_ = 7; ERR_MSG(__s__)
。除了7
,您将一无所获。隐藏外部名称并不总是一件好事。如果没有这个 gcc 扩展,这将无法编译:if (true) ERR_MSG(7); else ERR_MSG(6);
。请参阅我的便携式解决方案答案。
@j_random_hacker。嗯对。不知何故,我想不必要地将宏保留为表达式,尽管语句很好。
@PiotrNycz:我很困惑你为什么将ERR_MSG(__s__)
中的下划线加倍。错字?如果您只是说ERR_MSG(_s_)
,那么您说得很好。
@j_random_hacker 当然是错字,只是并行做一些其他的事情,而不是很好地计算下划线;)【参考方案3】:
使用do while (false)
成语将几行宏化。
#define ERR_MSG(inputs) \
do \
std::ostringstream osERR_MSG; \
osERR_MSG << inputs; \
errMsg(osERR_MSG.str()); \
while (false)
int main()
if (1) ERR_MSG("A number: " << 24 << ", a char: " << 'c' << ", that's all!");
else return 0;
我之所以取这个奇怪的名字osERR_MSG
,是为了尽量避免这样的情况:
int osERR_MSG = 7;
ERR_MSG(osERR_MSG);
【讨论】:
再次:您定义一个名为os___
(三个下划线)的变量,然后调用errMsg(os__.str())
(两个下划线)!【参考方案4】:
#include <sstream>
#include <iostream>
#define ERR_MSG(inputs) errMsg(std::ostringstream().flush()<<inputs)
int errMsg(std::ostream& os)
std::ostringstream& oss(static_cast<std::ostringstream&>(os));
const std::string& str(oss.str());
std::cout << "\nERROR: " << str << "\n\n";
return str.length();
int main()
int i = ERR_MSG("A number: " << 24 << ", a char: " << 'c' << ", that's all!");
std::cout << i << "\n";
【讨论】:
Uhm.... 这似乎可行,但是将 os 的地址转换为 std::ostringstream 指针并取消引用以初始化 oss 引用变量安全吗?不仅仅是 GCC? 这似乎也行得通...const std::string& str((static_cast<std::ostringstream&>(os)).str());
....但这安全吗(与上述问题相同)?
为什么在
是的,从ostream*
转换为ostringstream*
是有效的(就像从ostream&
转换为ostringstream&
),只要对象是问题确实是ostringstream
。 .flush()
是宏工作的原因。
我尝试将ostream&
转换为ostringstream&
,但最初无法正常工作。我不知道我搞砸了什么。我会修正这个例子。【参考方案5】:
我不会创建std::ostringstream
,而是使用从std::ostream
派生的类的析构函数调用的函数。这是这种方法的一个示例:
#include <sstream>
#include <iostream>
void someFunction(std::string const& value)
std::cout << "someFunction(" << value << ")\n";
void method(std::string const& value)
std::cout << "method(" << value << ")\n";
class FunctionStream
: private virtual std::stringbuf
, public std::ostream
public:
FunctionStream()
: std::ostream(this)
, d_function(&method)
FunctionStream(void (*function)(std::string const&))
: std::ostream(this)
, d_function(function)
~FunctionStream()
this->d_function(this->str());
private:
void (*d_function)(std::string const&);
;
int main(int ac, char* av[])
FunctionStream() << "Hello, world: " << ac;
FunctionStream(&someFunction) << "Goodbye, world: " << ac;
示例使用不使用宏,但这可以很容易地围绕上述FunctionStream()
的使用。请注意,在宏中,您可能希望确保宏用户看到的类型是std::ostream&
类型而不是临时类型,以便可以直接与用户定义的输出运算符一起使用。为此,您应该插入 std::ostream
直接支持的类型之一,它没有任何效果,但返回 std::ostream&
,例如:
#define SomeMacro(output) FunctionStream(&someFunction) << "" << output
【讨论】:
我认为这有点不妥。他不想用字符串调用任何函数;他希望能够使用 ERR_MSG 作为函数的参数。也可以有其他函数参数。 @NicolBolas:根据他的例子,他确实想要获得std::string const&
!似乎目标是创建可用于删除的宏,例如,基于某些宏定义的调试输出。
我觉得这是一个很好的答案,但它比 Rob's/Nicol's/ChrisDodd's 更复杂一些。【参考方案6】:
将 Nicol 的答案恢复为迄今为止最好的答案:
您当前的问题是所有各种operator<<
函数都返回ostream&
,而不是ostringstream&
。你可以用a simple cast解决这个问题:
#define ERR_MSG(inputs) errMsg((static_cast<std::ostringstream&>(std::ostringstream().flush() << inputs)).str())
当然,这仍然存在类似问题(就像这里的所有答案一样)
ERR_MSG(x ? "x is true" : "x is false")
会以一种奇怪而令人困惑的方式行为不端。
【讨论】:
这是一个很好的答案,除了 它不起作用。这就是我删除它的原因。它将第一个字符串打印为指针而不是char*
。
请注意我在 Rob 帖子下方的评论。如果您在 std::ostringstream() 和
回复:“...这仍然存在问题 (...),像 ERR_MSG(x ? "x is true" : "x is false")
这样的东西会以一种奇怪而令人困惑的方式出现错误。”:它甚至无法为我编译。但是,如果我将三元组括在括号中,它会编译,并且会输出预期的结果。
@NicolBolas - 是的,完全正确。我希望有人能解释一下 .flush() 到底做了什么使它起作用以及为什么省略它不起作用。
@phonetagger:我恢复了我的回答,解释了为什么需要flush
。以上是关于需要一个宏来从 std::ostringstream 和 << arg 列表创建 std::string的主要内容,如果未能解决你的问题,请参考以下文章
如何在 libreoffice calc 中编写 python 宏来发送和接收数据