需要一个宏来从 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&lt;&lt; 返回一个std::ostream &amp; 嗯。好的。关于如何实现这一目标的任何想法? (使用宏和 我相信在 std::ostream 的某个地方有一个不错的函数,您可以使用 const std::ostream &amp; 重载来打印它。可能是rdbuf,但我忘记了。 【参考方案1】:

您当前的问题是所有各种operator&lt;&lt; 函数都返回ostream&amp;,而不是ostringstream&amp;。你可以用a simple cast解决这个问题:

#define ERR_MSG(inputs) errMsg((static_cast<std::ostringstream&>(std::ostringstream().flush() << inputs)).str())

flush 是必需的,因为 std::ostringstream() 是临时的。因此,您不能在其上调用带有左值引用的函数(即:std::ostream&amp;)。与大多数 operator&lt;&lt; 变体类似的函数。 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&amp; str((static_cast&lt;std::ostringstream&amp;&gt;(os)).str()); ....但这安全吗(与上述问题相同)? 为什么在 是的,从ostream* 转换为ostringstream* 是有效的(就像从ostream&amp; 转换为ostringstream&amp;),只要对象是问题确实是ostringstream.flush() 是宏工作的原因。 我尝试将ostream&amp; 转换为ostringstream&amp;,但最初无法正常工作。我不知道我搞砸了什么。我会修正这个例子。【参考方案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&amp; 类型而不是临时类型,以便可以直接与用户定义的输出运算符一起使用。为此,您应该插入 std::ostream 直接支持的类型之一,它没有任何效果,但返回 std::ostream&amp;,例如:

#define SomeMacro(output) FunctionStream(&someFunction) << "" << output

【讨论】:

我认为这有点不妥。他不想用字符串调用任何函数;他希望能够使用 ERR_MSG 作为函数的参数。也可以有其他函数参数。 @NicolBolas:根据他的例子,他确实想要获得std::string const&amp;!似乎目标是创建可用于删除的宏,例如,基于某些宏定义的调试输出。 我觉得这是一个很好的答案,但它比 Rob's/Nicol's/ChrisDodd's 更复杂一些。【参考方案6】:

将 Nicol 的答案恢复为迄今为止最好的答案:

您当前的问题是所有各种operator&lt;&lt; 函数都返回ostream&amp;,而不是ostringstream&amp;。你可以用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 宏来发送和接收数据

需要一个查询来从所有表/视图中搜索一个值(“数字”)

XCode4的Perforce Checkout Macro [重复]

需要一个查询来从我的 sql 中的表中获取不同的数据

不能写出三个链接的所有结果,而只能写最后一个链接的结果

我是不是需要一个包装类来从 user32.dll 中 p/invoke'ing 一些函数