58 votes

Appeler une fonction nommée dans une variable chaîne en C

Je veux appeler une fonction à l'aide d'une variable. Est-ce possible en C?

En fait, ce que je veux faire est d'obtenir le nom de la fonction de l'utilisateur et de les stocker dans une variable. Maintenant, je veux appeler la fonction qui a son nom stockées. Quelqu'un peut me dire comment cela peut être fait en C?

Je veux développer une IA moteur de jeu pour une partie à deux joueurs. Deux programmes ayant pas pour fonction principale de mettre en place la logique pour gagner le jeu sera alimenté au moteur de jeu. Laissez-moi être clair que les noms de programme sera la même que celle de la primefunctions dans le programme qui implémente la logique pour gagner le jeu.

Ainsi, lorsque l'utilisateur entre le nom de la première et de la deuxième joueurs, je peux les stocker dans 2 variables différentes. Maintenant, depuis le primefunction noms sont les mêmes que les noms de programme, j'ai l'intention d'appeler les fonctions avec des variables contenant le prog noms.

41voto

Blair Conrad Points 56195

C ne supporte pas ce genre d'opération (les langues qui ont une réflexion le feraient). Le mieux que vous puissiez faire est de créer une table de recherche à partir des noms de fonction en pointeurs de fonction et de l’utiliser pour déterminer la fonction à appeler. Ou vous pouvez utiliser une instruction switch.

37voto

GManNickG Points 155079

Le meilleur que vous pouvez faire est quelque chose comme ceci:

#include <stdio.h>

// functions
void foo(int i);
void bar(int i);

// function type
typedef void (*FunctionCallback)(int);
FunctionCallback functions[] = {&foo, &bar};

int main(void)
{
    // get function id
    int i = 0;
    scanf("%i", &i);

    // check id
    if( i >= sizeof(functions))
    {
        printf("Invalid function id: %i", i);
        return 1;
    }

    // call function
    functions[i](i);

    return 0;
}

void foo(int i)
{
    printf("In foo() with: %i", i);
}

void bar(int i)
{
    printf("In bar() with: %i", i);
}

Il utilise des nombres plutôt que des chaînes de caractères pour identifier les fonctions, mais le faire avec des chaînes est tout simplement une question de la conversion de la chaîne en la bonne fonction.

Que faites-vous exactement? Si c'est juste pour la curiosité, ici vous allez, mais si vous essayez de résoudre un problème avec cela, je suis sûr qu'il y est un moyen de mieux adapté à votre tâche.

Modifier

Dans le souci avec votre modification, vous voulez aller avec onebyone la réponse, bien sûr.

