SwiftのC ++クラスとの対話


89

私はC ++で書かれたクラスの重要なライブラリを持っています。私はそれらをSwiftコードとして書き直すのではなく、Swift内のある種のブリッジを介してそれらを利用しようとしています。主な動機は、C ++コードが複数のプラットフォームで使用されるコアライブラリを表すことです。事実上、私はSwiftベースのUIを作成して、コア機能がOSXで機能できるようにしています。

「SwiftからC ++関数を呼び出すにはどうすればよいですか」という質問が他にもあります。これは私の質問ではありません。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))

私の質問はより具体的です。Swift内からC ++クラスをインスタンス化して操作するにはどうすればよいですか?これについては何も公開されていないようです。


9
私は個人的に、関連するすべてのC ++呼び出しを再現し、それらをC ++クラスの保持されたインスタンスに転送するObjective-Cクラスを公開するプレーンなObjective-C ++ラッパーファイルを作成することに頼りました。私の場合、C ++クラスと呼び出しの数は少ないので、特に労働集約的ではありません。しかし、誰かがもっと良いものを思いついたことを期待して、これを答えとして主張することは控えます。
トミー

1
まあ、それは何かです...待って見て(そして希望して)みましょう。
David Hoelzer 2016

IRCを介して、実際のC ++オブジェクトへのvoidポインターを維持し、Cブリッジとオブジェクトへのポインターを介して渡されるだけの必要なメソッドを公開するSwiftラッパークラスを作成するという提案を受けました。
David Hoelzer 2016

4
これに対する更新として、Swift 3.0がリリースされ、以前の約束にもかかわらず、C ++の相互運用性は「範囲外」としてマークされるようになりました。
David Hoelzer 2016

回答:


63

私は完全に扱いやすい答えを考え出しました。これをどれだけきれいにしたいかは、完全にあなたがやろうとしている仕事の量に基づいています。

まず、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ブリッジを使用していますが、Objective-CまたはObjective-C ++での再コーディングに頼ることなく、C ++オブジェクトを透過的に効果的に使用しています。


13
ここには、見逃されている重要な問題がいくつかあります。1つ目は、デストラクタが呼び出されず、オブジェクトがリークされることです。2つ目は、例外が厄介な問題を引き起こす可能性があることです。
Michael Anderson

2
この質問への回答で多く
Michael Anderson

2
@DavidHoelzer、このアイデアを強調したかったのは、一部の開発者がC ++ライブラリ全体をラップして、Swiftで記述されたとおりに使用しようとするためです。「Swift内からC ++クラスをインスタンス化して操作する」方法の優れた例を示しました。そして、このテクニックは慎重に使用する必要があることを付け加えたいと思います。あなたの例をありがとう!
Nicolai Nita

1
これが「完璧」だとは思わないでください。Objective-Cラッパーを作成してSwiftで使用するのとほぼ同じだと思います。
Bagusflyer

1
@Bagusflyer確かに... Objective-Cラッパーではないことを除いて。
DavidHoelzer20年

11

Swiftには現在C ++相互運用機能がありません。これは長期的な目標ですが、近い将来に発生する可能性はほとんどありません。


25
「Cラッパーの使用」を「C ++相互運用」の形式と呼ぶと、用語がかなり
長くなり

6
おそらく、しかしそれらを使用してC ++クラスをインスタンス化し、それに対してメソッドを呼び出すことができるようにすることで、それは有用になり、質問を満たします。
David Hoelzer 2016

2
実際、それはもはや長期的な目標ではありませんが、代わりに、ロードマップで「範囲外」とマークされています。
David Hoelzer 2017年

4
C-ラッパーを使用することなど、任意の言語やPython、Javaの、ほぼすべてのC ++相互運用のための標準的なアプローチである
アンドリュー・ポール・シモンズ

8

独自のソリューションに加えて、それを行う別の方法があります。C ++コードをObjective-C ++で呼び出すか、直接記述することができます。

したがって、C ++コードの上にObjective-C ++ラッパーを作成し、適切なインターフェイスを作成できます。

次に、SwiftコードからObjective-C ++コードを呼び出します。Objective-C ++コードを記述できるようにするには、ファイル拡張子の名前を.mから.mmに変更する必要がある場合があります。

必要に応じて、C ++オブジェクトによって割り当てられたメモリを解放することを忘れないでください。


7

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);
};

迅速:

class ViewController: UIViewController {
    func send(friend: Contact) {
        let c = Contact()

        contact.sendMessage("Hello", friend)
        contact.addTags(["a","b","c"])
        contact.addFriends([friend])
    }
}

面白い。2019
。– DavidHoelzer19年

@ boris-rasinこの例ではswiftブリッジへの直接のc ++が見つかりません。この機能は計画に含まれていますか?
sage4 4419年

2
はい、現時点ではSwiftブリッジへの直接C ++はありません。しかし、C ++からObjCへのブリッジはSwiftで完全にうまく機能します(AppleのObjCからSwiftへのブリッジを介して)。現在、ダイレクトブリッジに取り組んでいます(場合によってはパフォーマンスが向上します)。
ボリスRASIN

1
そうそう、あなたは完全に正しいですが、Linuxの素早いプロジェクトではそうではありません。あなたの実装を見るのを楽しみにしています。
sage4 4419年

6

別の回答として述べたように、ObjC ++を使用して対話する方がはるかに簡単です。ファイルに.mおよびxcode / clangの代わりに.mmという名前を付けるだけで、そのファイル内のc ++にアクセスできます。

ObjC ++はC ++の継承をサポートしていないことに注意してください。ObjC ++でc ++クラスをサブクラス化したいのですが、できません。サブクラスをC ++で記述し、それをObjC ++クラスにラップする必要があります。

次に、swiftからobjcを呼び出すために通常使用するブリッジヘッダーを使用します。


2
あなたは要点を逃したかもしれません。現在OSXでもサポートしている非常に大規模なマルチプラットフォームC ++アプリケーションがあるため、インターフェイスが必要です。Objective C ++は、実際にはプロジェクト全体のオプションではありません。
David Hoelzer 2018年

私はあなたを理解しているかどうかわかりません。呼び出しをswiftとc ++の間、またはその逆にする場合は、ObjC ++が必要です。プロジェクト全体ではなく、境界だけをobjc ++で記述する必要があります。
nnrales 2018年

1
ええ、私はあなたの質問を調べました。あなたはSwiftからC ++を直接操作しようとしているようです。どうすればいいのかわからない。
nnrales 2018年

1
それが私の投稿された答えが達成することです。C関数を使用すると、オブジェクトを任意のメモリの塊として出し入れしたり、いずれかの側でメソッドを呼び出したりすることができます。
David Hoelzer 2018年

1
@DavidHoelzerかっこいいと思いますが、この方法でコードをもっと複雑にしませんか?主観的だと思います。ObjC ++ラッパーは、私にはすっきりしているようです。
nnrales 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.