Tenho uma biblioteca significativa de classes escritas em C ++. Estou tentando fazer uso deles por meio de algum tipo de ponte dentro do Swift, em vez de reescrevê-los como código Swift. A principal motivação é que o código C ++ representa uma biblioteca central que é usada em várias plataformas. Efetivamente, estou apenas criando uma IU baseada em Swift para permitir que a funcionalidade principal funcione no OS X.
Existem outras perguntas: "Como faço para chamar uma função C ++ do Swift." Esta não é minha pergunta. Para fazer a ponte para uma função C ++, o seguinte funciona bem:
Defina um cabeçalho de ponte atravé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 */
O código Swift agora pode chamar funções diretamente
let type = String.fromCString(imageType(filename))
let dump = String.fromCString(hexdump(filename))
Minha pergunta é mais específica. Como posso instanciar e manipular uma classe C ++ de dentro do Swift? Não consigo encontrar nada publicado sobre isso.
Respostas:
Eu encontrei uma resposta perfeitamente administrável. O grau de limpeza que você gostaria que isso fosse depende inteiramente de quanto trabalho está disposto a fazer.
Primeiro, pegue sua classe C ++ e crie funções C "wrapper" para fazer a interface com ela. Por exemplo, se tivermos esta classe 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); };
Em seguida, implementamos essas funções 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; }
O cabeçalho da ponte contém:
#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 */
A partir do Swift, agora podemos instanciar o objeto e interagir com ele assim:
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!
Então, como você pode ver, a solução (que na verdade é bastante simples) é criar wrappers que irão instanciar um objeto e retornar um ponteiro para esse objeto. Isso pode então ser passado de volta para as funções de invólucro, que podem facilmente tratá-lo como um objeto em conformidade com aquela classe e chamar as funções-membro.
Tornando-o mais limpo
Embora seja um começo fantástico e prove que é completamente viável usar as classes C ++ existentes com uma ponte trivial, pode ser ainda mais limpo.
Limpar isso significaria simplesmente que removemos o
UnsafeMutablePointer<Void>
do meio de nosso código Swift e o encapsulamos em uma classe Swift. Essencialmente, usamos as mesmas funções de wrapper C / C ++, mas as interfaces com uma classe Swift. A classe Swift mantém a referência do objeto e essencialmente apenas passa todas as chamadas de referência de método e atributo através da ponte para o objeto C ++!Feito isso, todo o código de bridging é completamente encapsulado na classe Swift. Mesmo que ainda estejamos usando uma ponte C, estamos efetivamente usando objetos C ++ de forma transparente, sem ter que recorrer a recodificá-los em Objective-C ou Objective-C ++.
fonte
O Swift não tem interoperabilidade C ++ atualmente. É uma meta de longo prazo, mas é muito improvável que aconteça em um futuro próximo.
fonte
Além de sua própria solução, existe outra maneira de fazer isso. Você pode chamar ou escrever diretamente o código C ++ em object-c ++.
Portanto, você pode criar um wrapper objetivo-C ++ no topo de seu código C ++ e criar uma interface adequada.
Em seguida, chame o código objetivo-C ++ do seu código swift. Para poder escrever código C ++ objetivo, pode ser necessário renomear a extensão do arquivo de .m para .mm
Não se esqueça de liberar memória alocada por seus objetos C ++ quando apropriado.
fonte
Como outra resposta mencionada, usar ObjC ++ para interagir é muito mais fácil. Basta nomear seus arquivos .mm em vez de .m e xcode / clang, e você terá acesso a c ++ nesse arquivo.
Observe que ObjC ++ não oferece suporte à herança de C ++. Se você quiser criar uma subclasse de uma classe c ++ em ObjC ++, não pode. Você terá que escrever a subclasse em C ++ e envolvê-la em uma classe ObjC ++.
Em seguida, use o cabeçalho de ponte que você normalmente usaria para chamar objc do swift.
fonte
Você pode usar o Scapix Language Bridge para ligar automaticamente o C ++ ao Swift (entre outras linguagens). Código de ponte gerado automaticamente em tempo real, diretamente de arquivos de cabeçalho C ++. Aqui está um exemplo :
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]) } }
fonte