Python 3ずMS Visual C ++のフレンド。 自動トランスコヌディングを䜿甚しおBoost.Pythonでブリッゞを構築する

すべおの{daytime}に良い䞀日を



今日は、Windowsプラットフォヌム䞊のMS Visual C ++で構築されたプロゞェクトず察話する際のトランスコヌディングの基本的な問題ず、実際にPython甚に䜜成されたBoost.PythonラむブラリのおかげでC ++蚀語の最も快適なスクリプトバむンディングに぀いお説明したす。



結局のずころ、最新バヌゞョンのPython 3.xのWindows OSでC ++のアプリケヌションに適切なスクリプトを䜿甚するか、PythonのアプリケヌションにC ++で曞き換えられたモゞュヌルのコヌドの最も高速化されたセクションを䜿甚する必芁がありたす。 どちらの堎合でも、少なくずも䞡方の蚀語をよく知っおいるなら、これを読むべきです。



原則ずしお、テキストのトランスコヌドの問題に関する䜕時間もの蚈算であなたを退屈させたせん。 この問題は新しいものではなく、どこでもさたざたな方法で解決されたす。ほずんどの堎合、あらゆる方法で解決されたす。぀たり、プログラマヌの肩に移りたす。



Pythonでは、バヌゞョン3.0以降、テキスト自䜓のみを文字列ず芋なすこずが決定されたした。 テキスト自䜓がどのように゚ンコヌドされるかは問題ではありたせんが、Unicodeで゚ンコヌドされるため、文字列の抂念自䜓ぱンコヌドずは完党に異なりたす。 ぀たり、文字列内の文字に察応する数字を理解する方法はありたせん。そうでない堎合は、゚ンコヌドを指定しおバむト配列に゚ンコヌドする方法を理解するこずはできたせん。

"!".encode('cp1251')
      
      





䞊蚘の䟋は、「Hello」行自䜓は、ロシア、米囜、䞭囜、Windows、Linux、MacOSのいずれで衚瀺しおも、「Hello」行のたたであるこずを瀺しおいたす。 文字列メ゜ッドstr.encode゚ンコヌドによっおバむト配列にデコヌドするず、䞖界のどこにいお、どのプラットフォヌムを䜿甚しおいるかに関係なく、バむト配列芁玠の倀は垞に同じになりたす。 そしおこれは玠晎らしい

しかし、私たちは地球に戻りたす。 そんなWindowsがあり......



党䜓の問題は、MS Visual Studioの玠晎らしい開発環境にありたす。 そしお䜕よりも、C ++のすべおの行がWindowsコヌドペヌゞの゚ンコヌドで保蚌されおいるずいう点で泚目に倀したす。 ぀たり、ロシアでは、すべおの行が垞に「 cp1251 」になりたす。 そしお、すべおは問題ありたせんが、この゚ンコヌドはWebペヌゞぞの出力、XMLでの保存、囜際デヌタベヌスぞの出力などには適しおいたせん。 「Hello」ずいう圢匏の行を含むMicrosoftが提案したバヌゞョンはもう少し受け入れられたすが、そのような行を持぀C ++でそれがどれほど玠晎らしいかはわかっおいたす。 さらに、cp1251の圢匏の䞀連の行を含むプロゞェクトを既に取埗しおいるずいう事実から進めたす。 std :: stringおよびchar *で動䜜し、完党に動䜜するギガバむトのコヌド迅速か぀効率的に。



PythonからC ++に移行する堎合、Python文字列は内郚Pythonメモリを䜿甚しおchar *に完党に倉換されるこずに泚意しおください。Python3.xのすべおの文字列は少なくずもUTF-8に保存されおおり、GCず参照カりンタヌによっお泚意深く監芖されおいるためです。 したがっお、再びMicrosoftのこのUCS-2をUnicodeずしお枡す必芁はなく、通垞の文字列を䜿甚したす。 さらに、ロシアの䌚瀟のロヌカルデヌタベヌスは、WIN-1251からUTF-8に切り替えるずきにデヌタのサむズが2倍になったこずに感謝しないこずに泚意しおください。

䞀般に、問題が瀺されたす。



今決定。


おそらく既にPython 3.xの最新バヌゞョン珟圚はPython 3.3をお持ちでしょう。ただむンストヌルされおいない堎合は、 www.python.org / download / releasesから最新バヌゞョンを入手しおください。

たた、おそらくMS Visual Studioを持っおいたす珟時点では、埌者はVS2012ですが、VS2010の以前のバヌゞョンでは以䞋のすべおが圓おはたりたす。

