将 stringstream 换成 cout
Swapping a stringstream for cout
使用 glibc 的 stdio,我可以将 memstream 换成 stdout,从而捕获一段代码的输出编译输出到 stdout:
#include <stdio.h>
void swapfiles(FILE* f0, FILE* f1){ FILE tmp; tmp = *f0; *f0 = *f1; *f1 = tmp; }
void hw_c(){ puts("hello c world"); }
int c_capt(){
FILE* my_memstream;
char* buf = NULL;
size_t bufsiz = 0;
if( (my_memstream = open_memstream(&buf, &bufsiz)) == NULL) return 1;
FILE * oldstdout = stdout;
swapfiles(stdout, my_memstream);
hw_c();
swapfiles(stdout, my_memstream);
fclose(my_memstream);
printf("Captured: %s\n", buf);
}
我很好奇 iostreams
是否也可以这样做。
我天真的尝试不会编译:
#include <iostream>
#include <string>
#include <sstream>
void hw_cc(){ std::cout<<"hello c++ world\n"; }
int cc_capt(){
using namespace std;
stringstream ss;
string capt;
//std::swap(ss,cout); //<- the compiler doesn't like this
hw_cc();
//std::swap(ss,cout);
cout<<"Captured: "<<capt<<'\n';
}
int main(int argc, char** argv){
c_capt();
puts("---------------------------------");
cc_capt();
return 0;
}
可以,但不能交换整个流——只是流缓冲区。
void cc_capt() {
using namespace std;
stringstream ss;
auto orig = std::cout.rdbuf(ss.rdbuf());
hw_cc();
std::cout.rdbuf(orig);
std::cout << "captured: " << ss.str() << "\n";
}
请注意,在这种情况下,我们根本没有真正使用 stringstream
本身,只是使用它包含的 stringbuf
。如果我们愿意,我们可以只定义一个 basic_stringbuf<char>
并直接使用它,而不是定义一个 stringstream
然后只使用它包含的 stringbuf
。
根据 Jerry 的示例,我编写了一个模板,它有一个很大的优势,它是 安全的(即,如果发生异常,您的缓冲区会自动恢复。)
这样使用:
{
ostream_to_buf<char> buf(std::cout);
... run code which `std::cout << "data"` ...
std::string const output(buf.str());
... do something with `output` ...
} // <-- here the buffer is restored
这是我认为非常接近 STL 的功能模板。模板本身是一个 std::stringbuf
,它将自己插入到构造函数中。析构函数恢复原始缓冲区,因此它是异常安全的。
template<
class CharT
, class Traits = std::char_traits<CharT>
, class Allocator = std::allocator<CharT>
>
class ostream_to_buf
: public std::basic_stringbuf<CharT, Traits, Allocator>
{
public:
typedef CharT char_type;
typedef Traits traits_type;
typedef typename Traits::int_type int_type;
typedef typename Traits::pos_type pos_type;
typedef typename Traits::off_type off_type;
typedef Allocator allocator_type;
typedef std::basic_stringbuf<char_type, traits_type, allocator_type> stringbuf_type;
typedef std::basic_ostream<char_type, traits_type> stream_type;
typedef std::basic_streambuf<char_type, traits_type> streambuf_type;
typedef std::basic_string<char_type, traits_type, allocator_type> string_type;
ostream_to_buf<char_type, traits_type, allocator_type>(stream_type & out)
: f_stream(out)
, f_original(f_stream.rdbuf(this))
{
}
ostream_to_buf<char_type, traits_type, allocator_type>(ostream_to_buf<char_type, traits_type, allocator_type> const & rhs) = delete;
ostream_to_buf<char_type, traits_type, allocator_type> & operator = (ostream_to_buf<char_type, traits_type, allocator_type> const & rhs) = delete;
~ostream_to_buf()
{
f_stream.rdbuf(f_original);
}
private:
stream_type & f_stream;
streambuf_type * f_original = nullptr;
};
复制构造函数和赋值运算符被删除,因为它们在这种情况下不起作用。
您可能可以让它与 C++11 甚至 C++03 一起工作。我有 C++14,但我认为这些都不需要 C++14。
使用 glibc 的 stdio,我可以将 memstream 换成 stdout,从而捕获一段代码的输出编译输出到 stdout:
#include <stdio.h>
void swapfiles(FILE* f0, FILE* f1){ FILE tmp; tmp = *f0; *f0 = *f1; *f1 = tmp; }
void hw_c(){ puts("hello c world"); }
int c_capt(){
FILE* my_memstream;
char* buf = NULL;
size_t bufsiz = 0;
if( (my_memstream = open_memstream(&buf, &bufsiz)) == NULL) return 1;
FILE * oldstdout = stdout;
swapfiles(stdout, my_memstream);
hw_c();
swapfiles(stdout, my_memstream);
fclose(my_memstream);
printf("Captured: %s\n", buf);
}
我很好奇 iostreams
是否也可以这样做。
我天真的尝试不会编译:
#include <iostream>
#include <string>
#include <sstream>
void hw_cc(){ std::cout<<"hello c++ world\n"; }
int cc_capt(){
using namespace std;
stringstream ss;
string capt;
//std::swap(ss,cout); //<- the compiler doesn't like this
hw_cc();
//std::swap(ss,cout);
cout<<"Captured: "<<capt<<'\n';
}
int main(int argc, char** argv){
c_capt();
puts("---------------------------------");
cc_capt();
return 0;
}
可以,但不能交换整个流——只是流缓冲区。
void cc_capt() {
using namespace std;
stringstream ss;
auto orig = std::cout.rdbuf(ss.rdbuf());
hw_cc();
std::cout.rdbuf(orig);
std::cout << "captured: " << ss.str() << "\n";
}
请注意,在这种情况下,我们根本没有真正使用 stringstream
本身,只是使用它包含的 stringbuf
。如果我们愿意,我们可以只定义一个 basic_stringbuf<char>
并直接使用它,而不是定义一个 stringstream
然后只使用它包含的 stringbuf
。
根据 Jerry 的示例,我编写了一个模板,它有一个很大的优势,它是 安全的(即,如果发生异常,您的缓冲区会自动恢复。)
这样使用:
{
ostream_to_buf<char> buf(std::cout);
... run code which `std::cout << "data"` ...
std::string const output(buf.str());
... do something with `output` ...
} // <-- here the buffer is restored
这是我认为非常接近 STL 的功能模板。模板本身是一个 std::stringbuf
,它将自己插入到构造函数中。析构函数恢复原始缓冲区,因此它是异常安全的。
template<
class CharT
, class Traits = std::char_traits<CharT>
, class Allocator = std::allocator<CharT>
>
class ostream_to_buf
: public std::basic_stringbuf<CharT, Traits, Allocator>
{
public:
typedef CharT char_type;
typedef Traits traits_type;
typedef typename Traits::int_type int_type;
typedef typename Traits::pos_type pos_type;
typedef typename Traits::off_type off_type;
typedef Allocator allocator_type;
typedef std::basic_stringbuf<char_type, traits_type, allocator_type> stringbuf_type;
typedef std::basic_ostream<char_type, traits_type> stream_type;
typedef std::basic_streambuf<char_type, traits_type> streambuf_type;
typedef std::basic_string<char_type, traits_type, allocator_type> string_type;
ostream_to_buf<char_type, traits_type, allocator_type>(stream_type & out)
: f_stream(out)
, f_original(f_stream.rdbuf(this))
{
}
ostream_to_buf<char_type, traits_type, allocator_type>(ostream_to_buf<char_type, traits_type, allocator_type> const & rhs) = delete;
ostream_to_buf<char_type, traits_type, allocator_type> & operator = (ostream_to_buf<char_type, traits_type, allocator_type> const & rhs) = delete;
~ostream_to_buf()
{
f_stream.rdbuf(f_original);
}
private:
stream_type & f_stream;
streambuf_type * f_original = nullptr;
};
复制构造函数和赋值运算符被删除,因为它们在这种情况下不起作用。
您可能可以让它与 C++11 甚至 C++03 一起工作。我有 C++14,但我认为这些都不需要 C++14。