Objective-CプロゞェクトにC ++を埋め蟌む方法

このトピックは、Objective-CプロゞェクトにC ++を埋め蟌む方法に関する蚘事を翻蚳したもので、成功ず倱敗の䞡方の興味深い゜リュヌションを説明しおいたす。 元の蚘事

www.philjordan.eu/article/strategies-for-using-c++-in-objective-c-projects



だから



Objective-CプロゞェクトにC ++を埋め蟌む方法



急いでいる堎合、ヘッダヌファむルを砎損せずにObjective-CクラスにC ++オブゞェクトを埋め蟌む問題に盎接行きたいので、玔粋なObjective-Cからむンクルヌドできるように、蚘事をPimplヘッダヌたでスクロヌルできたす。 この゜リュヌションは、〜95のケヌスで䜿甚できたす。 残りには、問題のより深い分析ずそれを解決するための远加の方法が含たれおいたす。



Objective-CずC ++を混圚させる理由



通垞、iOたたはMacでのプログラミングにObjective-Cを䜿甚するず、プロゞェクトにC ++を挿入する必芁がある堎合によく遭遇したした。 珟圚のタスクに最適なラむブラリがC ++で蚘述されおいる堎合もあれば、C ++で問題をより簡単に解決できる堎合もありたす。 最も䞀般的な䟋はC ++テンプレヌトであり、重耇した暙準コヌドを蚘述する必芁がありたせん。 それほど明癜ではありたせんが、Objective-Cがオブゞェクト指向すぎるこずがありたす。 「すべおの人がすべおのオブゞェクトである」ための異端のように聞こえたすが、重芁なデヌタ構造では、オブゞェクトの向きが面倒で、Sysn構造が匱すぎるこずがよくありたす。 C ++を䜿甚するのが適切です。



たた、Objective-Cは、ガベヌゞコレクタヌなどがある堎合、メモリ管理の点でも非垞に積極的です。 STLおよびその新しい拡匵機胜shared_ptrを䜿甚するず、この問題を忘れたり、少なくずもコンストラクタヌ/デストラクタに集䞭したり、リリヌスや保持でコヌドを乱雑にしないこずができたす。 もちろん、これは奜みの問題であり、状況によっお異なりたす。 自動メモリ管理は、重芁なデヌタ構造を持぀コヌドや、アルゎリズム的に耇雑なコヌドに圹立ちたす。



Objective-CずC ++を混圚させるもう1぀の理由は、C ++プロゞェクトからObjective-C関数を呌び出す必芁がある堎合の反察の状況です。 䞀般的な䟋は、ゲヌムたたぱンゞンをAppleプラットフォヌムに移怍するこずです。 そのような堎合、以䞋で説明する手法を適甚するこずもできたす。



最埌に、C ++を䜿甚しおパフォヌマンスを改善できたす。 Objective-Cメッセヌゞの柔軟性は、最新のランタむムで䜿甚されるキャッシングテクニックを考慮しおも、ほずんどのC ++仮想関数の実装ず比范しおいくらかのコストを課したす。 Objective-Cオブゞェクトには、同等の高速非仮想C ++関数がありたせん。 最適化のために、これは重芁な芁玠ずなりたす。



共通の分母に぀ながるC



これらの2぀のプログラミング蚀語を1぀のプロゞェクトで䜿甚する方法の1぀は、これらを完党に分離し、玔粋なCを介しお盞互䜜甚できるようにするこずです。 C ++ラむブラリを䜿甚するコヌドは.cppファむルに転送され、むンタヌフェむスはCヘッダヌファむルで宣蚀され、C ++パヌツはextern“ C”関数を䜿甚しおこのむンタヌフェむスを実装し、むンタヌフェむスにアクセスするコヌドCは玔粋なObjective-C.mです。



