Come faccio a scrivere un dispatcher, se il supporto del mio compilatore per i puntatori alle funzioni è rotto?

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

  •  08-07-2019
  •  | 
  •  

Domanda

Sto lavorando su un'applicazione integrata in cui il dispositivo è controllato tramite un'interfaccia di comando. Ho deriso il dispatcher dei comandi in VC e l'ho fatto funzionare con mia soddisfazione; ma quando ho spostato il codice nell'ambiente incorporato, ho scoperto che il compilatore ha un'implementazione non corretta di pointer-to-func.

Ecco come ho originariamente implementato il codice (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);
  }

Senza l'uso dei puntatori a funzione, sembra che la mia unica speranza sia quella di usare le istruzioni switch () / case per chiamare le funzioni. Ma vorrei evitare di mantenere manualmente un'istruzione switch () di grandi dimensioni.

Quello che stavo pensando di fare è spostare tutte le righe COMMAND_ENTRY in un file include separato. Quindi esegue il wrapping di file che includono #define e #undefines variabili. Qualcosa del tipo:

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

Qualcuno vede un modo migliore per gestire questa situazione? Purtroppo, "ottenere un altro compilatore" non è un'opzione praticabile. : (

--- Modifica per aggiungere: Giusto per chiarire, il particolare ambiente incorporato è interrotto dal fatto che il compilatore è supposto per creare una tabella "pointer-function" " che viene quindi utilizzato dal compilatore per risolvere le chiamate alle funzioni tramite un puntatore. Sfortunatamente, il compilatore non funziona e non genera una tabella delle funzioni corretta.

Quindi non ho un modo semplice per estrarre l'indirizzo func per invocarlo.

--- Modifica n. 2: Ah, sì, l'uso di void * (set | get) _dispatcher è stato il mio tentativo di vedere se il problema riguardava il typedefine dei puntatori func. Inizialmente, avevo

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;
È stato utile?

Soluzione

Dovresti provare a cambiare il tuo comando struct in modo che i puntatori a funzione abbiano il tipo effettivo:

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

Sfortunatamente, i puntatori a funzione non possono essere convertiti in / da puntatori vuoti (che si applica solo ai puntatori a oggetti).

Qual è l'ambiente incorporato?


Date le informazioni pubblicate negli aggiornamenti alla domanda, vedo che è davvero un compilatore con errori.

Penso che la tua soluzione proposta sembri abbastanza ragionevole - probabilmente è simile a quello che avrei escogitato.

Altri suggerimenti

Un puntatore a funzione non è effettivamente necessario per rientrare in un vuoto *. Puoi verificare che il valore che stai chiamando sia effettivamente l'indirizzo della funzione. In caso contrario, utilizzare un tipo di puntatore a funzione nella struttura: get_fn_t o IIRC void (*) (void) è garantito per essere compatibile con qualsiasi tipo di puntatore a funzione.

Modifica: OK, supponendo che non sia possibile far funzionare la chiamata in base al valore, non riesco a pensare a un modo più ordinato di fare ciò di cui hai bisogno rispetto alla generazione automatica dell'istruzione switch. Potresti forse usare una modalità preprocessore standard in stile ASP per ruby ??/ python / perl / php / qualunque cosa prima del preprocessore C. Qualcosa del genere:

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

potrebbe essere un po 'più leggibile del trucco macro / include, ma l'introduzione di un nuovo strumento e l'impostazione dei makefile probabilmente non vale la pena per una quantità così piccola di codice. E i numeri di riga nelle informazioni di debug non si riferiscono al file che pensi sia il file sorgente a meno che tu non faccia un lavoro extra nel tuo preprocessore per specificarli.

Riesci a convincere il fornitore a riparare il compilatore?

In che misura il puntatore alla funzione è rotto?

Se il compilatore ti consente di ottenere l'indirizzo di una funzione (vengo da C ++, ma & amp; getenv è ciò che intendo), potresti racchiudere le cose della convenzione di chiamata in assembler.

Come detto, sono un ssie in C ++, ma qualcosa nel modo di

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

Se anche questo è rotto, è possibile definire una matrice di puntatori extern void * che si definiscono, ancora una volta, in assembly.

prova questa sintassi:

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

È passato un po 'di tempo da quando ho fatto C & amp; puntatori di funzione, ma credo che la sintassi C originale richiedesse il * quando si dereferenziavano i puntatori di funzione ma la maggior parte dei compilatori ti lascerebbe andare via senza di essa.

Hai accesso alla mappa dei collegamenti? In tal caso, forse puoi aggirare la tabella dei puntatori di funzione traballante:

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} 

Ora compila, prendi gli indirizzi rilevanti dalla mappa dei collegamenti, sostituisci le costanti e ricompila. Nulla dovrebbe muoversi, quindi la mappa dovrebbe rimanere la stessa. (Rendere uniche le costanti originali dovrebbe impedire al compilatore di comprimere valori identici in una posizione di archiviazione. Potrebbe essere necessario molto tempo, a seconda dell'architettura)

Se il concetto funziona, probabilmente potresti aggiungere un passaggio post-link che esegue uno script per effettuare la sostituzione automagicamente. Certo, questa è solo una teoria, potrebbe fallire miseramente.

Forse, è necessario esaminare nuovamente la struttura:

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;

Supponiamo che i tuoi dispatcher abbiano la seguente definizione di funzione simile:

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

Supponi che i dispatcher siano come di seguito:

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

Quindi, la struttura rivista sarà:

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

La tua macro sarà:

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

Quindi, puoi impostare l'array come al solito:

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

Correggimi, se sbaglio da qualche parte ...;)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top