6

AVR プロジェクトの PROGMEM で文字列の配列をきれいに定義する方法を探しています。コマンド文字列のリストを必要とするコマンド ライン プロセッサがあります。

AVR アーキテクチャでこれを行う従来の方法は、各文字列を個別に定義してから、それらの文字列へのポインターの配列を定義することです。これは非常に冗長で醜いです:

typedef struct 
{
    PGM_P   str;        // pointer to command string
    uint8_t str_len;    // length of command string
    uint8_t id;         // CLI_COM_* ID number
} CLI_COMMAND_t;

const char CLI_STR_TEMP[]   PROGMEM = "TEMP";
const char CLI_STR_POWER[]  PROGMEM = "POWER";
...

const CLI_COMMAND_t cli_cmd_table[] = { { CLI_STR_TEMP,     sizeof(CLI_STR_TEMP),   CLI_COM_TEMP },
                                        { CLI_STR_POWER,    sizeof(CLI_STR_POWER),  CLI_COM_POWER },
                                        ...
                                      };

(CLI_COM_* は列挙されたインデックスですが、関数ポインターなどで置き換えることができます)

この混乱は、次のようなマクロを使用して文字列を定義し、テーブルを構築することで減らすことができます。

#define FLASH_STRING(NAME...)    const char CLI_STR_ ## NAME [] PORGMEM = #NAME;
#define FSTR(NAME...)            { CLI_STR_ ## NAME, sizeof(CLI_STR_ ## NAME), CLI_COM_ ## NAME) }

FLASH_STRING(TEMP);
FLASH_STRING(POWER);

CLI_COMMAND_t cli_cmd_table[] = { FSTR(TEMP), FSTR(POWER) };

(テストされていませんが、うまくいくはずです)

ただし、すべての文字列を一度だけ定義し、マクロで個々の文字列とポインター/サイズ/列挙参照の配列の両方を生成したいと考えています。

Arduino プラットフォームには FLASH_STRING_ARRAY マクロがありますが、これはよくわかりませんが、どちらもコンパイルされていないようです。ここで見ることができます: http://pastebin.com/pMiV5CMrおそらく、C++ のみか何かです。また、グローバルではなく、関数内でのみ使用できるようです。

AVR の文字列テーブルは長い間面倒でエレガントではありませんでした。必要なコードを生成するための小さなプログラムを作成する以外に、マクロを使用してコードを定義する方法があると便利です。

ボーナス ポイント: CLI_COM_* 定数を同じマクロ (列挙型または #defines のいずれか) で生成します。

編集:これの別の名前は、マクロによる反復宣言であると思います。

解決策: luser のおかげで、次の解決策を思いつきました。

typedef struct 
{
    PGM_P   str;        // pointer to command string
    uint8_t str_len;    // length of command string
    uint8_t id;         // CLI_COM_* ID number
} CLI_COMMAND_LUT_t;



#define COMMAND_TABLE \
        ENTRY(testA) \
        ENTRY(testB) \
        ENTRY(testC)

enum {
#define ENTRY(a) CLI_COM_ ## a,
    COMMAND_TABLE
#undef ENTRY    
};


#define ENTRY(a)    const char CLI_STR_ ## a PROGMEM = #a;
COMMAND_TABLE
#undef ENTRY


CLI_COMMAND_LUT_t command_lut[] PROGMEM = {
#define ENTRY(a) {CLI_STR_ ## a, sizeof(CLI_STR_ ## a), CLI_COM_ ## a},
    COMMAND_TABLE
#undef ENTRY
};

は、プリプロセッサから次の出力を生成します。

typedef struct
{
 PGM_P str;
 uint8_t str_len;
 uint8_t id;
} CLI_COMMAND_LUT_t;

enum {
 CLI_COM_testA, CLI_COM_testB, CLI_COM_testC,
};

const char CLI_STR_testA PROGMEM = "testA"; const char CLI_STR_testB PROGMEM = "testB"; const char CLI_STR_testC PROGMEM = "testC";

CLI_COMMAND_LUT_t command_lut[] PROGMEM = {

 {CLI_STR_testA, sizeof(CLI_STR_testA), CLI_COM_testA}, {CLI_STR_testB, sizeof(CLI_STR_testB), CLI_COM_testB}, {CLI_STR_testC, sizeof(CLI_STR_testC), CLI_COM_testC},

};

したがって、これらすべてを 1 つの領域にまとめることができ、文字列名とコードの参照の両方として機能する、各コマンドのシンプルで最も重要な 1 つの定義になります。

どうもありがとうございました。

4

1 に答える 1