1. ホーム
  2. c++

[解決済み] SwiftからC++のクラスとインタラクトする

2023-02-15 04:23:49

質問

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

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

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

どのように解決するのですか?

私は完全に管理可能な答えを導き出しました。 どの程度きれいにしたいかは、あなたがどの程度の作業をしたいかによります。

まず、C++のクラスを取り出し、それとインターフェイスするためにCの"wrapper"関数を作成します。 例えば、この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 クラスにカプセル化することです。 基本的に、私たちは同じ C/C++ ラッパー関数を使用しますが、Swift クラスでそれらをインターフェイス化します。 Swiftクラスは、オブジェクトの参照を維持し、本質的に、C++オブジェクトへのブリッジを通して、すべてのメソッドと属性参照の呼び出しを渡すだけです!Swiftクラスは、オブジェクトの参照を維持し、本質的に、すべてのメソッドと属性参照の呼び出しを渡すだけです。

これを行うことで、すべてのブリッジングコードはSwiftクラスで完全にカプセル化されます。 C ブリッジをまだ使用しているにもかかわらず、Objective-C または Objective-C++ でそれらを再コード化することなく、C++ オブジェクトを効果的に透過的に使用しています。