= TOPPERS/JSPカーネル ユーザズマニュアル = (C++バインディング) (Release 1.4.1 対応,最終更新: 16-Sep-2004) ------------------------------------------------------------------------ TOPPERS/JSP Kernel Toyohashi Open Platform for Embedded Real-Time Systems/ Just Standard Profile Kernel Copyright (C) 2000-2003 by Embedded and Real-Time Systems Laboratory Toyohashi Univ. of Technology, JAPAN Copyright (C) 2003-2004 by Takagi Nobuhisa 上記著作権者は,以下の (1)〜(4) の条件か,Free Software Foundation によって公表されている GNU General Public License の Version 2 に記 述されている条件を満たす場合に限り,本ソフトウェア(本ソフトウェア を改変したものを含む.以下同じ)を使用・複製・改変・再配布(以下, 利用と呼ぶ)することを無償で許諾する. (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作 権表示,この利用条件および下記の無保証規定が,そのままの形でソー スコード中に含まれていること. (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使 用できる形で再配布する場合には,再配布に伴うドキュメント(利用 者マニュアルなど)に,上記の著作権表示,この利用条件および下記 の無保証規定を掲載すること. (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使 用できない形で再配布する場合には,次のいずれかの条件を満たすこ と. (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著 作権表示,この利用条件および下記の無保証規定を掲載すること. (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに 報告すること. (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること. 本ソフトウェアは,無保証で提供されているものである.上記著作権者お よびTOPPERSプロジェクトは,本ソフトウェアに関して,その適用可能性も 含めて,いかなる保証も行わない.また,本ソフトウェアの利用により直 接的または間接的に生じたいかなる損害に関しても,その責任を負わない. ------------------------------------------------------------------------ 1.C++バインディングの概要 1.1 目的 TOPPERS/JSPカーネルは、本来C言語APIを提供するものであり、そのままでは C++を用いたアプリケーション開発を行う上で支障がある。 C++バインディングは、TOPPERS/JSPカーネルが提供するサービスコールをC++ から呼出すことを可能にし、かつTOPPERS/JSPカーネル上で標準C++の機能を 利用できるようにするものである。 1.2 開発環境と実行環境 C++バインディングでは、GCC等のGNU開発環境を使用する。なお、Windows上 のシミュレーション環境(Visual C++)でC++を使用する際は、C++バインデ ィングの機能を必要としない。 現在、C++バインディングがサポートする実行環境は、MS7727CP01(SH3)およ びAKI-H8/3069Fである。他の実行環境で使用するには、ターゲット依存部の Makefile.configおよびリンカスクリプトの修正が必要となる。 また、Linux上のシミュレーション環境では使用することができない。 1.3 C++バインディングの機能の概要 標準C++の機能のうち、実行環境に依存する部分について、TOPPERS/JSPカー ネルに対応させている。具体的には、非ローカルオブジェクトの初期化処理、 終了処理、グローバルなnew演算子とdelete演算子、例外処理、およびフリー スタンディング環境における標準ライブラリ関数である。 GCC自体が未サポートの機能(export、ユニバーサル文字名等)を補完するも のではない。 1.4 C++バインディングの使用方法 TOPPERS/JSPカーネルでC++バインディングを使用するには、コンフィギュレ ーションファイル(.cfg)から、jsp/systask/cxxrt.cfgをインクルードする だけでよい。(静的APIのINCLUDEではなく、プロプロセッサ指令の#include を使用すること) TOPPERS/JSPカーネルのconfigureを使用する場合、C++を使うには"-l c++" オプションを付加する必要がある。 2.C++バインディングの機能 2.1 非ローカルオブジェクトの初期化処理 関数の外部で宣言されたオブジェクト(特定の名前空間やクラスの静的デー タメンバを含む)のコンストラクタを、起動時に呼出す機能を提供する。 非ローカルオブジェクトのコンストラクタは、kernel_start関数の呼出しよ り前に実行され、syslogの機能を使用することはできない。 また、コンストラクタの内部では、vsns_iniを除くサービスコールを呼出す ことはできない。 関数の内部で宣言されたオブジェクトは、起動時ではなく、宣言された箇所 に最初に実行パスが差し掛かったときにコンストラクタが呼出されるため、 起動時の初期化処理には含まれない。 そうしたコンストラクタの呼出しはマルチタスクに対応しないため、ユーザ は必要に応じて排他制御を独自に行う必要がある。 2.2 終了処理 静的なオブジェクト(関数内で宣言されたものを含む)のデストラクタは、 std::exit関数の中から呼出される。明示的にstd::exit関数を呼出さない限 り、全タスクが終了したとしてもデストラクタが呼出されることはない。 なお、kernel_exit関数を呼出した場合も、std::exit関数を呼出した場合と 同様に、静的なオブジェクトのデストラクタは呼出される。 終了時の動作順序は、静的APIのVATT_TERで登録した終了処理ルーチンが呼出 された後、std::atexit関数で登録した終了時関数が呼出され、最後にデスト ラクタが呼出される。 デフォルトの標準ストリーム(stdin, stdout, stderr)をサポートする場合、 それらのクローズ処理はデストラクタの呼出し後に行わなければならない。 (ユーザの手でカスタマイズする必要がある) 2.3 グローバルなnew演算子とdelete演算子 TOPPERS/JSPカーネルのマルチタスク環境および非マルチタスク環境(初期化 または終了処理時)で動作するnewおよびdelete演算子を提供する。 GCCはグローバルなnewおよびdelete演算子をstd::mallocおよびstd:free関数 を用いて実装しているため、NEWLIBのmallocおよびfree関数に排他制御を追 加することで実現している。 C++バインディングは、C++のフリースタンディング環境を対象としているが、 上記の理由により、本来フリースタンディング環境ではサポートされない、 std::mallocおよびstd::free関数も結果的にサポートしている。 おそらくはstd::callocおよびstd::realloc関数も動作すると思われるが、検 証は行っていない。 標準C++のグローバルなnewおよびdelete演算子には、例外をスローすること がない、const std::nothrow_t&を引数にとる配置構文型のものもあるが、通 常のもの同様に使用することができる。 2.4 例外処理 C++の言語機能である例外処理機能(try、catchおよびthrow)を提供する。 例外処理はコンパイラの実装に密接に絡むため、コンパイラ自体にパッチが 必要になる。 例外処理は、tryおよびcatchの位置と、throwからcatchまでの間に存在する ローカルオブジェクトのデストラクタを管理する必要がある。したがって、 コンパイラにとって未知の方法でコンテキストが切り替わった場合には、例 外処理用の管理情報も切り替える必要が生じる。 C++バインディングは、マルチタスク環境および非マルチタスク環境(初期化 または終了処理時)で、コンテキストに応じて管理情報を切り替える機能や 同期機能を実現している。 GCCでは、コンパイル時に-fno-exceptionsオプションを指定することで、例 外処理を無効にすることができるが、これはオプションを指定した翻訳単位 に対してのみ通用することであり、ライブラリ等は、通常通り、例外処理が 使える場合と同様に振舞うことになる。したがって、new、dynamic_cast、 あるいはtypeid演算子が失敗した場合等には、予期せぬ誤動作につながる危 険性があるため、必ずしも推奨しない。 2.5 標準ライブラリ関数 フリースタンディング環境のC++では、終了処理に関する標準ライブラリ関数 として、std::abort、std::atexit、およびstd::exit関数がサポートされる。 C++バインディングでも、これら3種類の関数を提供している。 (1) std::abort std::abort関数は、静的APIのVATT_TERで登録された終了処理ルーチンや、 std::atexit関数で登録された終了時関数、オブジェクトのデストラクタを呼 出すことなく、システムを異常終了する。 システムの終了処理は、マルチタスク動作中はターゲット依存部で定義され るkernel_abort関数に従い、非マルチタスク動作中(初期化または終了処理時) はNEWLIBの_exit関数に従う。 C++バインディングでは、関数へのポインタvoid (*_atabort)(void)が0以外 に定義されている場合、実際の終了処理より前にそれを呼出すようになって いる。 ホスト環境のstd::abortは、実際の終了処理の前にstd::raise(SIGABRT);を 呼出すが、_atabortを適切に設定することで同様の機能をエミュレートする ことができる。 (2) std::atexit std::atexit関数は、std::exitまたはkernel_exit関数から呼出される終了時 関数を登録する。std::atexit関数で登録した関数は、std::abort関数で終了 した場合や、vsns_iniがTRUEを返す期間にstd::exit関数を用いた場合には、 呼出されることがない。 静的なオブジェクトのデストラクタは、実際にはstd::atexit関数を用いるこ とで実現している。ただし、デストラクタの呼出しは、std::atexit関数で登 録されたその他のいかなる終了時関数よりも後に呼出される。 std::atexit関数で登録できる終了時関数は、(デストラクタの登録を除いて) 32個までである。 (3) std::exit std::exit関数は、C++の標準的な終了処理を行う。詳細な動作は、2.2 終了 処理を参照のこと。 std::exit関数を用いて終了した場合、ローカルオブジェクトのデストラクタ が呼出されることはない。 3.C++実行環境としてのTOPPERS/JSPカーネル 3.1 BOOL型 標準C++には、論理型であるbool型と、論理値であるtrueおよびfalseが存在 する。しかし、TOPPERS/JSPカーネルがC言語で実装されているため、言語間 の互換性をとる意味でもBOOL型にはint型を使用する必要がある。 カーネルのコンパイルにC99を用いた場合でも、_Bool型とbool型の間には、 厳密な互換性があるとは言いがたいため、やはりint型を使用した方が無難で ある。 3.2 タスクからの例外リーク タスクのエントリ関数からキャッチされない例外がリークした場合の動作は 未定義である。 3.3 ATT_INIとVATT_TER 静的APIのATT_INIおよびVATT_TERで登録された関数から例外がリークした場 合の動作は未定義である。 3.4 非タスクコンテキストとタスク例外処理ルーチン 非タスクコンテキストとタスク例外処理ルーチンでは、例外処理、newおよび delete演算子を使用することができない。参照型に対するdynamic_castや、 仮想関数を持つクラスへの参照やポインタに対するtypeidも、例外が発生す る可能性があるため、使用することができない。また、標準ライブラリ関数 を呼出すこともできない。 なお、タスク例外許可状態で自タスクに対するras_texによって呼出されたタ スク例外処理ルーチンに限り、上記の制約は受けない。ただし、タスク例外 処理ルーチンから例外をリークした場合の動作は未定義である。 3.5 サービスコールの例外指定 TOPPERS/JSPカーネルが提供する全サービスコールは例外をスローすることが ないため、例外指定throw()が付加される。 これにより、例外処理のためのテーブルが生成されることを最小限に抑えら れる場合があり、主として空間効率の向上が期待できる。 4.開発環境の構築 C++開発環境の構築は、基本的には「GNU開発環境構築マニュアル」の内容に 従うが、一部異なる点があるので、相違点を中心に解説する。 なお、動作確認には、ホスト環境としてはCygwin 1.3.22を使用した。 4.1 必要なソフトウェア C++開発環境を構築するには、GCC-COREおよびG++、またはGCCが必要になる。 また、NEWLIBは必須である。 GNU開発環境 BINUTILS(アセンブラ,リンカなど) GCC(C/C++コンパイラ) GDB(デバッガ) NEWLIB(標準Cライブラリ) Perl(動作確認は 5.8.0) GNU Make(動作確認は 3.80) GNU sed(動作確認は 4.0.7) patch(動作確認は 2.5.8) 4.2 BINUTILSのインストール BINUTILSは、GCCのインストールに必要なため,GCCに先だってインストール する。BINUTILSのインストール手順は次の通り。 % mkdir % cd % /configure --target= --prefix=\ --disable-nls % make % make install 4.3 GCCへのパッチ Cygwin上でGCC 3.2.3をmakeする場合に一部障害が発生するため、次のパッチ が必要となる。(SH版のみ。GCC 3.3.x以降では不要) % cp gcc-3.2.3_fixinc_gnu-regex.patch % cd % patch -p1 < gcc-3.2.3_fixinc_gnu-regex.patch 以下の手順に従って、GCCをTOPPERS/JSPカーネルに対応させるためのパッチ をあてる。 % cp gcc-3.2.3_gthr-toppers-1.patch % cd % patch -p1 < gcc-3.2.3_gthr-toppers-1.patch 4.4 GCCのインストール(1) GCCのインストールにはNEWLIBが必要であるが、NEWLIBをインストールするに はGCCが必要なため、若干の工夫が必要になる。 % mkdir % cd % /configure --target= --prefix=\ --enable-languages=c,c++ --enable-threads=toppers\ --disable-nls % make all-gcc % make install-host 4.5 NEWLIBのインストール 次の手順に従って、NEWLIBをインストールする. % mkdir % cd % /configure --target= --prefix= % make % make install なお、CygwinのバージョンによってはNEWLIBのインストールに失敗する場合 がある。そのような場合は、Cygwinのバージョンを変更するか、NEWLIBをバ イナリで入手すること。 4.6 GCCのインストール(2) 次の手順に従って、GCCの残り部分をインストールする. % cd % make % make install 5.C++バインディングの実装詳細 5.1 ファイル構成 (1) cxxrt.cfg cxxrt.cfgはC++対応ランタイムの内部で使用するオブジェクトを生成する。 C++の機能を使用する場合には、必ずコンフィギュレーションファイルから、 cxxrt.cfgをインクルードする必要がある。 (2) cxxrt.c cxxrt.cはC++対応ランタイムの本体であり、例外処理等で必要となる関数や 変数の定義が行われる。 (3) newlibrt.c newlibrt.cはNEWLIBの関数をTOPPERS/JSPカーネルに対応させるためのランタ イムであり、C++を使用する場合には必須である。 このランタイムは、C++を使用しない場合でも、NEWLIBを使用する際には単独 で使用することができる。 5.2 同期機能 例外処理および標準C++ライブラリ内部で使用する同期機能を提供する関数群。 標準C++ライブラリはフリースタンディング環境ではサポートされないが、お そらくlibstdc++の全機能を使用することができると思われる。(ただし、動 作は確認していない) (1) int _toppers_cxxrt_lock(_toppers_cxxrt_sync_t *sync); マルチタスク動作時では、ロックを掛け、クリティカルセクションを開始す る。非マルチタスク動作時には何もしない。 成功時には0を返し、失敗時には負の値を返す。 この関数はネスティング可能である。 (2) int _toppers_cxxrt_trylock(_toppers_cxxrt_sync_t *sync); _toppers_cxxrt_lock関数とほぼ同じであるが、既に他のタスクによってロック 中の場合でも待機状態には移行せず、単に呼出しに失敗する。 現在の実装では、_toppers_cxxrt_lock関数はディスパッチ禁止を用いて実現し ているため、どちらも同じである。 (3) int _toppers_cxxrt_unlock(_toppers_cxxrt_sync_t *sync); マルチタスク動作時では、ロックを解除し、クリティカルセクションを終了 する。非マルチタスク動作時には何もしない。 成功時には0を返し、失敗時には負の値を返す。 (4) int _toppers_cxxrt_once(_toppers_cxxrt_once_t *once, void (*func)(void)); 指定した関数を、システム全体で一度だけ実行させる。funcを実行中に、異 なるタスクでも同じ関数を実行させようとした場合、funcの実行が完了する まで待機状態に移行する。 成功時には0を返し、失敗時には負の値を返す。 5.3 タスクローカル記憶域機能 タスクごとに用意される、見かけ上静的な記憶域を提供する関数群であり、 例外処理で使用される。 タスクローカル記憶域として確保できるのは、デフォルトでは2個までである が、_CXXRT_KEY_MAXマクロをユーザ定義することで、3個以上に拡張すること ができる。 (1) int _toppers_cxxrt_key_create(struct _toppers_cxxrt_tls **key, void (*dtor)(void*)) タスクローカル記憶域に確保する変数を新規作成する。 成功時には0を返し、*keyに変数を識別するためのキーが格納される。 dtorには、キーを削除する際に、タスクごとの各変数に対して実行すべき終 了処理を指定する。 失敗時には負の値を返す。 (2) int _toppers_cxxrt_key_delete(struct _toppers_cxxrt_tls *key); _toppers_cxxrt_key_create関数で作成したキーを削除する。 成功時には0を返し、失敗時には負の値を返す。 5.4 NEWLIBのカスタマイズ (1) void __malloc_lock(struct _reent *ptr); mallocおよびfreeの排他制御を行う。他のタスクによってロックされている 状況で呼出された場合は待機状態に移行する。 (2) void __malloc_unlock(struct _reent *ptr); __malloc_lock関数によるロックを解除する。 (3) void *_sbrk_r(struct _reent *ptr, ptrdiff_t nbytes); プログラムのヒープ領域をnbyteだけ拡大する。成功時には、新しい領域の先 頭へのポインタを返す。失敗時は負の値を返し、errnoおよびptr->_errnoに ENOMEMを設定する。 ヒープ領域には、外部識別子end(.bssセクションの終端)から、マルチタス ク動作開始前のスタックポインタまでを使用する。 メモリマップ上、複数のメモリイメージが発生するターゲットでは、.bssセ クションとスタックポインタの初期値を同一イメージ上に配置する必要があ る。 ヒープ領域と起動時のスタック領域が連続していない場合、sys_config.h内 でHEAP_TOPマクロをヒープの終端アドレスに定義する必要がある。 本来であれば、_sbrk_r関数の原始関数にあたる_sbrk関数をカスタマイズす べきであるが、ターゲットによっては、_write関数等と同一の翻訳単位にオ リジナルの_sbrk関数が定義されており、リンク時に障害が発生する原因とな るため、_sbrk_r関数をカスタマイズしている。 5.5 その他 (1) int main(); JSPカーネル上で動作するC/C++プログラムはフリースタンディング環境であ るため、エントリ関数はmainではなくkernel_startである。しかし、NEWLIB ではエントリポイントとしてmainが使用されることを期待しているため、 main関数がなければリンクエラーになる場合がある。 C++対応ランタイムでは、リンクを解決することのみを目的としてmain関数を 定義する。 6.ターゲット依存部の実装 現在、C++バインディングがサポートする実行環境は、MS7727CP01(SH3)およ びAKI-H8/3069Fである。 また、GNU開発環境のターゲットとして、SH用にはsh-hitachi-elf、H8用には h8300-hmsを使用している。 6.1 GNU開発環境に関するもの GNU開発環境全般に関する要素として、以下のものがある。 (1) リンカスクリプトの対応 C++を用いる場合のリンカスクリプトには、.ctorおよび.dtor、すなわちグロ ーバルなコンストラクタやデストラクタのポインタテーブルを形成するため のセクション定義が必要である。 また、.eh_frameや.gcc_except_tableなどの例外処理用のセクション定義も 必要になる。 6.2 ELF形式に関連するもの SHに限らず、ELF形式に依存する要素として、下記内容の対応が必要である。 (1) crti.o, crtbegin.o, crtend.o, crtn.oのリンク グローバルなコンストラクタおよびデストラクタの呼出しや、例外処理関連 に必要なため、これらのファイルを正しい順序でリンクする必要がある。 リンクの順序は、 start.o crti.o crtbegin.o ユーザプログラム libkernel.a libstdc++.a libm.a libc.a libgcc.a crtend.o crtn.o である。 この指定には、ターゲット依存部のconfig/sh3/Makefile.configにおいて、 START_OBJS = \ start.o \ $(shell $(CC) $(COPTS) -print-file-name=crti.o | sed -e 's/\\/\//g') \ $(shell $(CC) $(COPTS) -print-file-name=crtbegin.o | sed -e 's/\\/\//g') END_OBJS = \ $(shell $(CC) $(COPTS) -print-file-name=crtend.o | sed -e 's/\\/\//g') \ $(shell $(CC) $(COPTS) -print-file-name=crtn.o | sed -e 's/\\/\//g') のように定義されている。 なお、文字'\'を'/'に置換しているのは、MinGW + MSysの環境で使用できる ようにするためである。 また、make時に誤ってcrti.oなどのコンパイルを試みようとすることを回避 する目的で、START_OBJSとEND_OBJS(およびstart.o)のmakeルールを特化し ている。 (2) リンカスクリプトの対応 .initや.fini等のELF形式特有のセクション定義が必要である。 6.3 SHに特有のもの ターゲットがSHの場合に特有な要素として、以下のものがある。 (1) _init関数および_fini関数 GCCの多くのターゲットでは、ELF形式の場合、初期化関数および終了時関数 として、_init関数と_fini関数を使用する。しかし、SHの場合にはinit関数 とfini関数(先頭の下線がない)を使用している。そのため、tool_defs.h において、initおよびfiniにそれぞれマクロ置換している。 7.アプリケーション作成におけるヒント 7.1 静的なオブジェクト 静的なオブジェクトは普通に定義することができるが、以下の点に注意する 必要がある。 (1) カーネル非動作状態 グローバルなコンストラクタやデストラクタは、カーネル非動作状態で呼出 される。したがって、これらの関数内では、μITRONのサービスコールを呼出 すことは出来ない。唯一呼出すことが出来るサービスコールは、JSPカーネル の独自拡張であるvsns_iniだけである。 クラスをグローバルでもローカルでも使えるようにするには、必要に応じて、 vsns_iniでカーネルの動作状態を判別し、状態に応じて処理を分ける必要が ある。 (2) 関数内の静的オブジェクト 関数内で定義された静的オブジェクトのコンストラクタは、必ずしも起動時 に呼出される訳ではなく、実行パスが最初にそのオブジェクトの定義箇所に 差掛かった時点で呼出される。 したがって、コンストラクタがカーネル動作状態で呼出されるか、非動作状 態で呼出されるかは、そのオブジェクトが定義された関数が最初に呼出され るタイミングに依存する。 一般的に、この問題を回避するにはDouble-Checked Lockingと呼ばれる手法 が用いられる。 class foo { // クラス定義 }; void func() { static foo* ptr = 0; if (ptr == 0) { wai_sem(SEMID); if (ptr == 0) { static foo x; ptr = &x; } sig_sem(SEMID); } // 関数本体の処理 } (3) システムログの使用 デストラクタでは、システムログを制限なく使用することができるが、グロ ーバルなコンストラクタでは、デフォルトではシステムログを使用すること ができない。 7.2 new演算子とdelete演算子 (1) 排他制御について new演算子とdelete演算子は、処理速度を向上するため、フリーストアからの メモリ割付け(std::malloc関数を用いて実装)はディスパッチ禁止状態で行 っている。 アプリケーションの要求によって、他の方法で排他制御を行う必要がある場 合には、systask/cxxrt.cの中の__malloc_lock関数と__malloc_unlock関数を カスタマイズすればよい。 (2) 使用に関する制約 非タスクコンテキストやタスク例外処理ルーチンで呼出すことはできない。 カーネル非動作状態(vsns_ini関数がTRUEを返す期間)では、通常通り使用 することができる。 (3) オーバーロードについて グローバルなnew演算子とdelete演算子は、実行効率はまずまずであるが、空 間効率は決して高いとはいえない。また、特定のクラスの最低生成数につい ても評価しにくいため、厳密な動作保証を必要とするクラスに関しては、new 演算子とdelete演算子をオーバーロードすることを推奨する。 オーバーロードに際しては、固定長メモリプール等を用いることが出来る。 例えば、以下のようにクラスを定義することが出来る。 #include #include #include "kernel_id.h" class foo { static ID mpfid_; public: static void* operator new(std::size_t size) throw() { VP ptr; get_mpf(mpfid_, &ptr); assert(ptr != 0); return ptr; } static void operator delete(void* ptr) throw() { rel_mpf(mpfid_, ptr); } }; static ID foo::mpfid_ = FOO_MPF; // 固定長メモリプールのID番号 また、new演算子の配置構文を使用することで、タイムアウト指定等の機能を 追加することも可能である。 static void* operator new(std::size_t size, TMO tmout); static void operator delete(void* ptr, TMO) throw(); なお、new演算子をオーバーロードする場合、必ず対応するdelete演算子も合 わせてオーバーロードしなければならない。 上記の場合、delete演算子でTMO型の引数を使用することはないが、newされ たクラスのコンストラクタから例外がスローされた場合(new演算子そのもの ではない)、対応するdelete演算子が必要になる。 7.3 例外処理 (1) 例外処理の内部処理 GCCでは、configure時に--enable-sjlj-exceptionsを指定した場合、例外処 理を実現するためにsetjmpおよびlongjmp関数を使用している。ターゲットに よっては他の実現方法を選択することもできるが、動作確認は行っていない。 例外をT型のオブジェクトをthrowした場合、内部的には以下の順序で処理が 行われる。 1) std::malloc関数により、T型のオブジェクトを格納可能なメモリブロ ックが割付けられる。 割付けに失敗した場合、緊急用の静的なバッファが当てられる。 2) T型のコピーコンストラクタが呼出され、throwされたオブジェクトの コピーが、1)で割付けたブロックを用いて生成される。 コピーコンストラクタ内で例外がthrowされた場合はstd::terminate 関数が呼出された後、std::abort関数により異常終了する。 3) 例外処理フレームを検索し、catchされるまでの経路に存在するロー カルなオブジェクトのデストラクタが呼出される。 ここで、デストラクタ内で例外がthrowされた場合はstd::terminate 関数が呼出された後、std::abort関数により異常終了する。 4) T型(またはその基底クラス)に対応するcatchブロックが見つかった 場合は、そのブロックに広域分岐する。catchブロックのパラメータ が値渡しの場合、コピーコンストラクタによって新しいローカルなオ ブジェクトが生成される。 対応するcatchブロックが見つからなかった場合や値渡しの際に例外 がthrowされた場合はstd::terminate関数が呼出された後、std::abort 関数により異常終了する。 5) catchブロック内の処理が実行される。 6) 上記2)で生成されたオブジェクトのデストラクタが呼出される。 (2) タスク再起動時の初期化 JSP 1.4では再起動時の初期化処理として_toppers_cxxrt_reset_specific関 数を呼び出す必要があったが、JSP 1.4.1では_toppers_cxxrt_reset_specific 関数の呼び出しは必要としない。 その代わり、タスクのエントリ関数から例外が動作した場合の動作は未定義 である。 (3) 効率の向上 configure時に--enable-sjlj-exceptionsを指定した場合、例外をthrowしな い場合でも、若干の実行効率の低下が起こる。また、空間効率が著しく低下 する場合がある。 効率を少しでも改善するには、以下の方法が有効である。 1) できるだけインライン関数を使用する。例外処理に関するオーバーヘ ッドの多くは、関数の呼出しに付随して発生する。 2) 決して例外を発生しない関数には、必ず例外指定throw()を付加する。 特に、C言語で実装された関数は、必ずthrow()を付けること。 3) 例外が発生するかもしれない関数には例外指定を付けない。 例外指定を付けるとフィルタリング処理が暗黙的に展開される。 4) 可能な限りデストラクタを定義しない。 関数内でデストラクタを持つローカルなオブジェクトを使わなければ、 例外をthrowするかもしれない関数を呼んでも、オーバーヘッドにな らない場合がある。 5) 一時オブジェクトの生成を避ける 一時オブジェクト生成はそれ自体がオーバーヘッドであるだけでなく、 コンストラクタで例外が発生する機会を作り、またデストラクタが呼 び出される機会を増やす。 7.4 C言語との混在 C++からC言語で定義した関数を呼出す場合、extern "C"により"C"リンケージ であることを明示的に指定しなければならない。 extern "C" int foo(); extern "C" { void hoge(); void bar(); } また、C言語から呼出す可能性のある関数をC++で定義する場合もextern "C" を関数定義に付加する必要がある。 CとC++では互換性のない型もあるため、両方から使用する関数に、そうした 型を使用すべきではない。例えば、boolやクラス型などである。 特に、kernel_cfg.cはC言語であるため、タスク等の拡張情報にクラスのポイ ンタを指定することはできない。 C関数からC++関数の呼出しは、原則として行うべきではない。内部で例外が 発生した場合、動作が保証できないのがその理由である。 通常の呼出しの他、コールバック関数を用いる場合も例外ではない。 C++関数からC関数を呼出す場合、C関数には例外指定throw()を付加すべきで ある。こうすることで大幅に効率の向上が期待できる。 以上