2012-10-12 34 views
1

鉴于以下情况,工作代码。不知道派生类型的静态CRTP类?

#include <iostream> 

template<class Detail> 
class AbstractLogger 
{ 
public: 
    static void log(const char* str) { 
     Detail::log_detailled(str); 
    } 
}; 

class Logger : public AbstractLogger<Logger> 
{ 
public: 
    static void log_detailled(const char* str) { 
     std::cerr << str << std::endl; 
    } 
}; 

int main(void) 
{ 
    AbstractLogger<Logger>::log("main function running!"); 
    return 0; 
} 

现在,我想把AbstractLogger到库中,并让用户库定义自己的记录,像Logger类在这里。这有一个缺点:AbstractLogger<Logger>不能在库中使用,因为库不能知道Logger

注:

  • 请为什么不虚函数或问题。另外,我知道“静态虚拟”成员是无效的类似问题。也许,在CRTP中有一个解决方法:)
  • C++ 11会很有趣,但是,我需要“通常”的C++。
+2

你是什么意思*我想把'AbstractLogger'放入一个库*。你的意思是你想在不知道实例化类型的情况下在库中使用它吗?或者你想要某种生成的代码在库中?要么...? –

+0

我的意思是第一个。 – Johannes

+0

闻起来像一个沉重的设计缺陷和CRT模式的滥用... –

回答

1

通常的做法是针对一个概念进行编码,同时提供助手,以便用户可以轻松生成满足一个或多个这些概念的类型。举个例子,boost::iterator_facade就是一个CRTP帮助器,可以让用户更容易编写迭代器。然后,该迭代器可用于接受迭代器的任何位置 - 例如在范围构造函数std::vector中。注意特定的构造函数对用户定义类型没有预知。

在你的情况下,AbstractLogger将是CRTP帮手。缺失的部分将是定义例如记录器的概念。因此,请注意,需要记录器的所有内容都需要作为模板实现,或者需要使用类型擦除容器来保存任意记录器。

概念检查(如Boost提供的那些检查)对于此类编程很方便,因为它们允许用实际代码表示一个概念。

+0

感谢您的回答。你能否简要概述一下这种“类型擦除容器”的外观? – Johannes

+0

@Johannes这与'std :: function '非常相似,它是一个容器,用于任何可调用签名'R(A ...)'的容器。所以它会有一个构造函数'template container(Logger log);'将任何符合记录器的东西,并且本身符合Logger概念 - 将实际操作委托给在构建时通过的记录器。 [This](http://stackoverflow.com/questions/5450159/type-erasure-techniques)概述了一些类型擦除技术。 –

2

如果你的意思是你希望有一个库在不知道确切的实例化类型的情况下使用它作为日志记录机制,我会反对它。

满足您的其他要求(即没有虚拟功能)的唯一方法是将库中需要记录的所有函数/类型转换为采用Logger类型的模板。最终的结果是,大部分的界面变成了一个模板(尽管你可能会将大量的实现移动到非模板化的代码中,但它会让你的生活变得比需要的更难,而且它仍然会产生更大的二进制文件) 。

如果您对虚拟功能的关注是性能,那么您应该重新考虑您的方法及其带来的问题。特别是,日志昂贵。大多数日志记录库通过优化非日志记录案例(通过避免在未启用日志级别/组/ ...时调用记录器的宏)来处理它,但仍然为实际写入留下动态分派。与写入控制台或文件相比,动态分派的代价可以忽略不计,甚至与生成将要记录的消息的代价相比(我假定您不仅记录文字字符串)

0

模板类不能'放入库中',因为它们被编译器实例化为模板参数的特化。

尽管您可以将模板实现中使用的参数独立的东西放到一个库中。