Как условно определить, какие функции вызываются во время компиляции?
Я работаю над реализацией очень, очень базовая компонентная система в C, но теперь я нахожусь в точке, где я хочу "динамически" вызывать некоторые функции. Настройка очень проста: основная программа-это просто бесконечный цикл while, в котором проверяются некоторые условия и вызывается функция "процесс" для каждого включенного компонента.
например, теперь он работает следующим образом:
while (1) {
input_process();
network_process();
graphics_process();
}
но я хочу разделить его на отдельные компоненты, и как-то определить в центральном месте, какие части используются. Это можно сделать с помощью простых определений, например:
#define HAS_NETWORK
...
while (1) {
input_process();
#ifdef HAS_NETWORK
network_process();
#endif
graphics_process();
}
как вы можете видеть, это нормально для 1 или, может быть, только для нескольких компонентов, но если я хочу сделать это для всех этих (вход, сеть и графика) и дополнительных компонентов в будущем, мне придется поместить туда отдельные #ifdefs для каждого из них, и это довольно утомительно.
в псевдо-коде, чего я пытаюсь достичь, это следующий:
components = {'input', 'network', 'graphics'}
...
foreach component in components
execute component_process()
таким образом компоненты могут быть легко добавлены в будущем. Я не возражаю, если проверка выполняется во время компиляции или во время выполнения (хотя я, очевидно, предпочитаю время компиляции, но я могу представить, что время выполнения проще реализовать). Я понятия не имею, с чего начать.
9 ответов
вам нужны указатели на функции, создать массив указателей на функции и подключать ее динамически.
здесь ссылке про указатели на функции.
решение времени компиляции: шаг предварительной сборки и включить директиву внутри этого цикла, например
while (1) {
#include "components.generated.c"
}
базовый скрипт для создания этого файла может выглядеть как (Python):
components = ('input', 'networking', 'graphics')
# this could also be e.g. a glob on a directory or a config file
with open('components.generated.c', 'w') as fp:
for component in components:
print >>fp, '%s_process();' % component
любая приличная система сборки позволит вам это сделать.
указатели на функции отлично!
typedef void (*process_fun)(void);
process_fun processes[] =
{ input_process, network_process, graphics_process };
#define NELEMS(A) (sizeof(A) / sizeof((A)[0]))
while (1) {
for (int i = 0; i < NELEMS(processes); i++)
processes[i]();
}
на NELEMS
макрос, который я узнал от Дэйва Хэнсона, также является одним из моих любимых.
С. П. Избежать #ifdef
любой ценой :-)
вы можете сделать это с помощью массива указателей на функции.Обычно я стараюсь избегать указателей функций, таких как чума, но это может быть ваш лучший выбор.
кроме того, вы можете создать функцию процесса компонента, которая принимает аргумент int, а затем имеет неприятный оператор switch... но для этого вам нужно продолжать добавлять функцию component_process.
альтернативно-альтернативно, вы можете сделать это в C++, создать класс виртуального компонента, который просто имеет один метод "process", с кучей подклассов, и вы запускаете массив компонентов (фактически объекты подклассов) и вызываете метод process.
ваши компоненты должны быть массивом указателей на функции
enum components
{
input,
network,
graphics,
num_components
}
void process_audio()
{
}
void process_network()
{
}
void process_graphics()
{
}
void (*process_component[num_components])();
process_component[0] = &process_audio;
process_component[1] = &process_network
process_component[2] = &process_graphics;
for (int i = 0; i < num_components; i++)
process_component[i]();
вот пример синтаксиса, чтобы сделать это во время выполнения с массивом указателей функций:
void f( void ) { puts( "f" ); } void g( void ) { puts( "g" ); } void h( void ) { puts( "h" ); } void (*array[])(void) = { f, h, 0 }; int main(void) { void (**t)(void); for( t = array; *t; t++ ) (*t)(); }
другая возможность: держите цикл как есть, т. е.
while (1) {
input_process();
network_process();
graphics_process();
}
и добавьте следующие директивы препроцессора в заголовок файла:
#ifndef HAS_NETWORK
#define network_process() ((void)0)
#endif
#ifndef HAS_GRAPHICS
#define graphics_process() ((void)0)
#endif