【问题标题】:Tee-ing input (cin) out to a log file (or clog)将输入 (cin) 输出到日志文件(或阻塞)
【发布时间】:2010-11-03 03:22:55
【问题描述】:

我正在寻找一种方法,将从 istream(在我的情况下为 cin)读取的输入分支(tee)到日志文件(clog/ofstream/etc),同时仍使用输入进行处理。

我已经阅读了关于 boost::tee_device 的信息,它与我的要求非常相似。不幸的是,它是作为 ostream 实现的,因此从“管道的另一端”解决了类似的问题。

我尝试编写一个 istream(适配器)类,它将输入函数转发到包装的输入流 (cin),并将读取的内容发送到日志文件。

这适用于直接调用 operator>>(...) 的基本类型,但是,我遇到了输入流的一些更高级用法的问题,例如,对于 operator>>(std::string ) 和 std::string getline 函数。

有没有更简单的方法来做到这一点(可能通过 rdbuf() 操作)?

谢谢!

编辑:我可以将我的代码到处更改为:cin >> value;堵塞

理想的解决方案:

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
}

等等

【问题讨论】:

标签: c++ io stream


【解决方案1】:

使用 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

编辑:或者在 straebuf 级别操作,让你的代码仍然使用 std::cin:

LoggingInputFilter cin_logger;
filtering_streambuf logged_cin(cin_logger);
logged_cin.push(std::cin.rdbuf());
std::cin.rdbuf(logged_cin);

【讨论】:

  • 这不是我使用的解决方案 - 但它让我走上了正轨。谢谢!
【解决方案2】:

我找到了一个简单的解决方案:

Boost::iostreams 提供源/汇过滤器之间的反转。

虽然 tee_filter 被建模为接收器,但您可以将其 invert() 转换为源,它仍会将过滤的内容“发送”到指定的接收器:

    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)));

这样,我已经登录了所有过滤的输入。

性能不是问题,但如果有人注意到任何危险信号,我会非常感兴趣。谢谢。

【讨论】:

  • 很高兴知道!我考虑过 invert(),但不确定它是否可以工作,也没有时间尝试。
  • 我刚刚再次测试了这段代码,除非你通过添加 in.push( std::cin ); 来“完成”“管道”,否则它将出现段错误。或任何其他“来源”。
【解决方案3】:

最终答案:

#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

【讨论】:

    【解决方案4】:

    不错的简单/快捷方式(我猜需要最新的提升和 -std=c++11) - 感谢 @mmocny、@Éric Malenfant 和 Boost lib 开发人员。

    此演示程序将您在 std::cin 和 std::cout 上的任何对话记录到“tmp.log”:

    #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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-25
      • 1970-01-01
      • 1970-01-01
      • 2020-07-13
      • 1970-01-01
      • 2017-11-06
      • 2012-04-21
      • 2013-06-26
      相关资源
      最近更新 更多