2016-08-12 191 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 
} 

這似乎可能與外部C庫,例如BlocksFFCALL,但現在看來我需要設置A,B,和而不是設置一個參數下進行。

+0

沒有關閉在C.你可以* *可能使用'static'對象的功能此,但是你失去了線程安全性。如果你有'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好的。我複製/粘貼OPs代碼,並沒有仔細觀察。編輯。 – dbush

0

C沒有像默認參​​數或函數重載這樣的功能。

此外,在此聲明

funcptr f = do_something; 

初始化器應是一個函數指針。

0

標準C沒有這樣的設施。編譯器提供了允許這樣做的擴展。 GCC中的Nested functions,叮噹中的Blocks或通過clisp中的CALLBACK等庫,或通過libffi組裝您的電話。

1

不,這不是C或C調用約定的語言特徵。

你需要構造一個函數,用你自己的參數調用你的函數指針;正如您已經注意到的那樣,提供這種功能的外部庫可用。

另一種方法,雖然我個人不太喜歡那個,但是使用<stdarg>和可變參數,讓你的函數做一件事或另一件事,這取決於傳遞的參數的數量。

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

爲了使功能部分應用,能夠我們需要

  • 一個結構保持(最終)已應用參數(我添加了一個init函數和一個分配函數)
  • 一個函數來解壓這些函數並調用實際函數
  • 函數來存儲(「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) 

現在進入宏。 DATA_DEF定義結構內容:

#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_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_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_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擴展到代碼存儲的參數:

#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__) 

添加小幫手來分配和釋放部分應用功能結構(data有望被分配by malloc here)...

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 PARTIAL(fn) make_fun(&(partially_applied_ ## fn), \ 
          &(partially_applied_ ## fn ## _store), \ 
          allocate_partially_applied_ ## fn ## _data()) 

當然,我們希望能夠運用一些參數:

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

DATA_STORE擴展到代碼多次調用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^Ë希望能夠調用這樣的功能(這也可能是一個功能,但遠):

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

一些缺點:

  • 宏。宏隨處可見。
  • 失去某種類型的安全性(在APPLY
  • 極品左值(APPLY(f, 1, integer, 42)不工作)