Ich habe eine bedeutende Bibliothek von Klassen in C ++ geschrieben. Ich versuche, sie über eine Art Brücke in Swift zu verwenden, anstatt sie als Swift-Code umzuschreiben. Die Hauptmotivation ist, dass der C ++ - Code eine Kernbibliothek darstellt, die auf mehreren Plattformen verwendet wird. Eigentlich erstelle ich nur eine Swift-basierte Benutzeroberfläche, damit die Kernfunktionalität unter OS X funktioniert.
Es gibt andere Fragen: "Wie rufe ich eine C ++ - Funktion von Swift aus auf?" Das ist nicht meine Frage. Um eine Brücke zu einer C ++ - Funktion zu schlagen, funktioniert Folgendes einwandfrei:
Definieren Sie einen Bridging-Header durch "C".
#ifndef ImageReader_hpp
#define ImageReader_hpp
#ifdef __cplusplus
extern "C" {
#endif
const char *hexdump(char *filename);
const char *imageType(char *filename);
#ifdef __cplusplus
}
#endif
#endif /* ImageReader_hpp */
Swift-Code kann jetzt Funktionen direkt aufrufen
let type = String.fromCString(imageType(filename))
let dump = String.fromCString(hexdump(filename))
Meine Frage ist spezifischer. Wie kann ich eine C ++ - Klasse in Swift instanziieren und bearbeiten? Ich kann anscheinend nichts dazu finden.
Antworten:
Ich habe eine perfekt handhabbare Antwort ausgearbeitet. Wie sauber dies sein soll, hängt ganz davon ab, wie viel Arbeit Sie bereit sind.
Nehmen Sie zuerst Ihre C ++ - Klasse und erstellen Sie C-Wrapper-Funktionen, um eine Schnittstelle zu ihr herzustellen. Wenn wir zum Beispiel diese C ++ - Klasse haben:
class MBR { std::string filename; public: MBR (std::string filename); const char *hexdump(); const char *imageType(); const char *bootCode(); const char *partitions(); private: bool readFile(unsigned char *buffer, const unsigned int length); };
Wir implementieren dann diese C ++ - Funktionen:
#include "MBR.hpp" using namespace std; const void * initialize(char *filename) { MBR *mbr = new MBR(filename); return (void *)mbr; } const char *hexdump(const void *object) { MBR *mbr; static char retval[2048]; mbr = (MBR *)object; strcpy(retval, mbr -> hexdump()); return retval; } const char *imageType(const void *object) { MBR *mbr; static char retval[256]; mbr = (MBR *)object; strcpy(retval, mbr -> imageType()); return retval; }
Der Bridge-Header enthält dann:
#ifndef ImageReader_hpp #define ImageReader_hpp #ifdef __cplusplus extern "C" { #endif const void *initialize(char *filename); const char *hexdump(const void *object); const char *imageType(const void *object); #ifdef __cplusplus } #endif #endif /* ImageReader_hpp */
Von Swift aus können wir jetzt das Objekt instanziieren und wie folgt damit interagieren:
let cppObject = UnsafeMutablePointer<Void>(initialize(filename)) let type = String.fromCString(imageType(cppObject)) let dump = String.fromCString(hexdump(cppObject)) self.imageTypeLabel.stringValue = type! self.dumpDisplay.stringValue = dump!
Wie Sie sehen können, besteht die Lösung (die eigentlich ziemlich einfach ist) darin, Wrapper zu erstellen, die ein Objekt instanziieren und einen Zeiger auf dieses Objekt zurückgeben. Dies kann dann an die Wrapper-Funktionen zurückgegeben werden, die es leicht als ein Objekt behandeln können, das dieser Klasse entspricht, und die Elementfunktionen aufrufen können.
Sauberer machen
Dies ist zwar ein fantastischer Start und beweist, dass es durchaus machbar ist, vorhandene C ++ - Klassen mit einer trivialen Brücke zu verwenden, aber es kann noch sauberer sein.
Dies zu bereinigen würde einfach bedeuten, dass wir den
UnsafeMutablePointer<Void>
Code aus der Mitte unseres Swift-Codes entfernen und ihn in eine Swift-Klasse einkapseln. Im Wesentlichen verwenden wir dieselben C / C ++ - Wrapperfunktionen, verbinden sie jedoch mit einer Swift-Klasse. Die Swift-Klasse verwaltet die Objektreferenz und übergibt im Wesentlichen alle Methoden- und Attributreferenzaufrufe über die Bridge an das C ++ - Objekt!Danach ist der gesamte Bridging-Code vollständig in der Swift-Klasse gekapselt. Obwohl wir immer noch eine C-Brücke verwenden, verwenden wir C ++ - Objekte effektiv transparent, ohne sie in Objective-C oder Objective-C ++ neu codieren zu müssen.
quelle
Swift hat derzeit kein C ++ - Interop. Es ist ein langfristiges Ziel, aber es ist sehr unwahrscheinlich, dass es in naher Zukunft erreicht wird.
quelle
Neben Ihrer eigenen Lösung gibt es noch eine andere Möglichkeit. Sie können C ++ - Code in Objective-C ++ aufrufen oder direkt schreiben.
Sie können also einen Objective-C ++ - Wrapper über Ihrem C ++ - Code erstellen und eine geeignete Schnittstelle erstellen.
Rufen Sie dann den Objective-C ++ - Code aus Ihrem schnellen Code auf. Um Objective-C ++ - Code schreiben zu können, müssen Sie möglicherweise die Dateierweiterung von .m in .mm umbenennen
Vergessen Sie nicht, bei Bedarf den von Ihren C ++ - Objekten zugewiesenen Speicher freizugeben.
quelle
Wie bereits erwähnt, ist die Verwendung von ObjC ++ für die Interaktion viel einfacher. Benennen Sie einfach Ihre Dateien .mm anstelle von .m und xcode / clang, um Zugriff auf c ++ in dieser Datei zu erhalten.
Beachten Sie, dass ObjC ++ die C ++ - Vererbung nicht unterstützt. Wenn Sie eine C ++ - Klasse in ObjC ++ unterordnen möchten, können Sie dies nicht. Sie müssen die Unterklasse in C ++ schreiben und um eine ObjC ++ - Klasse wickeln.
Verwenden Sie dann den Bridging-Header, den Sie normalerweise verwenden würden, um objc von swift aus aufzurufen.
quelle
Mit Scapix Language Bridge können Sie C ++ (unter anderem) automatisch mit Swift verbinden. Bridge-Code wird automatisch direkt aus C ++ - Header-Dateien generiert. Hier ist ein Beispiel :
C ++:
#include <scapix/bridge/object.h> class contact : public scapix::bridge::object<contact> { public: std::string name(); void send_message(const std::string& msg, std::shared_ptr<contact> from); void add_tags(const std::vector<std::string>& tags); void add_friends(std::vector<std::shared_ptr<contact>> friends); };
Schnell:
class ViewController: UIViewController { func send(friend: Contact) { let c = Contact() contact.sendMessage("Hello", friend) contact.addTags(["a","b","c"]) contact.addFriends([friend]) } }
quelle