2016-07-11 3 views
0

プラグインのようなアーキテクチャを使用して、モジュールをCocoaベースのアプリケーションにロードできるようにします。すべてのモジュールは同じAPIを使用しますが、モジュールの名前と数はさまざまで、アプリケーションをビルドするときには分かりません。C++コードをラップするCocoaバンドルの動的ランタイムロード

現在、私は静的ライブラリを使用していますが、これはアプリケーション私はモジュールを追加または削除するたびに再コンパイルする私を必要とします。これを動的に行うことができます。つまり、アプリケーションを再起動して、ファイルとして追加されたモジュールのリストを更新します。

私は2つのアプローチ検討している:

  • 使用バンドル(.bundleファイル)と負荷)(

    1. 使用動的ライブラリ(は.dylibファイル)とdlopenを使用して、実行時にそれらをロード()およびdlsymをそれら問題を複雑にココア機能

    を使用して、実行時に、モジュールのコードは、このようなインターフェイスを持つC++(レガシーコード)である:

    // MyModule_API.h 
    class MyModule_API { 
    public: 
        static MyModule* create(); 
        static void destroy(MyModule* m); 
    
        virtual void processMap(std::map<std::string, float>) = 0; 
        virtual std::vector<std::string> getNames() = 0; 
    } 
    

    現在の静的モジュールの1つは、この(モジュールは、静的作成/破棄機能とAPIの残りの部分を実装)のように定義されます。dylibを使用して、ため、C++での名前マングリングの

    #include "MyModule_API.h" 
    class MyModule : MyModule_API { 
    public: 
        explicit MyModule(std::string param1, std::string param2) : _param1(param1), _param2(param2) { } 
        ~MyModule() { }; 
        // MyModule_API: 
        void processMap(std::map<std::string, float>) override { ... } 
        std::vector<std::string> getNames() override { return std::vector<std::string({_param1, _param2}); } 
    private: 
        std::string _param1, _param2; 
    } 
    
    MyModule_API* MyModule_API::create() { 
        MyModule* m = new MyModule("foo", "bar");   
        //cast to base/API class before returning 
        return (MyModule_API*) m; 
    } 
    
    void MyModule_API::destroy(MyModule_API* m) { 
        if (m != nullptr) { 
         delete m; 
        } 
    } 
    

    アプリケーションはハードコーディングせずにシンボルを名前で見つけることができないため、実現不可能と考えられます。

    したがって、私はObjective-Cラッパーをモジュールに使用して、それらを.appのResources/PluginsフォルダーからNSBundlesとしてインポートしようとしています。

    // MyModule_ObjC.h 
    #import <Foundation/Foundation.h> 
    #include "MyModule_API.h" 
    @interface MyModule_ObjC : NSObject { 
        MyModule_API* _myModule; 
    } 
    - (id) init; 
    - (void) dealloc; 
    - (MyModule_API*) getMyModule; 
    @end 
    
    
    // MyModule_ObjC.mm 
    #import "MyModule_ObjC" 
    @implementation MyModule_ObjC 
    - (id)init { 
        self = [super init]; 
        if (self) { 
         _myModule = MyModule_API::create(); 
        } 
        return self; 
    } 
    - (void)dealloc { 
        MyModule_API::destroy(_myModule); 
    } 
    - (MyModule_API*) getMyModule { 
        return _myModule; 
    } 
    @end 
    

    これで、.bundleファイルを正常に構築できます。 私はその後、ココアベースのテストアプリにこのバンドルをインポートしよう:テストアプリケーションプロジェクトがないので、理にかなっている「_OBJC_CLASS _ $ _ MyModule_ObjC」...しかし、リンカは見つけることができません

    #import <Cocoa/Cocoa.h> 
    #import <mach-o/dyld.h> 
    #import "MyModule_ObjC.h" 
    #include "MyModule_API.h" 
    
    int main(int argc, const char * argv[]) {  
        NSBundle *appBundle = [NSBundle mainBundle]; 
        NSArray *bundlePaths = [appBundle pathsForResourcesOfType:@"bundle" inDirectory:@"PlugIns"]; 
        for (id bundlePath in bundlePaths) { 
         NSBundle* bundle; 
         bundle = [NSBundle bundleWithPath:bundlePath]; 
         [bundle load]; 
    
         if (bundle) { 
          MyModule_ObjC* moduleAPIClass = [bundle principalClass]; 
          if (moduleAPIClass) { 
           id moduleInstance; 
           moduleInstance = [[MyModule_ObjC alloc] init]; 
           if (moduleInstance) { 
            MyModule_API* module = [moduleInstance getMyModule]; 
           } 
          } 
         } 
        } 
        return 0; 
    } 
    

    MyModule_ObjC.mmのみが含まれます。 .mmを追加すると、モジュールが静的にリンクされていないため、create/destroyの静的実装が見つかりません。しかし、私はこれらの作成/破棄の実装をプラグイン/バンドルにしたい。

    私のアプローチは原則ですか?
    もしそうでなければ、このプラグインアーキテクチャを動作させるにはどのようなアプローチをお勧めしますか?

    ありがとうございました。ありがとうございました。

  • 答えて

    1

    まあ、あなたはほとんどそこにいます;-)。問題は、プラグインインターフェースを扱う方法にあります。現時点では、リンク時には未知であるため、コンパイラは実際のシンボルを見つけることができません。解決策は簡単です:

    クラスインターフェイスの代わりにプロトコルを使用してください。

    @protocol MyModule_ObjC <NSObject> 
    
    - (MyModule_API*) getMyModule; 
    
    @end 
    

    プラグインでは、このインターフェイスと対応するプリンシパルクラスを実装する必要があります。お使いのアプリケーションで

    // PluginA.h 
    
    #import <Foundation/Foundation.h> 
    #include "MyModule_ObjC.h" 
    
    @interface PluginA : NSObject<MyModule_ObjC> 
    
    @end 
    
    
    // PluginA.mm 
    
    #import "PluginA.h" 
    
    @implementation PluginA { 
    
        @private 
    
        MyModule_API* _myModule; 
    } 
    
    - (id)init { 
    
        self = [super init]; 
    
        if (self) { 
    
         _myModule = MyModule_API::create(); 
        } 
    
        return self; 
    } 
    
    - (void)dealloc { 
    
        MyModule_API::destroy(_myModule); 
    } 
    
    - (MyModule_API*) getMyModule { 
    
        return _myModule; 
    } 
    
    @end 
    

    、あなたはプロトコルに対してプラグインをロードする必要があります。

    #import <Cocoa/Cocoa.h> 
    #import <mach-o/dyld.h> 
    #import "MyModule_ObjC.h" 
    #include "MyModule_API.h" 
    
    int main(int argc, const char * argv[]) {  
        NSBundle *appBundle = [NSBundle mainBundle]; 
        NSArray *bundlePaths = [appBundle pathsForResourcesOfType:@"bundle" inDirectory:@"PlugIns"]; 
        for (id bundlePath in bundlePaths) { 
         NSBundle* bundle; 
         bundle = [NSBundle bundleWithPath:bundlePath]; 
         [bundle load]; 
    
         if (bundle) { 
          Class moduleAPIClass = [bundle principalClass]; 
          if (moduleAPIClass && [moduleAPIClass conformsToProtocol:@protocol(MyModule_ObjC)]) { 
           id<MyModule_ObjC> moduleInstance; 
           moduleInstance = [[moduleAPIClass alloc] init]; 
          } 
         } 
        } 
        return 0; 
    } 
    
    +0

    は、イェンスをありがとう! Objective-Cラッパーでこの間接指示のレベルが実際にはなくなったようです:-) これは正常にリンクし、2つの独立したモジュールをロードできます。 – SideLobe

    +0

    それは素晴らしいです!あなたがうまく動いてうれしいです。 –

    関連する問題