Взаимодействие с классами С++ из Swift
У меня есть значительная библиотека классов, написанная на С++. Я пытаюсь использовать их через некоторый тип моста в Swift, а не переписывать их как код Swift. Основная мотивация заключается в том, что код С++ представляет собой базовую библиотеку, которая используется на нескольких платформах. Фактически, я просто создаю пользовательский интерфейс Swift, чтобы позволить основной функциональности работать под OS X.
Возникают другие вопросы: "Как мне вызвать функцию C++ из Swift". Это не мой вопрос. Чтобы перейти к функции С++, следующее прекрасно работает:
Определите заголовок моста через "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 */
Быстрый код теперь может напрямую вызывать функции
let type = String.fromCString(imageType(filename))
let dump = String.fromCString(hexdump(filename))
Мой вопрос более конкретный. Как я могу создать экземпляр и управлять классом С++ из Swift? Кажется, я ничего не могу найти на этом.
Ответы
Ответ 1
Я разработал вполне управляемый ответ. Насколько чистым вы хотите, чтобы это было, полностью зависит от того, сколько работы вы готовы сделать.
Сначала возьмите свой С++-класс и создайте 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);
};
Затем мы реализуем эти С++-функции:
#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;
}
Затем заголовок моста содержит:
#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 */
Из Swift мы можем теперь создать экземпляр объекта и взаимодействовать с ним следующим образом:
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!
Итак, как вы можете видеть, решение (на самом деле довольно простое) состоит в создании оберток, которые будут создавать экземпляр объекта и возвращать указатель на этот объект. Затем это может быть передано обратно в функции обертки, которые могут легко относиться к нему как к объекту, соответствующему этому классу, и вызвать функции-члены.
Сделать его чистым
Хотя это фантастический старт и доказывает, что вполне возможно использовать существующие классы С++ с тривиальным мостом, он может быть даже более чистым.
Очистка этого просто означает, что мы удаляем UnsafeMutablePointer<Void>
с середины нашего кода Swift и инкапсулируем его в класс Swift. По сути, мы используем те же функции обертки C/С++, но связываем их с классом Swift. Класс Swift поддерживает ссылку на объект и, по сути, просто передает все вызовы ссылок на метод и атрибуты через мост на объект С++!
Сделав это, весь код моста полностью инкапсулирован в классе Swift. Несмотря на то, что мы все еще используем мост C, мы эффективно используем объекты С++ прозрачно, не прибегая к их перекодированию в Objective-C или Objective-C ++.
Ответ 2
В настоящее время Swift не имеет взаимодействия с С++. Это долгосрочная цель, но вряд ли произойдет в ближайшем будущем.
Ответ 3
В дополнение к вашему собственному решению есть другой способ сделать это. Вы можете вызывать или напрямую писать код С++ в objective-c ++.
Итак, если вы создаете обертку objective-c ++ поверх кода на С++ и создаете подходящий интерфейс
и вызовите objective-c ++ код из вашего быстрого кода. Чтобы иметь возможность писать код objective-c ++, вам, возможно, придется переименовать расширение файла с .m на .mm
Не забудьте освободить память, выделенную вашими объектами С++, когда это подходит.
Ответ 4
Как уже упоминалось, использование ObjC++ для взаимодействия намного проще. Просто назовите свои файлы.mm вместо.m и xcode/clang, дает вам доступ к c++ в этом файле.
Обратите внимание, что ObjC++ не поддерживает наследование c++. Я хочу подклассифицировать класс c++ в ObjC++, вы не можете. Вам придется написать подкласс в c++ и обернуть его вокруг класса ObjC++.
Затем используйте заголовок моста, который вы обычно используете для вызова objc из swift.
Ответ 5
У меня та же проблема использования библиотеки C++ со Swift. Недавно у меня был проект, который хочет использовать opencv 4 и tesserect 4 для обработки изображений некоторых документов на Mac. Я не знаю, как я могу использовать swift4, чтобы легче вызывать эти библиотеки.
Это действительно болезненный процесс написания обёрток. Если есть инструмент, который может автоматически генерировать все эти обертки, это сэкономит много усилий.
Ответ 6
Вы можете использовать Scapix Language Bridge для автоматического соединения C++ со Swift (среди других языков). Код моста автоматически генерируется на лету непосредственно из заголовочных файлов C++. Вот пример:
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);
};
Swift:
class ViewController: UIViewController {
func send(friend: Contact) {
let c = Contact()
contact.sendMessage("Hello", friend)
contact.addTags(["a","b","c"])
contact.addFriends([friend])
}
}