C ++格式宏/内联ostringstream

时间:2008-11-19 22:08:08

标签: c++ format printing macros 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-string的地址而无法使用std::string进行编译?不是1和3基本相同吗?

我怀疑C ++ 0x可变参数模板会使format(a, "b", c, d)成为可能。但现在有办法解决这个问题吗?

7 个答案:

答案 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*

或者我们可以使用:

我们无法使用:

  • <击> 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)

为什么不使用函数而不是宏?