2016-08-12 4 views
0

C에서 함수 포인터를 사용하여 함수 인수를 설정하는 방법은 해당 함수 포인터에 대한 모든 호출이 set 매개 변수를 사용하기 만합니까? 예를 들어, 아래 코드와 같은 것을 할 수 있습니까?함수 포인터로 함수 인수 설정

void do_something(int *a, int *b, int *c){ 
    *a = *c - 5; 
    *b = *c - 10; 
} 

typedef void (*funcptr)(int *a, int *b, int *c); 

int main(){ 
    int num = 5; 
    funcptr f = do_something(c=&num); // c = 5 for all calls to f(a, b) 
    int *a; int *b; 
    f(a, b); //a = 0, b = -5 

    num = 10; 
    f = do_something(c=&num); // c = 10 for all calls to f(a, b) 
    f(a, b); //a = 5, b = 0 
} 

BlocksFFCALL 같은 외부 C 라이브러리 가능 보이지만, 내가 A, B를 설정해야 할 것입니다 표시 및 C 대신에 단일 인수를 설정.

+0

당신은 * * 가능성이의 기능에'static' 개체를 사용할 수 C.에 더 클로저가 없습니다지는 하지만 스레드 안전을 잃게됩니다. 'C11'을 가지고 있다면,'_Thread_local' 객체가 당신을 아주 가깝게 할 수 있습니다. – EOF

답변

0

함수 포인터를 사용하는 대신 전역 상태를 사용하는 래퍼 함수가 필요합니다.

void do_something(int *a, int *b, int c){ 
    *a = c - 5; 
    *b = c - 10; 
} 

static int c_val = NULL; 

void do_something_wrap(int *a, int *b) 
{ 
    do_something(a, b, c); 
} 

int main(){ 
    int num = 5; 
    c_val = num; 
    int a; int b; 
    do_something_wrap(&a, &b); //a = 0, b = -5 

    num = 10; 
    c_val = num; 
    do_something_wrap(&a, &b); //a = 5, b = 0 
} 
+0

**하지만 ** 전역 적으로 'c'에 가능한 값은 하나뿐입니다. 'do_something_wrap'에 대한 서로 다른 호출에 대해 서로 다른 두 개의'c_val'을 가질 수 없습니다. –

+0

이것은 가장 깨끗한 해결책 인 것처럼 보이지만 모든 전역 변수를 사용하지 않는 것을 선호합니다. – voltnor

+1

@RudyVelthuis 좋은 캐치. 나는 OP 코드를 복사/붙여 넣었고 충분히 자세히 보지 않았다. 편집 됨. – dbush

0

C에는 기본 인수 나 함수 오버로딩과 같은 기능이 없습니다.

또한이 선언

funcptr f = do_something; 

의 이니셜 함수 포인터한다.

0

표준 C에는 이러한 기능이 없습니다. 컴파일러는 이것을 허용하는 확장 기능을 제공합니다. GCC의 Nested functions, clang의 Blocks 또는 012isp같은 라이브러리를 통하거나 libffi을 통해 전화를 거십시오.

1

아니요, 이것은 C 또는 C 호출 규칙의 언어 기능이 아닙니다.

함수 포인터를 직접 호출해야합니다. 이러한 기능을 제공하는 외부 라이브러리를 사용할 수 있습니다.

또 다른 방법은 개인적으로 지나치게 좋아하지는 않지만 <stdarg>과 varargs를 사용하는 것입니다. 전달 된 인수의 수에 따라 함수가 한 가지 또는 다른 것을 수행하도록하는 것입니다.

C 프레임 워크에서 어떤 종류의 문맥에서 작동하는 많은 기능을 자주 볼 수 있습니다. 이것은 종종 객체 지향 프로그래밍의 구현에도 사용됩니다. 당신이 구현하기를 원하는 것의 더 큰 그림을 알지는 못하지만, 종종 이런 질문들은 누군가가 C++에서 함수 오버로딩과 비슷한 것을하고 싶어하는 상황에서 일어난다. 그렇다면 상태에 따라 "상태/문맥"인수를 취하는 함수와 상태에 따라 관련이있을 수도 있고 그렇지 않을 수도있는 인수를 추가 할 수도 있습니다.

0

[...] 방법이있다 [...]?

항상 방법이 있습니다. 문제는 가능한 노력과 단점이 가치가 있는지입니다.

다음은 지연된 (즉, 스코프의 끝에서 호출 된) 기능 실행에 관한 것입니다 (another answer I gave).

시작 떨어져 "부분적으로 적용 기능"에 대한 통합 유형 :

struct partially_applied { 
    void * data; // parameters 
    void (*function)(void *); // function unpacking parameters and calling actual function 
    void (*store)(void *, char const *, void *); // storing parameters 
}; 
typedef struct partially_applied * FUN; 

