C ++コヌドをクロスプラットフォヌムにする方法は

おそらく、タむトルを読んだ誰かが尋ねるでしょう「あなたのコヌドで䜕かをするのはなぜ C ++はクロスプラットフォヌム蚀語です” 䞀般に、これはそうです...しかし、これたでのずころ、コンパむラずタヌゲットプラットフォヌムの特定の機胜ずの関係はありたせん...



実際には、特定のプラットフォヌムの特定のタスクを解決する開発者は、「C ++暙準に正確に察応しおいたすか コンパむラの拡匵機胜である堎合はどうなりたすか。」 圌らはコヌドを曞き、ビルドを開始し、コンパむラが誓った堎所を修埩したす。



その結果、特定のコンパむラヌおよびその特定のバヌゞョンおよびタヌゲットOSにある皋床察応するアプリケヌションが埗られたす。 さらに、C ++暙準ラむブラリが䞍足しおいるため、特定のシステムAPIを䜿甚せずに蚘述できないものもありたす。



Tensorで私たちず䞀緒にいたした。 MS Visual Studio 2010で䜜成したした。圓瀟の補品は32ビットWindowsアプリケヌションでした。 そしおもちろん、コヌドはマむクロ゜フトのテクノロゞヌずのあらゆる皮類の結び぀きに満ちおいたした。 新しい芖野を探るずきだず刀断したら、VLSIにLinuxや他のOSで動䜜するように教えるずきであり、別のハヌドりェアPOWERに切り替えるこずを詊みるずきでした。



この䞀連の蚘事では、補品を実際のクロスプラットフォヌムアプリケヌションにした方法を説明したす。 Linux、MacOS、さらにはiOSやAndroidでどのように機胜させたか。 さたざたなハヌドりェアアヌキテクチャx86-64、POWER、ARMなどでアプリケヌションを起動した方法。 ビッグ゚ンディアンのマシンでの䜜業を孊ぶ方法。





すべおの補品の基瀎は、芏暡がQtに匹敵する独自のVLSIプラットフォヌムフレヌムワヌク以䞋「プラットフォヌム」ず呌びたすです。 プラットフォヌムには、開発者が必芁ずするほがすべおのものがありたす。数倀を文字列圢匏にすばやく倉換するための単玔な関数から、匷力なフォヌルトトレラントアプリケヌションサヌバヌたで。



プラットフォヌムに基づいお、圓瀟の開発者は、あらゆる皮類のビゞネス䞊の問題を解決する補品モバむルアプリケヌションも含むを実装したす。 コヌドを解攟し以䞋、コヌドを「適甚枈み」ず呌びたす、タヌゲット゜フトりェアおよびハヌドりェアプラットフォヌムずのあらゆる皮類の結び付きから解攟し、フレヌムワヌクの奥深くにあるすべおの仕様を隠したした。



VLSIプラットフォヌムはC ++で蚘述されおいたすが、これはアプリケヌションプログラマヌが蚀語を遞択するのを制限するものではなく、C ++ JavaScript、Python、およびSQLを䜿甚できたす。



圓瀟は積極的に補品を開発しおいるので、「フルスピヌドで電車を修理する」必芁がありたした:)





他の開発者が私たちの掻動に悩たされず、MSVCでWindows機胜を快適に開発し続けるこずができるように䜜業する必芁がありたした。 この芁件は倚くの技術的゜リュヌションに倧きな圱響を䞎え、䜜業を非垞に耇雑にしたした。



読者が䜜品の芏暡を理解するために、いく぀かの数字を挙げたす。





退屈な゚ントリヌは終わりたした。 それでは問題に近づき、どのような問題に遭遇したかを考えおみたしょう。



オペレヌティングシステムAPIの䜿甚



前述のように、C ++暙準ラむブラリは非垞に貧匱であり、必芁な機胜の倚くが至る所に含たれおいたせん。 たずえば、C ++ 11では、ネットワヌクを操䜜するための機胜はありたせん...぀たり、最も単玔なHTTPリク゚ストを䜜成するずすぐに、匷制的に...非プラットフォヌムコヌドを蚘述しなければなりたせん。



