需要一个宏来从 std::ostringstream 和 << arg 列表创建 std::string

Need a macro to create a std::string from a std::ostringstream and a << arg list

本文关键字:lt std arg 列表 string 创建 ostringstream 一个      更新时间:2023-10-16

我想写一个宏,它的唯一参数是std::ostream&运算符<lt;连接对象,并将合并字符串作为单个std::string对象传递给函数。将合并字符串传递给函数的能力是关键;在下面的示例中,我知道可以通过将宏定义为ERR_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 << "nn";
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() << "nn";
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::字符串,而不管"输入"是什么。很抱歉之前没有提到。

您当前的问题是所有各种operator<<函数都返回ostream&,而不是ostringstream&。你可以用一个简单的演员阵容来解决这个问题:

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

需要flush,因为std::ostringstream()是临时的。因此,您不能在其上调用采用左值引用的函数(即:std::ostream&)。与大多数operator<<变体类似的函数。flush调用所做的只是返回this指针作为左值引用。

如果您愿意使用某些GCC扩展,您可以在块中的宏中声明一个实际的ostringstream,这样就可以在不强制转换的情况下使用.str()方法:
#define ERR_MSG(inputs) 
do { std::ostringstream _s_; _s_<<inputs;errMsg(_s_.str()); } while(false)

演示:http://ideone.com/clone/y56lc

使用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);
#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 << "nn";
return str.length();
}
int main()
{
int i = ERR_MSG("A number: " << 24 << ", a char: " << 'c' << ", that's all!");
std::cout << i << "n";
}

我不会创建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

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

您当前的问题是,所有不同的operator<<函数都返回ostream&,而不是ostringstream&。你可以用一个简单的演员阵容来解决这个问题:

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

当然,这仍然存在问题(就像这里的所有答案一样),比如

ERR_MSG(x ? "x is true" : "x is false")

会以一种奇怪和令人困惑的方式表现不端。