3

コンテキスト固有の情報をログメッセージに自動的に追加できるロギングフレームワークがあります。

これは、 /を使用してサブクラスの関数を呼び出してオブジェクトのコンテキストを取得するオフContextクラス テンプレートを継承することによって実現されます。CRTP patternstatic polymorphismToken()

ログを記録するときは、展開されるマクロを使用しGetLog()ます。

  • メンバー関数 内でGetLog、ロガーを取得して context を追加する基本クラスで見つかった場合Token
  • フリー関数またはContext派生クラス内でGetLog、グローバル名前空間で見つかった場合、ロガーを取得するだけです。

以下に完全に機能する例を示します。

問題

私の問題は、オフから派生したクラステンプレートがある場合ですContext。たとえば、次のようになります。

template<typename T>
struct Foo : Context<Foo<T>>;

メンバー関数内にログインしようとすると、メソッドを従属名LOGにするために接頭辞を付ける必要があります。this->GetLog()

クラステンプレートメンバー関数を使用LOGせずthis->に解決する方法はありContext::GetLog()ますか?

考え

  • Token仮想関数を作成し、動的ポリモーフィズムを使用してコンテキストを取得します。欠点は、これによりすべてのロギング呼び出しに v-table ルックアップが追加されることです (多くのContext派生オブジェクトを使用すると、コンパイラはインライン化できますか?) これをできるだけ無駄のないものにしたいと思います。
  • と を使用std::enable_ifして、と派生オブジェクトstd::is_base_ofを区別します。私はこれを無料の機能で動作させることができるとは思いませんか?Contextnon-Context
  • その他の方法で?

これが実際の例です:

#include <iostream>

// stub logging object to make the example work - just logs to stdout
struct Log
{
    template<typename T>
    friend Log& operator<<(Log& l, T d)
    {
        std::cout << d;
        return l;
    }
    friend Log& operator<<(Log& l, std::ostream& (*f)(std::ostream&))
    {
        std::cout << f;
        return l;
    }
};
Log gLog;

#define LOG GetLog()

// GetLog in the global namespace for non-Context derived classes, free functions etc
Log& GetLog()
{
    return gLog;
}

// classes derive from this to add context specific information when logging
template<typename Self>
struct Context
{
    // this GetLog adds prefix to Context derived classes
    Log& GetLog()
    {
        static_cast<const Self*>(this)->Token(gLog); // add the Context's Token to the log
        return gLog << ": ";
    }
};

//-------------------------

template<typename T>
struct Foo : Context<Foo<T>>
{
    void Func1()
    {
        LOG << __func__ << std::endl;       // resolves to the global GetLog() free-function
    }
    void Func2()
    {
        this->LOG << __func__ << std::endl; // notice the 'this->' prefix to make GetLog() a dependent name
    }

    Log& Token(Log& l) const { return l << "Foo"; }
};

// logging inside a non-Context derived class
struct Bar
{
    void Func()
    {
        LOG << __func__ << std::endl;
    }
};

// logging inside a free function 
void Baz()
{
    LOG << __func__ << std::endl;
}

//-------------------------

int main()
{
    Foo<int> f;
    f.Func1();
    f.Func2();

    Bar b;
    b.Func();

    Baz();

    exit(0);
}
4

2 に答える 2

1

簡単な解決策がありますが、Context 派生クラスで追加の作業が必要になります。

の代わりに基底クラス名による修飾を使用できますthis->。たとえばContext<Foo<T>>::GetLog、参照が依存していること (およびインスタンス化まで遅延する必要があること) をコンパイラに示すには、を使用するだけで十分です。

残念ながら、Contextそれ自体がテンプレートであるため、少し退屈です。代わりに、別のクラスを使用します (Baseここではそれを呼び出します)。

// 1. Wrap the generic GetLog into a `Base` class:
struct Base {
    static Log& GetLog() { return glog; }
};

// 2. Introduce a typedef into the derived class:
template <typename T>
struct Foo: Context<Foo<T>> {
    typedef Context<Foo<T>> Base;

    ...
};

// 3. Change the macro
#define LOG Base::GetLog()

ここでは、Baseルックアップは典型的なスコープ規則に従い、最も近いBaseものが見つかります...最悪の場合、グローバルスコープに存在するものにデフォルト設定されます。

完全に動作するデモはideoneにあります。

于 2013-05-02T06:40:13.510 に答える