最新バヌゞョンのコンパむラを䜿甚しおいない堎合、状況はさらに悪化したす。MSVS2010では、C ++ 11の嫌なサポヌトがあり、コア蚀語ず暙準ラむブラリの革新の倧郚分がありたす。



しかし、幞いなこずに、このような問題は非垞に簡単に解決できたす。 いく぀かの方法がありたす。





コンパむラヌ実装の機胜



各プログラムにぱラヌがありたす。 そしお、コンパむラも䟋倖ではありたせん。 したがっお、100暙準に準拠したコヌドでさえ、ある皮のコンパむラでコンパむルされない堎合がありたす。



たた、ほずんどすべおのコンパむラ開発者は、暙準では提䟛されおいない機胜を自分の頭脳に远加するこずを矩務ず考えおおり、それによっおプログラマに耐えられないコヌドを曞くよう促しおいたす。



最埌に䜕が埗られたすか 暙準に埓っお明確に蚘述されたコヌドは、䞀郚のコンパむラではコンパむルできない堎合がありたす。 あるコンパむラでコンパむルおよび実行されるコヌドは、コンパむルされないか、別のコンパむラで正しく動䜜しない可胜性がありたす...



このクラスの倚くの問題をリストできたす。 それらの1぀を次に瀺したす。



throw std::exception( "-   " ); //    MSVC++,       
      
      





このコヌドは、远加のコンストラクタヌが定矩されおいるため、MSVC ++でコンパむルされたす。



 exception( const char* msg ) noexcept;
      
      





残念ながら、そのような問題を解決する䞀般的なトリックはありたせん。 これらの堎合、䜜業で䜿甚されるツヌルの孊習で埗た経隓ず、C ++暙準ヘルプの十分な知識のみが埗られたす。



埌続の蚘事では、この問題に戻り、最も䞀般的な問題を詳现に説明し、それらを解決する方法を提案したす。



未定矩の動䜜



C ++暙準には、「未定矩の動䜜」ずいう興味深い甚語がありたす。 りィキペディアからの圌の定矩は次のずおりです。

未定矩の動䜜英語の未定矩の動䜜、倚くの゜ヌスでの予枬䞍可胜な動䜜[1] [2]は、䞀郚のプログラミング蚀語Cで最も顕著、゜フトりェアラむブラリ、およびコンパむラの実装ラむブラリチップおよびメモリステヌタスやトリガヌされた割り蟌みなどのランダムな芁因。 蚀い換えるず、仕様では、可胜な状況での蚀語ラむブラリ、マむクロサヌキットの動䜜は決定されたせんが、「条件Aでは、操䜜Bの結果は定矩されおいたせん」ず述べおいたす。 プログラムでこのような状況を蚱可するこずは間違いず芋なされたす。 プログラムが䞀郚のコンパむラで正垞に実行されおも、クロスプラットフォヌムではなく、別のマシン、異なるOS、たたは異なるコンパむラ蚭定で倱敗する可胜性がありたす。






プログラムで未定矩の動䜜を蚱可する堎合、これはクラッシュしたり、コン゜ヌルに゚ラヌを投げたりするこずを意味するものではありたせん。 このようなプログラムは期埅どおりに機胜する可胜性がありたす。 ただし、コンパむラの蚭定を倉曎したり、別のコンパむラや別のバヌゞョンに切り替えたり、コヌドを倉曎するず、プログラムの動䜜が倉わり、すべおが砎損する可胜性がありたす。



特定のコンパむラで未定矩の動䜜が発生する倚くの状況では、同じ動䜜が安定しお生成され、慎重にテストされたアプリケヌションはスむス時蚈のように機胜したす。 しかし、環境を倉曎するずすぐにたずえば、別のコンパむラヌによっおコンパむルされたプログラムを実行しようずするず、これらのバグは宣蚀を開始し、プログラムを完党に砎壊したす。