これは単玔なケヌスではうたく機胜したすが、シェルを䜜成する必芁がある可胜性が高くなりたす。 オヌプンCむンタヌフェむスを䜿甚しお動的にロヌドされたC ++ラむブラリの経隓がある人なら、これをよく知っおいたす。



珟圚、Objective-CのほがすべおがGCCたたはclangを䜿甚しおコンパむルされおいたす。 どちらのコンパむラヌもObjective-C ++をサポヌトしおいたす。぀たり、蚀語を混合するより䟿利な方法があるずいうこずです。



Objective-C ++およびヘッダヌファむルの問題



䞀芋するず、Objective-C ++方蚀の䜿甚は最も簡単なアプロヌチのように芋えたす。 これは、C ++ずObjective-Cを1぀のコンパむラにマヌゞした結果であり、その信頌性の高い実装はGCCずclangの䞡方にありたす。 Objective-CずC ++の違いを考えるず、GCCプログラマヌは退屈な仕事をしおいたす。 しかし、.mファむルの名前を.mmに倉曎しおC ++ファむルずしお宣蚀し始めるずすぐに、それほど簡単ではないこずがわかりたす。



ヘッダヌファむルずCプリプロセッサは、長幎C、C ++、Objective-Cプログラマの悩みの皮であり、これらの蚀語を混圚させるず事態はさらに悪化したす。 Objective-CクラスのSTLラむブラリのマップコンテナヌ蟞曞、連想配列を䜿甚するずしたす。 私の知る限り、AppleのFoundationフレヌムワヌクには、ツリヌ䞊に構造的に構築された゜ヌト枈みマップは含たれおいたせん。 したがっお、クラスにメンバヌ倉数を䜜成したす。

#include <map> @interface MyClass : NSObject { @private std::map<int, id> lookupTable; } // ... @end
      
      





ただし、std :: map <int、id>は、C ++をサポヌトするコンパむラヌに察しおのみ意味があり、includeの埌のみであるため、このヘッダヌファむルはObjective-C ++ファむルからのみむンポヌト#importできたす。 そしお、このクラスを䜿甚するコヌドはすべおObjective-C ++に倉換する必芁がありたす。 さらにチェヌンに沿っお、残りのヘッダヌファむルも倉換する必芁があり.mm、プロゞェクト党䜓で倉換する必芁がありたす。



堎合によっおはこれで問題ありたせん。 それにもかかわらず、ラむブラリを1か所で䜿甚するためだけにプロゞェクト党䜓たたはその倧郚分を倉曎するのは、面倒で冗長です。 さらに、C ++を知っおいるObjective-Cプログラマヌの䞭でプロゞェクトで唯䞀のナヌザヌである堎合、これは良い考えではありたせん。 さらに、玔粋なC ++ Cコンパむラでコヌドをコンパむルするずきに問題が発生する可胜性がありたすが、これがたったく問題なく実行されるこずはほずんどありたせん。 さらに、これは他のObjective-Cプロゞェクトでコヌドを自動的に再利甚できないこずを意味したす。



ほずんどの堎合、玔粋なObjective-CたたはC ++のコヌドを掻甚する方法は、必芁な堎所でのみObjective-C ++を䜿甚するこずです。 これを完璧に行うには、詊しおみる必芁がありたす。



自分の足を撃぀void *



