2014-06-23 2 views
2

VA_ARGS을 사용하여 함수에 전달 된 매개 변수의 유형을 올바른 처리기로 전달하지만 컴파일 타임에 결정하려면 (va_args()).컴파일시에 __VA_ARGS__에서 인수 유형을 결정하십시오.

유형을 결정하여 나는 추적에 정수 만 포함되어 있는지, 아니면 문자열도 포함하고 있는지를 알아야한다는 것을 의미합니다. 그러나 컴파일 타임에 있어야합니다. 예를 들어

:

#define TRACE_HANDLER(type_branch) (Invoke_ ## type_branch) 

#define TYPE_ARGS(args) ______//Determine if all arguments are uint32________ 

#define TRACE_(formatString,...) TRACE_HANDLER(TYPE_ARGS(__VA_ARGS__))(__VA_ARGS__) 

#define TRACE(Id,formatString,...) TRACE_(formatString,__VA_ARGS__) 

어떤 아이디어가?

감사합니다.

답변

1

이렇게 할 방법이 없습니다. 전처리 기가 매크로 확장을 수행 할 때 모든 매개 변수는 텍스트로 처리됩니다. 컴파일러는 심지어 C 코드를 분석하기 시작하지 않았으므로 유형도 아직 존재하지 않습니다.

#define TRACE(Id, type_branch, formatString,...) 
2

당신은 _Generic 연산자 식의 유형에 컴파일 시간 파견을 수행 할 수 있습니다 작동하도록

유일한 방법은 명시 적 형식 매개 변수를 사용하는 것입니다. 이것은 전 처리기 매크로가 아니라 주 C 언어의 일부입니다.

int x = 0; 
_Generic(x, int: invoke_int, 
      float: invoke_float, 
      double: invoke_double)(x); //calls invoke_int with x 

만, 컴파일 시간에, 그 입력에 사용되는 인라인 값을 선택 _Generic에 첫 번째 인수로서 수득 식 (이 경우, 함수는 런타임 변수를 전달한다).


_Generic

은 하나의 매개 변수와 함께 사용하기위한 것입니다, 그리고 결과적으로 대부분의 예는 단 하나의 인수로 함수를 오버로드하는 방법을 보여줍니다. 당신은 모든 전달 된 인수를 통해 그들의 방법을 씹어 깊게 중첩 _Generic 트리를 만들 수있는 몇 가지 무거운 메타 프로그래밍에 참여하지만, 여기에 여러 유형의 여러 인수로 함수를 오버로드 한 훨씬 더 간단 가능한 방법의 수 :

#include <stdlib.h> 
#include <stdio.h> 

// specialized definitions 
void overload_1(int a, int b, int c) { 
    printf("all ints (%d, %d, %d)\n", a, b, c); 
} 

void overload_2(int a, char * b, int c) { 
    printf("b is a string (%d, %s, %d)\n", a, b, c); 
} 

void overload_3(char * a, int b, char * c) { 
    printf("a and c are strings (%s, %d, %s)\n", a, b, c); 
} 

void static_error(int l) { printf("error with overload on %d\n", l); exit(1); } 

// type indices 
enum ARG_TYPE { 
    INT = 0, CHAR_P 
}; 

// get the ID of a specialization by the list of arg types 
static inline int get_overload_id(int ac, int av[]) { 
    return (ac == 3 && av[0] == INT && av[1] == INT && av[2] == INT)  ? 1 
     : (ac == 3 && av[0] == INT && av[1] == CHAR_P && av[2] == INT) ? 2 
     : (ac == 3 && av[0] == CHAR_P && av[1] == INT && av[2] == CHAR_P) ? 3 
     : -1 //error 
    ; 
} 

