Comment puis-je écrire un répartiteur si le support de mon compilateur pour les pointeurs vers des fonctions est cassé?

StackOverflow https://stackoverflow.com/questions/284952

  •  08-07-2019
  •  | 
  •  

Question

Je travaille sur une application intégrée dans laquelle le périphérique est contrôlé via une interface de commande. Je me suis moqué du répartiteur de commande dans VC et je l'ai fait fonctionner à ma satisfaction; mais lorsque j'ai ensuite transféré le code dans l'environnement intégré, j'ai découvert que le compilateur implémentait mal le pointeur sur la fonction.

Voici comment j'ai initialement implémenté le code (dans VC):

/* Relevant parts of header file  */
typedef struct command {
  const char *code;
  void *set_dispatcher;
  void *get_dispatcher;
  const char *_description;
} command_t;

#define COMMAND_ENTRY(label,dispatcher,description) {(const char*)label, &set_##dispatcher, &get_##dispatcher, (const char*)description} 


/* Dispatcher data structure in the C file */
const command_t commands[] = {
  COMMAND_ENTRY("DH", Dhcp, "DHCP (0=off, 1=on)"),
  COMMAND_ENTRY("IP", Ip, "IP Address (192.168.1.205)"),
  COMMAND_ENTRY("SM", Subnet, "Subunet Mask (255.255.255.0)"),
  COMMAND_ENTRY("DR", DefaultRoute, "Default router (192.168.1.1)"),
  COMMAND_ENTRY("UN", Username, "Web username"),
  COMMAND_ENTRY("PW", Password, "Web password"),
  ...
}


/* After matching the received command string to the command "label", the command is dispatched */
if (pc->isGetter)
  return ((get_fn_t)(commands[i].get_dispatcher))(pc);
else
  return ((set_fn_t)(commands[i].set_dispatcher))(pc);
  }

Sans l'utilisation de pointeurs de fonction, il semble que mon seul espoir soit d'utiliser les instructions switch () / case pour appeler des fonctions. Mais j'aimerais éviter d'avoir à maintenir manuellement une instruction switch () volumineuse.

Ce que je pensais faire était de déplacer toutes les lignes COMMAND_ENTRY dans un fichier include séparé. Ensuite, les enveloppes qui incluent les fichiers avec des variables #define et #undefines. Quelque chose comme:

/* Create enum's labels */
#define COMMAND_ENTRY(label,dispatcher,description) SET_##dispatcher, GET_##dispatcher
typedef enum command_labels = {
#include "entries.cinc"
  DUMMY_ENUM_ENTRY} command_labels_t;
#undefine COMMAND_ENTRY


/* Create command mapping table */
#define COMMAND_ENTRY(label,dispatcher,description) {(const char*)label, SET_##dispatcher, GET_##dispatcher, (const char*)description} 
const command_t commands[] = {
#include "entries.cinc"
  NULL /* dummy */ };
#undefine COMMAND_ENTRY

/*...*/

int command_dispatcher(command_labels_t dispatcher_id) {
/* Create dispatcher switch statement */
#define COMMAND_ENTRY(label,dispatcher,description) case SET_##dispatcher: return set_##dispatcher(pc); case GET_##dispatcher: return get_##dispatcher(pc);
switch(dispatcher_id) {
#include "entries.cinc"
default:
  return NOT_FOUND;
}
#undefine COMMAND_ENTRY
}