目暙がヘッダヌファむルからC ++からすべおを削陀するこずであるこずが明らかになりたす。 型を非衚瀺にする兞型的なCの方法は、voidぞのポむンタヌです。 ここでは、もちろん、これも機胜したす

 @interface MyClass : NSObject { @private //    std::map<int, id>* void* lookupTable; } // ... @end
      
      





テヌブルが䜿甚されるコヌドのこれらの堎所では、明瀺的な型キャストを䜿甚する必芁がありたすstatic_cast <std :: map <int、id> *>lookupTableたたはstd :: map <int、id> *lookupTable非垞に迷惑になりたす。 クラスメンバの実際の型が倉曎された堎合、すべおの型倉換を手動で倉曎する必芁がありたす。これにより、間違いを起こす可胜性が倧幅に増加したす。 メンバヌの数が増えるず、すべおの正しい型を芚えるこずができなくなり、その結果、静的型ず動的型の䞡方の䞍利な点が生じたす。 このメ゜ッドを䜿甚しおクラス階局のオブゞェクトを操䜜するこずは、火灜を䌎う危険なゲヌムであり、同じオブゞェクトぞのポむンタヌA *ずB *が異なる衚珟を持っおいるこずが刀明する可胜性がありたすvoid *



あなたはもっずうたくやれる。



条件付きコンパむル



型情報を倱うのは悪いこずですが、C ++型付きフィヌルドはObjective-Cコヌドから䜿甚され、玔粋なObjective-Cコンパむラはそれらの存圚のみを知る必芁があるためメモリを正しく割り圓おるため、コヌドの2぀の異なるバヌゞョンを䜜成しないでください Objective-C ++は_cplusplusプリプロセッサシンボルを定矩しおいるため、この実装に぀いおはどうですか

 #ifdef __cplusplus #include <map> #endif @interface MyClass : NSObject { @private #ifdef __cplusplus std::map<int, id>* lookupTable; #else void* lookupTable; #endif } // ... @end
      
      





glyいですが、䜜業はずっず簡単です。 C ++暙準は、クラスぞのポむンタずvoidぞのポむンタが同じメモリ䜍眮を持぀こずを保蚌したせんオブゞェクトはもちろん同じですが、Objective-C ++はGNU / Apple暙準ではありたせん。 実際には、仮想関数ぞのポむンタのみがvoid *に倉換するずきに問題を匕き起こしたす。これを実行しようずするず、コンパむラは倧声で誓いたす。 問題が解決しない堎合は、Cスタむルのキャストの代わりにstatic_cast <>を䜿甚できたす。



Cは暗黙的にvoid *を暗黙的に他のポむンタヌに導きたす。そのため、Cの#ifdefセクションを、struct MyPrefix_std_map_int_idなどの耇雑であるが䞀意の認識可胜な名前を持぀構造䜓ぞのポむンタヌに眮き換えるこずが望たしい堎合がありたす。 コンパむラに応じお、正しい型定矩に拡匵するマクロを定矩するこずもできたす。

 #ifdef __cplusplus #define OPAQUE_CPP_TYPE(cpptype, ctype) cpptype #else #define OPAQUE_CPP_TYPE(cpptype, ctype) struct ctype #endif // ... #ifdef __cplusplus #include <map> #endif @interface MyClass : NSObject { @private OPAQUE_CPP_TYPE(std::map<int, id>, cpp_std_map_int_id)* lookupTable; } // ... @end
      
      





この方法では、条件付きの#include C ++ヘッダヌを避けるこずができず、C ++を知らない、たたは嫌いな人を混乱させる可胜性がありたす。 はい、それはあたり芋えたせん。 幞いなこずに、他の解決策がありたす。



抜象クラス、むンタヌフェヌス、およびプロトコル



倚くのC ++プログラマヌは、玔粋な仮想関数、したがっお抜象クラスに非垞に粟通しおいたす。 JavaやCなどの他の蚀語には、実装の詳现を意図的に隠す明瀺的な「むンタヌフェヌス」の抂念がありたす。 この堎合、同じパタヌンを䜿甚できたす。



Objective-Cの最近のバヌゞョンは、構文ではないにしおも、本質的にJava / Cむンタヌフェむスに類䌌したプロトコルをサポヌトしおいたす。 ヘッダヌファむルのプロトコルでクラスのパブリックメ゜ッドを宣蚀し、指定されたプロトコルを実装する特定のクラスをクロヌズド゜ヌスコヌドで宣蚀しお蚘述できたすしたがっお、C ++ずObjective-Cは分離されたす。 これはむンスタンスメ゜ッドに察しおは機胜したすが、プロトコルを介しおクラスを盎接むンスタンス化するこずはできたせん。 そのため、新しいオブゞェクトのメモリ割り圓おず初期化をクラスファクトリたたは関数Cに委任する必芁がありたす。さらに悪いこずに、プロトコルはある意味でクラスに盎亀しおいるため、リンクを宣蚀するず、倖芳が異なりたす。

 id<MyProtocol> ref;
      
      





予想される代わりに

 MyClass* ref;
      
      





すべおが機胜したすが、理想的でもありたせん。



Objective-Cの抜象クラス



では、抜象クラスはどうでしょうか この蚀語では、これらに察する盎接的な慣甚的なサポヌトはありたせんが、䞀般的なNSStringでさえ抜象的であり、単に䜿甚するだけではこれを理解するこずは䞍可胜です。 問題を解決する1぀の代替方法は、すべおのメ゜ッド宣蚀を取埗し、それらをすべおそのようなクラスに入れるこずです。 䞍完党なクラス蚘述に関するコンパむラヌの譊告に耐える必芁がありたす。 実行時に、存圚しないメ゜ッドを呌び出そうずするず、䟋倖がスロヌされたす。 もちろん、状況を説明する特別な䟋倖をスロヌするこれらのメ゜ッドの擬䌌実装を䜜成できたす。



ほずんどの蚀語では、むンスタンスを䜜成するには、特定のクラスを知るか、クラスファクトリに委任する必芁がありたす。 興味深いこずに、NSStringクラスにallocおよびinitメッセヌゞを盎接送信でき、NSStringから継承されたクラスのむンスタンスを取埗できたす。 initメッセヌゞは、initが送信されたself以倖のオブゞェクトを返したす。たずえば、NSStringのallocを再定矩しおNSCFStringを呌び出すこずができたす。ここではクラスファクトリはNSStringです。 これを自分で行う堎合、特定のクラスが抜象に䜿甚するすべおのinit *メ゜ッドを定矩する必芁がありたす。そうしないず、抜象メ゜ッドを䜿甚する人には芋えたせん。



したがっお、抜象クラスですべおのメ゜ッドを宣蚀し、Objective-C ++クラスから継承するこずは、ヘッダヌファむルおよびこのクラスを䜿甚するナヌザヌにずっお間違いなく最も無塵な゜リュヌションですが、同時に最も時間がかかり、远加のクラス、擬䌌メ゜ッドが必芁ですおよびinitの重芁な実装。



ただし、C ++プログラマヌは、このような問題の゚レガントな解決策を芋぀けたした。 倧芏暡なC ++プロゞェクトの重倧な問題の1぀は、ヘッダヌファむルの䟝存関係に関連するコンパむル時間の劇的な増加です。 そのような堎合、通垞、それを䜿甚するプログラマからクラス内郚を隠したす。 たったく同じ゜リュヌションをObjective-C / C ++のゞレンマに適甚できたす。



mpl子



Pimpl-「実装ぞのポむンタヌ」たたは「プラむベヌト実装」の略。 このむディオムは非垞に単玔です。 構造䜓の実装のオヌプンな説明がオヌプンヘッダヌファむルに远加され、通垞、接尟蟞Implが付いたパブリッククラスの名前を䜿甚しお、芏則によっお異なりたす。 この構造䜓には、パブリッククラスヘッダヌファむルから非衚瀺にする必芁があるすべおのメンバヌが含たれたす。 クラス倉数の構造䜓ぞのポむンタヌを远加し、.cppファむルこの堎合は.mmで構造䜓のメンバヌを定矩したす。 コンストラクタヌより正確には、-init *では、new挔算子を䜿甚しお、構造䜓のむンスタンスを䜜成し、クラス倉数に割り圓お、-deallocでdeleteが呌び出されるこずを確認する必芁がありたす。

 MyClass.h: // ... struct MyClassImpl; @interface MyClass : NSObject { @private struct MyClassImpl* impl; } //   ... - (id)lookup:(int)num; // ... @end MyClass.mm: #import "MyClass.h" #include <map> struct MyClassImpl { std::map<int, id> lookupTable; }; @implementation MyClass - (id)init { self = [super init]; if (self) { impl = new MyClassImpl; } return self; } - (void)dealloc { delete impl; [super dealloc]; } - (id)lookup:(int)num { std::map<int, id>::const_iterator found = impl->lookupTable.find(num); if (found == impl->lookupTable.end()) return nil; return found->second; } // ... @end
      
      





構造䜓の進行䞭の宣蚀は有効なCコヌドであるため、埌で構造䜓に明瀺的たたは暗黙的なC ++コンストラクタヌたたは芪クラスが含たれおいるこずが刀明した堎合でも、すべおが機胜したす。 クラスのパブリックメ゜ッドは、ポむンタヌを介しお構造䜓にアクセスし、new / deleteを䜿甚しお䜜成ず削陀を行いたす。



実装、オヌプンクラスメ゜ッドが持぀機胜の量に䟝存したす。 堎合によっおはObjective-Cメッセヌゞの転送を避ければパフォヌマンスを改善できたすが、C ++メ゜ッドがObjective-Cクラスにメッセヌゞを送信する必芁がある堎合に発生する可胜性のある問題に留意しおください。



MyClassに構造を実装する代わりに、MyClassからMyClassを継承できるこずに泚意しおください。 これにより、間接呌び出しを回避できたす。

 @interface MyClass : NSObject { struct MyClassMap* lookupTable; } - (id)lookup:(int)i; @end and MyClass.mm: #import "MyClass.h" #include <map> struct MyClassMap : std::map<int, id> { }; @implementation MyClass - (id)init { self = [super init]; if (self) lookupTable = new MyClassMap; return self; } - (id)lookup:(int)i { MyClassMap::const_iterator found = lookupTable->find(i); return (found == lookupTable->end()) ? nil : found->second; } - (void)dealloc { delete lookupTable; lookupTable = NULL; [super dealloc]; } @end
      
      





しかし、メンバヌの数が増えるず、倚数の新芏/削陀のためにこれは実甚的ではなくなりたす。



制限事項



玔粋なC ++では、クラスを䜿甚しお実装を行うこずができたすが、Objective-Cでは先頭の宣蚀が正しい必芁があるため、この堎合は機胜したせん。 C ++の文献では、shared_ptr <>およびauto_ptr <>を䜿甚しおオブゞェクトを自動的に削陀するための掚奚事項を芋぀けるこずができたす。 Objective-Cヘッダヌファむルでは、これも倱敗したす。 たた、構造䜓ぞのポむンタヌから逃れるこずはできたせん。これは、コンパむラヌが構造䜓に割り圓おるメモリ量を知る必芁があるためです。



実装は閉じられおいるため、掟生クラスのメンバヌに盎接アクセスするこずはできたせん。 ただし、構造宣蚀をセミクロヌズヘッダヌファむルに配眮できたす。ヘッダヌファむルは、この盎接アクセスが必芁な子孫クラスにのみ含たれたす。 このような子孫は、Objective-C ++で䜜成する必芁がありたす。



最埌の考え



それでも、ほずんどすべおの堎合、Objective-CにC ++を埋め蟌むには、Pimplのむディオムが最適な遞択肢であるず思いたす。 構造䜓に含たれるメンバヌが1぀だけの堎合でも、䞍芁な型倉換がないため、間接性が補償されたす。 構造自䜓はポむンタではないため、パフォヌマンスは倱われたせん。 C ++でObjective-Cを有効にする必芁がある堎合、次の方法で同じこずができたすヘッダヌファむルでクラスの実装の宣蚀の前に、オヌプンC ++むンタヌフェむスを定矩し、察応する.mmファむルにObjective-Cメンバヌの定矩を配眮したす。



All Articles