내 컴파일러가 포인터 투 포인트에 대한 지원이 고장 나면 디스패처를 어떻게 작성합니까?

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

  •  08-07-2019
  •  | 
  •  

문제

기기가 명령 인터페이스를 통해 제어되는 임베디드 애플리케이션에서 작업 중입니다. 나는 VC에서 명령 파견관을 조롱하고 내 만족을 위해 노력했다. 그러나 코드를 임베디드 환경으로 옮겼을 때 컴파일러에 포인터 투-퓨트의 구현이 깨질 수 있음을 알았습니다.

내가 원래 코드를 구현 한 방법은 다음과 같습니다 (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 문을 사용하여 함수를 호출하는 것 같습니다. 그러나 큰 스위치 () 명령문을 수동으로 유지하지 않아도됩니다.

내가 생각했던 것은 모든 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
}

이 상황을 처리하는 더 좋은 방법이 있습니까? 안타깝게도 '다른 컴파일러 받기'는 실행 가능한 옵션이 아닙니다. :(

--- 추가 할 편집 : 명확히하기 위해, 특정 내장 환경은 컴파일러가 추정된 그런 다음 컴파일러가 사용하여 포인터를 통해 함수에 대한 호출을 해결하기 위해 "function-pointer 테이블"을 만들려면. 불행히도 컴파일러가 고장 났고 올바른 기능 테이블을 생성하지 않습니다.

따라서 기능을 호출하기 위해 기능 주소를 추출하는 쉬운 방법이 없습니다.

--- 편집 #2 : 아, 예, void *(set | get) _dispatcher의 사용은 문제가 func pointers의 typedefine에 있는지 확인하려는 시도였습니다. 원래, 나는 가지고있었습니다

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;
도움이 되었습니까?

해결책

당신은 당신의 변화를 시도해야합니다 struct command 따라서 기능 포인터는 실제 유형을 갖습니다.

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

불행히도, 기능 포인터는 void 포인터로오고 나오는 것으로 보장되지 않습니다 (객체에 대한 포인터에만 적용됨).

임베디드 환경은 무엇입니까?


질문에 대한 업데이트에 게시 된 정보를 감안할 때, 나는 그것이 실제로 버그가 큰 컴파일러라는 것을 알 수 있습니다.

귀하의 제안 된 솔루션이 꽤 합리적이라고 생각합니다. 아마도 내가 생각해 낸 것과 비슷할 것입니다.

다른 팁

기능 포인터는 실제로 무효*에 맞추기 위해 필요하지 않습니다. 당신이 호출하는 값이 실제로 함수의 주소인지 확인할 수 있습니다. 그렇지 않은 경우 구조물에서 함수 포인터 유형을 사용하십시오. get_fn_t 또는 iirc void (*) (void)는 모든 기능 포인터 유형과 호환됩니다.

편집 : 좋아, 가치로 전화를 걸 수 없다고 가정하면, 나는 스위치 명령문을 자동 생성하는 것보다 필요한 것을 할 수있는 깔끔한 방법을 생각할 수 없다. C 프리 프로세서 이전에 Ruby/Python/Perl/PHP/에 대한 기성품 ASP 스타일 전처리 모드를 사용할 수 있습니다. 이 같은:

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

매크로/포함 트릭보다 조금 더 읽기 쉬울 수도 있지만 새로운 도구를 도입하고 makefiles를 설정하는 것은 소량의 코드에 가치가 없을 것입니다. 디버그 정보의 줄 번호는 전처리 서에서 추가 작업을 수행하여이를 지정하지 않는 한 소스 파일로 생각하는 파일과 관련이 없습니다.

공급 업체가 컴파일러를 수정하도록 할 수 있습니까?

포인터 간 기능은 어느 정도까지 깨 졌습니까?

컴파일러에서 기능 주소를 얻을 수있는 경우 (C ++에서 나 왔지만 &getenv 내가 의미하는 바입니다), 당신은 호출 컨벤션 물건을 어셈블러로 포장 할 수 있습니다.

말했듯이, 나는 c ++ ssie이지만

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

그것이 고장 나면 배열을 정의 할 수 있습니다. extern void* 다시 조립에서 정의하는 포인터.

이 구문을 시도하십시오.

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

C & Function Pointers를 수행 한 이래로 잠시가 걸렸지 만 원래 C 구문에는 해석 기능 포인터가 필요할 때 * 대부분의 컴파일러가 당신이 그것없이 도망 갈 수 있다고 생각합니다.

링크 맵에 액세스 할 수 있습니까? 그렇다면 아마도 당신은 Wony Function-Pointer 테이블을 중심으로 해킹 할 수 있습니다.

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} 

이제 컴파일하고 링크 맵에서 관련 주소를 잡고 상수를 교체하고 다시 컴파일하십시오. 아무것도 움직이지 않아야하므로지도는 동일하게 유지되어야합니다. (원래 상수를 고유하게 만드는 것은 컴파일러가 동일한 값을 하나의 저장 위치로 붕괴시키는 것을 방지해야합니다. 아키텍처에 따라 긴 길이가 필요할 수 있습니다)

개념이 작동하면 스크립트를 실행하는 링크 후 단계를 추가하여 교체를 자동으로 수행 할 수 있습니다. 물론 이것은 단지 이론 일 뿐이며 비참하게 실패 할 수 있습니다.

어쩌면 구조를 다시 조사해야 할 수도 있습니다.

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

내가 어딘가에 틀렸다면 나를 바로 잡으세요 ...;)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top