c ++ stringstream to ostream to string

时间:2011-11-02 10:59:26

标签: c++ stl

我希望能够做到:

foo(stringstream()<<"number = " << 500);

编辑:单线解决方案至关重要,因为这是用于记录目的。这些将围绕代码。

在foo里面会将字符串打印到屏幕或类似的东西。

现在因为stringstream的运算符&lt;&lt;返回ostream&amp;,foo的签名必须是:

foo(ostream& o);

但是如何转换ostream&amp;串? (或char *)。 实现此用例的不同方法也是受欢迎的。

8 个答案:

答案 0 :(得分:5)

显而易见的解决方案是在dynamic_cast中使用foo。但是给定的 代码仍然无法正常工作。 (你的例子将编译,但它不会做什么 你认为它应该。)表达式std::ostringstream()是一个 临时的,你不能用临时的初始化非const引用, 和std::operator<<( std::ostream&, char const*)的第一个参数 是一个非const引用。 (你可以调用一个成员函数 临时。像std::ostream::operator<<( void const* )一样。所以代码 将编译,但它不会做你期望的。

您可以使用以下内容解决此问题:

foo( std::ostringstream().flush() << "number = " << 500 );

std::ostream::flush()返回非const引用,因此没有 进一步的问题在新创建的流上,它是一个无操作。 不过,我认为你会同意它不是最优雅或最直观的 溶液

在这种情况下我通常做的是创建一个包装类 包含它自己的std::ostringstream,并提供模板化 成员 operator<<转发给所包含的人 std::ostringstream。您的函数foo将占用const 引用这个 - 或者我做的是有析构函数调用 直接foo,以便客户端代码甚至不必担心 它;它的确如下:

log() << "number = " << 500;

函数log()返回包装类的实例(但请参阅 下面),这个类的(最终)析构函数调用你的函数 foo

这有一个小问题。可以复制返回值, 复制后立即销毁。哪个会破坏大灾难 我刚刚解释过的;事实上,因为std::ostringstream不是 可复制,甚至不会编译。这里的解决方案是把所有的 实际逻辑,包括std::ostringstream的实例和。{ 析构函数逻辑在一个单独的实现类中调用foo 公共包装器有一个boost::shared_ptr和向前。要么 只需重新实现类中的一些共享指针逻辑:

class LogWrapper
{
    std::ostringstream* collector;
    int* useCount;
public:
    LogWrapper()
        : collector(new std::ostringstream)
        , useCount(new int(1))
    {
    }

    ~LogWrapper()
    {
        -- *useCount;
        if ( *useCount == 0 ) {
            foo( collector->str() );
            delete collector;
            delete useCount;
        }
    }

    template<typename T>
    LogWrapper& operator<<( T const& value )
    {
        (*collector) << value;
        return *this;
    }
};

请注意,扩展它以便支持可选日志记录很容易;只是 为LogWrapper提供一个构造函数,用于将collector设置为 NULL,并在operator<<

中对此进行测试

编辑:

另一件事发生在我身上:你可能想检查一下 析构函数是由异常调用而不是调用 在这种情况下foo。从逻辑上讲,我希望你唯一的例外 可能得到的是std::bad_alloc,但总会有一个用户 写道:

log() << a + b;

其中+是用户定义的重载抛出。

答案 1 :(得分:3)

您可以使用代理对象;这是一个框架,但如果你想在很多地方使用这种表示法,那么它可能是值得的:

#include <iostream>
#include <sstream>

static void foo( std::string const &s )
{
    std::cout << s << std::endl;
}

struct StreamProxy
{
    std::stringstream stream;
    operator std::string() { return stream.str(); }
};

template <typename T>
StreamProxy &operator<<( StreamProxy &s, T v )
{
    s.stream << v;
    return s;
}

static StreamProxy make_stream()
{
    return StreamProxy();
}

int main()
{
    foo( make_stream() << "number = " << 500 );
}

此程序打印

number = 500

这个想法是有一个小的包装类,可以隐含地转换为std::string<<运算符只是转发到包含的std::stringstreammake_stream()函数严格来说没有必要(你也可以说StreamProxy(),但我觉得它看起来好一些。

答案 2 :(得分:3)

我建议你使用这个实用程序结构:

struct stringbuilder
{
   std::stringstream ss;
   template<typename T>
   stringbuilder & operator << (const T &data)
   {
        ss << data;
        return *this;
   }
   operator std::string() { return ss.str(); }
};

并将其用作:

void f(const std::string & s );

int main() 
{
  char const *const pc = "hello";

  f(stringbuilder() << '{' << pc << '}' );

  //this is my most favorite line
  std::string s = stringbuilder() << 25  << " is greater than " << 5 ;
}

演示(仅举几例):http://ideone.com/J995r

更多关于我的博客:Create string on the fly just in one line

答案 3 :(得分:1)

除了Frerich Raabe刚才提出的好代理解决方案之外,还有其他一些选择:

  • 在标头中定义一个静态字符串流变量,用于定义日志记录功能,并在调用日志记录函数时使用逗号运算符,以便传递此变量而不是流返回的ostream&插入操作符。您可以使用日志记录宏来隐藏这种丑陋。这个解决方案的问题在于它有点丑陋,但这是一种常用的日志记录方法。

  • 不要使用C ++ I / O.请改用varargs C风格的解决方案。传递格式字符串作为第一个参数,其余参数是该格式字符串的目标。此解决方案的一个问题是,即使您的编译器足够智能以确保printf及其表兄弟是安全的,编译器可能也不会知道此新函数是printf系列的一部分。尽管如此,这也是一种常用的方法。

答案 4 :(得分:1)

如果您不介意使用宏功能,可以使记录功能接受const string&,并使用以下宏

#define build_string(expr) \
    (static_cast<ostringstream*>(&(ostringstream().flush() << expr))->str())

假设您foo有签名void foo(const string&),您只需要一行

foo(build_string("number = " << 500))

这是受到James Kanze关于static_caststringstream.flush的回答的启发。如果没有.flush(),则上述方法会因意外输出而失败。

请注意,此方法不应泄漏内存,因为临时值(无论是否为指针形式)仍然在堆栈上分配,因此在返回时会被破坏。

答案 5 :(得分:0)

因为你要转换成字符串,为什么不呢

void foo(const std::string& s)
{
    std::cout << "foo: " << s << std::endl;
}

...

std::stringstream ss;
ss << "number = " << 500;
foo(ss.str());

答案 6 :(得分:0)

这是不可能的。正如名称ostream所暗示的那样,它用于输出,用于写入。您可以将参数更改为stringstream&。此类的方法str()会返回std::string供您使用。

编辑我没有看到operator <<返回ostream&的问题。所以我想你不能简单地在函数参数列表中编写你的语句,但必须先编写它。

答案 7 :(得分:0)

您可以在std::ostringstream周围创建一个小包装器,在使用时将转换回std::string,并使该功能采用std::string const &。这个解决方案的第一种方法可以在answer中找到另一个问题。

最重要的是,如果需要,您可以添加对操纵器(std::hex)的支持。