Wie schreibe ich einen Dispatcher, wenn die Unterstützung meines Compilers für Zeiger auf Funktionen nicht funktioniert?

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

  •  08-07-2019
  •  | 
  •  

Frage

Ich arbeite an einer eingebetteten Anwendung, bei der das Gerät über eine Befehlsschnittstelle gesteuert wird.Ich habe den Befehlsdispatcher in VC verspottet und festgestellt, dass er zu meiner Zufriedenheit funktioniert.Aber als ich den Code dann in die eingebettete Umgebung verschoben habe, stellte ich fest, dass der Compiler eine fehlerhafte Implementierung von Zeiger-auf-Funktionen hat.

So habe ich den Code ursprünglich implementiert (in 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);
  }

Ohne die Verwendung von Funktionszeigern scheint meine einzige Hoffnung darin zu bestehen, switch()/case-Anweisungen zum Aufrufen von Funktionen zu verwenden.Aber ich möchte vermeiden, dass ich eine große switch()-Anweisung manuell verwalten muss.

Ich habe darüber nachgedacht, alle COMMAND_ENTRY-Zeilen in eine separate Include-Datei zu verschieben.Anschließend wird die Include-Datei mit unterschiedlichen #define und #undefines umschlossen.Etwas wie:

/* 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
}

Sieht jemand einen besseren Weg, mit dieser Situation umzugehen?Leider ist „einen anderen Compiler“ keine praktikable Option.:(

--- Bearbeiten, um Folgendes hinzuzufügen:Nur zur Verdeutlichung: Die spezielle eingebettete Umgebung ist insofern fehlerhaft, als der Compiler angeblich um eine „Funktionszeigertabelle“ zu erstellen, die dann vom Compiler verwendet wird, um Aufrufe von Funktionen über einen Zeiger aufzulösen.Leider ist der Compiler defekt und generiert keine korrekte Funktionstabelle.

Daher habe ich keine einfache Möglichkeit, die Funktionsadresse zu extrahieren, um sie aufzurufen.

--- Bearbeiten Nr. 2:Ah ja, die Verwendung von void *(set|get)_dispatcher war mein Versuch herauszufinden, ob das Problem bei der Typdefinition der Funktionszeiger lag.Ursprünglich hatte ich

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;
War es hilfreich?

Lösung

Sie sollten versuchen, Ihre struct command zu ändern, so dass die Funktionszeiger den tatsächlichen Typen haben:

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

Leider Funktionszeiger sind nicht in der Lage garantiert zu / von void-Zeiger zu konvertieren (das nur auf Zeiger auf Objekte angewandt wird).

Was ist die eingebettete Umgebung?


Die Angaben in den Aktualisierungen die Frage gestellt, wie ich sehe, dass es wirklich ein abgehört Compiler.

Ich denke, dass Ihre vorgeschlagene Lösung ziemlich vernünftig erscheint - es wahrscheinlich ähnlich ist, was ich mit einfallen lassen würde

.

Andere Tipps

Ein Funktionszeiger nicht tatsächlich erforderlich ist * im luftleeren Raum zu passen. Sie könnten überprüfen, um sicherzustellen, dass der Wert, den Sie fordern tatsächlich die Adresse der Funktion ist. Wenn nicht, einen Funktionszeiger-Typen in der Struktur verwenden:. Entweder get_fn_t oder IIRC void (*) (void) ist garantiert mit jedem Funktion Zeigertyp kompatibel sein

Edit: OK, unter der Annahme, dass durch Wert Aufruf nicht an der Arbeit gemacht wird, kann ich nicht von einer sauberere Art und Weise denken zu tun, was Sie brauchen, als Auto-Generierung die switch-Anweisung. Man könnte vielleicht einen Off-the-shelf ASP-Prä-Prozessor-Modus für Ruby / Python / Perl / PHP / was auch immer vor dem C-Präprozessor verwenden. So etwas wie folgt aus:

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;
}

könnte ein bisschen besser lesbar als die Makro / include Trick sein, aber ein neues Werkzeug zur Einführung und die Makefiles Einrichtung ist wahrscheinlich nicht wert für eine so kleine Menge an Code. Und die Zeilennummern in der Debug-Informationen werden beziehen sich nicht auf die Datei, die Sie denken, wie die Quelldatei, wenn Sie zusätzliche Arbeit in der Vorverarbeitungsbefehl tun, um sie zu spezifizieren.

Können Sie den Verkäufer die Compiler zu beheben?

Inwieweit ist der Zeiger auf die Funktion defekt?

Wenn der Compiler es Ihnen ermöglicht, die Adresse einer Funktion abzurufen (ich komme aus C++, aber &getenv ist das, was ich meine), könnten Sie die aufrufenden Konventionen in Assembler einbinden.

Wie gesagt, ich bin ein C++-Ssie, aber etwas im Weg

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

Wenn selbst das kaputt ist, könnten Sie ein Array von definieren extern void* Zeiger, die Sie wiederum in der Assembly definieren.

versuchen, diese Syntax:

return (* ((get_fn_t) Befehle [i] .get_dispatcher)) (PC);

Es ist eine Weile her, seit ich habe C & Funktionszeiger getan, aber ich glaube, dass die Original-C-Syntax, die * erforderlich, wenn dereferencing Funktionszeiger, aber die meisten Compiler Sie weg lassen würde, ohne es.

Haben Sie Zugriff auf den Link Karte haben? Wenn ja, vielleicht können Sie Ihren Weg durch die wonky Funktionszeigertabelle Hack:

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} 

Nun kompilieren, die entsprechenden Adressen aus dem Link Karte greifen, ersetzen Sie die Konstanten und neu kompilieren. Es soll nichts bewegen, so dass die Karte soll gleich bleiben. (Herstellung der ursprünglichen Konstanten einzigartig sollte den Compiler kollabiert identische Werte in einem Lagerort verhindern. Sie können eine lange brauchen lange, je nach Architektur)

Wenn das Konzept funktioniert, könnte man wahrscheinlich einen post-Link Schritt fügen Sie ein Skript ausgeführt wird den Ersatz automagically zu tun. Natürlich ist dies nur eine Theorie ist, kann es kläglich scheitern.

Vielleicht, müssen Sie in die Struktur suchen wieder:

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;

Lassen Sie sagen, Ihre Disponenten die folgende ähnliche Funktionsdefinition haben:

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

, dass die Disponenten sind wie folgt:

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

So wird die überarbeitete Struktur sein:

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

Ihr Makro wird:

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

Dann können Sie Ihr Array wie gewohnt gesetzt:

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;
}

Korrigieren Sie mich, wenn ich falsch irgendwo bin ...;)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top