Vous voulez que votre utilisateur de construire des bibliothèques dynamiques (c'est un objet partagé [.so] en Linux, et une bibliothèque de liens dynamiques [.dll] dans Windows).

Une fois que vous le faire, si ils vous fournir le nom de leur bibliothèque, vous pouvez demander le chargement du système d'exploitation à la bibliothèque pour vous, et de demander un pointeur à une fonction au sein de la bibliothèque.

31voto

Falaina Points 4760

Alors que ce n'est pas exactement une solution pratique, je parie que vous pourriez certainement appeler une fonction par une chaîne en ayant un programme de lecture dans son propre fichier exécutable et d'analyser la table des symboles. Le symbole de la table doit contenir le nom de la fonction ainsi que c'est la première adresse d'instruction. Vous pouvez ensuite placer cette adresse dans un pointeur de fonction variable et de l'appeler.

Je pense que je peut l'essayer et le fouet cela de.

EDIT: s'il vous Plaît ne jamais écrire de code réel de ce genre, mais ici, c'est comment vous pouvez appeler une fonction à l'aide d'une chaîne de caractères pour un Linux binaire ELF avec une intacte table de symboles (nécessite libelf):

#include <fcntl.h>
#include <stdio.h>
#include <elf.h>
#include <libelf.h>
#include <stdlib.h>
#include <string.h>

void callMe() {
  printf("callMe called\n");
}

int main(int argc, char **argv) {
  Elf64_Shdr *    shdr;
  Elf64_Ehdr *    ehdr;
  Elf *        elf;
  Elf_Scn *    scn;
  Elf_Data *    data;
  int cnt;
  void (*fp)() = NULL;

  int fd = 0;

  /* This is probably Linux specific - Read in our own executable*/
  if ((fd = open("/proc/self/exe", O_RDONLY)) == -1)
    exit(1);

  elf_version(EV_CURRENT);

  if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
    fprintf(stderr, "file is not an ELF binary\n");
    exit(1);
  }
    /* Let's get the elf sections */
    if (((ehdr = elf64_getehdr(elf)) == NULL) ||
    ((scn = elf_getscn(elf, ehdr->e_shstrndx)) == NULL) ||
    ((data = elf_getdata(scn, NULL)) == NULL)) {
      fprintf(stderr, "Failed to get SOMETHING\n");
      exit(1);
    }

    /* Let's go through each elf section looking for the symbol table */
    for (cnt = 1, scn = NULL; scn = elf_nextscn(elf, scn); cnt++) {
      if ((shdr = elf64_getshdr(scn)) == NULL)
    exit(1);

      if (shdr->sh_type == SHT_SYMTAB) {
    char *name;
    char *strName;
    data = 0;
    if ((data = elf_getdata(scn, data)) == 0 || data->d_size == 0) {
      fprintf(stderr, "No data in symbol table\n");
      exit(1);
    }

    Elf64_Sym *esym = (Elf64_Sym*) data->d_buf;
    Elf64_Sym *lastsym = (Elf64_Sym*) ((char*) data->d_buf + data->d_size);

    /* Look through all symbols */ 
    for (; esym < lastsym; esym++) {
      if ((esym->st_value == 0) ||
          (ELF64_ST_BIND(esym->st_info)== STB_WEAK) ||
          (ELF64_ST_BIND(esym->st_info)== STB_NUM) ||
          (ELF64_ST_TYPE(esym->st_info)!= STT_FUNC)) 
        continue;

      name = elf_strptr(elf,shdr->sh_link , (size_t)esym->st_name);

      if(!name){
        fprintf(stderr,"%sn",elf_errmsg(elf_errno()));
        exit(-1);
      }
      /* This could obviously be a generic string */
      if(strcmp("callMe", name) == 0 ) {
        printf("Found callMe @ %x\n", esym->st_value);
        fp = esym->st_value;
      }
    }    
    /* Call and hope we don't segfault!*/
    fp();
    elf_end(elf);
    return 0;
  }   

15voto

Steve Jessop Points 166970

Il n'est pas possible dans le plus pur C, cependant, vous pouvez être en mesure de jouer des tours à la dll. Mettez toutes les fonctions que vous souhaitez sélectionner à partir d'une dll, puis utilisez dlsym (ou GetProcAddress sur Windows, ou tout autre API de votre système) pour obtenir le pointeur de fonction par son nom, et l'appel à l'aide que.

Cela ne fonctionne pas sur certaines plates-formes, soit parce qu'ils n'ont pas de dll, ou parce que, comme Symbian les fonctions de la dll ne peut pas être accessible par un nom au moment de l'exécution, que par le nombre.

Soyez conscient que si votre utilisateur astuces que vous en sélectionnant une fonction qui n'a pas le droit de paramètres pour l'appel que vous voulez faire, alors votre programme ira mal. C n'est pas vraiment conçu pour faire face à ce genre de chose.

15voto

Sean Bright Points 39480
#include <stdio.h>
#include <string.h>

void function_a(void) { printf("Function A\n"); }
void function_b(void) { printf("Function B\n"); }
void function_c(void) { printf("Function C\n"); }
void function_d(void) { printf("Function D\n"); }
void function_e(void) { printf("Function E\n"); }

const static struct {
  const char *name;
  void (*func)(void);
} function_map [] = {
  { "function_a", function_a },
  { "function_b", function_b },
  { "function_c", function_c },
  { "function_d", function_d },
  { "function_e", function_e },
};

int call_function(const char *name)
{
  int i;

  for (i = 0; i < (sizeof(function_map) / sizeof(function_map[0])); i++) {
    if (!strcmp(function_map[i].name, name) && function_map[i].func) {
      function_map[i].func();
      return 0;
    }
  }

  return -1;
}

int main()
{
  call_function("function_a");
  call_function("function_c");
  call_function("function_e");
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X