Tengo una biblioteca importante de clases escritas en C ++. Estoy tratando de hacer uso de ellos a través de algún tipo de puente dentro de Swift en lugar de reescribirlos como código Swift. La motivación principal es que el código C ++ representa una biblioteca central que se utiliza en múltiples plataformas. Efectivamente, solo estoy creando una interfaz de usuario basada en Swift para permitir que la funcionalidad principal funcione en OS X.
Hay otras preguntas que plantean: "¿Cómo llamo a una función C ++ desde Swift?". Ésta no es mi pregunta. Para pasar a una función de C ++, lo siguiente funciona bien:
Definir un encabezado puente a través de "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 */
El código Swift ahora puede llamar a funciones directamente
let type = String.fromCString(imageType(filename))
let dump = String.fromCString(hexdump(filename))
Mi pregunta es más específica. ¿Cómo puedo crear una instancia y manipular una clase C ++ desde Swift? Parece que no puedo encontrar nada publicado sobre esto.
Respuestas:
He elaborado una respuesta perfectamente manejable. Lo limpio que le gustaría que fuera esto se basa completamente en la cantidad de trabajo que esté dispuesto a hacer.
Primero, tome su clase C ++ y cree funciones de "envoltura" de C para interactuar con ella. Por ejemplo, si tenemos esta clase C ++:
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); };
Luego implementamos estas funciones de C ++:
#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; }
El encabezado del puente luego contiene:
#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 */
Desde Swift, ahora podemos instanciar el objeto e interactuar con él así:
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!
Entonces, como puede ver, la solución (que en realidad es bastante simple) es crear contenedores que crearán una instancia de un objeto y devolverán un puntero a ese objeto. Esto luego se puede pasar de nuevo a las funciones contenedoras que pueden tratarlo fácilmente como un objeto conforme a esa clase y llamar a las funciones miembro.
Haciéndolo más limpio
Si bien este es un comienzo fantástico y demuestra que es completamente factible usar clases de C ++ existentes con un puente trivial, puede ser aún más limpio.
Limpiar esto simplemente significaría que eliminamos el
UnsafeMutablePointer<Void>
del medio de nuestro código Swift y lo encapsulamos en una clase Swift. Esencialmente, usamos las mismas funciones contenedoras de C / C ++ pero las interconectamos con una clase Swift. La clase Swift mantiene la referencia del objeto y esencialmente solo pasa todas las llamadas de referencia de método y atributo a través del puente al objeto C ++.Una vez hecho esto, todo el código puente está completamente encapsulado en la clase Swift. Aunque todavía estamos usando un puente C, estamos usando efectivamente objetos C ++ de forma transparente sin tener que recurrir a recodificarlos en Objective-C o Objective-C ++.
fuente
Swift no tiene interoperabilidad C ++ actualmente. Es un objetivo a largo plazo, pero es muy poco probable que suceda en un futuro próximo.
fuente
Además de su propia solución, hay otra forma de hacerlo. Puede llamar o escribir directamente código C ++ en aim-c ++.
Por lo tanto, puede crear un contenedor de objetivo-C ++ sobre su código C ++ y crear una interfaz adecuada.
Luego llame al código objetivo-C ++ desde su código rápido. Para poder escribir código objetivo-C ++, es posible que deba cambiar el nombre de la extensión del archivo de .m a .mm
No olvide liberar la memoria asignada por sus objetos C ++ cuando sea adecuado.
fuente
Como se mencionó en otra respuesta, usar ObjC ++ para interactuar es mucho más fácil. Simplemente nombre sus archivos .mm en lugar de .my xcode / clang, le da acceso a c ++ en ese archivo.
Tenga en cuenta que ObjC ++ no admite la herencia de C ++. Si desea subclasificar una clase de C ++ en ObjC ++, no puede. Tendrá que escribir la subclase en C ++ y envolverla alrededor de una clase ObjC ++.
Luego use el encabezado de puente que normalmente usaría para llamar a objc desde swift.
fuente
Puede utilizar Scapix Language Bridge para conectar automáticamente C ++ a Swift (entre otros idiomas). Código puente generado automáticamente sobre la marcha directamente desde archivos de encabezado C ++. He aquí un ejemplo :
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); };
Rápido:
class ViewController: UIViewController { func send(friend: Contact) { let c = Contact() contact.sendMessage("Hello", friend) contact.addTags(["a","b","c"]) contact.addFriends([friend]) } }
fuente