从__VA_ARGS__在编译时确定的参数类型 [英] determine argument type from __VA_ARGS__ in compile time

查看:140
本文介绍了从__VA_ARGS__在编译时确定的参数类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望利用来确定类型传递给函数的参数 VA_ARGS ,以便将其路由到正确的处理程序,但是在编译时(而不是va_args函数内())

I wish to determine the types of the parameters passed to a function using VA_ARGS in order to route it to the right handler, but in compile time (and not inside a function with va_args()).

由确定类型我的意思是,我需要知道,如果跟踪只包含整数或者含有字符串为好,但我希望它会在编译的时候。

by determine type i mean i need to know if the trace contains only integers or has strings in it as well, but i wish it will be in compile time.

例如:

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

什么想法?

谢谢!

推荐答案

可以做与 _Generic一个前pression类型编译时调度运营商。请注意,这是主要的C语言,而不是preprocessor宏的一部分。

You can do a compile-time dispatch on the type of an expression with the _Generic operator. Note that this is part of the main C language, not preprocessor macros.

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

你给作为第一个参数 _Generic 只用于其类型,在编译时的前pression,选择一个值内联(以这种情况下,一个功能通过运行变量)。

The expression you give as the first argument to _Generic is only used for its type, at compile-time, to select a value to inline (in this case, a function to pass the runtime variable).

_Generic 仅用于与单个参数使用,并因此大部分示例只显示如何使用一个参数重载函数。你可以搞一些沉重的元编程来创建咀嚼他们的方式通过所有传递的参数深度嵌套的 _Generic 树木,但这里的超载有多个参数的函数简单得多可能的方式多种类型:

_Generic is only intended for use with a single parameter, and as a consequence most examples only show how to overload functions with a single argument. You could engage in some hefty metaprogramming to create deeply-nested _Generic trees that chew their way through all passed arguments, but here's one much simpler possible way to overload a function with multiple arguments of multiple types:

#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_EACH M_GET_ELEM 是实用宏...您可以扩展它们的更多的参数容易,但他们没有直接连接到这一点。)

(M_NARGS, M_FOR_EACH and M_GET_ELEM are utility macros... you can extend them for more arguments easily, but they aren't directly connected to this.)

这工作的方式是建立一个大三元操作符条件的前$ P $包含的所有的专业可能为函数pssion。我们使用传递给一个专业化,使用选择每个参数的 GET_ARG _Generic 是否提供实际参数(如果它是该分支权型),​​或合适的默认置换(如果这是错误的,在这种情况下,它就会转到未使用的)。 _Generic 使用 M_FOR_EACH 来生成类型-ID整数运行数组也映射了所有参数。这阵,加上参数的数量,被传递到 get_overload_id 来得到我们真正要调用的函数的整数ID,用作控制前pression在大三元前pression。

The way this works is to build a big ternary-operator conditional expression that contains all possible specializations for the function. We use the GET_ARG macro for each argument passed to a specialization, to choose using _Generic whether to supply an actual argument (if it's the right type for this branch), or a suitable default replacement (if this is the wrong one, in which case it will just go unused). _Generic is also mapped over all arguments using M_FOR_EACH to build a "runtime" array of type-id integers. This array, plus the number of arguments, is passed to get_overload_id to get the integer ID of the function we actually want to call, for use as a controlling expression in the big ternary expression.

尽管使用运行时C级结构(包括所有变化的大三元,调度功能来控制它),这其实并没有任何真正的运行成本:因为参数到调度功能是不变的,它本身是静态内联,海湾合作委员会(和presumably任何其他半像样的编译器)可以完全内嵌,并优化了所有的大三元的未使用的分支机构,只留下专业化,我们其实是想在生成组件(你可以用 GCC -S 编译并看到这种情况)。它实际上是一个完全编译时操作。

Despite using runtime-level C constructs (a big ternary with all variations, a dispatch function to control it), this actually doesn't have any real runtime cost: since the arguments to the dispatch function are constant and it itself is static inline, GCC (and presumably any other half-decent compiler) can completely inline it and optimise out all of the unused branches of the big ternary, leaving only the specialization we actually want in the generated assembly (you can compile with gcc -S and see that this is the case). It is effectively a completely compile-time operation.

这篇关于从__VA_ARGS__在编译时确定的参数类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