4

多くの std::cout ステートメントを配置して、このクラスが処理している大量のシグナルに関する有益なテキスト メッセージを出力する C++ クラスがあります。私の意図は、これらのテキスト メッセージを log という名前の関数にリダイレクトすることです。この関数には、ログ テキストを出力するかどうかを定義する mVerbose という名前のフラグがあります。この関数の内容は次のとおりです。

 void XXXProxy::log(std::stringstream& ss)
 {
   if(mVerbose)
   {
     std::cout << ss;
     ss << "";
   }
 }

次に、この関数の呼び出し元コード スニペットは次のようになります。 std::stringstream logStr;

 logStr << "SE"
        << getAddr().toString()
        << ": WAITING on epoll..."
        << std::endl;      
 log(logStr);

std::stringstream オブジェクトを作成してログ関数を呼び出す必要がないように、XXXProxy で << 演算子をオーバーロードしたいと考えています。以下のようにテキスト メッセージをログに記録し、 << 演算子ですべてを次のように集約できるようにしたいと考えています。

 << "SE"
 << getAddr().toString()
 << ": WAITING on epoll..."
 << std::endl;      

したがって、次のようなメンバー << 関数が必要です。

 void XXXProxy::operator << (std::stringstream& ss)
 {
   if(mVerbose)
   {
     std::cout << ss;
     ss << "";
   }
 } 

質問

私は比較的初心者の C++ 開発者であり、上記のような << 演算子を記述しようとすると、多くのコンパイル エラーが発生します。この << 演算子を正しく実装するために、いくつかの提案をするか、いくつかのリンクに誘導してください。ありがとう。

4

2 に答える 2

7

直接使用std::coutするのではなく、独自の Log クラスが必要な場合は、std::ostream: operator<<の同じインターフェイスを提供する単純なラッパーを実装できます。

class Log {
private: 
    std::ostream& _out_stream;

    //Constructor: User provides custom output stream, or uses default (std::cout).
    public: Log(std::ostream& stream = std::cout): _out_stream(stream) {} 

    //Implicit conversion to std::ostream
    operator std::ostream() {
        return _out_stream;
    } 

    //Templated operator>> that uses the std::ostream: Everything that has defined 
    //an operator<< for the std::ostream (Everithing "printable" with std::cout 
    //and its colleages) can use this function.    
    template<typename T> 
    Log& operator<< (const T& data) 
    {
        _out_stream << data;
    }
}

したがって、クラスに実装std::ostream& operator>>(std::ostream& os , const YourClass& object)する場合は、この Log クラスを使用できます。

このアプローチの利点は、同じメカニズムを使用して機能させstd::cout << your_class_object、クラスをログで機能させることです。

例:

struct Foo
{
    int x = 0; //You marked your question as C++11, so in class initializers 
               //are allowed. 

    //std::ostream::operator<< overload for Foo:
    friend std::ostream& operator<<(std::ostream& os , const Foo& foo)
    {
        os << foo.x;
    }
};

int main()
{
  Log my_log;
  Foo my_foo;

  my_foo.x = 31415;

  my_log << my_foo << std::endl; //This prints "31415" using std::cout.
}

考えられる改善:

  • extern constクラス Log を記述し、そのクラスにsingletonを実装させることができます。これにより、プログラムのどこからでもログにアクセスできます。
  • のようなヘッダーを持つログ出力は一般的Log output (17:57): log messageです。これを行うにはstd::endl、番兵として を使用して、次の出力が行の先頭 (ログ メッセージの先頭) になるタイミングを示すフラグを格納できます。完全で機能する実装については、次の回答を確認してください。

参考文献:

于 2013-07-11T15:39:49.860 に答える
7

例のタイムスタンプはそれだけでした、例:)。

しかし、それが気に入れば、実装を試みることができます。C++11 とその STL の大幅な改善のおかげで、優れた時刻/日付 API: std::chronoを利用できます。

std::chrono次の 3 つの側面に基づいています。

  • 時計
  • 期間
  • 時点

また、chrono には 、 、 の 3 種類のクロックが用意されstd::system_clockています。私たちの場合、(正確な時間間隔を測定するのではなく、日時にアクセスしたい) を使用します。 std::chrono の詳細については、このすばらしい Bo Qian の youtube チュートリアル を参照してください。std::steady_clockstd::high_resolution_clockstd::system_clock

したがって、ログ ヘッダーにタイム スタンプを実装する必要がある場合は、次のようにすることができます。

編集:他の優れた機能と同様に、C++ テンプレートは使いすぎない限り優れたツールです。
私たちの問題std::endlは、テンプレート化された関数であるoperator<<ため、コンパイラが std::endl テンプレート引数を直接推測できないため、別のテンプレート化された関数にパラメーター (この場合) として直接渡すことができないことでした。それは、 「未解決のオーバーロードされた関数型」という再発エラーです。

しかし、これを行うためのはるかに簡単な方法があります: for onlyの明示的なオーバーロードを使用し、他のすべてのテンプレート化された other を使用します。operator<<std::endl

class Log
{
private:
    std::ostream& _out_stream;
    bool         _next_is_begin;

    const std::string _log_header;
    using endl_type = decltype( std::endl ); //This is the key: std::endl is a template function, and this is the signature of that function (For std::ostream).

public:
    static const std::string default_log_header;

    //Constructor: User passes a custom log header and output stream, or uses defaults.
    Log(const std::string& log_header = default_log_header , std::ostream& out_stream = std::cout) : _log_header( log_header ) , _out_stream( out_stream ) , _next_is_begin( true ) {}

    //Overload for std::endl only:
    Log& operator<<(endl_type endl)
    {
        _next_is_begin = true;

        _out_stream << endl;

        return *this;
    }

    //Overload for anything else:
    template<typename T>           
    Log& operator<< (const T& data) 
    {
        auto now        = std::chrono::system_clock::now();
        auto now_time_t = std::chrono::system_clock::to_time_t( now ); //Uhhg, C APIs...
        auto now_tm     = std::localtime( &now_time_t ); //More uhhg, C style... 

        if( _next_is_begin )
            _out_stream << _log_header << "(" << now_tm->tm_hour << ":" << now_tm->tm_min << ":" << now_tm->tm_sec << "): " << data;
        else
            _out_stream << data;

        _next_is_begin = false;

        return *this;
    }
};

const std::string Log::default_log_header = "Log entry";

このコード スニペットは完全に機能します。完全な実装を github アカウントにプッシュしました。

参照:

于 2013-07-11T23:37:02.470 に答える