2016-10-26 5 views
2

私は非常に単純なロギングライブラリに納得のいくインターフェイスを書いています。次の2つのコードを読んでください。最初のものは、私が今何をすべきかである、もう一つは、直感的なインターフェイスのための私の考えです:iostreamのようなインターフェイスをロギングライブラリに書き込む方法は?

std::ostringstream stream; 
stream<<"Some text "<<and_variables<<" formated using standard string stream" 
logger.log(stream.str()); //then passed to the logger 

そして

logger.convinient_log()<<"Same text "<<with_variables<<" but passed directly"; 

そのアイデアの背後にある私の思考の設計プロセスは、一時的なのいくつかの種類を返すことですストリングストリームのようなオブジェクトからlogger.convinient_log()機能。破滅の目的(私はそれが行の終わりまたは同様の、convinient場所で起こることを望む)自体から文字列を収集し、実際のlogger.log()を呼び出します。要点は、log()で追加できるように、term-by-termではなく全体を処理したいということです。接頭辞とsufixをテキスト行全体に適用します。

私は非常にうまくいきます。何か重い魔法がなければ、それはまったく不可能で不可能かもしれません。そうであれば、それを実現するためのほぼ納得のいく方法とそれを実装する方法は何でしょうか?私はcollect-call-logger.log()の操作を強制する特別な変数を渡すことに賭ける。

正確な答えがわからない場合は、トピックのリソース(例:ストリングストリームの拡張)も歓迎します。

+1

の可能性のある重複(http://stackoverflow.com/questions/511768/ – user4581301

+0

ほとんど重複していますが、私は何か違うものを求めています:私は1つの 'convinient_log()'の全入力を一度に処理したいのであって、一度に処理したくはありません一本ずつ。 「幸運」と言えば、ソリューションの実装と同様の納得された点で、より優れたソリューションはありますか?私も本当に感謝するでしょう:) – psorek

答えて

5

これは、たとえばBoost.Logの動作方法です。基本的な考え方は単純です:例外が途中でスローされた場合、不完全なメッセージをログに記録避けるために使用される

struct log 
{ 
    log() { 
     uncaught = std::uncaught_exceptions(); 
    } 

    ~log() { 
     if (uncaught >= std::uncaught_exceptions()) { 
      std::cout << "prefix: " << stream.str() << " suffix\n"; 
     } 
    } 

    std::stringstream stream; 
    int uncaught; 
}; 

template <typename T> 
log& operator<<(log& record, T&& t) { 
    record.stream << std::forward<T>(t); 
    return record; 
} 

template <typename T> 
log& operator<<(log&& record, T&& t) { 
    return record << std::forward<T>(t); 
} 

// Usage: 
log() << "Hello world! " << 42; 

std::uncaught_exceptions()

+0

OPの場合は、コンストラクタ内のloggerインスタンスへの参照を単に保存することができるので、 'convenient_log(logger)<<" same text "<< with_variables <<"のようになります。直接渡された "; ' –

+0

それは私が必要とするものです!もう1つの疑問: 'log()'のスコープは何か。いつ破壊されますか? – psorek

+0

@ psorek一時変数は、それらを作成した式の終わり、つまりセミコロンまで生きています。 [一時的なオブジェクトの存続期間](http://en.cppreference.com/w/cpp/language/lifetime#Temporary_object_lifetime)を参照してください。 –

1

例えば、あなたのロガーへの書き込みにstd::basic_streambufから派生したカスタムクラスを作成します。例えば、

class LoggerBuf : public std::stringbuf 
{ 
private: 
    Logger logger; 
public: 
    LoggerBuf(params) : std::stringbuf(), logger(params) { 
     ... 
    } 

    virtual int sync() { 
     int ret = std::stringbuf::sync(); 
     logger.log(str()); 
     return ret; 
    } 
}; 

そして、あなたはそれをLoggerBufオブジェクトへのポインタを与えるstd::basic_ostreamオブジェクトをインスタンス化することができます

LoggerBuf buff(params); 
std::ostream stream(&buf); 
stream << "Some text " << and_variables << " formated using standard string stream"; 
stream << std::flush; // only if you need to log before the destructor is called 

クラスをラップするには、std::basic_ostreamからカスタムクラスを派生させます。例:

class logger_ostream : public std::ostream 
{ 
private: 
    LoggerBuf buff; 
public: 
    logger_ostream(params) : std:ostream(), buff(params) 
    { 
     init(&buff); 
    } 
}; 

std::logger_ostream logger(params); 
logger << "Some text " << and_variables << " formated using standard string stream"; 
logger << std::flush; // only if you need to log before the destructor is called 
1

これまでに私がtogeatherしたクラスです。これはあなたが探しているようなものです。私はostreams、stream_buf、または他の何かを継承することなく、それを達成することができました。フラッシュがキャッチされるたびに、ファイル、コンソール、ソケット、または任意のものに書き込むことができます。

ostream_iteratorsでは機能しませんが、すべてのio_manip関数をうまく処理します。

使用法:[?どのようSTDのC++ストリームのように私のロギングクラスを使用する]

Logger log; 

int age = 32; 
log << "Hello, I am " << age << " years old" << std::endl; 
log << "That's " << std::setbase(16) << age << " years in hex" << std::endl; 
log(Logger::ERROR) << "Now I'm logging an error" << std::endl; 
log << "However, after a flush/endl, the error will revert to INFO" << std::end; 

実装

#include <iostream> 
#include <sstream> 
#include <string> 

class Logger 
{ 
public: 
    typedef std::ostream& (*ManipFn)(std::ostream&); 
    typedef std::ios_base& (*FlagsFn)(std::ios_base&); 

    enum LogLevel 
    { 
     INFO, 
     WARN, 
     ERROR 
    }; 

    Logger() : m_logLevel(INFO) {} 

    template<class T> // int, double, strings, etc 
    Logger& operator<<(const T& output) 
    { 
     m_stream << output; 
     return *this; 
    } 

    Logger& operator<<(ManipFn manip) /// endl, flush, setw, setfill, etc. 
    { 
     manip(m_stream); 

     if (manip == static_cast<ManipFn>(std::flush) 
     || manip == static_cast<ManipFn>(std::endl)) 
      this->flush(); 

     return *this; 
    } 

    Logger& operator<<(FlagsFn manip) /// setiosflags, resetiosflags 
    { 
     manip(m_stream); 
     return *this; 
    } 

    Logger& operator()(LogLevel e) 
    { 
     m_logLevel = e; 
     return *this; 
    } 

    void flush() 
    { 
    /* 
     m_stream.str() has your full message here. 
     Good place to prepend time, log-level. 
     Send to console, file, socket, or whatever you like here. 
    */  

     m_logLevel = INFO; 

     m_stream.str(std::string()); 
     m_stream.clear(); 
    } 

private: 
    std::stringstream m_stream; 
    int    m_logLevel; 
}; 
関連する問題