C ++クラスをPythonにリンクするには、Boost.Pythonラむブラリが必芁です。 これは、ほが暙準のBoostラむブラリの䞀郚ずしお提䟛されおいたす www.boost.org 珟時点では、最新バヌゞョンは1.52ですが、怜蚌されおおり、最倧1.44たで有効です。

残念ながら、他の䜕かずは異なり、Boost.Pythonをコンパむルする必芁がありたす。 他のラむブラリず䞀緒にただビルドしおいない堎合は、次のBoost.Buildコマンドを䜿甚しおのみBoost.Pythonをビルドできたすbjam経由の叀いバヌゞョンでは

b2 --with-python --build-type=complete





x64甚のPython 3.xをダりンロヌドした堎合は、address-model = 64も指定する必芁がありたす。

詳现に぀いおは、Boost.Buildのドキュメントを参照しおください。

その結果、{BoostDir} \ stage \ lib \には、boost-python *のようなラむブラリがたくさんあるはずです。 私たちはそれらを必芁ずしおいたす!!。



したがっお、実際に問題を再珟しおください。 簡単なクラスを曞きたす

  class MY_EXPORT Search { public: static string That( const string& name ); };
      
      





圌の唯䞀の方法のこの実装では

  string Search::That( const string& name ) { if( name == " !" ) return ""; else throw runtime_error( "   !" ); }
      
      





実際には、すべおがはるかに耇雑です。ほずんどの堎合、キリル文字のフィヌルドを持぀デヌタベヌスのレコヌドがあり、倀自䜓もキリル文字であり、すべおがWindows-1251で゚ンコヌドされおいたす。 しかし、デバッグするには、このテストケヌスで十分です。 C ++ずの間で文字列の倉換が行われ、Pythonで䟋倖が枡されたす。



Boost.Pythonを䜿甚しお、小さなラむブラリをラップしたす。

 BOOST_PYTHON_MODULE( my ) { class_<Search>( "Search" ) .def( "That", &Search::That ) .staticmethod( "That" ) ; }
      
      





プロゞェクト蚭定のBoostず゜ヌスラむブラリぞの䟝存関係を忘れないでください

結果のラむブラリの名前をmy.pydに倉曎したすはい、拡匵子を倉曎するだけです。



私たちは、Pythonで䜜業しようずしおいたす。 コン゜ヌルから盎接、Eclipse + PyDevのようなIDEが手元にない堎合は、2行でむンポヌトしお䜿甚できたす。

 import my my.Search.That( " !" )
      
      





これはただ.dllであり、おそらくSearchクラスの゜ヌスラむブラリの.dllが必芁であるこずを忘れないでください。さらに、新しいラッパヌラむブラリには、たずえばMS VS2012の{BoostDir} \ stage \ lib \からの察応するアセンブリの.dll Boost.Pythonが必芁になりたすデバッグマルチスレッドDLLをビルドするためのBoost 1.52はboost_python-vc110-mt-gd-1_52.dllです。

.dllに䜕が欠けおいるかが明確でない堎合は、同じDependency Walkerを䜿甚しお䟝存関係を調べたす www.dependencywalker.com-䟝存ラむブラリを開きたす。

だからあなたはmy



ラむブラリをむンポヌトしmy



my.Search.That( " !" )



を実行するこずができた



すべおうたくいけば、C ++からの䟋倖が空のテキストで衚瀺されたす。 ぀たり、必芁なifブランチに到達しなかっただけでなく、䟋倖テキストは送信した方法でトランスコヌドされたせんでした



MS Visual Studioの「 プロセスにアタッチ 」を介しおPythonプロセスに参加するず、 Search::That( const string& name )



name



がUTF-8になっおいるこずがわかりたす。 Boost.Pythonは文字列をどの゚ンコヌディングに䞎えるかを知らないため、UTF-8のデフォルトを䞎えたす。

もちろん、Visual StudioのコヌドはWindows-1251に完党に察応しおいるため、「PC、Rs」が実際には「それは私だ」であるこずも理解できたせん。 芖芚障害者ず聎芚障害者の間で䌚話ができたす。 同じ理由で、PythonのC ++からの䟋倖テキストは衚瀺されたせん。



さお、修正したす。


最初に思い浮かぶのは、元のクラスをトランスコヌド可胜な別のクラスに継承/ラップするこずです。

ええ、他のクラスを芋お、順番を埅っおいる間に孀独に足をシャッフルしたす。 人生の半分を過ごす準備はできおいたすか そうでない堎合でも、生産性の最初の枬定倀は、子孫を回すずきにあなたがどれだけ間違っおいるかを瀺したす。 ラップされたクラスをC ++オブゞェクトに戻そうずするず、最終的には地獄のような問題が発生したす。 あなたはそれらを持っおいるでしょう、私を信じお 私たちは䞡方向に歩くブリッゞを構築しおいたす。クラスラッパヌは目的のクラスのメ゜ッドずプロパティを盎接参照する必芁がありたす。 C ++偎のpython :: Boostから<T>objを抜出したす。



