2016-10-31 2 views
0

read C가 동적 함수 호출을 지원하지 않습니다. 내 프로그램처럼 별도의 함수로 구현 된 테스트 케이스의 계속 증가하고있다 -C 동적 함수 호출을 허용하는 해결 방법이 있습니까?

int main(int argc, char **argv){ 
    assert(!testcase1()); 
    assert(!testcase2()); 
    assert(!testcase3()); 
} 
-

int testcase1(void); 
int testcase2(void); 
int testcase3(void); 

나는 새로운 테스트 케이스를 추가 할 때마다, 나는 또한처럼 내 주요 기능에 호출을 추가 할 필요가있다

assert(!testcase*())과 같이 호출하는 것을 선호합니다. 여기서 *는 프로그램에서 유효한 함수 이름으로 해석되는 문자열과 일치합니다.

더 편리한 솔루션을 생각해 볼 수 있습니까?

+0

프로토 타입을 'bool testcase1 (void);'가 아니어야합니까? – Lundin

+0

적절한 단위 테스트 프레임 워크를 고려할 시간입니다. – taskinoor

+1

POSIX는 런타임에 동적 기호를 찾는 ['dlsym()'] (http://man7.org/linux/man-pages/man3/dlsym.3.html)을 제공합니다. 그것 (그리고'dlopen()')은 종종 POSIXy 운영체제 (즉, 현재는 Microsoft Windows를 제외한 모든 곳)에서 런타임 플러그인 기능을 제공하기 위해 사용됩니다. –

답변

7

모두 당신을 testcases가 동일한 서명이있는 경우 다음 함수 포인터의 배열을 사용할 수 있습니다

void (*func[])() = { testcase1, testcase2 }; 

for (size_t i = 0; i < sizeof(func)/sizeof(func[0]); i++) { 
    assert(!func[i]()); 
} 
+0

함수 포인터 사용을 고려해 봤지만 새 테스트 케이스를 추가하거나 (이름을 변경 할 때마다) 수동으로'void (* func [])()'초기화를해야합니다. 그래서 어떤 이점도 보이지 않습니다. 현재'assert (! testcasennn())'반복적 인 접근법보다 – user2309803

+0

모든 * testcase 함수를 자동으로 호출하는 "정규식"종류는 없습니다. 여전히 어레이를 업데이트해야합니다. 하지만 전체 함수 호출을 수행하는 것보다 관리하기가 쉽습니다. 간단히 말해 C에서이 작업을 수행하는 방법 일 수 있습니다. 그렇지 않으면 CPPunit과 같은 단위 테스트 프레임 워크를 살펴볼 수 있습니다. – usr

+0

음, x 매크로가 있습니다. 함수 이름을 하나가 아닌 두 위치에 작성해야하는 "문제"가 해결됩니다. 그러나 그들은 코드를 읽을 수없는 혼란으로 바꿀 것입니다. 함수 포인터 배열은 훨씬 더 우아합니다. – Lundin

1

가장 좋은 방법은 당신이 새로운 테스트 케이스를 추가 할 때 코드의 몇 가지 추가 라인을 쓸 가능성이 높습니다 - 그것을 정말로 큰 문제는 아닙니다. 나는 다른 답변에서 제안 된 것처럼, 함수 포인터 배열의 라인을 따라 뭔가를 추천한다. 각각의 새로운 들어

testcase1 
testcase2 
testcase3 
Assertion failed! 

:

#include <assert.h> 
#include <stdbool.h> 
#include <stdio.h> 

#define TEST_CASES \     // list of "x macros" 
    X(testcase1) \ 
    X(testcase2) \ 
    X(testcase3) 

#define X(func) bool func (void);  // declare function prototypes 
    TEST_CASES 
#undef X 

bool (*const test_cases[])(void) =  // array of read-only function pointers 
{ 
    #define X(func) &func,    // point at each function 
    TEST_CASES 
    #undef X 
}; 

int main (void) 
{ 
    for(size_t i=0; i<sizeof(test_cases)/sizeof(test_cases[0]); i++) 
    { 
    assert(test_cases[i]()); 
    } 
} 

bool testcase1 (void) { puts(__func__); return true; } 
bool testcase2 (void) { puts(__func__); return true; } 
bool testcase3 (void) { puts(__func__); return false; } 

출력 :

그러나, 당신이 문제에 추한 매크로를 던져 모든 C에서 가능하다는 것을 보여주기 위해, 여기에 권장되지 대안 테스트 케이스의 경우 함수 정의를 작성한 다음 "x 매크로"목록에 추가하면됩니다. TEST_CASES. 그러나 프로덕션 코드에서 이러한 추악한 트릭을 소개하는 데는 매우 좋은 이유가 필요합니다.

+0

흥미로운 솔루션. 여분의 코드 라인을 입력하는 것이 큰 문제는 아니지만, 그 여분의 라인이 (1) 변경되지 않은 규칙에 의해 원래 라인에서 변환된다면 (예를 들어, 다음과 같은 규칙은 - int testcaseNNN (void)','testcaseNNN()'에 대한 호출을 추가한다.) (2) 원래의 줄과 동기화 될 필요가있다. (예를 들어'testcase1'의 이름을'testcaseInsertRecord'로 바꾸기로 결정했다면 testcase1), 나는 그것을 피하는 것이 최선이라고 생각합니다. 또는 프로그램으로 관리하는 것이 오류가 발생하기 쉬운 인간보다 낫습니다. – user2309803

0

function pointers을 사용할 수 있습니다. closures (C99 또는 C11에는 포함되지 않음) 및 callbacks도 참조하십시오.

많은 operating systemsdynamic loading을 제공합니다. POSIX 운영 체제 (예 : Linux 또는 MacOSX)에서는 dlopen & dlsym을 사용하여 일부 라이브러리 (또는 프로그램 실행 파일)의 이름에서 함수 포인터 (실제로 주소)를 가져올 수 있습니다. 다른 운영 체제에서도 유사한 기능을 제공 할 수 있습니다.

마지막으로,은 기술 (metaprogramming)을 사용하여 스크립트 (또는 C 코드를 내보내는 일부 프로그램)에서 테스트 main이 생성되도록하는 것이 좋습니다. 따라서 main의 C 코드를 생성하고 긴 시퀀스가 ​​assert 인 코드를 작성하고 build procedure (예 : Makefile, make을 사용하는 경우)을 개선하여 특수 C 코드 생성기를 적절히 실행하십시오. 세부 사항은 물론 귀하의 코드에만 해당됩니다. 몇 가지 규칙을 추가 할 수 있습니다 (예 : 테스트 생성기에서 파싱 할 몇 가지 특별한 설명을 추가하는 등).

0

@Nominal Animal 및 @Basile Starynkevitch의 접근 방식을 따르기로 결정했습니다. mymainprog.c, 나는 추가 -

int runtests(void){ 
    void *testh; 
    int (*testp)(void); 
    char *dlmsg; 
    int rc; 
    char funcname[8]; 
    int testnum; 

    testh = dlopen("libsmtests.so", RTLD_LAZY); 

    if (!testh){ 
     printf("%s\n", dlerror()); 
     return 1; 
    } 
    dlerror(); 
    for (testnum =1; testnum < 1000; testnum++){ 
     sprintf(funcname,"testcase%d", testnum); 
     *(void **) (&testp) = dlsym(testh, funcname); 
     dlmsg = dlerror(); 
     if (dlmsg == NULL) { 
      rc = (*testp)(); 
      printf("%s called, rc=%d\n", funcname, rc); 
     } 
    } 
    dlclose(testh); 
    return 0; 
} 

나는 (을 testcases 별도의 파일로 내을 testcases를 추가합니다.C)이 같은 -

int testcase1(void){ 
    return [some testcase expression] 
} 
int testcase2(void){ 
    return [another testcase expression] 
} 

다음은이 libsmtests.so하는 위치 독립적 인 코드 (-fPIC)와 공유 라이브러리로 컴파일합니다. 새로운 기능의 구현을 추가 한 후에 testNNNN()에 대한 호출을 코딩 할 필요가 없으므로 이점은 약간 적게 입력됩니다. int testcaseNNN(void)을 testcases.c에 입력하십시오.

관련 문제