【发布时间】:2013-09-07 18:41:35
【问题描述】:
我正在尝试将大型代码库中的日志代码模块化,因为实时日志框架 (Apache) 使我们的代码与其紧密耦合,这使得编写单元测试变得非常困难。我被我无法拥有虚拟模板化功能的事实所困扰。我目前的做法可以总结如下:
// Context.h
struct Logger
{
template <typename... Args>
void operator()(const char* aFormat, Args&&... aArgs)
{
// This function would ideally be virtual.
// Is there a funky way to get this function to call
// a derived class' implementation instead.
std::cerr << "I don't want to see this printed" << std::endl;
}
};
class Context
{
public:
Context(const Logger& aLogger)
: iLogger(aLogger)
{
}
template <typename... Args>
void DEBUG(const char* aFormat, Args&&... aArgs)
{
iLogger(aFormat, aArgs...);
}
private:
const Logger& iLogger;
};
// MyType.h
#include "Context.h"
class MyType
{
public:
MyType(Context& aCtx)
: iCtx(aCtx)
{
DEBUG("> ctor");
DEBUG("< ctor. this=%p", this);
}
private:
template <typename... Args>
void DEBUG(const char* aFormat, Args&&... aArgs)
{
iCtx.DEBUG(aFormat, aArgs...);
}
Context& iCtx;
};
// main.cpp
#include "MyType.h"
template <typename... Args>
static void StdErrLog(const char* aFormat, Args&&... aArgs)
{
fprintf(stderr, aFormat, aArgs...);
}
struct StdErrLogger : public Logger
{
// This function never gets called because it's not virtual.
template <typename... Args>
void operator(const char* aFormat, Args&&... aArgs)
{
StdErrLog(aFormat, aArgs...);
}
}
int main(...)
{
StdErrLogger logger; // For unit tests this could be 'EmptyLogger' for example.
Context ctx(logger);
MyType t(ctx);
}
这么近,这么远。
在没有模板化 Context 类的情况下,我能做些什么来完成这项工作?代码库根本没有模板化,我非常不愿意走这条路,因为这将是很多乏味的工作。
如果可以通过将模板保持在功能级别范围内来完成,我会很高兴看到解决方案。函数指针也是可以接受的,但我不确定获取可变参数模板函数地址的可行性。
谢谢
【问题讨论】:
-
您需要实现中的格式字符串和参数,还是只需要格式化的结果?如果您需要的不仅仅是格式化的结果,是否有一种中间形式可以做到?您的记录器或格式化程序的实现有何不同?
-
问题在于模板需要由编译器实例化,即编译器必须从函数模板创建实际函数。因此,功能模板的实现需要在每个翻译单元中可用(如果要实例化它们)。函数模板不能是虚拟的,因为使用基类的函数需要实例化(所有)派生类的覆盖器,而这对于您想要/可以使用虚拟函数实现的单独编译是不可能的。
-
@Yakk 为了测试,我打算使用一个什么都不打印的记录器(或者如果我需要它可能是 stderr)。对于生产,我会调用 Apache 运行时的 ap_log_error。我想我需要实现中的参数。
-
@cooky451 CRTP 意味着我必须在类范围内对 Context 进行模板化。这将导致其他地方很多其他变化
-
为什么?为什么不是预先格式化的字符串?然后将
"%s", string发送到实际的后端记录器。通过将格式化程序与记录器分离,您的问题就会消失。
标签: c++ templates variadic-templates