[..]是有辦法[..]?
總有辦法。問題是如果它值得努力和可能的缺點。
以下內容基於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)
不工作)
沒有關閉在C.你可以* *可能使用'static'對象的功能此,但是你失去了線程安全性。如果你有'C11','_Thread_local'對象可以讓你非常接近。 – EOF