Взаимодействие с классами C++ от Swift
У меня есть значительная библиотека классов, написанных на C++. Я пытаюсь использовать их через какой-то тип моста в Swift, а не переписывать их как код Swift. Основной мотивацией является то, что код C++ представляет собой базовую библиотеку, которая используется на нескольких платформах. По сути, я просто создаю интерфейс на основе Swift, чтобы основные функции работали под OS X.
Есть и другие вопросы: "Как вызвать функцию C++ из Swift". Это не мой вопрос. Чтобы соединиться с функцией C++, хорошо работает следующее:
Определите соединительный заголовок через "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 теперь может вызывать функции напрямую
let type = String.fromCString(imageType(filename))
let dump = String.fromCString(hexdump(filename))
Мой вопрос более конкретный. Как я могу создать экземпляр класса C++ и манипулировать им изнутри Swift? Кажется, я не могу найти ничего опубликованного по этому вопросу.
4 ответа
Я разработал вполне управляемый ответ. Насколько вы хотите, чтобы это было чисто, полностью зависит от того, сколько работы вы готовы сделать.
Во-первых, возьмите свой класс C++ и создайте функции "обертки" C для взаимодействия с ним. Например, если у нас есть этот класс 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);
};
Затем мы реализуем эти функции 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;
}
Заголовок моста тогда содержит:
#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!
Итак, как вы можете видеть, решение (которое на самом деле довольно простое) заключается в создании оболочек, которые будут создавать экземпляр объекта и возвращать указатель на этот объект. Затем это может быть передано обратно в функции-оболочки, которые могут легко обрабатывать его как объект, соответствующий этому классу, и вызывать функции-члены.
Делать это чище
Хотя это фантастическое начало и доказывает, что вполне возможно использовать существующие классы C++ с тривиальным мостом, это может быть даже чище.
Очистка это просто означает, что мы удаляем UnsafeMutablePointer<Void>
с середины нашего кода Swift и инкапсулировать его в класс Swift. По сути, мы используем те же функции-оболочки C / C++, но связываем их с классом Swift. Класс Swift поддерживает ссылку на объект и, по сути, просто передает все вызовы ссылок на методы и атрибуты через мост в объект C++!
Сделав это, весь код моста полностью инкапсулирован в класс Swift. Несмотря на то, что мы все еще используем мост C, мы эффективно используем объекты C++ прозрачно, не прибегая к перекодированию их в Objective-C или Objective-C++.
У Swift в настоящее время нет взаимодействия с C++. Это долгосрочная цель, но вряд ли это произойдет в ближайшем будущем.
В дополнение к вашему собственному решению, есть еще один способ сделать это. Вы можете позвонить или напрямую написать C++ код в Objective-C++.
Так что если вы создаете обёртку для C++ на основе вашего C++ кода и создаете подходящий интерфейс
и вызовите код Objective-C++ из вашего быстрого кода. Чтобы иметь возможность писать код Objective-C++, вам может потребоваться переименовать расширение файла с.m на.mm
Не забывайте освобождать память, выделенную вашими объектами C++, когда это необходимо.
Вы можете использовать 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])
}
}
Как уже упоминалось в другом ответе, использовать ObjC++ для взаимодействия намного проще. Просто назовите ваши файлы.mm вместо.m и xcode/clang, и вы получите доступ к C++ в этом файле.
Обратите внимание, что ObjC++ не поддерживает наследование C++. Если вы хотите создать подкласс класса C++ в ObjC++, вы не можете. Вам придется написать подкласс на C++ и обернуть его вокруг класса ObjC++.
Затем используйте соединительный заголовок, который вы обычно используете для вызова objc из swift.