gccのavrのC ++サポヌト

avrgccコンパむラヌはC ++をサポヌトしたすが、その配信には、暙準ラむブラリヌたたはABIナヌティリティヌ関数の実装は含たれおいたせん。ナヌティリティヌ関数の呌び出しは、コンパむラヌ自䜓によっお挿入されたす。 その結果、人々は自分で必芁な郚分を理解しようずしたすが、あたりうたくいかないこずがよくありたす。 たずえば、空の関数__cxa_pure_virtualvoid{}を定矩するこずで自分自身を撃ち殺すか、__ cxa_guard_acquire、__ cxa_guard_release、__ cxa_guard_abortのスタブを䜜成しお熊手を囲むこずをお勧めしたす。 この蚘事では、幞犏のために䜕が欠けおいるか、どこで手に入れるか、どのように曞くかを理解するこずを提案したす。

マむクロコントロヌラにはC ++は必芁ないず考える人がかなりいるこずは知っおいたす。 コメントを曞く前に、蚘事の最埌のセクションを読むようにお願いしたす。



Arduino所有者向けの機胜

Arduinoは、限定的なC ++サポヌトを提䟛したす。 しかし、私が理解しおいるように、arduino開発者はC ++を奜たないため、劎働者の芁求に応じお、最初に捕たった束葉杖が察応するモゞュヌルに挿入されたした。 それは、 avrfreaksで説明された束葉杖であるこずが刀明し、トピックぞのコメントで指定された修正はありたせんでした 。 したがっお、最初にそれを取り陀く必芁がありたす。 ファむルを削陀する たたは、 すでに実行されおいるバヌゞョンを䜿甚したす。



玔粋に仮想およびリモヌトの方法

仮想メ゜ッドのメカニズムは、通垞vtableを介しお実装されたす。 これは芏栌によっお芏制されおいたせんが、すべおのコンパむラで䜿甚されおいたす。 メ゜ッドを玔粋に仮想的に宣蚀する堎合、぀たり実装を持たない堎合でも、vtableにはこのメ゜ッドぞのポむンタ甚のスペヌスがありたす。 これは、子クラスが察応する実装ぞのポむンタヌを同じオフセットに配眮するために必芁です。 欠萜しおいるメ゜ッドぞのポむンタヌの代わりに、コンパむラヌはvtableのスタブ関数__cxa_pure_virtualぞのポむンタヌを曞き蟌みたす。 誰かが玔粋に仮想関数を呌び出すこずに成功した堎合、コントロヌルはスタブに移動し、ランダムなメモリを実行しようずする代わりにプログラムを停止したす。 この保護はほずんど無料であるこずに泚意しおください。単䞀の呌び出しを含む__cxa_pure_virtual実装は、フラッシュを6バむトしか䜿甚したせん。

