Как реализовать динамическую таблицу рассылки в C
Прежде всего, я понимаю, как реализовать таблицу рассылки с помощью указателей функций и строки или другого поиска, это не проблема.
То, что я ищу, – это способ динамически добавлять записи в эту таблицу во время компиляции .
Тип структуры кода, на которую я надеюсь, это что-то вроде:
- Как выбрать узлы XML с пространствами имен XML из XmlDocument?
- Почему CLOCKS_PER_SEC не является фактическим числом часов в секунду?
- Список инициализаторов C ++ 11 сбой - но только в списках длины 2
- Преимущества списков инициализации
- Передача ссылок на указатели в C ++
Strategy.h – содержит определение функции для определения диспетчера и таблицы рассылки Strategy.c – содержит код для диспетчера
MyFirstStrategy.c – включает Strategy.h и обеспечивает одну реализацию страtagsи MyOtherStrategy.c – включает Strategy.h и обеспечивает вторую реализацию stratgy
Идея состоит в том, что код для вставки указателей функций и имен страtagsй в таблицу рассылки не должен находиться в Strategy.c, но должен быть в отдельных файлах реализации страtagsи, и таблица поиска должна быть каким-то образом динамически построена во время компиляции.
Для таблицы диспетчеризации с фиксированным размером это можно было бы использовать, как показано ниже, но мне нужна таблица с динамическим размером, я не хочу, чтобы реализация Strategy.c включала все файлы заголовков для реализаций, и я хотел бы отправить таблицу, которая будет построена во время компиляции, а не время выполнения.
Пример с фиксированным размером
Strategy.h
typedef void strategy_fn_t(int); typedef struct { char *strategyName; strategy_fn_t *implementation; } dispatchTableEntry_t;
MyFirstStrategy.h
#include "Strategy.h" void firstStrategy( int param );
MyOtherStrategy.h
#include "Strategy.h" void otherStrategy( int param );
Strategy.c
#include "Strategy.h" #include "MyFirstStrategy.h" #include "MyOtherStrategy.h" dispatchTableEntry_t dispatchTable[] = { { "First Strategy", firstStrategy }, { "Other Strategy", otherStrategy } }; int numStrategies = sizeof( dispatchTable ) / sizeof(dispatchTable[0] );
То, что я действительно хочу, – это препроцессорная магия, которую я могу вставить в файлы реализации страtagsи, чтобы автоматически обрабатывать это
MyFirstStrategy.c
#include "Strategy.h" void firstStrategy( int param ); ADD_TO_DISPATCH_TABLE( "First Strategy", firstStrategy );
Есть предположения ?
- Что такое идиома C ++, эквивалентная статическому блоку Java?
- Использование слоев и битмаски с Raycast в Unity
- Обнаружение подписанного переполнения в C / C ++
- Как правильно назначить новое строковое значение?
- Почему «while (! Feof (file))« всегда неправильно?
- Как разрешены круговые #includes?
- Использованы случаи чистых виртуальных функций с телом?
- Время жизни
В системах с компоновщиком gnu и компилятором или что-то совместимое, можно поместить определенные объекты в разные разделы. Затем компоновщик генерирует символы для начала и конца раздела. Используя это, вы можете поместить все свои структуры в этот раздел в разные объекты, компоновщик будет консолидировать эти разделы при связывании, и вы сможете получить к ним доступ в виде массива. Для этого требуется гораздо больше возможностей, если вы делаете это в разделяемых библиотеках и, безусловно, не переносимы вне ELF Linux / * BSD.
Я сделал аналогичную вещь (хотя этот пример не будет работать) на MacOS и BS.exe., Но я потерял этот код. Вот пример того, как это сделать в Linux:
$ cat link_set.c #include struct dispatch_table { const char *name; void (*fun)(int); }; #define ADD_TO_DISPATCH_TABLE(name, fun) \ static const struct dispatch_table entry_##fun \ __attribute__((__section__("link_set_dispatch_table"))) = \ { name, fun } int main(int argc, char **argv) { extern struct dispatch_table __start_link_set_dispatch_table; extern struct dispatch_table __stop_link_set_dispatch_table; struct dispatch_table *dt; for (dt = &__start_link_set_dispatch_table; dt != &__stop_link_set_dispatch_table; dt++) { printf("name: %s\n", dt->name); (*dt->fun)(0); } return 0; } void fun1(int x) { printf("fun1 called\n"); } ADD_TO_DISPATCH_TABLE("fun 1", fun1); void fun2(int x) { printf("fun2 called\n"); } ADD_TO_DISPATCH_TABLE("fun 2", fun2); $ cc -o link_set link_set.c $ ./link_set name: fun 1 fun1 called name: fun 2 fun2 called $
Объяснить, что делает макрос. Он создает структуру dispatch_table с именем, которое, как мы надеемся, уникально, поскольку вы можете использовать его несколько раз в одном объекте (если вы используете одну и ту же функцию несколько раз, выясните другой способ для названия структуры) и определите его с помощью gnu, чтобы указать, в какой части объекта должен закончиться. Если мы поместим объекты в «some_section_name», то компоновщик автоматически добавит символы __start_some_section_name и __end_some_section_name. Затем мы можем пройти между этими символами и получить все структуры, которые мы помещаем в этот раздел.
Обновлен рабочим примером для MacOS:
#include #include #include #include struct dispatch_table { const char *name; void (*fun)(int); }; #define ADD_TO_DISPATCH_TABLE(name, fun) \ static const struct dispatch_table entry_##fun \ __attribute__((__section__("__DATA,set_dt"))) = \ { name, fun } int main(int argc, char **argv) { struct dispatch_table *start; unsigned long sz; intptr_t s; int i; s = (intptr_t)getsectdata("__DATA", "set_dt", &sz); if (s == 0) return 1; s += _dyld_get_image_vmaddr_slide(0); start = (struct dispatch_table *)s; sz /= sizeof(*start); for (i = 0; i < sz; i++) { struct dispatch_table *dt = &start[i]; printf("name: %s\n", dt->name); (*dt->fun)(0); } return 0; } void fun1(int x) { printf("fun1 called\n"); } ADD_TO_DISPATCH_TABLE("fun 1", fun1); void fun2(int x) { printf("fun2 called\n"); } ADD_TO_DISPATCH_TABLE("fun 2", fun2);
Раздел должен называться «set_dt» здесь, потому что имена разделов ограничены по длине в этом исполняемом формате.
Конечно, если вы дошли настолько, насколько это необходимо, вы наверняка понимаете, что это все очень опасно, невозможно, не гарантировано работать (код, который я три года назад не работал с текущей версией macos) , не имеет никакого типа или другой безопасности, и если что-то другое решит поместить вещи в раздел с тем же именем, все закончится очень красивым фейерверком. Но это очень аккуратный трюк. Я использую этот метод в двух больших проектах, и это действительно экономит много работы. Таблицы централизованной рассылки не нужно редактировать в общем хранилище, которое использовало для всех конфликтов.
Вы должны иметь возможность сделать это со связанным списком функций-указателей-имеющих-структур:
struct dispatch_entry { const char *name; void (*func)(int); struct dispatch_entry *next; }; struct dispatch_entry *dispatch_head = NULL; #define ADD_TO_DISPATCH_TABLE(entry) do { \ (entry)->next = dispatch_head; \ dispatch_head = entry; \ } while (0)
Затем вы можете просмотреть список, чтобы найти нужную запись, или позже отсортировать ее / поместить в оптимизированные подпрограммы поиска и т. Д. Во время выполнения. Обратите внимание, что для этого требуется создать экземпляр структуры dispatch_entry за пределами макроса, но я не думаю, что это серьезная проблема.
Как всегда, предостерегайте emptor, поскольку я не компилировал / не запускал этот код и не ударил его только для иллюстрации техники (которую я использовал несколько раз в различных рабочих проектах).
Поскольку ваша Strategy.c, очевидно, уже знает о экземплярах страtagsи по имени («#include» XYstrategy.h »), вы можете пройти всю милю и использовать файлы заголовков вместо файлов реализации, чтобы сообщить свою страtagsю центральному диспетчеру:
MyFirstStrategy.h
#include "Strategy.h" void firstStrategy( int param ); #define MY_FIRST_STRATEGY {"First Strategy", firstStrategy}
MyOtherStrategy.h
#include "Strategy.h" void otherStrategy( int param ); #define MY_OTHER_STRATEGY {"Other Strategy", otherStrategy }
Strategy.c
#include "Strategy.h" #include "MyFirstStrategy.h" #include "MyOtherStrategy.h" dispatchTableEntry_t dispatchTable[] = { MY_FIRST_STRATEGY, MY_OTHER_STRATEGY }; int numStrategies = sizeof( dispatchTable ) / sizeof(dispatchTable[0] );
Это должно работать на любом компиляторе и платформе C, без каких-либо трюков времени соединения.