Como faço para escrever um despachante, se o suporte do meu compilador para ponteiros-a-funções está quebrado?

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

  •  08-07-2019
  •  | 
  •  

Pergunta

Eu estou trabalhando em um aplicativo incorporado onde o dispositivo é controlado através de uma interface de comando. Eu zombou o despachante comando no VC e tinha que trabalhar para a minha satisfação; mas quando eu, em seguida, mudou-se o código para o ambiente incorporado, eu descobri que o compilador tem uma implementação quebrada de do ponteiro-para-func.

Aqui está como eu originalmente implementado o código (em 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);
  }

Sem o uso de ponteiros de função, parece que minha única esperança é a chave de uso () / caso declarações para funções de chamada. Mas eu gostaria de evitar ter que manter manualmente uma declaração grande switch ().

O que eu estava pensando em fazer é mover todas as linhas COMMAND_ENTRY em um separado arquivo de inclusão. Então embrulha que incluem arquivo com diferentes #define e #undefines. Algo 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
}

Alguém vê uma maneira melhor de lidar com esta situação? Infelizmente, 'obter outro compilador' não é uma opção viável. : (

--- Editar para adicionar: Só para esclarecer, o ambiente incorporado particular é quebrado em que o compilador é deveria para criar uma "tabela de funções-pointer", que é então usada pelo compilador para chamadas determinação de funções através de um ponteiro. Infelizmente, o compilador está quebrado e não gera uma função de tabela correta.

Então, eu não tenho uma maneira fácil de extrair o endereço de func para invocá-lo.

--- Edit # 2: Ah, sim, o uso de void * (set | get) _dispatcher foi minha tentativa para ver se o problema foi com o typedefine dos ponteiros func. Originalmente, eu tinha

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

Solução

Você deve tentar mudar o seu struct command assim que os ponteiros de função tem o tipo real:

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

Infelizmente, ponteiros de função não são garantidos para ser capaz de converter para / de ponteiros void (que se aplica apenas aos ponteiros para objetos).

O que é o ambiente incorporado?


Dada a informações postadas nas atualizações para a pergunta, eu vejo que ele é realmente um compilador escutas.

Eu acho que a solução proposta parece bastante razoável - é provavelmente semelhante ao que eu teria que vir para cima com

.

Outras dicas

Um ponteiro de função não é realmente necessário para caber em um * vazio. Você pode verificar para se certificar de que o valor que você está chamando é realmente o endereço da função. Se não, use um tipo de ponteiro de função na estrutura:. Quer get_fn_t, ou IIRC void (*) (void) é garantido para ser compatível com qualquer tipo de ponteiro de função

Edit: OK, assumindo que chamar por valor não pode ser feita para o trabalho, eu não posso pensar de uma maneira mais limpa para fazer o que você precisa de auto-gerar a instrução switch. Talvez você possa usar um modo de pré-processador ASP de estilo off-the-shelf para ruby ??/ python / perl / php / whatever antes do pré-processador C. Algo parecido com isto:

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

pode ser um pouco mais legível do que a macro / incluem truque, mas introduzindo uma nova ferramenta e configurar os makefiles não é provavelmente vale a pena para uma pequena quantidade de código. E os números de linha na informação de depuração não vai se relacionar com o arquivo que você pensar em como o arquivo de origem, a menos que fazer um trabalho extra em sua pré-processador para especificá-los.

Você pode obter o fornecedor para corrigir o compilador?

Até que ponto é o ponteiro-para-função quebrado?

Se o compilador permite que você obtenha o endereço de uma função (eu sou de C ++, mas &getenv é o que quero dizer), você poderia envolver o material convenção de chamada em assembler.

Como disse, eu sou um C ++ ssie, mas algo na maneira de

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

Se mesmo que está quebrado, você pode definir uma matriz de ponteiros extern void* que você define, novamente, em assembléia.

tentar esta sintaxe:

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

Tem sido um tempo desde que eu fiz C & função ponteiros, mas acredito que a sintaxe C original exigia o * quando dereferencing ponteiros de função, mas a maioria dos compiladores iria deixá-lo sair sem ele.

Você tem acesso ao mapa link? Se assim for, talvez você pode cortar o seu caminho em torno da tabela de funções-ponteiro 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} 

Agora, compilar, pegar os endereços relevantes do mapa link, substitua as constantes e recompilação. Nada deve mover-se, de modo que o mapa deve permanecer o mesmo. (Fazendo as constantes originais única deve impedir que o compilador de entrar em colapso valores idênticos em local um armazenamento. Pode ser necessário um longo, dependendo da arquitetura)

Se o conceito funciona, você provavelmente poderia adicionar uma etapa de pós-link executando um script para fazer a substituição automagicamente. Claro, isso é apenas uma teoria, pode falhar miseravelmente.

Talvez, você precisa olhar para a estrutura novamente:

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;

Vamos dizer que seus despachantes têm a seguinte definição função semelhante:

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

Suponha que os despachantes são como abaixo:

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

Assim, a estrutura revista será:

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

A sua macro será:

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

Em seguida, você pode definir sua matriz, como de costume:

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

me corrigir se eu estou em algum lugar errado ...;)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top