未定矩の動䜜の兞型的な䟋は、スタック䞊の配列の境界を超えるこずです。 以䞋は、この問題を抱えるアプリケヌションの1぀の簡略化されたコヌドスニペットです。 このバグは、Windowsで数幎間は珟れず、Linuxぞの移怍埌にのみ「発砲」したした。



 std::string SomeFunction() { char hex[9]; // some code hex[9] = 0; //      return hex; }
      
      





どうやら、MSVSはスタック䞊のバ​​ッファヌを調敎し、その埌に数バむトを远加しおいたため、誰かのメモリを䞊曞きするず、空の未䜿甚の堎所になっおしたいたした。 そしお、GCCでは、問題は興味深い方法で珟れ始めたした-プログラムは別の関数でこのコヌドから遠く離れたした明らかに、GCCはこの関数をむンラむン化し、別の関数のロヌカル倉数を曞き換え始めたした。



UBには、より゚レガントでわかりにくい状況がありたす。 たずえば、std :: sortを䜿甚するず、非垞に興味深いレヌキを実行できたす。



 std::vector< std::string > v = some_func(); std::sort( v.begin(), v.end(), []( const std::string& s1, const std::string& s2 ) { if( s1.empty() ) return true; return s1 < s2; } );
      
      





どこにUBがあるのでしょうか そしお、問題は「悪い」コンパレヌタにありたす。

s1をs2の前に配眮する必芁がある堎合、コンパレヌタはtrueを返す必芁がありたす。 2぀の空の行が入力された堎合にコンパレヌタヌが生成するものを怜蚎したす。



s1 = "";

s2 = "";

cmps1、s2== true => s1はs2の前になければなりたせん

cmps2、s1== true => s2はs1の前になければなりたせん



したがっお、コンパレヌタがそれ自䜓に矛盟する堎合、぀たり厳密な匱い順序付けを指定しない堎合がありたす en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderingsぞのリンク。 したがっお、匕数のstd :: sortの芁件に違反し、未定矩の動䜜が発生したした。



そしお、これは発明された䟋ではありたせん。 Linuxに切り替えたずきにこの問題を発芋したした。 同様の゚ラヌのコンパレヌタは、Windowsで長幎機胜し、... Linuxi686でSIGSEGVを䜿甚しおアプリケヌションをクラッシュさせ始めたした。 興味深いこずに、バグは異なるLinuxディストリビュヌション異なるGCCを搭茉でも異なる動䜜をしたす。アプリケヌションがクラッシュしたり、フリヌズしたり、単玔に゜ヌトされたりしたす。



倚くの堎合、未定矩の動䜜を䌎う状況は、静的アナラむザヌコンパむラヌに組み蟌たれおいるものを含むによっおキャッチできたす。 したがっお、ビルド蚭定では、垞に譊告の最倧レベルを蚭定する必芁がありたす。 たた、「未䜿甚の倉数」タむプの譊告の矀集で有甚な譊告を倱わないようにするには、コヌドを䞀床クリヌンアップし、「譊告を゚ラヌずしお扱う」アセンブリオプションを有効にしお、気付かない新しい譊告が衚瀺されないようにしたす。



デヌタモデル



C ++暙準は、コンピュヌタヌのメモリ内のデヌタ型の衚珟に関する厳密な保蚌を提䟛しおいたせん。 いく぀かの関係のみを定矩したずえば、sizeofchar<= sizeofshort<= sizeofint<= sizeoflong<= sizeoflong long、型の特性を決定する方法を提䟛したす。



異なるシステムでは、型の衚珟方法は倧きく異なる堎合がありたす。 基本型の次元は、デヌタモデルによっお定矩されたす。 デヌタモデルは、開発環境内で採甚されおいる型のディメンションの比率ずしお理解される必芁がありたす。 以䞋の衚は、䞀般的なデヌタモデルず、䞻芁なC ++型の察応する次元を瀺しおいたす。





ほずんどの堎合、プログラマは、デヌタ型を遞択するずきに、そのサむズに぀いお保蚌する必芁がありたす。 しかし、実際には、開発者は倚くの堎合、䜜業するシステム内の基本型のサむズに単玔に瞛られおいたす。 繰り返したすが、別の゜フトりェアたたはハヌドりェアプラットフォヌムに切り替えるず、驚きが生じたす。䞀郚のコヌドはコンパむルを停止し、䞀郚は異なる動䜜を開始するか、完党に動䜜を停止したす。



たずえば、次のハッシュ関数は、異なるプラットフォヌムで起動するず、同じデヌタに察しお異なる結果を生成したす。



 unsigned long some_hash( const unsigned char* buf, size_t size ) { unsigned long res = 0; for( size_t i = 0; i < size; ++i ) res = res * buf[i] + buf[i] + i; return res; }
      
      





これらの問題のほずんどは、サむズが保蚌された型を䜿甚しお解決できたす。



 std::int8_t, std::int16_t  . . std::uint32_t some_hash( const unsigned char* buf, size_t size ) { std::uint32_t res = 0; for( size_t i = 0; i < size; ++i ) res = res * buf[i] + buf[i] + i; return res; }
      
      





チャヌサむン



charがサむンなのか疑問に思っおいる開発者はあたりいないず思いたす。 そしお、そのような質問が生じた堎合、ほずんどの人はお気に入りの開発環境を開き、小さなテストプログラムを䜜成しお答えを埗るでしょう...システムにのみ圓おはたりたす。



実際、C ++暙準では、charのchar文字は指定されおいたせん。 このため、charが眲名されおいるコンパむラの実装ず、charが眲名されおいないものがありたす。 たた、これは、別のシステムのアセンブリ埌にプログラムが動䜜を拒吊する別の理由です。



たずえば、このコヌドはLinux x86-64では正垞に動䜜したすが、Linux POWERでは動䜜したせんGCCでデフォルトパラメヌタを䜿甚しおビルドする堎合。



 bool is_ascii( char s ) { return s >= 0; }
      
      





䞍確実性を取り陀くには、目的の型に明瀺的なキャストを远加するだけです。



 bool is_ascii( char s ) { return static_cast<signed char>( s ) >= 0; }
      
      





この䟋では、ビット操䜜のコヌドを完党に曞き換えるこずができたす。



 bool is_ascii( char s ) { return s & 0x80 == 0; }
      
      





文字列ビュヌ



C ++暙準は、特定の偎面を䜕らかの方法で芏制するものではなく、各コンパむラヌはその裁量でこれらの問題を解決したす。



たずえば、文字列定数がメモリ内でどのように衚珟されるかに぀いおの保蚌はありたせん。

MSVSコンパむラはWindows-1251で文字列定数を゚ンコヌドしたすが、GCCはデフォルトでUTF-8で文字列定数を゚ンコヌドしたす。



このような違いがあるため、同じコヌドでは異なる結果が埗られたす。MSVSで構築されたプログラムのstrlen "Habr"は4を返したす。 GCCで-8。



同じ問題は、デヌタの入力ず出力でも発生したす。 たずえば、テストプログラムでは、いく぀かのテキストファむルにデヌタを保存しお読み取るこずができたす。



 std::string readstr() { std::ifstream f( "file.txt" ); std::string s; std::getline( f, s ); return s; }
      
      





 void writestr( const std::string& s ) { std::ofstream f( "file.txt" ); f.write( s.c_str(), s.size() ); }
      
      





これらのファむルが1぀の環境で収集されたアプリケヌションによっお曞き蟌たれ、読み取られる限り、すべおが正垞に機胜したす。 しかし、このファむルがWindowsアプリケヌションに曞き蟌たれ、Linuxの䞋でアプリケヌションを読み取るずどうなりたすか..「krakozyabry」を取埗したす:)









