Ich möchte eine pluginartige Architektur verwenden, um Module in eine Cocoa-basierte Anwendung laden zu können. Alle Module verwenden dieselbe API, aber Name und Anzahl der Module können variieren und sind beim Erstellen der Anwendung nicht bekannt.Dynamisches Laden der Laufzeit von Cocoa Bundles mit C++ Code
Derzeit verwende ich statische Bibliotheken, aber das erfordert, dass ich die Anwendung jedes Mal neu kompiliere, wenn ich ein Modul hinzufüge oder entferne. Ich möchte in der Lage sein, dies dynamisch zu tun - d. H. Meine Anwendung neu starten, um eine Liste von Modulen zu aktualisieren, die als Dateien hinzugefügt wurden.
Ich erwäge 2 Ansätze:
- Verwenden dynamische Bibliotheken (.dylib-Dateien) und laden sie zur Laufzeit mit dlopen() und dlsym()
- Verwenden Bundles (.bundle-Dateien) und Last sie zur Laufzeit mit den Cocoa Funktionen
Erschwerend kommt hinzu, der Module Code ist C++ (Legacy-Code) mit einer Schnittstelle wie folgt aus:
// 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;
}
Eine der aktuellen statischen Module würde wie folgt (Das Modul implementiert die statischen erstellen/Funktionen zerstören und den Rest der API) definiert werden:
#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;
}
}
Wegen name-Mangeln in C++ unter Verwendung des dylib Ansatz scheint nicht durchführbar, da die Anwendung nicht in der Lage wäre, die Symbole nach Namen zu lokalisieren, ohne sie hart zu codieren.
Daher versuche ich Objective-C-Wrapper für die Module zu verwenden und sie dann als NSBundles aus Resources/Plugins-Ordner in der .app zu importieren.
// 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
Mit diesem kann ich erfolgreich eine .bundle-Datei erstellen. Ich versuche dann dieses Bündel in ein Cocoa-basierten Test-App zu importieren:
#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;
}
der Linker ist jedoch nicht in der Lage zu finden „_OBJC_CLASS _ $ _ MyModule_ObjC“ ... was Sinn macht, da Projekt der Testanwendung nicht Include MyModule_ObjC.mm, nur .h. Wenn Sie das .mm hinzufügen, wird es die statischen Implementierungen von create/destroy nicht finden, da das Modul nicht mehr statisch verknüpft ist. Ich möchte diese create/destroy-Implementierungen jedoch im Plugin/Bundle haben.
Im Prinzip klingt mein Ansatz?
Wenn nicht, welchen Ansatz würden Sie empfehlen, damit diese Plugin-Architektur funktioniert?
Vielen Dank im Voraus und Entschuldigung für die lange Post.
Danke, Jens! Es scheint so, als ob nur diese zusätzliche Ebene der Indirektion im Objective-C Wrapper fehlte :-) Es ist jetzt erfolgreich verlinkt und ich kann 2 unabhängige Module laden. – SideLobe
Das ist großartig! Freut mich, dass du rennst. –