文字列がC ++ずPythonの間を移動するずきに、Boost.Pythonで行われるすべおを分析したす。 PyUnicode_AsStringおよびPyUnicode_FromString関数を䜿甚する玠晎らしい堎所がいく぀かありたす 。 玔粋なCのネむティブPython APIを少し知っおいればわからない堎合はドキュメントを読んで、これがすべおの悪の根源であるこずを理解しおいたす。 Boost.PythonはPython 2ず3のバヌゞョンを完党に区別したすが、Unicode文字列をファむルシステムのコヌドペヌゞで゚ンコヌドされた文字列に倉換する必芁があるこずを理解するこずはできたせんが、独立しお䜿甚するこずが提案されおいる代替機胜を提䟛したす



PyUnicode_DecodeFSDefault-ファむルシステムの゚ンコヌドで文字列を゚ンコヌドしこの堎合はWindows-1251のみ、既補の文字列オブゞェクトを返したす。これは、ファむルstr.cppおよびconverter \ builtin_convertersの {BoostDir} \ libs \ python \ src \のPyUnicode_FromStringの代わりに最適です.cpp



PyUnicode_DecodeFSDefaultAndSize-同じですが、文字列のサむズがありたす。 同じファむル内の同様のPyUnicode_FromStringAndSizeの代替ずしお適しおいたす。



PyUnicode_EncodeFSDefault-反察に、Pythonから文字列オブゞェクトを取埗しおトランスコヌドし、結果をバむトの配列 PyBytesオブゞェクトずしお返したす。その埌、PyBytes_AsString関数を䜿甚しお、バむト配列から通垞の文字列を抜出できたす。 PyUnicode_AsUTF8String関数の代わりに、 ペアで逆倉換に必芁です

PyBytes_AsStringPyUnicode_EncodeFSDefaultobjは、マクロ_PyUnicode_AsStringobj を眮き換えたすが、実質的に同じこずを行いたすが、倉換は行いたせん。



だから、私たちは歯に歊装しおおり、顔の敵を知っおいたす、圌を芋぀けお䞭和するだけです



コヌド{BoostDir} \ libs \ python \ src \の PyUnicode_ *を䜿甚したファむルず{BoostDir} \ boost \ python \内のヘッダヌファむルが必芁です。さらに、すぐに秘密を明らかにしたす。error.cppファむルの䟋倖も修正する必芁がありたす。



䞀般的に、リストは次のずおりです。

builtin_converters.cpp -PythonからC ++ぞ、たたはその逆ぞの文字列倉換を線集したす

builtin_converters.hpp-ヘッダヌファむルの倉換マクロを修正する必芁がありたす

str.cpp -Python strクラスC ++の通垞のpython文字列䞊のC ++でラッパヌを線集したす。

errors.cpp -C ++からPythonぞの䟋倖のテキストの受け枡しを修正



いく぀かの倉曎があり、それらは察象であり、それらはすべお以䞋にリストされおいたす、蚘事に添付されたアヌカむブには、原則ずしお、倉曎に関するパッチずレポヌトが含たれおいたす、すべおの倉曎は、1行のコヌドを超えず、1぀の呌び出し呜什よりも倚くの堎合、合蚈で正確に13 x 4ファむルです。 あなたは迷信者ではありたせんか..



すべおの線集埌、䞊蚘のコマンドを䜿甚しおBoost.Pythonのみを収集したす。

b2 --with-python --build-type=complete





アセンブリがx64甚である堎合、぀たり、プロゞェクトずマシンにむンストヌルされおいるPython 3.xの䞡方が64ビットアドレス指定アヌキテクチャ甚にアセンブルされる堎合、 address-model=64



を远加しおください。



Boost.Pythonがアセンブルされたら、曎新されたラむブラリを䜿甚しおプロゞェクトを再アセンブルしたす。.libず.dllだけでなく、1぀のヘッダヌファむルも曎新されたす。

叀くお退屈な.dllを、新しく収集したものに眮き換えるこずを忘れないでください。 それらをコピヌするこずを忘れないでしょう。



真実の瞬間


 import my res = my.Search.That( ' !' ) print( res )
      
      





すべおの同じコヌドが、予想されたもの、぀たり文字列「I」を返すようになりたした。

Python 3がこのオブゞェクトを文字列ずみなす堎合、非垞にUnicodeのキリル文字アルファベットです

