Как мне написать диспетчер, если поддержка моего компилятора указателей на функции нарушена? - PullRequest
1 голос
/ 12 ноября 2008

Я работаю над встроенным приложением, где устройство управляется через командный интерфейс. Я высмеял командного диспетчера в VC и заставил его работать к моему удовлетворению; но когда я переместил код во встроенную среду, я обнаружил, что компилятор имеет неверную реализацию указателя на func.

Вот как я изначально реализовал код (в 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);
  }

Без использования указателей на функции, похоже, моя единственная надежда - использовать операторы switch () / case для вызова функций. Но я бы хотел избежать необходимости вручную поддерживать большой оператор switch ().

Я думал о том, чтобы переместить все строки COMMAND_ENTRY в отдельный включаемый файл. Затем переносит этот файл с различными #define и #undefines. Что-то вроде:

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

Кто-нибудь видит лучший способ справиться с этой ситуацией? К сожалению, «получить другой компилятор» не является жизнеспособным вариантом. (

--- Изменить, чтобы добавить: Просто для пояснения, конкретная встроенная среда нарушена тем, что компилятор предполагается для создания «таблицы указателей на функции», которая затем используется компилятором для разрешения вызовов функций через указатель. К сожалению, компилятор не работает и не генерирует правильную таблицу функций.

Так что у меня нет простого способа извлечь адрес func для его вызова.

--- Правка № 2: Ах да, использование void * (set | get) _dispatcher было моей попыткой выяснить, не связана ли проблема с определением типа указателей на функции. Первоначально у меня было

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;

Ответы [ 7 ]

7 голосов
/ 12 ноября 2008

Вы должны попробовать изменить struct command, чтобы указатели функций имели фактический тип:

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

К сожалению, указатели на функции не гарантируют возможность конвертации в / из указателей void (это относится только к указателям на объекты).

Что такое встроенная среда?


Учитывая информацию, размещенную в обновлениях к вопросу, я вижу, что это действительно ошибочный компилятор.

Я думаю, что предложенное вами решение кажется довольно разумным - вероятно, оно похоже на то, что я бы придумал.

3 голосов
/ 12 ноября 2008

Указатель функции на самом деле не требуется для размещения в пустоте *. Вы можете убедиться, что вызываемое вами значение действительно является адресом функции. Если нет, используйте тип указателя на функцию в структуре: либо get_fn_t, либо IIRC void (*) (void) гарантированно совместимы с любым типом указателя на функцию.

Редактировать: ОК, предполагая, что вызов по значению не может быть выполнен, я не могу придумать более удобный способ сделать то, что вам нужно, чем автоматическая генерация оператора switch. Возможно, вы могли бы использовать готовый режим препроцессора в стиле ASP для ruby ​​/ python / perl / php / любой другой до препроцессора Си. Примерно так:

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

может быть немного более читабельным, чем трюк с макросом / включением, но введение нового инструмента и настройка make-файлов, вероятно, не стоит этого небольшого количества кода. И номера строк в отладочной информации не будут относиться к файлу, который вы считаете исходным, если вы не выполняете дополнительную работу в своем препроцессоре, чтобы указать их.

2 голосов
/ 12 ноября 2008

Можете ли вы заставить поставщика исправить компилятор?

1 голос
/ 12 ноября 2008

Насколько нарушен указатель на функцию?

Если компилятор позволяет вам получить адрес функции (я из C ++, но я имею в виду &getenv), вы можете заключить соглашение о вызовах в ассемблер.

Как я уже сказал, я - С ++, но что-то вроде

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

Если даже это не работает, вы можете определить массив extern void* указателей, которые вы снова определите в сборке.

0 голосов
/ 03 декабря 2008

Может быть, вам нужно снова заглянуть в структуру:

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;

Допустим, у ваших диспетчеров есть следующее похожее определение функции:

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

Предположим, что диспетчеры похожи ниже:

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

Итак, пересмотренная структура будет:

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

Ваш макрос будет:

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

Затем вы можете установить свой массив как обычно:

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

Поправь меня, если я где-то ошибаюсь ...;)

0 голосов
/ 13 ноября 2008

У вас есть доступ к карте ссылок? Если это так, возможно, вы сможете взломать таблицу указателей функций Wickky:

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} 

Теперь скомпилируйте, извлеките соответствующие адреса из карты ссылок, замените константы и перекомпилируйте. Ничто не должно двигаться, поэтому карта должна оставаться прежней. (Создание оригинальных констант уникальными должно препятствовать тому, чтобы компилятор свернул идентичные значения в одно место хранения. Вам может понадобиться long long в зависимости от архитектуры)

Если концепция работает, вы, вероятно, могли бы добавить шаг после ссылки, запустив сценарий, чтобы выполнить замену автоматически. Конечно, это всего лишь теория, она может с треском провалиться.

0 голосов
/ 12 ноября 2008

попробуйте этот синтаксис:

return (* ((get_fn_t) команды [i] .get_dispatcher)) (pc);

Прошло некоторое время с тех пор, как я сделал указатели на функции C &, но я считаю, что оригинальный синтаксис C требовал * при разыменовании указателей на функции, но большинство компиляторов позволяли бы вам обходиться без него.

...