[...] 방법이있다 [...]?
항상 방법이 있습니다. 문제는 가능한 노력과 단점이 가치가 있는지입니다.
다음은 지연된 (즉, 스코프의 끝에서 호출 된) 기능 실행에 관한 것입니다 (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)
가 작동하지 않습니다)
당신은 * * 가능성이의 기능에'static' 개체를 사용할 수 C.에 더 클로저가 없습니다지는 하지만 스레드 안전을 잃게됩니다. 'C11'을 가지고 있다면,'_Thread_local' 객체가 당신을 아주 가깝게 할 수 있습니다. – EOF