5

元の質問

これを実行できるようにすることを目標とするロギング クラスを作成しています。

// thread one
Logger() << "Some string" << std::ios::hex << 45;
// thread two
Logger() << L"Some wide string" << std::endl;

現在、私の Logger ヘッダーは次のようになっています。

#pragma once;
#include <ostream>    
class Logger
{
public:
    Logger();
    ~Logger();

    std::ostream* out_stream;
};

template <typename T>
Logger& operator<< (Logger& logger, T thing) {
    *logger.out_stream << thing;
    return logger;
}

このクラスに関する注意事項:

  1. クロス プラットフォームの互換性は問題ではありません。
  2. Logger.cpp の内部には、「実際の」ostream の作成を処理するシングルトン クラスがあります。
  3. Logger コンストラクターとデコンストラクターは、シングルトンの必要なロックを実行します。

3 つの問題があります。

  • operator<< 関数をフレンドまたはメンバーにして、out_stream をプライベートとして設定するにはどうすればよいですか?
  • operator<< 関数をマニピュレータで機能させるにはどうすればよいですか?
  • T が WCHAR* または std::wstring の場合、out_stream に渡す前に char* または std::string に変換するように特殊化を追加するにはどうすればよいですか? (私は変換を行うことができます。私の場合、高いユニコード文字を失うことは問題ではありません。)

回答で学んだことの要約:

  • 後ではなく、友人の前にテンプレートを置きます。
  • std::ios::hex はマニピュレータではありません。std::hex はマニピュレータです。

最終結果

#pragma once
#include <ostream>
#include <string>

std::string ConvertWstringToString(std::wstring wstr);

class Logger
{
public:
    Logger();
    ~Logger();

    template <typename T>
    Logger& operator<< (T data) {
        *out << data;
        return *this;
    }
    Logger& operator<< (std::wstring data) {
        return *this << ConvertWstringToString(data);
    }
    Logger& operator<< (const wchar_t* data) {
        std::wstring str(data);
        return *this << str;
    }

private:
    std::ostream* out;
};
4

4 に答える 4

7

フレンド定義を使用して、クラスの周囲の名前空間で演算子を定義し、演算子のオーバーロードの解決に対してのみ可視にすることができます (::operator<<... 構文を使用して手動で呼び出すことはできません)。

class Logger
{
public:
    Logger();
    ~Logger();

    std::ostream* out_stream;

    template <typename T>
    friend Logger& operator<< (Logger& logger, T thing) {
        *logger.out_stream << thing;
        return logger;
    }

    /* special treatment for std::wstring. just overload the operator! No need
     * to specialize it. */
    friend Logger& operator<< (Logger& logger, const std::wstring & wstr) {
        /* do something here */
    }

};

別の方法として、コードをそのままにして operator<< テンプレートをフレンドにするには、次の行をクラス定義に追加します。

template <typename T>
friend Logger& operator<< (Logger& logger, T thing);

マニピュレータの問題については、私が以前に書いたコードを提供します。

#include <iostream>
#include <cstdlib>
using namespace std;

template<typename Char, typename Traits = char_traits<Char> >
struct logger{
    typedef std::basic_ostream<Char, Traits> ostream_type;
    typedef ostream_type& (*manip_type)(ostream_type&);
    logger(ostream_type& os):os(os){}
    logger &operator<<(manip_type pfn) {
        if(pfn == static_cast<manip_type>(std::endl)) {
            time_t t = time(0);
            os << " --- " << ctime(&t) << pfn; 
        } else
            os << pfn;
        return *this; 
    }
    template<typename T> 
    logger &operator<<(T const& t) { 
        os << t; 
        return *this;
    }
private:        
    ostream_type & os;
};

namespace { logger<char> clogged(cout); }
int main() { clogged << "something with log functionality" << std::endl; }

};

std::hex であり、 std::ios::hex ではないことに注意してください。後者はsetf、ストリームの関数のマニピュレータ フラグとして使用されます。あなたの例では、マニピュレータの特別な扱いは必要ないことに注意してください。上記の std::endl の特別な処理は、std::endl が使用されているときにさらに時間をストリーミングするためにのみ必要です。

于 2008-11-14T16:29:55.053 に答える
2

友情宣言は必要ありません:

class Logger
{
public:
    Logger();
    ~Logger();

template <typename T>
inline Logger& Display(T thing)
{
   *out_stream << thing;
    return *this;
}
private:
    std::ostream* out_stream;
};

template <typename T>
Logger& operator<< (Logger& logger, T thing) 
{
    return logger.Display(thing);
}
于 2008-11-14T18:13:25.047 に答える
2

テンプレートを使用するのが正しい方法ですが、テンプレートが実装ファイル ( ) ではなく、ヘッダーファイル ( logger.h、または呼び出したもの) にあることを確認する必要があります。これは、 で定義されたすべての型に対して自動的に機能します。また、ストリーム マニピュレータ オブジェクトでも自動的に動作します。これらは実際には、パラメータを受け取る単なる関数であり、.logger.cppoperator <<std::ostreamstd::ostreamoperator <<ostream

operator <<次のように友達機能を作成できます。

template <typename T> friend Logger& operator<< (Logger& logger, T thing);

特殊化は簡単です - テンプレートの特殊化を使用するだけです (ヘッダー ファイルで):

template <typename T>
Logger& operator<< (Logger& logger, T thing) {
    *logger.out_stream << thing;
    return logger;
}

// Template specialization - the "template <>" part is necessary
template <>
Logger& operator<< (Logger& logger, const wchar_t *wstr)
{
  // convert wstr to an ANSI string and log it
}

template <>
Logger& operator<< (Logger& logger, const std::wstring & wstr)
{
  // convert wstr to an ANSI string and log it
}
于 2008-11-14T16:32:14.533 に答える
0

なぜそれをprintfの方法で行い、マルチパラメータメソッドを使用しないのですか(3つのドットを使用して...)。これでも多くの書式設定機能が提供され、<< を使用している場合のように煩雑になることはありません。

例えば:

Logger("This is my log msg %0X", 45);

2 秒待ってください。コード サンプルが表示されます。

編集:

void Logger(const char* format, ...)
{
    char szMsg[3000];

    va_list args;
    va_start( args, format );
    vsnprintf( szMsg, sizeof(szMsg) - 1, format, args );
    va_end(args);

    // code to print szMsg to a file or whatever here
}

これをスタンドアロン関数ではなくクラスとして使用する場合は、ロガー演算子 () をオーバーロードすることができ、まったく同じように機能します。

于 2008-11-14T16:26:55.043 に答える