我们有一个日志框架,它允许我们自动将上下文特定信息添加到日志消息中。
这是通过继承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_if
andstd::is_base_of
区分Context
和non-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);
}