2013-09-13 4 views
7

Я получил кучу функций C, которые получают назначенные на массив указателей на функции, вдоль линий этого:определения массива функций C

typedef int (*func)(int); 

int SomeLongName1(int a) {   
    // ... 
}      

// ... 

int SomeLongName1000(int a) {   
    // ... 
}      

func f[] = { SomeLongName1, ... , SomeLongName1000 }; 

Это много работы по созданию и подвержен ошибкам. Например, в имени функции может быть опечатка, так что действительная функция все еще называется, но неправильная. Или, если в конце добавлена ​​новая функция, можно забыть войти и явно добавить ее в список указателей функций.

Чтобы избежать явного объявления массива указателей функций, я пробовал различные трюки, такие как макросы, которые затрудняют понимание кода и требуют знать, как работает макрос, и я вообще не удовлетворен ими.

То, что я хотел бы сделать что-то вроде этого:

typedef int (*func)(int); 

func f[] = {    
    int SomeLongName1(int a) {   
    // ... 
    }      
    // ...     
    int SomeLongName1000(int a) {   
    // ... 
    }      
};      

Таким образом, массив будет создан автоматически, и если есть какой-то способ поставить пустой указатель в конце, так что я могу определить, сколько указателей функций есть, это было бы здорово.

Однако вышеприведенное недействительно C, и я прихожу пустым с любым способом этого. Если это нечто специфическое для компилятора (например, расширение GCC), это будет нормально.

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

Этот связанный вопрос How to define an array of functions, кажется, задает тот же вопрос, но не доводит его до его логического завершения. В частности, я не хочу переписывать все, что я уже набрал, чтобы сэкономить время и избежать ошибок.

ответ

2

Что я хотел бы использовать для решения такой ситуации (конечно, если не могу этого избежать), это использовать предварительную обработку. Не тот, который доступен из препроцессора C, он не обеспечивает требуемую функциональность в разумном синтаксисе, но действительно мощный, например, m4.

С m4, ваш код может выглядеть следующим образом:

define(`functionList', `, 0') 
define(`functionArrayMember', `define(`functionList', `$1, 'FunctionList)$1') 
define(`buildFunctionArray', `{ functionList }') 

int functionArrayMember(SomeLongName1)(int a) { 
    return a+1; 
} 
//... 
int functionArrayMember(SomeLongName1000)(int a) { 
    return a+1; 
} 

func f[] = buildFunctionArray(); 

Вам просто нужно, чтобы обеспечить правильное определение m4 для functionArrayMember() и buildFunctionArray(), и у вас есть необходимые функции.

+0

Мне очень нравится эта идея. Какой-то учебник о том, как использовать m4 в контексте программы на C, будет приятным (пока не удастся с Google ...) – Michael

+0

Я добавил определения 'm4', которые должны сделать трюк. Однако я его не тестировал, поэтому в нем может быть ошибка. Идея состоит в том, что макрос 'functionList' накапливает список имен функций, чтобы впоследствии выгрузить их в литерал массива. – cmaster

+0

Спасибо, это очень полезно. Каков рекомендуемый способ запуска C-источника через m4? Я надеюсь, что смогу сделать это аналогично существующему препроцессору, то есть без необходимости сохранять промежуточный вывод m4 для компиляции. – Michael

2

Я не думаю, что есть какой-либо другой способ делать то, что нужно делать.

То, что вы написали

func f[] = { SomeLongName1, ... , SomeLongName1000 }; 

уже делает то, что лучше всего.

Возможно, вы могли бы назвать свои функции префиксом 0000 до 1000, чтобы вы могли убедиться, что каждая функция находится в нужном месте в вашем массиве указателей функций.

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

3

Вы можете использовать C препроцессор (X-Macros) для этого:

#include <stdio.h> 

// define a list of function names and bodies 
#define FUNCS \ 
    FUNC(add, { return a+b; }) \ 
    FUNC(mul, { return a*b; }) \ 
    FUNC(div, { return a/b; }) 

// let the preprocessor make up the actual function implementations 
#define FUNC(name, body) int name(int a, int b) body 
FUNCS 
#undef FUNC 

typedef int (*func)(int, int); 

// let the preprocessor populate the array of function pointers 
func f[] = { 
#define FUNC(name, body) name, 
FUNCS 
#undef FUNC 
}; 

// use it: 
int main() { 
    int a = 2, b = 3, i = 0; 
    for (; i < sizeof(f)/sizeof(*f); i++) { 
     printf("%d\n", f[i](a,b)); 
    } 
    return 0; 
} 

Выход есть:

$ gcc test.c && ./a.out 
5 
6 
0 
4

Если вы не заботитесь о порядке функций в массиве, и готовы использовать расширение GCC, тогда вы можете добиться того, что хотите, используя целую кучу функций инициализатора (конструктора). Это, очевидно, не является идеальным из-за большого количества дополнительных функций, определенных, но это, безусловно, один подход, который вы можете рассмотреть. Он создает массив во время выполнения.

Определите функцию append, чтобы добавить одну функцию в массив (перераспределяя при необходимости). Затем код в основном

#define ARRAYFUNC(name) int name(int); \ 
    void __attribute__((constructor)) __init_##name(void) { append(func); } \ 
    int name(int a) 

ARRAYFUNC(func1) { 
    ... 
} 

ARRAYFUNC(func2) { 
    ... 
} 
+0

Прохладная идея, пусть компоновщик выясняет, что принадлежит массиву. Приятно, что это даже будет работать, если функции не все определены в одном файле. Вы получили мой голос :-) – cmaster

Смежные вопросы