Могу ли я использовать препроцессор, чтобы сделать это более ясным?


Я писал небольшую функцию исходного файла для моего Pic32, и я застрял на одной вещи. Это в основном утилита, которая должна хранить данные incomming char в буфер, а затем, если 'r' получен, он сравнивает буфер со списком команд (в массиве names), и если соответствие найдено, возвращается индекс элемента.

Эта часть из заголовка:

#define NAMECNT 6    
static const char names[NAMESCNT][10] = {   // 6commands, max 10 char each
        "korr",         // 1
        "adc",          // 2
        "fft",          // 3
        "data",         // 4 
        "pr",           // 5
        "prsc"};        // 6

/* functions */
extern int comm(char cdata);

В главном файле есть один большой переключатель:

switch( comm(recieved_ch) ){
case 1: foo1(); break; 
case 2: foo2(); break;
...
}

Теперь, для большей ясности, я хотел использовать вместо 1, 2,... оригинальные имена (например, case KORR: case ADC:), поэтому я написал дефиниции для каждого из них

#define KORR 1
#define ADC 2
Но мне не нравится это решение, потому что я хочу использовать этот исходный файл в большем количестве проектов, и для каждого из них будет свой список команд. Есть ли способ, как это сделать? Лучше всего было бы создать имена массивов в препроцессоре, но я сомневаюсь, что это вообще возможно. Я думал об использовании типа enum (который будет иметь те же элементы, что и список команд names), но я не знаю, как это пройдет.
2 2

2 ответа:

Вы можете использовать X-макросы для построения enum и заполнения массива, затем вы можете использовать значения enum в switch:

#define VARS \
    X(korr) \
    X(adc)  \
    X(fft)  \
    X(data) \
    X(pr)   \
    X(prsc)

static const char names[][10] = {   // 6commands, max 10 char each
#define X(name) #name,
    VARS
#undef X
};

enum evars {
#define X(name) name, 
    VARS
#undef X
};

extern int comm(char cdata);

int main(void)
{
    char x = 1;

    switch (comm(x)) {
        case korr:
            printf("korr");
            break;
        case adc:
            printf("adc");
            break;
        /* ... and so on */
    }
    return 0;
}

Расширение X таково:

static const char names[][10] = {

 "korr", "adc", "fft", "data", "pr", "prsc",

};

enum evars {

 korr, adc, fft, data, pr, prsc,

};

Edit: как указывает @5gon12eder, вам не нужно жестко кодировать 6 в первом измерении массива (вы можете оставить его неопределенным).

Я думаю, что препроцессор мог бы сделать здесь все более ясным, используя оператор конкатенации ##, но это не даст преимущества в производительности. Оператор switch может быть оптимизирован компилятором, но это зависит от реализации.

Вместо "одного большого переключателя" используйте массив указателей функций. Что-то вроде

func_ptrs[comm(received_ch) - 1]();

Вызовет соответствующую функцию, где foo1 находится в индексе 0, foo2 at 1 и др. Чтобы добавить команду, просто добавьте имя команды в поле список команд и указатель на функцию func_ptrs.

В конце концов, вы убиваете двух зайцев одним выстрелом: вы создаете простой способ добавлять команды и улучшать производительность.
Кроме того, линейный поиск по массиву строк довольно неэффективен. Хэш-таблица даст преимущество в производительности.