Comment puis-je implémenter une table de répartition dynamic en C

Tout d’abord, je comprends comment implémenter une table de répartition en utilisant des pointeurs de fonction et une chaîne ou une autre recherche, ce n’est pas le défi.

Ce que je recherche, c’est un moyen d’append dynamicment des entrées à cette table au moment de la compilation .

Le type de structure de code que j’espère est quelque chose comme:

Strategy.h – contient la définition de la fonction pour le répartiteur et la définition de la table de dissortingbution Strategy.c – contient le code pour le répartiteur

MyFirstStrategy.c – inclut Strategy.h et fournit une implémentation de la stratégie MyOtherStrategy.c – inclut Strategy.h et fournit une deuxième implémentation de la stratégie

L’idée est que le code permettant d’insérer des pointeurs de fonction et des noms de stratégie dans la table de répartition ne doit pas résider dans Strategy.c mais se trouve dans les fichiers de mise en œuvre de stratégie individuels et que la table de recherche doit être construite de manière dynamic au moment de la compilation.

Pour une table de dissortingbution de taille fixe, cela pourrait être géré comme ci-dessous, mais je veux une table de taille dynamic. Je ne souhaite pas que l’implémentation Strategy.c doive inclure tous les fichiers d’en-tête pour les implémentations et j’aimerais l’envoi. table à construire à la compilation, pas à l’exécution.

Exemple de taille fixe

Stratégie.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 ); 

Stratégie.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] ); 

Ce que je veux vraiment, c’est une certaine magie de préprocesseur que je peux insérer dans les fichiers d’implémentation de stratégies pour gérer cela automatiquement, par exemple.

MyFirstStrategy.c

 #include "Strategy.h" void firstStrategy( int param ); ADD_TO_DISPATCH_TABLE( "First Strategy", firstStrategy ); 

Des pensées ?

Sur les systèmes avec linker gnu et compilateur ou quelque chose de compatible, il est possible de placer certains objects dans différentes sections. L’éditeur de liens générera ensuite des symboles pour le début et la fin de la section. En utilisant cela, vous pouvez placer toutes vos structures dans cette section dans différents objects. L’éditeur de liens consolidera ces sections lors de la liaison et vous pourrez y accéder sous forme de tableau. Cela nécessite beaucoup plus de bidouillage si vous le faites dans des bibliothèques partagées et n’est certainement pas portable en dehors de ELF Linux / * BSD.

J’ai fait la même chose (même si cet exemple ne fonctionnera pas) sous MacOS et a.out BSD, mais j’ai perdu ce code. Voici un exemple de procédure qui fonctionne sous 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 \ __atsortingbute__((__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 $ 

Pour expliquer ce que fait la macro. Il crée une structure dispatch_table avec un nom qui, nous l’espérons, est unique car vous pouvez l’utiliser plusieurs fois dans un même object (si vous utilisez la même fonction plusieurs fois, déterminez un autre moyen de nommer la structure) et définissez-le avec extension gnu pour spécifier la section avec laquelle l’object doit se terminer. Si nous plaçons les objects dans “nom_section_section”, l’éditeur de liens appenda automatiquement les symboles __start_some_section_section et __end_some_section_section. Nous pouvons ensuite marcher entre ces symboles et obtenir toutes les structures que nous avons insérées dans cette section.

Mis à jour avec un exemple de travail pour 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 \ __atsortingbute__((__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); 

La section doit être appelée “set_dt” ici car les noms de section sont limités en longueur dans ce format exécutable.

Bien sûr, si vous en êtes venu à cela, vous comprendrez sûrement que tout cela est très dangereux, inapplicable, et que rien ne garantira de fonctionner (le code que j’avais de trois ans auparavant ne fonctionnait pas sur une version actuelle de macos). , n’a aucun type ni aucune autre sécurité et si quelque chose d’autre décide de placer les éléments dans une section du même nom, ils se termineront par de très beaux feux d’artifice. Mais c’est un truc très soigné. J’utilise cette méthode dans deux grands projets et cela économise beaucoup de travail. Il n’est pas nécessaire de modifier les tables de répartition centrales dans un référentiel partagé, ce qui donnait auparavant des conflits à tout le monde.

Vous devriez pouvoir le faire avec une liste chaînée de fonctions-pointeur-avoir-struct:

 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) 

Vous pouvez ensuite parcourir la liste pour trouver l’entrée souhaitée ou la sortinger / la placer ultérieurement dans des routines de recherche optimisées, etc., lors de l’exécution. Notez que cela nécessite que vous instanciez la structure dispatch_entry en dehors de la macro, mais je ne pense pas que ce soit un problème majeur.

Comme toujours, caveat emptor, car je n’ai pas encore compilé / exécuté ce code, et ne l’ai mis au point que pour illustrer la technique (que j’ai utilisée à quelques resockets dans divers projets de travail).

Comme votre Strategy.c connaît évidemment déjà les instances de stratégie par leur nom (“#include” XYstrategy.h “), vous pouvez parcourir tout le chemin et utiliser les fichiers d’en-tête au lieu des fichiers d’implémentation pour communiquer votre stratégie au répartiteur central:

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 } 

Stratégie.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] ); 

Cela devrait fonctionner sur n’importe quel compilateur et plate-forme C sans astuces liées au temps des liens.