Как условно определить, какие функции вызываются во время компиляции? - PullRequest
6 голосов
/ 03 декабря 2009

Я работаю над реализацией очень, очень базовой системы компонентов в C , но сейчас я нахожусь в точке, где я хочу "динамически" вызывать некоторые функции. Установка очень проста: основная программа - это просто бесконечный цикл while, в котором проверяются некоторые условия и в котором вызывается функция «process» для каждого включенного компонента.

Например, теперь это работает так:

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 ]

11 голосов
/ 03 декабря 2009

Вам нужны указатели на функции, создайте массив указателей на функции и динамически индексируйте его.

Здесь ссылка о функциональных указателях.

5 голосов
/ 03 декабря 2009

Решение во время компиляции: шаг перед сборкой и включение директивы внутри этого цикла, например

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

Любая приличная система сборки позволит вам сделать это.

3 голосов
/ 04 декабря 2009

Функциональные указатели великолепны!

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, который я узнал от Дейва Хансона, также является одним из моих любимых.


P.S. Избегайте #ifdef любой ценой: -)

2 голосов
/ 03 декабря 2009

Что не так с условием ol 'if?

if (hasNetwork)
{
   network_process();
}
1 голос
/ 03 декабря 2009

Во время компиляции с макросом X:

component.x - это файл, содержащий:

COMPONENT( graphic , "3D graphics rendering" )
COMPONENT( network , "Network" )
COMPONENT( other , "usefull stuff" )
#undef COMPONENT

Используйте его с:

#define COMPONENT( what , description ) what ## _process();
while (1)
{
#include "components.x"
}

А в другом месте, например:

std::cout << "We use :\n" ;
#define COMPONENT( what , description )\
std::cout << #what << " : " << description << "\n" ;
#include "components.x"

и с этим вы можете поместить определение HAS_ в одном месте в component.x:

#ifdef HAS_GRAPHIC
COMPONENT( graphic , "3D graphics rendering" )
#endif
#ifdef HAS_NETWORK
COMPONENT( network , "Network" )
#endif
#ifdef HAS_OTHER
COMPONENT( other , "usefull stuff" )
#endif
#undef COMPONENT
1 голос
/ 03 декабря 2009

ваши компоненты должны быть массивом указателей на функции

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]();
1 голос
/ 03 декабря 2009

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

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

Альтернативно-альтернативно, вы могли бы сделать это в C ++, создать виртуальный класс Component, который имеет только один метод «process», с кучей подклассов, и вы пробегаете массив компонентов (фактически объектов подклассов) и вызовите метод процесса.

0 голосов
/ 03 декабря 2009

Другая возможность: сохранить цикл как есть, т.е.

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
0 голосов
/ 03 декабря 2009

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


void f( void ) { puts( "f" ); }
void g( void ) { puts( "g" ); }
void h( void ) { puts( "h" ); }
void (&#42;array[])(void) = { f, h, 0 };
int main(void) {
    void (&#42;&#42;t)(void);
    for( t = array; &#42;t; t++ )
        (&#42;t)();
}


...