我正在寻找一种方法来分支(tee)从istream(在我的例子中是cin)读取的输入到日志文件(clog / ofstream / etc),同时仍然使用输入进行处理。
我已经阅读过boost :: tee_device,它与我的要求非常相似。不幸的是,它被实现为ostream,因此从“管道的另一侧”解决了类似的问题。
我尝试编写一个istream(adapter)类,它将输入函数转发到包装的输入流(cin),并将读取的内容发送到日志文件。
这适用于直接调用运算符>>(...)的基本类型,但是,我遇到了输入流的一些更高级用法的问题,例如,对于运算符>>(标准: :string)和std :: string getline函数。
有没有更简单的方法(可能通过rdbuf()操作)?
谢谢!
编辑:我可以将我的代码更改为:cin>>值; clog<<值; - 但这将是一个重大而丑陋的变化。我也希望有一个简单的方法来关闭注销。因此,我想用一种方法将其建模为istream“过滤器”,然后简单地用这个istream“logger”替换对cin的所有引用。
理想解决方案:
class log_istream : public std::istream
{
public:
log_istream( std::istream & in , std::ostream & out );
/* ... istream forwarding functions ... */
private:
std::istream & in_;
std::ostream & out_;
};
int main() {
log_istream logger( std::cin , std::ofstream("logfile.out") );
logger >> value; // this implies infile >> value and logfile << value
getline(logger,my_string); // this also implies logfile.writeline(value)
// etc
}
等
答案 0 :(得分:3)
使用Boost.IOStreams,您可以定义一个输入过滤器,记录它读入clog的内容。类似的东西:
(警告:未经测试的代码)
class LoggingInputFilter : public multichar_input_filter {
public:
template<typename Source>
std::streamsize read(Source& Src, char* S, std::streamsize N)
{
streamsize result = read(Src, S, N);
if (result == -1){
return result;
}
if (std::clog.write(S, result)){
return result;
}
return -1;
}
};
用std :: cin链接它:
LoggingInputFilter cin_logger;
filtering_stream logged_cin(cin_logger);
logged_cin.push(std::cin);
并使用logged_cin而不是std :: cin
编辑:或者在streabuf级别操作,这样你的代码仍然使用std :: cin:
LoggingInputFilter cin_logger;
filtering_streambuf logged_cin(cin_logger);
logged_cin.push(std::cin.rdbuf());
std::cin.rdbuf(logged_cin);
答案 1 :(得分:1)
我找到了一个简单的解决方案:
Boost :: iostreams在源/接收器过滤器之间提供反转。
虽然tee_filter被建模为接收器,但您可以将它反转()到一个源中,并且它仍然会“发送”过滤到指定接收器的内容:
boost::iostreams::file log_file("sample.txt", std::ios::trunc); // or std::ios::app
// Single parameter tee() function returns a tee_filter , and invert() inverts that filter
boost::iostreams::filtering_istream in(
boost::iostreams::invert(
boost::iostreams::tee(log_file)));
这样,我就登录了所有已过滤的输入。
性能不是问题,但如果有人注意到任何红旗,我会非常感兴趣。感谢。
答案 2 :(得分:1)
最终答案:
#ifndef TEE_ISTREAM_H_
#define TEE_ISTREAM_H_
/*****************************************************************************/
#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/invert.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/tr1/memory.hpp>
#include <iostream>
/*****************************************************************************/
namespace bio = boost::iostreams;
/*****************************************************************************/
class tee_source : public bio::source {
public:
tee_source( std::istream & in, const std::string & filename )
: in_(in), log_file_(filename, std::ios::app), tee_(bio::tee(log_file_), 1)
{ }
std::streamsize read(char* s, std::streamsize n)
{
return tee_.read(in_,s,n);
}
private:
std::istream & in_;
bio::file log_file_;
bio::inverse< bio::tee_filter< bio::file > > tee_;
};
/*****************************************************************************/
typedef bio::filtering_istream tee_istream_t;
typedef std::tr1::shared_ptr< tee_istream_t > tee_istream_ptr_t;
/*****************************************************************************/
inline tee_istream_ptr_t make_tee_istream( std::istream & in, const std::string & filename )
{
return tee_istream_ptr_t( new tee_istream_t( tee_source( in , filename ), 0 ) );
}
/*****************************************************************************/
#endif
答案 3 :(得分:0)
很好的简单/简短的方式(需要最新的提升我猜测和-std = c ++ 11) - 感谢@mmocny,@ÉricMalenfant和Boost lib devs。
这个演示程序记录到“tmp.log”你在std :: cin和std :: cout上的任何对话:
#include <iostream>
#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/file.hpp>
namespace bio = boost::iostreams;
int main(int, char**)
{
// Logging
bio::filtering_ostream log;
log.push(bio::file_sink("tmp.log", std::ios_base::app));
// Tee filter instance (will be copied into each filter stream)
const bio::tee_filter<std::ostream> teeFilter(log);
// std::out tee
bio::filtering_ostream out;
out.push(teeFilter);
out.push(std::cout);
// std::in tee
bio::filtering_istream in;
in.push(teeFilter, 0); // If you don't pass 0 for buffer size, on std::cin it'll try to read 4096 chars and basically be useless
in.push(std::cin, 0);
out << "What is your name ?" << std::endl << std::flush;
std::string name;
getline(in, name);
out << "Hello " << name << std::endl << std::flush;
}