3

我们有一个日志框架,它允许我们自动将上下文特定信息添加到日志消息中。

这是通过继承Context类模板来实现的,该类模板使用CRTP pattern/static polymorphism调用Token()子类上的函数来获取对象的上下文。

记录时,我们使用扩展为GetLog().

  • 当在基类中找到成员函数 GetLog时,它会获取记录器并添加上下文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_ifandstd::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

3

您可以使用 using 声Context::GetLog明显式引入派生类的范围:

template<typename T>
struct Foo : Context<Foo<T>>
{
    using Context<Foo<T>>::GetLog;

    // the rest as before
};

活生生的例子

于 2013-05-02T06:42:57.927 回答
1

好吧,有一个简单的解决方案,尽管它需要在上下文派生类中进行一些额外的工作。

您可以通过基类名称而不是 来使用限定,this->例如 usingContext<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 回答