일부 기능을 적용-수 우리가

  • 구조체 (결국) 이미 적용 들고 필요 매개 변수 (초기화 함수와 할당 함수를 추가했습니다)
  • 이들을 풀고 실제 함수를 호출하는 함수
  • 함수를 저장하는 함수입니다 ("par tially) "적용 우리가 갈 여기

매개 변수 :

#define MAKE_PARTIAL(fn, N, ...) \ 
struct partially_applied_ ## fn ## _data { \ 
    DATA_DEF(N, __VA_ARGS__) \ 
}; \ 
\ 
static void init_partially_applied_ ## fn ## _data (void * p) { \ 
    struct partially_applied_ ## fn ## _data * data = p; \ 
    DATA_INIT(N, __VA_ARGS__); \ 
} \ 
\ 
static void * allocate_partially_applied_ ## fn ## _data (void) { \ 
    void * data = malloc(sizeof(struct partially_applied_ ## fn ## _data)); \ 
    if (data == NULL) { \ 
     fprintf(stderr, "Allocation failure for " #fn " data\n"); \ 
     exit(1); \ 
    } \ 
    init_partially_applied_ ## fn ## _data(data); \ 
    return data; \ 
} \ 
\ 
static void partially_applied_ ## fn (void * p) { \ 
    struct partially_applied_ ## fn ## _data * data = p; \ 
    if (DATA_CHECK(N, __VA_ARGS__)) { \ 
     fn(DATA_ACCESS(N, __VA_ARGS__)); \ 
    } else { \ 
     fprintf(stderr, "Not all parameters for " #fn " are vaild\n"); \ 
    } \ 
} \ 
\ 
static void partially_applied_ ## fn ## _store (\ 
    void * p, char const * id, void * src) { \ 
    struct partially_applied_ ## fn ## _data * data = p; \ 
    DATA_STORE_CODE(N, __VA_ARGS__) \ 
    fprintf(stderr, "Cannot store %s in " #fn "!\n", id); \ 
} 

을 위의 몇 가지 매크로가 포함되어 있습니다. 이것들은 매크로 인자의 수에 달려 있는데 (전 처리기로 계산할 수 있지만, 더 간단하게하고 싶다). 인수의 수에 따라 올바른 매크로를 확장하려면 약간의 도우미가 필요합니다.

#define SPLICE_2(l,r) l##r 
#define SPLICE_1(l,r) SPLICE_2(l,r) 
#define SPLICE(l,r) SPLICE_1(l,r) 

이제 매크로를 살펴 보겠습니다. 모든 인수가 적용된 경우

#define DATA_INIT_0(...) 
#define DATA_INIT_1(t, name)  data->name ## _valid = false; 
#define DATA_INIT_2(t, name, ...) data->name ## _valid = false; DATA_INIT_1(__VA_ARGS__) 
#define DATA_INIT_3(t, name, ...) data->name ## _valid = false; DATA_INIT_2(__VA_ARGS__) 
#define DATA_INIT_4(t, name, ...) data->name ## _valid = false; DATA_INIT_3(__VA_ARGS__) 
// add more to support more parameters 
#define DATA_INIT(N, ...) SPLICE(DATA_INIT_,N)(__VA_ARGS__) 

DATA_CHECK은 조건 테스트로 확장 :

#define DATA_DEF_0(...) 
#define DATA_DEF_1(type, name)  type name; bool name ## _valid; 
#define DATA_DEF_2(type, name, ...) type name; bool name ## _valid; DATA_DEF_1(__VA_ARGS__) 
#define DATA_DEF_3(type, name, ...) type name; bool name ## _valid; DATA_DEF_2(__VA_ARGS__) 
#define DATA_DEF_4(type, name, ...) type name; bool name ## _valid; DATA_DEF_3(__VA_ARGS__) 
// add more to support more parameters 
#define DATA_DEF(N, ...) SPLICE(DATA_DEF_,N)(__VA_ARGS__) 

DATA_INIT 코드로 확장

는 이러한 구조를 초기화 :

#define DATA_CHECK_0(...) true 
#define DATA_CHECK_1(t, name)  data->name ## _valid 
#define DATA_CHECK_2(t, name, ...) data->name ## _valid && DATA_CHECK_1(__VA_ARGS__) 
#define DATA_CHECK_3(t, name, ...) data->name ## _valid && DATA_CHECK_2(__VA_ARGS__) 
#define DATA_CHECK_4(t, name, ...) data->name ## _valid && DATA_CHECK_3(__VA_ARGS__) 
// add more to support more parameters 
#define DATA_CHECK(N, ...) SPLICE(DATA_CHECK_,N)(__VA_ARGS__) 

DATA_DEF은 구조 내용을 정의 DATA_ACCESS은 실제 함수에 매개 변수를 전달하기위한 코드로 확장됩니다 (실제로는 t

#define DATA_ACCESS_0(...) 
#define DATA_ACCESS_1(t, name)  data->name 
#define DATA_ACCESS_2(t, name, ...) data->name, DATA_ACCESS_1(__VA_ARGS__) 
#define DATA_ACCESS_3(t, name, ...) data->name, DATA_ACCESS_2(__VA_ARGS__) 
#define DATA_ACCESS_4(t, name, ...) data->name, DATA_ACCESS_3(__VA_ARGS__) 
// add more to support more parameters 
#define DATA_ACCESS(N, ...) SPLICE(DATA_ACCESS_,N)(__VA_ARGS__) 

그리고 마지막으로 DATA_STORE_CODE이 매개 변수를 저장하는 코드로 확장 : (data를 할당하고 무료 부분적으로 적용 기능을 구조하기 위해 작은 헬퍼를 추가

#define DATA_STORE_CODE_OP(type, name) \ 
if (strcmp(id, #name) == 0) { data->name = *((type *) src); data->name ## _valid = true; return; } 

#define DATA_STORE_CODE_0(...) 
#define DATA_STORE_CODE_1(type, name)  DATA_STORE_CODE_OP(type, name) 
#define DATA_STORE_CODE_2(type, name, ...) DATA_STORE_CODE_OP(type, name) DATA_STORE_CODE_1(__VA_ARGS__) 
#define DATA_STORE_CODE_3(type, name, ...) DATA_STORE_CODE_OP(type, name) DATA_STORE_CODE_2(__VA_ARGS__) 
#define DATA_STORE_CODE_4(type, name, ...) DATA_STORE_CODE_OP(type, name) DATA_STORE_CODE_3(__VA_ARGS__) 
// more 
#define DATA_STORE_CODE(N, ...) SPLICE(DATA_STORE_CODE_,N)(__VA_ARGS__) 

가 할당 될 것으로 예상된다 그는 분리 된 인수 목록) 쉼표 작성자 : malloc 여기)

FUN make_fun(void (*function)(void *), void (*store)(void *, char const *, void *), void * data) { 
    FUN f = malloc(sizeof(*f)); 
    if (f == NULL) { 
     fprintf(stderr, "Allocation of FUN failed\n"); 
     exit(1); 
    } 
    f->function = function; 
    f->store = store; 
    f->data = data; 
    return f; 
} 
void free_fun(FUN f) { 
    free(f->data); 
    free(f); 
} 

...

#define APPLY(PFN, N, ...) \ 
do { \ 
    struct partially_applied * pfn = (PFN); \ 
    DATA_STORE(N, __VA_ARGS__) \ 
} while(0) 

매크로 DATA_STORE은으로 확장

물론
#define PARTIAL(fn) make_fun(&(partially_applied_ ## fn), \ 
          &(partially_applied_ ## fn ## _store), \ 
          allocate_partially_applied_ ## fn ## _data()) 

우리는 몇 가지 인자를 적용 할 수 있도록하려면 : 우리는 실제로 부분적으로 적용 함수의 인스턴스를 만드는 매크로를 정의하는 데에 갈 수 있습니다 우리는 한 번에 여러 인수를 적용 할 수 있도록 코드는 store 함수를 여러 번 호출 :

#define DATA_STORE_OP(name, value) pfn->store(pfn->data, #name, &(value)); 
#define DATA_STORE_0(...) 
#define DATA_STORE_1(name, value) DATA_STORE_OP(name, value) 
#define DATA_STORE_2(name, value, ...) DATA_STORE_OP(name, value) DATA_STORE_1(__VA_ARGS__) 
#define DATA_STORE_3(name, value, ...) DATA_STORE_OP(name, value) DATA_STORE_2(__VA_ARGS__) 
#define DATA_STORE_4(name, value, ...) DATA_STORE_OP(name, value) DATA_STORE_3(__VA_ARGS__) 
#define DATA_STORE(N, ...) SPLICE(DATA_STORE_,N)(__VA_ARGS__) 

마지막으로 적어도 w하지 E (이 또한 잘 기능 할 수 있지만, 수)와 같은 함수를 호출 할 수 있도록하려면 :

#define CALL(fn) (fn)->function((fn)->data) 
마지막으로

, an example :

void foo(char * str, int i) { 
    printf("FOO| str = %s, i = %d\n", str, i); 
} 
void bar(float f, int i, size_t s) { 
    printf("BAR| f = %f, i = %d, s = %zu\n", f, i, s); 
} 
MAKE_PARTIAL(foo, 2, char *, string, int, integer) 
MAKE_PARTIAL(bar, 3, float, floating, int, INT, size_t, SOME_SIZE) 


int main() { 
    FUN f = PARTIAL(foo); 
    char * c = "Crazy"; 
    APPLY(f, 1, string, c); 
    printf("doing other stuff\n"); 
    FUN g = PARTIAL(bar); 
    size_t size = 99; 
    APPLY(g, 1, SOME_SIZE, size); 
    int answer = 42; 
    APPLY(f, 1, integer, answer); 
    answer = 21; 
    float pi = 3.14; 
    APPLY(g, 2, INT, answer, floating, pi); 
    CALL(f); 
    printf("done\n"); 
    CALL(g); 
    printf("now completely done\n"); 
    return 0; 
} 

일부 단점 :

  • 매크로 . 매크로를 사방에.
  • lvalues ​​일부 (APPLY에서) 형 안전
  • 필요 (APPLY(f, 1, integer, 42)가 작동하지 않습니다)