コマンド インターフェイスを介してデバイスを制御する組み込みアプリケーションに取り組んでいます。私は VC でコマンド ディスパッチャーを嘲笑し、満足のいく動作をさせました。しかし、コードを組み込み環境に移したとき、コンパイラーの関数へのポインターの実装が壊れていることがわかりました。
これが私が最初にコードを実装した方法です(VCで):
/* Relevant parts of header file */
typedef struct command {
const char *code;
void *set_dispatcher;
void *get_dispatcher;
const char *_description;
} command_t;
#define COMMAND_ENTRY(label,dispatcher,description) {(const char*)label, &set_##dispatcher, &get_##dispatcher, (const char*)description}
/* Dispatcher data structure in the C file */
const command_t commands[] = {
COMMAND_ENTRY("DH", Dhcp, "DHCP (0=off, 1=on)"),
COMMAND_ENTRY("IP", Ip, "IP Address (192.168.1.205)"),
COMMAND_ENTRY("SM", Subnet, "Subunet Mask (255.255.255.0)"),
COMMAND_ENTRY("DR", DefaultRoute, "Default router (192.168.1.1)"),
COMMAND_ENTRY("UN", Username, "Web username"),
COMMAND_ENTRY("PW", Password, "Web password"),
...
}
/* After matching the received command string to the command "label", the command is dispatched */
if (pc->isGetter)
return ((get_fn_t)(commands[i].get_dispatcher))(pc);
else
return ((set_fn_t)(commands[i].set_dispatcher))(pc);
}
関数ポインターを使用しない場合、私の唯一の望みは、switch()/case ステートメントを使用して関数を呼び出すことです。しかし、大きな switch() ステートメントを手動で維持する必要は避けたいと思います。
私が考えていたのは、すべての COMMAND_ENTRY 行を別のインクルード ファイルに移動することです。次に、そのインクルード ファイルをさまざまな #define および #undefines でラップします。何かのようなもの:
/* Create enum's labels */
#define COMMAND_ENTRY(label,dispatcher,description) SET_##dispatcher, GET_##dispatcher
typedef enum command_labels = {
#include "entries.cinc"
DUMMY_ENUM_ENTRY} command_labels_t;
#undefine COMMAND_ENTRY
/* Create command mapping table */
#define COMMAND_ENTRY(label,dispatcher,description) {(const char*)label, SET_##dispatcher, GET_##dispatcher, (const char*)description}
const command_t commands[] = {
#include "entries.cinc"
NULL /* dummy */ };
#undefine COMMAND_ENTRY
/*...*/
int command_dispatcher(command_labels_t dispatcher_id) {
/* Create dispatcher switch statement */
#define COMMAND_ENTRY(label,dispatcher,description) case SET_##dispatcher: return set_##dispatcher(pc); case GET_##dispatcher: return get_##dispatcher(pc);
switch(dispatcher_id) {
#include "entries.cinc"
default:
return NOT_FOUND;
}
#undefine COMMAND_ENTRY
}
この状況を処理するためのより良い方法を見ている人はいますか? 残念ながら、「別のコンパイラを入手する」という選択肢はありません。:(
--- 編集して追加: 明確にするために、特定の組み込み環境は、コンパイラが「関数ポインタ テーブル」を作成することになっているという点で壊れています。これは、ポインタを介して関数への呼び出しを解決するためにコンパイラによって使用されます。残念ながら、コンパイラは壊れており、正しい関数テーブルを生成しません。
そのため、関数アドレスを抽出して呼び出す簡単な方法がありません。
--- 編集 #2: ああ、はい、void *(set|get)_dispatcher の使用は、func ポインターの typedefine に問題があるかどうかを確認するための私の試みでした。もともと、持っていた
typedef int (*set_fn_t)(cmdContext_t *pCmdCtx);
typedef int (*get_fn_t)(cmdContext_t *pCmdCtx);
typedef struct command {
const char *code;
set_fn_t set_dispatcher;
get_fn_t get_dispatcher;
const char *_description;
} command_t;