本編「【Unity】iOSネイティブプラグイン開発を完全に理解する」の付録記事です。
記事中での用語や略称についてはそのまま本編に倣う形で記載していきます。
「Objective-C
は何となく聞いたことあるけど、Objective-C++
は聞いたこと無い」「これらの違いは何?」と思う方も居るかもしれないので簡単に補足します。1
ざっくりと言ってしまうと**「Objective-C
とC++
を混在させることが出来る言語」**になります。
Objective-Cとの違い
ObjCはあくまで「C言語をベースに拡張した言語 (言語仕様的にはC言語の完全上位互換な言語)」であり、ObjCからは「C++の構文や機能、C++で書かれた既存のライブラリ」などを利用することが出来ません。
**一方でObjC++の方は「C++の構文や機能、C++で書かれた既存のライブラリ」を利用することが可能となり、**これによって「C++で書かれた既存のライブラリ」と言った資産を使い回すことができるようになります。2
導入方法
Xcode上から**「ObjCのソースファイルの拡張子(.m
)」を「ObjC++のソースファイルの拡張子(.mm
)」に変換するだけで自動的にObjC++として認識されるようになります。**
(Xcode上の○○Settingsをいじって〜的な操作は必要無く、本当にこれだけ)
Unity上でも同様に、拡張子が.mm
のソースファイルをAssets以下にインポートすると「ObjC++で書かれたネイティブプラグイン」として扱われるようになります。
P/InvokeでObjC++(.mm)
のコードを呼び出すならextern "C"
を指定して外部宣言する必要がある
C#からP/Invokeで「拡張子が.mm
のソースに実装されているObjC++
のコード」を呼び出すなら、P/Invokeで呼び出される関数を外部宣言する際にextern "C"
を指定する必要があります。
(言い方を変えるとC言語のリンケージを指定する必要がある)
サンプルコードを載せておくと以下のようになります。
(この際には__cplusplus
マクロも用いてC++コンパイラから読まれたときだけ有効にしている)
// MARK:- extern "C" (Cリンケージで宣言)
#ifdef __cplusplus
extern "C" {
#endif
// NOTE: この関数が実際にUnity(C#)から呼び出される
int printHelloWorld() {
return [Example printHelloWorld];
}
#ifdef __cplusplus
}
#endif
逆にObjC(.m)
ならextern "C"
は不要
逆に「拡張子が.m
のソースに実装されているObjC
のコード」を呼び出すならこれを行う必要はなく、外部宣言時に以下のように実装することでC#からP/Invokeで呼び出せるようになります。
// NOTE: この関数が実際にUnity(C#)から呼び出される
// 拡張子が`.m`のソースなら、これだけでC#から呼び出し可能
extern int printHelloWorld() {
return [Example printHelloWorld];
}
なぜObjC(.m)
だと不要なのか?
これらの理由について話すと、C++のコンパイラで「名前マングリング」と言う処理が行われているのが原因であり、C言語ベースのObjCだと名前マングリングが行われない為にextern "C"
を付けなくても呼び出せるようになってます。
もっと具体的に「何が起こっているのか?」の詳細については以下の記事が参考になります。