2012-02-14 59 views
9

這裏的工作原理是什麼,我試圖做的:如何實現在C++中的適配器框架,在Linux和Windows

我正在開發一個支持插件的跨平臺IDE(Linux和Windows)。我需要使用類似於Eclipse提供的適配器框架來支持可擴展性。見here更多細節,但基本上我需要以下條件:

AdapteeAdapted是已經存在的,而我們是不允許以任何方式改變完全無關的類。我想創建一個AdapterManager類有一個方法

template <class Adaptee, class Adapted> Adapted* adapt(Adaptee* object); 

這將創造給予Adaptee實例的Adapted一個實例。如何創建實例取決於必須使用AdapterManager註冊的適配器功能。每個新插件都應該能夠爲任意類型提供適配器功能。

下面是我對一個可能的解決方案的想法,爲什麼它不工作:

  • C++ 11的RTTI功能和type_info類提供hash_code()方法,它返回在每個類型的唯一整數程序。請參閱here。因此AdapterManager可能只包含一個映射,給定Adaptee和Adapter類的散列碼返回一個指向適配器函數的函數指針。這使得adapt()函數的實現以上簡單:

    template <class Adaptee, class Adapted> Adapted* AdapterManager::adapt(Adaptee* object) 
    { 
        AdapterMapKey mk(typeid(Adapted).hash_code(), typeid(Adaptee).hash_code()); 
        AdapterFunction af = adapterMap.get(mk); 
        if (!af) return nullptr; 
        return (Adapted*) af(object); 
    } 
    

    任何插件可以很容易地通過簡單地插入一個附加功能到地圖延伸的框架。還要注意,任何插件都可以嘗試將任何類適配到任何其他類,並且如果存在相應的適配器函數(使用AdapterManager註冊),則無論是誰註冊它都會成功。

  • 此問題與模板和插件(共享對象/ DLL)的組合有關。由於兩個插件可以使用相同的參數實例化模板類,因此這可能會導致兩個單獨的相應結構實例和潛在的不同hash_code()結果,這將打破上述機制。從一個插件註冊的適配器功能可能並不總是在另一個插件中運行。
  • 在Linux中,根據this(點4.2),在一些條件下,動態鏈接器似乎能夠處理不同共享庫中多個類型的聲明。然而真正的問題是在Windows中,似乎每個DLL都會得到它自己版本的模板實例化,而不管它是否也在其他加載的DLL或主要可執行文件中定義。與在Linux中使用的動態鏈接器相比,動態鏈接器看起來非常不靈活。
  • 我已經考慮過使用顯式模板實例化,這似乎減少了這個問題,但仍然沒有解決它,因爲兩個不同的插件可能仍然以相同的方式實例化相同的模板。

問題:

  1. 有誰知道的一種方式在Windows中實現這一目標?如果你被允許修改現有的類,這會有幫助嗎?
  2. 您是否知道另一種在C++中實現這種功能的方法,同時仍然保留了所有期望的屬性:對現有類沒有改變,可以使用模板,支持插件並且是跨平臺的?

更新1:
本項目採用Qt框架的很多東西,包括插件的基礎設施。 Qt真的有助於跨平臺開發。如果你知道Qt特定的解決方案,那也是受歡迎的。

更新2:
N.M.的評論使我意識到,我只知道在理論上的問題,並沒有實際測試它。所以,我沒有使用下面的定義在Windows和Linux的一些測試:

template <class T> 
class TypeIdTest { 
    public: 
     virtual ~TypeIdTest() {}; 
     static int data; 
}; 
template <class T> int TypeIdTest<T>::data; 

該類在兩個不同的共享庫/有T = INT的DLL實例化。這兩個庫都在運行時顯式加載。以下是我發現:

的Linux一切都只是工作:

  • 這兩個實例中使用的相同虛函數表
  • typeid返回的對象位於同一地址。
  • 即使是靜態數據成員也是一樣的。
  • 因此,模板在多個動態加載的共享庫中實例化的事實完全沒有區別。鏈接器似乎只是簡單地使用第一個加載的實例並忽略其餘部分。

的Windows兩個實例都'有點'不同:

  • typeid的不同實例返回不同的地點type_info對象。然而,這些物體在使用==進行測試時是相同的。相應的哈希碼也相同。它看起來像在Windows上類型之間的平等是使用類型的名稱建立的 - 這是有道理的。到現在爲止還挺好。
  • 但是vtables這兩個實例是不同的。我不確定這是多少問題。在我的測試中,我能夠使用dynamic_castTypeIdTest的一個實例向下轉錄到跨共享庫邊界的派生類型。
  • 還有一個問題是,每個實例都使用自己的靜態字段副本data。這可能會導致很多問題,並且基本上禁止模板類中的靜態字段。

總體而言,似乎連在Windows事情並不像我想象的那樣糟糕,但我仍然不願意使用這種方法,因爲模板實例仍然使用不同的虛函數表靜態存儲。有誰知道如何避免這個問題?我沒有找到任何解決方案。

+1

我想你在編譯時試圖解決運行時問題。我認爲在這種情況下,繼承和虛擬函數比模板更直接,更容易出錯(因爲正如你所指出的那樣,你正在嘗試做的事情有很多魔法)。 – 2012-02-14 10:17:48

+0

我認爲這個問題的實質是沒有辦法讓我的手掌握一個獨特的類型標識符(即使在運行時),這個標識符可以跨插件邊界使用。至少在Windows中似乎是這種情況。我不確定繼承和虛函數是否可行,因爲每個插件都可以貢獻新的類,這些類也應該是任意適應的。你有一個想法,它可以做什麼? – 2012-02-14 10:29:01

+0

您不能在編譯時從插件調整arbirtrary類型,因爲您需要所述插件的頭文件。這打破了插件的目的,你可以同時編譯所有的東西。 很明顯,你不能在運行時使用C++中的arbirtrary類型。另一方面,您可以調整具有相同基類的任意類型。 如果這還不夠,那麼每個插件都必須提供一個註冊函數,該函數可以在程序開始時被調用,該註冊函數將進行必要的註冊所有適應類型及其各自的適配函數。 – 2012-02-14 10:36:25

回答

1

我認爲加速擴建涉及的正是這種問題域:

特別是lar你會對作者寫在this blog post: "Resource Management Across DLL Boundaries中的內容感興趣:

RTTI並不總是按照DLL邊界期望的那樣工作。看看type_info類,看看我如何處理。

我不確定他的解決方案是否確實健壯,但他確實給出了這個想法。實際上,有一些使用Boost Extensions的示例,您可以放棄,您可能需要使用它。

+0

感謝您的建議。我看了一下Boost Extension,但它似乎沒有提供解決我的問題的方法。我已經在使用Qt(請參閱更新後的問題)來管理我的跨平臺插件基礎架構,並且效果很好。從我看到的Boost Extension只是一個插件管理器框架,它提供了與Qt的插件系統類似的功能。 我的問題非常具體,Qt和Boost Extension都沒有提供這方面的任何細節。 – 2012-02-15 09:46:23