我正在尝试编写一个允许我执行以下操作的宏: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-string的地址而无法使用std::string
进行编译?不是1和3基本相同吗?
我怀疑C ++ 0x可变参数模板会使format(a, "b", c, d)
成为可能。但现在有办法解决这个问题吗?
答案 0 :(得分:20)
这是我使用的。这一切都适合头文件中的一个整洁的类定义。
更新:感谢litb对代码进行了重大改进。
// makestring.h:
class MakeString
{
public:
std::stringstream stream;
operator std::string() const { return stream.str(); }
template<class T>
MakeString& operator<<(T const& VAR) { stream << VAR; return *this; }
};
以下是它的使用方法:
string myString = MakeString() << a << "b" << c << d;
答案 1 :(得分:19)
你们几乎已经把这个钉死了。但要遵循它有点挑战。那么让我来总结一下你所说的......
这里的困难是:
我们正在玩一个临时的ostringstream
对象,因此禁止使用地址。
因为它是暂时的,我们不能通过强制转换来轻易转换为ostream
对象。
构造函数[明显]和str()
都是类ostringstream
方法。
(是的,我们需要使用.str()
。直接使用ostringstream
对象将最终调用ios::operator void*()
,返回类似指针的好/坏值而不是字符串对象。)
operator<<(...)
作为继承的ostream
方法和全局函数存在。在所有情况下,它都会返回ostream&
引用。
此处ostringstream()<<"foo"
的选择是继承的方法ostream::operator<<(void* )
和全局函数operator<<(ostream&,const char* )
。继承的ostream::operator<<(void* )
胜出,因为我们无法转换为ostream
对象引用来调用全局函数。 [感谢 coppro !]
所以,为了解决这个问题,我们需要:
ostringstream
。ostream
。ostringstream
。str()
。 分配: ostringstream()
。
转换: 有多种选择。其他人建议:
ostringstream() << std::string() // Kudos to *David Norman*
ostringstream() << std::dec // Kudos to *cadabra*
或者我们可以使用:
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. 我们无法使用:
operator<<( ostringstream(), "" )
击> (ostream &) ostringstream()
击> 追加: 现在直截了当。
转换回来: 我们可以使用(ostringstream&)
。但是dynamic_cast
会更安全。如果dynamic_cast
返回NULL
(不应该),则以下.str()
会触发一个coredump。
调用str()
: 猜猜。
全部放在一起。
#define FORMAT(ITEMS) \
( ( dynamic_cast<ostringstream &> ( \
ostringstream() . seekp( 0, ios_base::cur ) << ITEMS ) \
) . str() )
参考文献:
答案 2 :(得分:4)
您遇到的问题与operator << (ostream&, char*)
不是ostream的成员有关,而您的临时ostream实例无法绑定到非const
引用。相反,它选择void*
重载,它是ostream的成员,因此没有这个限制。
最好的(但不是最简单或最优雅的,通过任何想象力!)将使用Boost预处理器来生成大量的函数重载,每个函数都在大量对象上进行模板化(包括已被省略和假设using namespace std;
):
#define MAKE_OUTPUT(z, n, data) \
BOOST_PP_TUPLE_ELEM(2, 0, data) << BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(2, 1, data), n);
#define MAKE_FORMAT(z, n, data) \
template <BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(n), typename T)> \
inline string format(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, BOOST_PP_INC(n), T, p)) \
{ \
ostringstream s; \
BOOST_PP_REPEAT_##z(z, n, MAKE_OUTPUT, (s, p)); \
return s.str(); \
}
它不能保证完全正常工作(没有经过测试就写出来),但这基本上就是这个想法。然后调用BOOST_PP_REPEAT(N, MAKE_FORMAT, ())
来创建一系列函数,这些函数最多需要N个参数,这些参数将根据需要格式化字符串(用选择的整数替换N.较高的值可能会对编译时间产生负面影响)。这应该足够,直到您获得具有可变参数模板的编译器。您应该阅读boost预处理器文档,它具有非常强大的功能,例如此类。 (在调用#undef
调用生成函数之后,您可以随后BOOST_PP_REPEAT
宏
答案 3 :(得分:2)
这是一个像cadabra的答案,它不会影响ostream状态:
#define FORMAT(items) static_cast<std::ostringstream &>((std::ostringstream() << std::string() << items)).str()
我相信coppro的第一段回答描述了为什么事情会这样。
答案 4 :(得分:1)
这是一个有效的解决方案:
#define FORMAT(items) \
((std::ostringstream&)(std::ostringstream() << std::dec << items)).str()
我不太了解第一个参数的行为。
答案 5 :(得分:1)
当我采用mrree的解决方案(标记为“首选”的解决方案,精心解释的解决方案,以及适合G ++的解决方案)时,我遇到了MSVC ++的问题:使用此宏构建的所有字符串都为空。
小时(很多人在这里挠头问我"reloaded"问题)后来,我发现seekp()电话是罪魁祸首。我不确定MSVC ++对此有何不同,但替换
ostringstream().seekp( 0, ios_base::cur )
与cadabra的
ostringstream() << std::dec
也适用于MSVC ++。
答案 6 :(得分:0)
为什么不使用函数而不是宏?