2012-10-12 35 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

模板類不能'放入庫中',因爲它們被編譯器實例化爲模板參數的特化。

儘管您可以將模板實現中使用的參數獨立的東西放到一個庫中。