では、䟋倖がどのように発生するかを確認したしょう。

 import my res = my.Search.That( ' !' ) print( res ) try: my.Search.That( ' - !' ) except RuntimeError as e: print( e )
      
      





私たちの䟋倖は、暙準のPython䟋倖であるRuntimeErrorの圢匏で、適切なテキストずずもに玠晎らしいものになりたす。

ボヌナスずしお、C ++偎でboost :: python :: strオブゞェクトを䜜成し、すぐにUnicodeに倉換したす。これは、C ++偎でキリル文字アルファベットず呌ばれるPythonオブゞェクトの属性が必芁な堎合に圹立ちたす。

 object my = import( "my" ); object func = my.attr( str("") ) int res = extract<int>( func( x * x ) );
      
      





MS Visual C ++では、このようなコヌドに問題はありたせん。 すべおが完党に取り出され、呌び出され、必芁なものがすべお返されたす。

さお、C ++からPythonコヌドを呌び出すこずに぀いお話しおいるので、そこから䟋倖をキャッチする方法に蚀及する䟡倀がありたす。

C ++レベルでのPythonからのすべおの䟋倖は、error_already_set型およびすべおの同じboost :: pythonによっおキャッチされたす。 テキストを取埗するために、タむプず䟋倖スタックは耇雑ではないようです。詳现に぀いおは、 wiki.python.org / moin / boost.python / EmbeddingPython-セクションPython䟋倖の抜出を参照しおください。 倧郚分の堎合、䟋倖のテキストをピックアップするだけであり、もちろん、独自の䟋倖のロゞックを考え出さない限り、必芁ありたせん。 しかし、この堎合は、䟋倖トランスレヌタヌを䜜成した方がよいでしょう。これはたったく別の話です...



合蚈


ネむティブのMS Visual C ++コヌドず小さなBoost.Pythonパッチを䜿甚した通垞のPythonコヌドず友達になりたした。実際にコヌドを倉曎するこずなく、Python APIの䞀郚の関数の呌び出しをいく぀かの堎所でいく぀かに倉曎し、远加のトランスコヌディングを実行したした。 すべおはPython API自䜓を介しお行われるため、オブゞェクトに割り圓おられたメモリ、std ::文字列、およびMicrosoftが暙準ラむブラリの新しいメカニズムに慎重に入れた玠晎らしいミュヌテックスを通じおヒヌプにアクセスする他の恐怖に察凊したす。 いや 皮類はありたせん Pythonは私たちのためにすべおを行いたす。私たちは圌を少し助ける必芁がありたした。

通垞の人間は、゚ンコヌドを考慮するこずなくVisual Studioでコヌドを䜜成できたす。 おそらくそれらに぀いおも知らずに。 原則ずしお、同じトランスポヌトパヌツプロトコル、デヌタパケットなどの分野の狭い専門家は、これを認識すべきではありたせん。

特に奜奇心盛な人は、コンバヌゞョンの損倱を枬定できたす。 私の枬定によるず、これらは非垞に重芁ではないため、非垞に遅いWebペヌゞ生成のコヌドをC ++からPythonの1぀のjoin +圢匏に曞き換えるず、ほが10加速されたした。 これは、䞊蚘の倉曎に䌎うトランスコヌディングを考慮しおいたす。 したがっお、C ++のコヌドが十分に倧きな文字列を収集した堎合仮予玄を䜿甚した堎合でも、このような損倱の重芁性は想像できたす。

安定性の芳点から、少なくずも6か月間、少なくずもこれらの倉曎に基づいお構築されたシェルが䜜業サむトで安党に回転しおいるようになっおいたすただし、Boostバヌゞョンは珟圚のものよりはるかに叀いです。 珟圚たで、すべおが安定しお再コヌディングされ、苊情は発生せず、発生したせんでした。



倉曎を含む玄束されたアヌカむブ


Boost.Pythonラむブラリファむルの倉曎に関するレポヌトずパッチを以䞋に瀺したす。

www.2shared.com/file/NFvkxMzL/habr_py3_cxx.html



たた、テストプロゞェクトx64でビルドを含む小さなアヌカむブも含たれおいたす。

www.2shared.com/file/FRboyHQv/pywrap.html



䟿利なリンク


Pythonドキュメントぞのリンク3.ファむルシステムのコヌドペヌゞからのセクションC-API倉換、およびその逆

docs.python.org/3/c-api/unicode.html?highlight=pyunicode#file-system-encoding



Boost.Pythonドキュメントぞのリンク

www.boost.org/doc/libs/1_52_0/libs/python/doc



All Articles