メインのソースに手を入れず処理を追加する方法
投稿:1970-01-01、更新:2018-01-19
2012.09.19
冒頭からすみません。もっと良い方法が見つかりましたので名前参照によらない関数呼び出しをご覧ください。
プロジェクトに処理を追加していく際に当然ながら、メインの処理から呼び出すコーディングを入れますよね。
実は、単に1回呼べばいい訳じゃないので面白くありません。
呼び出しは3回必要です。なぜか?そりゃ私が決めたから。
2018-01-19 【この動画を削除しました】
- クロック設定の前
- タスクスケジュール開始前
- スケジュール開始直後
2018-01-19 【この動画を削除しました】
さて、普通に「将来呼ぶ予定の関数」をメイン処理で呼び出すコーディングをしてしまうと、リンク時に「そんな関数ないよ」と言われて失敗します。
形態はどうであれ、何かしらの呼び出すコーディングが必要です。
この一見矛盾する条件をクリアする良い方法を見つけました。
weakとaliasって属性をコーディングで使うとリンク時に面白いことが起きます。
- 本来のラベルが見つかると、そのラベルで解決して、aliasのラベルを破棄する。
- 本来のラベルがない場合、aliasで解決する。
#define INIT_WEAK(empty) extern __attribute__ ((weak, alias (#empty))) TAGITEM_TYPE INIT_WEAK(init_empty) init_01[1]; INIT_WEAK(init_empty) init_02[1]; INIT_WEAK(init_empty) init_03[1]; INIT_WEAK(init_empty) init_04[1]; INIT_WEAK(init_empty) init_05[1]; INIT_WEAK(init_empty) init_06[1]; INIT_WEAK(init_empty) init_07[1]; INIT_WEAK(init_empty) init_08[1]; INIT_WEAK(init_empty) init_09[1]; INIT_WEAK(init_empty) init_10[1]; TAGITEM_TYPE init_empty[] = { {TAG_END, TAG_NONE} };はい、これでinit_01~init_10の10個の配列は全部init_emptyで解決されますから、無事リンクできます。 そして、このプロジェクトにinit_03という配列を含むソースを追加してリンクすると、init_emptyの代わりに本物のinit_03で解決しますから、 追加したinit_03にメイン処理がアクセス出来る訳です。もちろん初めに説明した通り、メイン処理に変更は不要です。
プロジェクトのスケルトンに習って関数を解決すると済む話をわざわざ配列にした事情があります。
冒頭で説明しましたように呼び出し機会は1個の機能に3回あります。
機能を10個(そんなに入れるのか!)用意すると呼び出し回数=3回/機能×10機能=30回となってラベルが30個必要なのと、
ループ処理で10機能分の呼び出し処理を済ませる都合があります。
もっと大きな理由が、呼び出し機会を追加した際に、例えば3回→4回に増えた時に30個→40個と増やさなくて済むようにしました。
古参の機能(?)は新設の呼び出し機会を知らなくてよく、新参の機能で必要であれば、新設の呼び出しで呼んでもらえばいい訳です。
さて、今回の鉄ゲタコーナーは…
//メインの初期処理 実際の呼び出しは離れています ksrk_initcall(INIT_CLOCK); ksrk_initcall(INIT_POWER); ksrk_initcall(INIT_MAIN); //10個まとめて呼ぶための配列 TAGITEM_TYPE *(InitCalls[]) = { init_01, init_02, init_03, init_04, init_05, init_06, init_07, init_08, init_09, init_10, 0 }; //10個まとめて呼ぶ(tag_getvalueは配列内のキーを検索して値を返す) void ksrk_initcall(uint32_t ulTagKey) { int i; for (i = 0; InitCalls[i]; i++) { void (*pFunc)(void) = (void (*)(void))tag_getvalue(InitCalls[i], ulTagKey, 0); if (pFunc) { (pFunc)(); } } } //追加したソース static void init_main(void) { } //追加したソース内の配列 INIT_MAINのときに呼ばれる TAGITEM_TYPE init_03[] = { {INIT_MAIN, (uint32_t)init_main}, {TAG_END, TAG_NONE} };これをコーディングしていて、なんだかC++のメンバ関数の動作を作ってるような気がしてきました。 結構似てるでしょ?
さて、今回の鉄ゲタコーナーは…
当初考えていた方法は、
「呼んでほしい関数のアドレスをマジックコード(珍しい数字)で挟んでおいて、メイン処理がメモリ内をスキャンして呼ぶ。」
と言うモノでした。
いざ実行してみるとちっとも見つけてくれない、デバッガでメモリの中身を人力で探してみるけど見つからない。
あれ、マジックコードどこ行った?
リンカの実行結果のマップを見ると追加のオブジェクトをばっさり破棄してた。
確かに、ちょっとでも所要サイズを削りたいもんね。
メイン処理から見て関連のないオブジェクトを削除するので、今回の用途には使えませんでした。
まあ、どうせ話が通じないと思うので、あとはソース見てください(^^;
yrntrlmnmnt20120209.zip
まあ、どうせ話が通じないと思うので、あとはソース見てください(^^;
yrntrlmnmnt20120209.zip