Подтвердить что ты не робот

Взаимодействие с классами С++ из 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? Кажется, я ничего не могу найти на этом.

4b9b3361

Ответ 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])
    }
}