// overloaded definition 
#define overload(...) overload_ex(get_overload_id(M_NARGS(__VA_ARGS__), (int[]){ M_FOR_EACH(GET_ARG_TYPE, __VA_ARGS__) }), __VA_ARGS__) 
#define overload_ex(getID, ...) \ 
    ((getID == 1) ? overload_1(GET_ARG(0, INT, __VA_ARGS__), GET_ARG(1, INT, __VA_ARGS__), GET_ARG(2, INT, __VA_ARGS__)) \ 
    :(getID == 2) ? overload_2(GET_ARG(0, INT, __VA_ARGS__), GET_ARG(1, CHAR_P, __VA_ARGS__), GET_ARG(2, INT, __VA_ARGS__)) \ 
    :(getID == 3) ? overload_3(GET_ARG(0, CHAR_P, __VA_ARGS__), GET_ARG(1, INT, __VA_ARGS__), GET_ARG(2, CHAR_P, __VA_ARGS__)) \ 
    :static_error(__LINE__)) 

#define GET_ARG_TYPE(A) _Generic(((void)0, (A)), int: INT, char*: CHAR_P), 
#define GET_ARG(N, T, ...) GET_ARG_DEFAULT_##T(M_GET_ELEM(N, __VA_ARGS__,0,0,0,0,0,0,0,0,0,0,0,0,0)) 
#define GET_ARG_DEFAULT_INT(A) _Generic((A), int: (A), default: 0) 
#define GET_ARG_DEFAULT_CHAR_P(A) _Generic(((void)0, (A)), char*: (A), default: NULL) 


// metaprogramming utility macros (not directly related to this 
#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N 

#define M_CONC(A, B) M_CONC_(A, B) 
#define M_CONC_(A, B) A##B 

#define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__) 
#define M_FOR_EACH_0(ACTN, E) E 
#define M_FOR_EACH_1(ACTN, E) ACTN(E) 
#define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__) 

#define M_GET_ELEM(N, ...) M_CONC(M_GET_ELEM_, N)(__VA_ARGS__) 
#define M_GET_ELEM_0(_0, ...) _0 
#define M_GET_ELEM_1(_0, _1, ...) _1 
#define M_GET_ELEM_2(_0, _1, _2, ...) _2 
#define M_GET_ELEM_3(_0, _1, _2, _3, ...) _3 
#define M_GET_ELEM_4(_0, _1, _2, _3, _4, ...) _4 
#define M_GET_ELEM_5(_0, _1, _2, _3, _4, _5, ...) _5 
// (end of utility stuff) 


int main(void) { 
    overload(1, 2, 3);   // prints "all ints (1, 2, 3)" 
    overload(1, "two", 3);  // prints "b is a string (1, two, 3)" 
    overload("one", 2, "three"); // prints "a and c are strings (one, 2, three)" 
} 

(M_NARGS , M_FOR_EACHM_GET_ELEM은 유틸리티 매크로입니다. 더 많은 인수에 대해 쉽게 확장 할 수 있지만 직접 연결되지는 않습니다.)

이 방식의 작동 방식은 다음과 같은 큰 삼항 연산자 조건부 표현식을 작성하는 것입니다. 모두 가능한 특수 기능 . 전문에 전달 된 각 인수에 GET_ARG 매크로를 사용하고 _Generic 실제 인수를 제공할지 여부 (이 분기에 적합한 유형 인 경우) 또는 적합한 기본 대체 (해당하는 경우 잘못된 경우)를 선택합니다. 그것은 사용되지 않을 것입니다). _GenericM_FOR_EACH을 사용하여 모든 인수에 매핑되어 유형 ID 정수의 "런타임"배열을 작성합니다. 이 배열과 인수의 수는 get_overload_id으로 전달되어 큰 삼항 표현식에서 제어 표현식으로 사용하기 위해 실제로 호출하려는 함수의 정수 ID를 가져옵니다.

런타임 수준의 C 구문 (모든 변형이있는 큰 삼항 함수,이를 제어하는 ​​디스패치 함수)을 사용하더라도 실제로 디스패치 함수에 대한 인수가 상수이기 때문에 런타임에 비용이 들지 않습니다. static inline 인 경우 GCC (및 다른 반은 괜찮은 컴파일러)가 완전히 인라인하여 큰 삼항의 사용하지 않는 모든 분기를 최적화 할 수 있으므로 생성 된 어셈블리에서 실제로 원하는 특수화 만 남게됩니다 (gcc -S으로 컴파일하고 이것이 사실 인 경우). 이것은 사실 완전히 컴파일 타임 작업입니다.

관련 문제