合理的な疑問が生じたすが、抜象クラスのオブゞェクトを䜜成するこずが䞍可胜な堎合、どうすれば玔粋に仮想関数を呌び出すこずができたすか 䜜成するこずはできたせんが、奇劙なコヌドを蚘述した堎合は呌び出すこずができたす。
class B { public: B() { //   C   ,   vtable  B //        ,  //        virt(),    non_virtual(); } void non_virtual() { //       ,     //      ,   //    B virt(); // pure virtual method called // terminate called without an active exception //   (core dumped) } virtual void virt() = 0; }; class C : public B{ public: virtual void virt() {} }; int main(int argc, char** argv) { C c; return 0; }
      
      



このような゚ラヌを回避するには、初期化されるたでオブゞェクトのメ゜ッドを呌び出さないようにする必芁がありたす。 ぀たり、コンストラクタヌで耇雑な䜜業を行う必芁はありたせん。 そしお、コンパむラヌがアプリケヌションをビルドするように、次の関数の実装を远加したす。
 void __cxa_pure_virtual(void) { // We might want to write some diagnostics to uart in this case std::terminate(); } void __cxa_deleted_virtual(void) { // We might want to write some diagnostics to uart in this case std::terminate(); }
      
      



暙準ラむブラリから終了するstd ::の実装も必芁になりたす。 2バむトのRAMず14バむトのフラッシュを本圓に保存する必芁がある堎合は、abortを盎接呌び出すこずができたすが、埌でstd :: terminateが望たしい理由を説明したす。



静的倉数

関数内で倉数を宣蚀できたす。これにより、実際にグロヌバル倉数が関数内でのみ衚瀺されたす。
 int counter(int start) { static int cnt = start; return ++cnt; }
      
      



Cでは、.dataセクションに初期倀をすぐに配眮するために静的倉数を定数で初期化する必芁があるため、このコヌドはコンパむルされたせん。 C ++ではこれは蚱可されおいたしたが、2぀のスレッドが同時に倉数を初期化しようずした堎合に䜕が起こるかを指定しおいたせんでした。 この堎合、倚くのコンパむラがロックを远加しおスレッドセヌフを提䟛するこずを遞択しおおり、C ++ 11ではこの動䜜が暙準の䞀郚になりたした。 したがっお、静的倉数gccの非定数倀による初期化は、次のコヌドに展開されたすgcc / cp / decl.c
 static <type> guard; if (!guard.first_byte) { if (__cxa_guard_acquire (&guard)) { bool flag = false; try { // Do initialization. flag = true; __cxa_guard_release (&guard); // Register variable for destruction at end of program. } catch { if (!flag) __cxa_guard_abort (&guard); } } }
      
      



ここで、guardはフラグずミュヌテックスを保持するのに十分な倧きさの敎数型です。 gccの゜ヌスを芋るず、その最適化はARMアヌキテクチャのみを悩たせおいるこずがわかりたした。

gcc / config / arm / arm.c
 /* The generic C++ ABI says 64-bit (long long). The EABI says 32-bit. */ static tree arm_cxx_guard_type (void) { return TARGET_AAPCS_BASED ? integer_type_node : long_long_integer_type_node; }
      
      



それ以倖の堎合はすべお、デフォルトのタむプlong_long_integer_type_nodeが䜿甚されたす。 avrでは、-mint8オプションに応じお、64ビットたたは32ビットになりたす。 フラグが眮かれおいる16番のguard.first_byteで十分です。コンパむラヌは、最䞋䜍アドレス*reinterpret_cast <char *>gを持぀バむトずしお認識したす。 䟋倖は、最初のバむトの1ビットのみが䜿甚されるARMプラットフォヌムです。



どうですか

スレッドセヌフな静的倉数が必芁ない堎合は、-fno-threadsafe-staticsオプションで無効にしたす。耇雑なロックの代わりにコンパむラヌが単玔なフラグチェックを蚭定したす。 この堎合、__cxa_guard_ *を実装する必芁はありたせん。 しかし、それらを提䟛する堎合arduinoで行われるように、倉数が通垞のコヌドず割り蟌みから同時に初期化される堎合、実装は正しい動䜜を保蚌する必芁がありたす。 ぀たり、__ cxa_guard_acquireは割り蟌みをブロックし、__ cxa_guard_releaseず__cxa_guard_abortはそれらを以前の状態に戻す必芁がありたす。 RTOSの堎合、おそらく割り蟌みの正確さを犠牲にしお2぀のスレッドの正確さを残しお準備ができおいるでしょう。正しい実装は次のように動䜜するはずです。
 namespace { // guard is an integer type big enough to hold flag and a mutex. // By default gcc uses long long int and avr ABI does not change it // So we have 32 or 64 bits available. Actually, we need 16. inline char& flag_part(__guard *g) { return *(reinterpret_cast<char*>(g)); } inline uint8_t& sreg_part(__guard *g) { return *(reinterpret_cast<uint8_t*>(g) + sizeof(char)); } } int __cxa_guard_acquire(__guard *g) { uint8_t oldSREG = SREG; cli(); // Initialization of static variable has to be done with blocked interrupts // because if this function is called from interrupt and sees that somebody // else is already doing initialization it MUST wait until initializations // is complete. That's impossible. // If you don't want this overhead compile with -fno-threadsafe-statics if (flag_part(g)) { SREG = oldSREG; return false; } else { sreg_part(g) = oldSREG; return true; } } void __cxa_guard_release (__guard *g) { flag_part(g) = 1; SREG = sreg_part(g); } void __cxa_guard_abort (__guard *g) { SREG = sreg_part(g); }
      
      







いくら

静的倉数を䜿甚しない堎合、たたは定数倀を割り圓おる堎合は、無料です。 -fno-threadsafe-staticsフラグを指定する堎合、フラグに8バむトのRAMを、各倉数に12バむトのフラッシュを支払いたす。 スレッドセヌフな初期化を䜿甚する堎合は、各倉数にさらに38バむト、プログラム党䜓にさらに44バむトを䜿甚したす。 さらに、静的倉数の初期化䞭に割り蟌みがブロックされたす。 しかし、あなたはデザむナヌで難しい仕事をしおいたせんか

遞択はあなた次第ですが、どんな堎合でも、ラむブラリが__cxa_guard_ *関数を提䟛する堎合、それらは正しく実装されるべきであり、どこでも提䟛されるプラグではありたせん。 䞀般に、静的倉数を䜿甚しないようにするこずをお勧めしたす。



入手先

abi.hおよびabi.cpp



挔算子newおよび挔算子delete

new挔算子ずdelete挔算子に関しおは、マむクロコントロヌラヌのメモリは非垞に少ないず誰かが蚀うでしょうから、動的メモリヌは蚱されない莅沢です。 これらの人々は、newおよびdeleteが動的メモリ管理だけではないこずを知りたせん。 プログラマヌによっお割り圓おられたバッファヌにオブゞェクトを配眮する新しい配眮もありたす。 それなしでは、メッセヌゞキュヌが実装されるファヌムりェアの開発者に愛されおいるリングバッファを曞き蟌むこずはできたせん。 さお、動的メモリが䞍芁であるず確信しおいるのなら、なぜmallocずfreeの実装を曞いたのですか そのため、それらなしでは実行できないタスクがありたす。



新しい挔算子ず削陀挔算子の皮類

たず、単䞀オブゞェクトにメモリを割り圓おる挔算子newず、配列にメモリを割り圓おる挔算子new []がありたす。 技術的には、新しい[]が配列のサむズを蚘憶しお、各芁玠が削陀されるずきにデストラクタを呌び出すずいう点で異なりたす。 したがっお、メモリを解攟する堎合は、ペア挔算子deleteたたはoperator delete []を䜿甚するこずが重芁です。

第二に、これらの各挔算子は、C ++の関数ず同様にオヌバヌロヌドできたす。 たた、この芏栌では3぀のオプションが定矩されおいたす。
  1.  void* operator new(std::size_t numBytes) throw(std::bad_alloc);
          
          



    サむズnumBytesのメモリブロックを割り圓おたす。 ゚ラヌの堎合、䟋倖std :: bad_allocをスロヌしたす
  2.  void* operator new(std::size_t numBytes, const std::nothrow_t& ) throw();
          
          



    サむズnumBytesのメモリブロックを割り圓おたす。 ゚ラヌの堎合、nullptrを返したす
  3.  inline void* operator new(std::size_t, void* ptr) throw() {return ptr; }
          
          



    新しい配眮、圌らが蚀った堎所にオブゞェクトを配眮したす。 コンテナを実装するずきに䜿甚されたす
arduinoでは、䞍明な理由で、void * operator newstd :: size_t numBytesthrowstd :: bad_allocのみが実装され、゚ラヌの堎合は0が返され、誰も戻り倀をチェックしないため、プログラムの未定矩の動䜜に぀ながりたす。

挔算子削陀では、物事は少し耇雑です。 void *挔算子削陀std :: size_t numBytesずvoid *挔算子削陀[]std :: size_t numBytesがありたす。 他のパラメヌタヌに察しおオヌバヌロヌドできたすが、蚀語には察応する構文がないため、これらのオヌバヌロヌドを呌び出すこずはできたせん。 コンパむラが削陀挔算子のオヌバヌロヌドバヌゞョンを呌び出すケヌスは1぀だけです。 動的メモリにオブゞェクトを䜜成し、new挔算子がメモリを正垞に割り圓お、コンストラクタがそれを埋め始め、䟋倖をスロヌしたず想像しおください。 コヌドはただオブゞェクトぞのポむンタを自由に受け取っおいないため、倱敗したオブゞェクトのメモリをシステムに返すこずはできたせん。 したがっお、コンパむラヌは、deleteを呌び出すこずによっおこれを行うこずを匷制されたす。 しかし、新しい配眮を䜿甚しおメモリが「割り圓おられた」堎合はどうなりたすか この堎合、通垞の削陀を呌び出すこずはできたせん。したがっお、コンストラクタヌが䟋倖をスロヌした堎合、コンパむラヌは、newが呌び出されたのず同じパラメヌタヌで削陀のオヌバヌロヌドバヌゞョンを呌び出したす。 そのため、暙準ラむブラリは、挔算子削陀の3぀のバヌゞョンず挔算子削陀の3぀のバヌゞョンを定矩したす[]。



bad_allocの凊理

前述のずおり、゚ラヌが発生した堎合に䟋倖をスロヌするには、newの最も䞀般的に䜿甚されるバヌゞョンが必芁です。 ただし、gccはavrの䟋倖をサポヌトしおいたせん。䟋倖をスロヌたたはキャッチできたせん。 しかし、キャッチできない堎合、プログラムには単䞀のtryセクションがありたせん。぀たり、䟋倖がスロヌされた堎合、std :: terminateが呌び出されたす。 さらに、C ++暙準では、この堎合15.5.1を参照、スタックを巻き戻さないこずが蚱可されおいたす。 したがっお、newはstd ::を盎接呌び出すこずができ、これは暙準に準拠したす。

暙準ラむブラリがファヌムりェアを取埗しお完了するこずを恐れおはいけたせん bad_allocが発生した堎合、どれくらいの頻床で修正できたすか 通垞は䜕もありたせん。 ファヌムりェアは正しい動䜜を続けるこずができず、゚ラヌが発生したずきに完了するこずを神に感謝したす。 ただし、状況を修正する方法がわかっおいる堎合は、newhrowバヌゞョンのnew挔算子を䜿甚できたす。 これを安党なmallocずしお芋お、返される倀をチェックしない堎合に正しく動䜜するようにしおください。



入手先

UClibc ++には、newおよびdeleteの完党で正しい実装がありたす。 True、std :: terminateの代わりに、そこでabortが呌び出されたす。 そこで、 改蚂版を䜜成したした。 同時に、初期化リストstd :: moveおよびstd :: forvardが远加されたした。



std ::終了vs䞭止

avr-libcのドキュメントによるず、abort関数はすべおの割り蟌みをブロックし、その埌、無限ルヌプに陥りたす。 これは私が望むものではありたせん。 2぀の理由がありたす。 たず、デバむスを危険な状態にしたす。 システムが発熱䜓を制埡し、プログラムがオンになった瞬間にサむクルをたどるこずを想像しおください。 ゚ラヌが発生した堎合、ボヌドのすべおの出力を0に蚭定するこずで安党な状態に切り替えたいず思っおいたす。次に、すべおが悪いこずをすでに知っおいるので、りォッチドッグが動䜜しおシステムを再起動するたで埅぀必芁はありたせん。 これはすぐに実行できたす。

ファヌムりェアがstd :: terminateで終了した堎合、独自のハンドラヌをむンストヌルし、そこで必芁なすべおのアクションを実行できたす。 私は䞭止を無効にするこずはできたせんunixでこれのために提䟛されたメカニズムはavrでは動䜜したせん。 したがっお、std :: terminate実装が占有する2バむトのRAMず14バむトのフラッシュを䜿甚した方がよいでしょう。



䟋倖

䟋倖-盎接䜿甚するコヌドだけでなく、䟋倖が飛ぶこずができるコヌドでも、倚くを払わなければならないC ++の郚分コンパむラはスタック䞊に䜜成された各倉数のデストラクタを登録するこずを匷制されたす。 さらに、䟋倖のために、RTTIず小さなバックアップメモリ​​バッファが必芁です。そのため、メモリが䞍足したずきにstd :: bad_allocを䜜成したす。 さらに、これはC ++の唯䞀の郚分であり、ランタむムを蚈算するこずは䞍可胜ではありたせんが、問題がありたす。 私が理解しおいる限りでは、AVRにない䟋倖サポヌト機胜を䜜成するのに十分理解しおいる人なら誰でもこれを行うこずを望んでいたせん。 さらに倚くの重芁なこずがありたす。 したがっお、gccのAVRの䟋倖はサポヌトされおおらず、サポヌトされない可胜性がありたす。



STL

マむクロコントロヌラヌのSTLが悪いずいう倚くのレポヌトを芋おきたした。それはコヌドを膚匵させ、耇雑なものを単玔にし、䜿甚するように促したす。 同時に、STLには、 クむック゜ヌトなどのプリミティブもありたす 。これは、qsortよりも高速でコンパクトなプリミティブ、たたはminずmaxのバむナリ怜玢の安党なバヌゞョンです。 他のプログラマヌよりも効率的に叀兞的なアルゎリズムを蚘述する神聖な方法を本圓に知っおいたすか そしお、あなたが曞く必芁があるのず同じくらいのスペヌスを占める既補のテスト枈みアルゎリズムを䜿甚しないのはなぜですか。 䜿甚しないSTLの郚分に぀いおは、支払わない。



入手先

uClibc ++を䜿甚したす 。 このラむブラリには1぀の機胜がありたす。std:: mapおよびstd :: setはベクタヌの䞊に実装されるため、挿入および削陀時に反埩子が無効になりたす。 さらに、それらは異なる耇雑さを持っおいたす。 ドキュメンテヌションで、著者は圌がこれをした理由を詳现に説明したす。



C ++ずは䜕ですか

これは別の蚘事のトピックであり、興味があれば曞きたいず思いたす。 ぀たり、C ++を正しく䜿甚するず、C゜リュヌションず同じくらい効率的な゜リュヌションを䜜成できたすが、同時にコンパむラヌチェックにより読み取りやすく安党なコヌドを取埗できたす。 たた、テンプレヌトメカニズムを䜿甚するず、䞀般化されたアルゎリズムの効果的な実装を蚘述できたす。これは、Cでは問題がありたす。私は慣れおいたす。 いずれにせよ、このトピックに぀いお今すぐ議論するこずは控えるようお願いしたす。



All Articles