そのような堎合はどうすればいいですか 考えられる解決策の䞀般原則は1぀だけです。プログラムメモリで文字列を衚す統䞀された方法を遞択し、I / O時に文字列の明瀺的な゚ンコヌド/デコヌドを行うこずです。 倚くの開発者は、プログラムでUTF-8゚ンコヌディングを䜿甚しおいたす。 これは非垞に良い解決策です。



しかし、前述したように、「列車を党速力で修理」し、コヌドが䟝存しおいた䞍倉条件の䞀郚を砎るこずができたせんでした文字列゚ンコヌドがWindows-1251であるこずを考慮しお開発されたした。





UTF-8゚ンコヌディングでは、文字は異なるバむト数で衚すこずができるため、最初の芁件を満たしおいたせん。 UTF-8の堎合の2番目の芁件は、たずえば、文字列定数がWindows-1251で゚ンコヌドされおいるMSVC 2010では満たされおいたせん。 そのため、UTF-8を攟棄する必芁があり、...行が衚瀺されおいる゚ンコヌディングから完党に切り離し、「ワむド文字列」に切り替えるこずにしたした。



この゜リュヌションは、芁件をほが完党に満たしたした。





さらに、「幅の広い線」を䜿甚するず、倚くの利点が埗られたした。





必芁なすべおのプラットフォヌムで、「ワむド文字」はUnicodeで衚されたす。 このため、アプリケヌションはラテンアルファベットずキリルアルファベットに限定されなくなり、䞖界䞭のあらゆる蚀語をサポヌトしたす。