Quelqu'un voit-il une meilleure façon de gérer cette situation? Malheureusement, "obtenir un autre compilateur" n'est pas une option viable. : (

--- Modifier pour ajouter: Juste pour clarifier, l’environnement embarqué particulier est fragmenté en ce que le compilateur est supposé créer un "tableau de pointeur de fonction". qui est ensuite utilisé par le compilateur pour résoudre les appels de fonctions via un pointeur. Malheureusement, le compilateur est cassé et ne génère pas une table de fonctions correcte.

Je n'ai donc pas de moyen facile d'extraire l'adresse func pour l'invoquer.

--- Modifier n ° 2: Ah, oui, l'utilisation de void * (set | get) _dispatcher a été ma tentative de voir si le problème était lié à la définition de type des pointeurs de fonction. À l'origine, j'avais

typedef int (*set_fn_t)(cmdContext_t *pCmdCtx);
typedef int (*get_fn_t)(cmdContext_t *pCmdCtx);

typedef struct command {
  const char *code;
  set_fn_t set_dispatcher;
  get_fn_t get_dispatcher;
  const char *_description;
} command_t;
Était-ce utile?

La solution

Vous devriez essayer de changer votre commande de structure afin que les pointeurs de fonction aient le type actuel:

typedef struct command {
  const char *code;
  set_fn_t set_dispatcher;
  get_fn_t get_dispatcher;
  const char *_description;
} command_t;

Malheureusement, il n'est pas garanti que les pointeurs de fonction puissent convertir en pointeurs vides (cela ne s'applique qu'aux pointeurs vers des objets).

Qu'est-ce que l'environnement intégré?

Étant donné les informations publiées dans les mises à jour de la question, je vois que c'est vraiment un compilateur qui a un bug.

Je pense que votre solution proposée semble assez raisonnable - elle est probablement similaire à celle que j'aurais proposée.

Autres conseils

Un pointeur de fonction n'est pas réellement requis pour tenir dans un vide *. Vous pouvez vérifier que la valeur que vous appelez correspond bien à l'adresse de la fonction. Sinon, utilisez un type de pointeur de fonction dans la structure: get_fn_t ou IIRC void (*) (void) est garanti pour être compatible avec tout type de pointeur de fonction.

Éditer: OK, en supposant que l'appel par valeur ne puisse pas fonctionner, je ne peux pas penser à une manière plus ordonnée de faire ce dont vous avez besoin que de générer automatiquement l'instruction switch. Vous pourriez peut-être utiliser un mode pré-processeur standard de type ASP pour ruby ??/ python / perl / php / peu importe ce qui existait avant le préprocesseur C. Quelque chose comme ça:

switch(dispatcher_id) {
<% for c in commands %>
    case SET_<% c.dispatcher %>: return set_<% c.dispatcher %>(pc); 
    case GET_<% c.dispatcher %>: return get_<% c.dispatcher %>(pc);
<% end %>
default:
    return NOT_FOUND;
}

est peut-être un peu plus lisible que l’astuce macro / include, mais introduire un nouvel outil et configurer les makefiles n’en vaut probablement pas la peine pour une si petite quantité de code. Et les numéros de ligne dans les informations de débogage ne concernent pas le fichier que vous considérez comme le fichier source, à moins que vous ne fassiez un travail supplémentaire dans votre pré-processeur pour les spécifier.

Pouvez-vous demander au fournisseur de réparer le compilateur?

Dans quelle mesure le pointeur sur la fonction est-il cassé?

Si le compilateur vous permet d'obtenir l'adresse d'une fonction (je viens de C ++, mais & amp; getenv est ce que je veux dire), vous pouvez envelopper les éléments de la convention d'appel dans assembleur.

Comme dit, je suis un ssie C ++, mais quelque chose dans la voie de

; function call
push [arg1]
push [arg2]
call [command+8] ; at the 4th location, the setter is stored
ret

Si même cela est cassé, vous pouvez définir un tableau de pointeurs extern void * que vous définissez, encore une fois, en assembleur.

essayez cette syntaxe:

return (* ((get_fn_t) commandes [i] .get_dispatcher)) (pc);

Cela fait longtemps que je n’ai pas fait C & amp; Les pointeurs de fonction, mais je crois que la syntaxe C d'origine exigeait le * lors du déréférencement des pointeurs de fonction, mais la plupart des compilateurs vous laisseraient vous en passer.

Avez-vous accès à la carte des liens? Si tel est le cas, vous pouvez peut-être vous frayer un chemin autour de la table des pointeurs wonky:

unsigned long addr_get_dhcp = 0x1111111;
unsigned long addr_set_dhcp = 0x2222222; //make these unique numbers.

/* Relevant parts of header file  */
typedef struct command {
  const char *code;
  unsigned long set_dispatcher;
  unsigned long get_dispatcher;
  const char *_description;
} command_t;

#define COMMAND_ENTRY(label,dispatcher,description) {(const char*)label, 
    addr_set_##dispatcher, addr_get_##dispatcher, (const char*)description} 

Maintenant, compilez, récupérez les adresses pertinentes dans la carte de liens, remplacez les constantes et recompilez. Rien ne doit bouger, la carte doit rester la même. (Rendre les constantes originales uniques devrait empêcher le compilateur de réduire les valeurs identiques dans un emplacement de stockage. Vous aurez peut-être besoin d'une valeur longue, selon l'architecture)

Si le concept fonctionne, vous pouvez probablement ajouter une étape post-lien exécutant un script pour effectuer le remplacement automatiquement. Bien sûr, ceci est juste une théorie, il peut échouer lamentablement.

Vous devez peut-être examiner à nouveau la structure:

typedef struct command {
  const char *code;
  void *set_dispatcher; //IMO, it does not look like a function pointer...
  void *get_dispatcher; //more like a pointer to void
  const char *_description;
} command_t;

Supposons que vos régulateurs aient la définition de fonction similaire suivante:

//a function pointer type definition
typedef int (*genericDispatcher)(int data);

Supposons que les répartiteurs soient comme ci-dessous:

int set_DhcpDispatcher(int data) { return data; }
int get_DhcpDispatcher(int data) { return 2*data; }

La structure révisée sera donc:

typedef struct command {
  const char *code;
  genericDispatcher set_dispatcher; 
  genericDispatcher get_dispatcher; 
  const char *_description;
} command_t;

Votre macro sera:

#define COMMAND_ENTRY(label,dispatcher,description) \
{   (const char*)label, \
    set_##dispatcher##Dispatcher, \
    get_##dispatcher##Dispatcher, \
    (const char*)description } 

Ensuite, vous pouvez configurer votre tableau comme d'habitude:

int main(int argc, char **argv)
{
    int value1 = 0, value2 = 0;

    const command_t commands[] = {
      COMMAND_ENTRY("DH", Dhcp, "DHCP (0=off, 1=on)")
    };

    value1 = commands[0].set_dispatcher(1);
    value2 = commands[0].get_dispatcher(2);

    printf("value1 = %d, value2 = %d", value1, value2);

    return 0;
}

Corrigez-moi si je me trompe quelque part ...;)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top