c ++ stringstream to ostream to string [英] c++ stringstream to ostream to string
问题描述
我想要能够:
foo(stringstream()<<number = ; <500);
编辑:单行解决方案至关重要,因为这是为了记录目的。这些将在代码周围。
在foo内部将打印字符串到屏幕或某种排序。
现在由于stringstream的运算符<<返回ostream& foo的签名必须是:
foo(ostream& o);
但是如何转换ostream&到字符串? (或char *)。
显而易见的解决方案是使用 dynamic_cast
中 foo
。但给定的
代码仍然不能工作。 (你的例子将编译,但它不会做你认为应该的
。)表达式 std :: ostringstream()
是一个
临时的,你不能用临时的
和 std :: operator<<<(std :: ostream& char const *)的第一个参数初始化一个非const引用。 / code>
是一个非常量引用。 (你可以在
上临时调用一个成员函数,如 std :: ostream :: operator<<(void const *)
你可以使用类似的方法解决这个问题:
foo(std :: ostringstream()。flush()<<number =< <500);
std :: ostream :: flush()
返回一个非const引用,所以没有
进一步的问题。在一个新创建的流,它是一个无操作。
仍然,我认为你会同意,这不是最优雅或直观的$ b $在这种情况下,我通常做的是创建一个包装类,其中
包含它自己的 std :: ostringstream
,并提供一个模板化的
成员
运算符<<
code> std :: ostringstream 。你的函数 foo
会取一个 const
引用这个—或者我离开做的是直接调用
foo
,以便客户端代码甚至不必担心关于it;它会做类似的事情:
log()< number =< 500;
函数 log()
的封装类(但是看下面的
),这个类的(final)析构函数调用你的函数
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))
{
} b
$ b〜LogWrapper()
{
- * useCount;
if(* useCount == 0){
foo(collector-> str());
delete collector;
delete useCount;
}
}
template< typename T>
LogWrapper&运算符<<(T const& value)
{
(* collector)<值;
return * this;
}
};
请注意,很容易将其扩展为支持可选日志记录;只是
为LogWrapper提供了一个构造函数,它将 collector
设置为
NULL
运算符
已编辑:
还有一件事发生在我身上:你可能想要检查
析构函数是否被作为异常的结果调用,而不是调用
foo
在这种情况下。逻辑上,我希望
可能获得的唯一的例外是 std :: bad_alloc
,但总是有一个用户
例如:
log()< a + b;
其中 +
是用户定义的重载这引发了。
I would like to be able to do:
foo(stringstream()<<"number = " << 500);
EDIT: single line solution is crucial since this is for logging purposes. These will be all around the code.
inside foo will print the string to screen or something of the sort.
now since stringstream's operator<< returns ostream&, foo's signature must be:
foo(ostream& o);
but how can I convert ostream& to string? (or char*). Different approaches to achieving this use case are welcome as well.
The obvious solution is to use dynamic_cast
in foo
. But the given
code still won't work. (Your example will compile, but it won't do what
you think it should.) The expression std::ostringstream()
is a
temporary, you can't initialize a non-const reference with a temporary,
and the first argument of std::operator<<( std::ostream&, char const*)
is a non-const reference. (You can call a member function on a
temporary. Like std::ostream::operator<<( void const* )
. So the code
will compile, but it won't do what you expect.
You can work around this problem, using something like:
foo( std::ostringstream().flush() << "number = " << 500 );
std::ostream::flush()
returns a non-const reference, so there are no
further problems. And on a freshly created stream, it is a no-op.
Still, I think you'll agree that it isn't the most elegant or intuitive
solution.
What I usually do in such cases is create a wrapper class, which
contains it's own std::ostringstream
, and provides a templated
member operator<<
which forwards to the contained
std::ostringstream
. Your function foo
would take a const
reference to this—or what I offen do is have the destructor call
foo
directly, so that the client code doesn't even have to worry about
it; it does something like:
log() << "number = " << 500;
The function log()
returns an instance of the wrapper class (but see
below), and the (final) destructor of this class calls your function
foo
.
There is one slight problem with this. The return value may be copied,
and destructed immediately after the copy. Which will wreck havoc with
what I just explained; in fact, since std::ostringstream
isn't
copyable, it won't even compile. The solution here is to put all of the
actual logic, including the instance of std::ostringstream
and the
destructor logic calling foo
in a separate implementation class, have
the public wrapper have a boost::shared_ptr
to it, and forward. Or
just reimplement a bit of the shared pointer logic in your class:
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;
}
};
Note that it's easy to extend this to support optional logging; just
provide a constructor for the LogWrapper which sets collector
to
NULL
, and test for this in the operator<<
.
EDITED:
One other thing occurs to me: you'll probably want to check whether the
destructor is being called as a result of an exception, and not call
foo
in that case. Logically, I'd hope that the only exception you
might get is std::bad_alloc
, but there will always be a user who
writes something like:
log() << a + b;
where the +
is a user defined overload which throws.
这篇关于c ++ stringstream to ostream to string的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!