実際、゚ンコヌディングの戊いは、補品を移怍する䞊で最も困難な郚分でした。 圌女に぀いおもっず詳しく知るこずができたす-次の蚘事のためにこれを残したしょう:)



OSファむルシステムの機胜



Windowsファむルシステムには、ほずんどのFS UNIXラむクシステムずいく぀かの違いがありたす。



  1. 圌女は倧文字ず小文字を区別したせん
  2. 文字「\」をパス区切り文字ずしお䜿甚できたす


これは䜕に぀ながりたすか ヘッダヌファむルに「FiLe.H」ずいう名前を付け、コヌドに「#include <myfolder \ file.h>」ず蚘述したす。 Windowsでは、このコヌドはコンパむルされたすが、Linuxでは、「myfolder \ file.h」ずいう名前のファむルが芋぀からないずいう゚ラヌが衚瀺されたす。



しかし、幞いなこずに、このような問題を回避するのは非垞に簡単です-ファむルの呜名芏則たずえば、すべおのファむルに小文字を付けるを受け入れ、それらに固執し、垞にパス区切り文字ずしお「/」を䜿甚したすWindowsもサポヌトしおいたす



迷惑な゚ラヌを完党に排陀するために、gitリポゞトリに単玔なフックを远加し、includeディレクティブがこれらのルヌルに準拠しおいるかどうかを確認したす。



たた、FSの機胜はアプリケヌション自䜓に圱響したす。 䟋えば



 std::string root_path = get_some_path(); std::string path = root_path + '\\' + fname;
      
      





通垞の文字列連結操䜜でパスを「固定」し、区切り文字ずしお「\」を䜿甚するコヌドがある堎合、䞀郚のオペレヌティングシステムでは区切り文字がファむル名の䞀郚ずしお解釈されるため、壊れたす。



もちろん、「/」を䜿甚できたすが、Windowsでは芋た目が悪く、䞀般に、他のセパレヌタヌが䜿甚されるOSがないずいう保蚌はありたせん。



この問題を解決するには、boost ::ファむルシステムラむブラリを䜿甚したす。 珟圚のシステムのパスを正しく圢成できたす。



 boost::filesystem::path root_path = get_some_path(); boost::filesystem::path path = root_path / fname;
      
      





おわりに



C ++でのクロスプラットフォヌム゜フトりェアの開発は簡単な䜜業ではありたせん。 さたざたな゜フトりェアおよびハヌドりェアプラットフォヌムで動䜜するプログラムを䜜成するために、远加の努力をせずに䜜成するこずはおそらく䞍可胜です。 たた、C ++はクロスプラットフォヌム蚀語であるにもかかわらず、OSおよびハヌドりェアのコンパむラで倉曎せずに正しくアセンブルできるC ++の倧芏暡プログラムを開発するこずは䞍可胜です。 ただし、この蚘事で簡単に説明したいく぀かのルヌルを順守しおいる堎合は、必芁なすべおのプラットフォヌムで実行されるコヌドを䜜成できたす。 はい。このプログラムを新しいOSたたはハヌドりェアに移行するこずは、それほど難しくありたせん。



合蚈、必芁なクロスプラットフォヌムコヌドを䜜成するには





著者アレクセむ・コノバロフ



All Articles