2014-09-02 72 views
2

(由作者重新陳述以使其更清楚)。編譯時插件/具有單獨模塊約束的自動工廠註冊

是的,另一個自動化工廠註冊問題。但是我有新的(可能太有雄心的)限制。我爲一個大型(人類大腦)項目工作,需要使圖像I/O模塊可擴展。這是我想要實現的管道:

  1. 開發人員根據一個簡單的基於工廠的API編寫自己的圖像I/O插件。
  2. 他/她不允許修改我的代碼。
  3. 他/她將源文件放入我爲此創建的插件文件夾中。
  4. 他/她啓動cmake並編譯整個項目。 CMake會自動發現新文件並將其包含在項目中。
  5. main()時間,新的插件類被發現並自動註冊到工廠。
  6. main(),我可以做

    Base* b = Factory::create("DerivedClass"); 
    b->dosomething(); 
    

這裏是我的建議,其中混合以前的工作和建議,在網絡上找到。但是,這是行不通的,除非插件的頭被包含在main.cpp,它打破了要求2

ImageFormat.h

class Format 
{ 
    public: 

     virtual std::string id() const = 0; 
     virtual void dosomething() = 0; 
     virtual ~Format() {} 
}; 

typedef Format* (*PluginCreator)(void); 

class FormatFactory 
{ 
    private: 

     std::map<std::string, PluginCreator> registry; //map of registered plugins 

    public: 

     static FormatFactory* instance() 
     { 
      static FormatFactory* uniqueInstance = new FormatFactory(); 
      return uniqueInstance; 
     } 

     static std::string registerPlugin(PluginCreator creator, std::string id) 
     { 
      instance()->creators[id] = creator; 
      return id; 
     } 

     static Format* create(std::string id) throw (myexception) 
     { 
      if(instance()->creators.find(id) == instance()->creators.end()) 
       throw myexception("Format not found"); 
      return (instance()->creators[id])(); 
     } 
}; 

ExampleFormat.h

class ExampleFormat : public Format 
{ 
    private: 

     static const std::string id; 
     static Format* create() { return instance(); } 

    public: 

     static ExampleFormat* instance(){ 
      static ExampleFormat* uniqueInstance = new ExampleFormat(); 
      return uniqueInstance; 
     } 

     std::string id() const { return id; } 

     void dosomething(){"I am an example format and I do pretty cool stuff";} 
}; 


// this should guarantee that the singleton is instantiated. But works only if this header is included in the main.cpp or other modules of the main project. 
namespace{ 
    static const ExampleFormat* object = ExampleFormat::instance(); 
} 

ExampleFormat.cpp

#include "ExampleFormat.h" 

const std::string ExampleFormat::id = FormatFactory::registerPlugin(&create, "ExampleFormat"); 

main.cpp

#include "ImageFormat.h" 

int main() 
{ 
    FormatFactory::create("ExampleFormat")->dosomething(); 
    return 1; 
} 

謝謝 亞歷山德羅

+0

你想使你的插件動態庫?那麼當你加載這個庫的時候,這些單例變得可用(可以通過將插件發現者作爲一個將所有動態庫加載到插件目錄中的單例來做到這一點) – BeyelerStudios 2014-09-03 09:40:56

+1

@BeyelerStudios:是的,我們已經討論過它,此時此刻。跨平臺可移植性是主要要求,我們希望儘可能使這個項目儘可能便攜。 DLL插件是一件好事(我開發了很多),但可移植性是一個問題。 – alessandro 2014-09-03 09:46:24

+0

你可能可以做的就是讓CMake創建一個包含所有發現插件的頭文件。在你的main.cpp中包含這個頭文件,你應該是金手指。 – BeyelerStudios 2014-09-03 09:59:34

回答

0

這是非常接近我如何做到這一點。但有一點不同的是,您正在爲您正在註冊的課程分配一個變量。我只是在匿名命名空間中聲明一個靜態全局變量。

我相信你的變量沒有被創建,因爲你的類的實例沒有被創建。就像函數中的靜態變量在函數第一次運行之前一樣。

嘗試使用全局變量在工廠中註冊而不是類變量。

從我的一個項目一個例子:

namespace { 
    static RegisterInFactory_2<Reader, Reader_3> 
    registerMe_3(
     Reader_3::identifier() 
    ); 
}; 

const std::string& Reader_3::identifier() 
{ 
    static const std::string id("Reader_3"); 
    return id; 
} 
+0

謝謝你的迴應。我認爲我明白了你的觀點,儘管你發佈的代碼對我來說並不是100%清楚。但是,我不能玷污全局名稱空間,因爲它是一個非常大的(人類大腦)項目。此外,使用全局靜態變量似乎有點危險,因爲「靜態順序初始化失敗」問題。我正在尋找更安全(不僅是工作)的解決方案。但可能是我誤解了你的建議。 – alessandro 2014-09-02 20:24:09

+0

@alessandro:匿名命名空間保持全局名稱空間足夠乾淨,符合我的目的。我不知道大家是否同意。我以爲你解決了單身的init命令問題? – 2014-09-02 20:45:50

+0

看來我在這裏學到了一些東西。你對這個未命名的命名空間是正確的。但我想完全理解你的代碼。我不明白註冊是如何運作的。你能提供更多細節嗎?謝謝。 – alessandro 2014-09-02 21:27:35