¿Cómo escribo un despachador, si el soporte de mi compilador para punteros a funciones está roto?

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

  •  08-07-2019
  •  | 
  •  

Pregunta

Estoy trabajando en una aplicación integrada donde el dispositivo se controla a través de una interfaz de comando. Me burlé del despachador de comandos en VC y lo hice funcionar a mi satisfacción; pero cuando moví el código al entorno incrustado, descubrí que el compilador tiene una implementación rota de puntero a func.

Así es como implementé originalmente el código (en 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);
  }

Sin el uso de punteros de función, parece que mi única esperanza es usar sentencias switch () / case para llamar a funciones. Pero me gustaría evitar tener que mantener manualmente una instrucción switch () grande.

Lo que estaba pensando hacer es mover todas las líneas COMMAND_ENTRY a un archivo de inclusión separado. Luego, envolturas que incluyan archivos con #define y #undefines variables. Algo así como:

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

¿Alguien ve una mejor manera de manejar esta situación? Lamentablemente, 'obtener otro compilador' no es una opción viable. :(

--- Editar para agregar: Solo para aclarar, el entorno incrustado particular se rompe porque el compilador se supone para crear una "tabla de puntero de función". que luego utiliza el compilador para resolver llamadas a funciones a través de un puntero. Desafortunadamente, el compilador no funciona y no genera una tabla de funciones correcta.

Así que no tengo una manera fácil de extraer la dirección de func para invocarla.

--- Editar # 2: Ah, sí, el uso de void * (set | get) _dispatcher fue mi intento de ver si el problema era con el typedefine de los punteros de func. Originalmente, tenía

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;
¿Fue útil?

Solución

Debería intentar cambiar su comando de estructura para que los punteros de función tengan el tipo real:

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

Desafortunadamente, no se garantiza que los punteros de función puedan convertir a / desde punteros vacíos (eso se aplica solo a punteros a objetos).

¿Cuál es el entorno incrustado?


Dada la información publicada en las actualizaciones de la pregunta, veo que es realmente un compilador con errores.

Creo que su solución propuesta parece bastante razonable, es probable que sea similar a lo que habría ideado.

Otros consejos

No se requiere un puntero de función para caber en un vacío *. Puede verificar para asegurarse de que el valor que está llamando es en realidad la dirección de la función. De lo contrario, utilice un tipo de puntero de función en la estructura: get_fn_t o IIRC void (*) (void) se garantiza que es compatible con cualquier tipo de puntero de función.

Editar: OK, suponiendo que no se puede hacer que la llamada por valor funcione, no se me ocurre una forma más ordenada de hacer lo que necesita que generar automáticamente la declaración de cambio. Tal vez podría usar un modo de preprocesador de estilo ASP listo para usar para ruby ??/ python / perl / php / lo que sea anterior al preprocesador C. Algo como esto:

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

podría ser un poco más legible que el truco macro / include, pero la introducción de una nueva herramienta y la configuración de los makefiles probablemente no valga la pena para una cantidad tan pequeña de código. Y los números de línea en la información de depuración no se relacionan con el archivo que usted considera como el archivo fuente a menos que haga un trabajo adicional en su preprocesador para especificarlos.

¿Puedes hacer que el vendedor arregle el compilador?

¿En qué medida se rompe el puntero a función?

Si el compilador le permite obtener la dirección de una función (yo soy de C ++, pero & amp; getenv es lo que quiero decir), podría envolver las cosas de la convención de llamadas en ensamblador.

Como dije, soy un C ++ ssie, pero algo en el camino de

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

Si incluso eso está roto, puede definir una matriz de punteros extern void * que defina, nuevamente, en el ensamblaje.

pruebe esta sintaxis:

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

Ha pasado un tiempo desde que hice C & amp; punteros de función, pero creo que la sintaxis original de C requería el * al desreferenciar punteros de función, pero la mayoría de los compiladores te dejarían escapar sin él.

¿Tiene acceso al mapa de enlaces? Si es así, tal vez puedas hackear la tabla de puntero de función:

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} 

Ahora compile, tome las direcciones relevantes del mapa de enlaces, reemplace las constantes y vuelva a compilar. Nada debe moverse, por lo que el mapa debe permanecer igual. (Hacer que las constantes originales sean únicas debería evitar que el compilador contraiga valores idénticos en una ubicación de almacenamiento. Es posible que necesite mucho tiempo, dependiendo de la arquitectura)

Si el concepto funciona, probablemente podría agregar un paso posterior al enlace que ejecute un script para hacer el reemplazo automáticamente. Por supuesto, esto es solo una teoría, puede fallar miserablemente.

Tal vez, necesite examinar nuevamente la estructura:

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;

Digamos que sus despachadores tienen la siguiente definición de función similar:

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

Suponga que los despachadores son los siguientes:

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

Entonces, la estructura revisada será:

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

Su macro será:

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

Luego, puede configurar su matriz como de costumbre:

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

Corrígeme, si me equivoco en alguna parte ...;)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top