我理解单参数函数的C11泛型,如下所示:(从这里)
#define acos(X) _Generic((X), \
long double complex: cacosl, \
double complex: cacos, \
float complex: cacosf, \
long double: acosl, \
float: acosf, \
default: acos \
)(X)
Run Code Online (Sandbox Code Playgroud)
但是,对于具有两个参数的函数来说似乎很痛苦,你需要嵌套调用_Generic,这真的很难看; 摘自同一篇博客:
#define pow(x, y) _Generic((x), \
long double complex: cpowl, \
double complex: _Generic((y), \
long double complex: cpowl, \
default: cpow), \
float complex: _Generic((y), \
long double complex: cpowl, \
double complex: cpow, \
default: cpowf), \
long double: _Generic((y), \
long double complex: cpowl, \
double complex: cpow, \
float complex: cpowf, \
default: powl), \
default: _Generic((y), \
long double complex: cpowl, \
double complex: cpow, \
float complex: cpowf, \
long double: powl, \
default: pow), \
float: _Generic((y), \
long double complex: cpowl, \
double complex: cpow, \
float complex: cpowf, \
long double: powl, \
float: powf, \
default: pow) \
)(x, y)
Run Code Online (Sandbox Code Playgroud)
有没有办法为多参数函数提供更多人类可读的泛型,例如:
#define plop(a,b) _Generic((a,b), \
(int,long): plopii, \
(double,short int): plopdd)(a,b)
Run Code Online (Sandbox Code Playgroud)
提前感谢您的回复.基本的想法是有一个宏包装器_Generic.
tor*_*rek 14
鉴于_Generic没有评估控制表达式,我建议应用一些算术运算,进行适当的类型组合,然后打开结果.从而:
#define OP(x, y) _Generic((x) + (y), \
long double complex: LDC_OP(x, y), \
double complex: DC_OP(x, y), \
... )
Run Code Online (Sandbox Code Playgroud)
当然,这仅适用于某些情况,但您可以随时展开"折叠"类型无效的那些情况.(例如,这可以让人们处理数组-N-of-char vs char *与链接的printnl示例一样,然后如果组合类型是int,则可以返回并检查char和short.)
Ela*_*zar 12
由于C没有元组,让我们创建自己的元组:
typedef struct {int _;} T_double_double;
typedef struct {int _;} T_double_int;
typedef struct {int _;} T_int_double;
typedef struct {int _;} T_int_int;
typedef struct { T_double_double Double; T_double_int Int;} T_double;
typedef struct { T_int_double Double; T_int_int Int;} T_int;
#define typeof1(X) \
_Generic( (X), \
int: (T_int){{0}}, \
double: (T_double){{0}} )
#define typeof2(X, Y) \
_Generic( (Y), \
int: typeof1(X).Int, \
double: typeof1(X).Double )
Run Code Online (Sandbox Code Playgroud)
这是客户端代码:
#include <stdio.h>
#include "generics.h"
#define typename(X, Y) \
_Generic( typeof2(X, Y), \
T_int_int: "int, int\n", \
T_int_double: "int, double\n", \
T_double_double: "double, double\n", \
T_double_int: "double, int\n", \
default: "Something else\n" )
int main() {
printf(typename(1, 2));
printf(typename(1, 2.0));
printf(typename(1.0, 2.0));
printf(typename(1.0, 2));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它有效:
~/workspace$ clang -Wall -std=c11 temp.c
~/workspace$ ./a.out
int, int
int, double
double, double
double, int
Run Code Online (Sandbox Code Playgroud)
是的,您仍然需要以指数大小编写代码.但至少你可以重复使用它.
小智 7
我真的觉得上述解决方案并不比OP的原始实现更容易或更干净。我认为最好的方法是保持简单,只是用更多的宏来抽象宏。下面是一个例子。
#include<stdio.h>
double multiply_id ( int a, double b )
{
return a * b;
}
double multiply_di ( double a, int b )
{
return a * b;
}
double multiply_dd ( double a, double b )
{
return a * b;
}
int multiply_ii ( int a, int b )
{
return a * b;
}
/*
#define multiply(a,b) _Generic((a), \
int: _Generic((b), \
int: multiply_ii, \
double: multiply_id), \
double: _Generic((b), \
int: multiply_di, \
double: multiply_dd) ) (a,b)
*/
#define _G2(ParamB,ParamA_Type, TypeB1, TypeB1_Func, TypeB2, TypeB2_Func) \
ParamA_Type: _Generic((ParamB), \
TypeB1: TypeB1_Func, \
TypeB2: TypeB2_Func)
#define multiply(a,b) _Generic((a), \
_G2(b,int,int,multiply_ii,double,multiply_id), \
_G2(b,double,int,multiply_di,double,multiply_dd) ) (a,b)
int main(int argc, const char * argv[]) {
int i;
double d;
i = 5;
d = 5.5;
d = multiply( multiply(d, multiply(d,i) ) ,multiply(i,i) );
printf("%f\n", d);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
_G2是两个参数泛型的宏。这可以很容易地扩展到 a_G3或更多种。诀窍是正常地执行它,然后从它的形式构建一个宏。
这是一个版本,只需要您手工编写线性量的代码,所有这些代码都与手头的事情直接相关(没有大型的手工定义类型的树)。首先,用法示例:
#include <stdio.h>
// implementations of print
void print_ii(int a, int b) { printf("int, int\n"); }
void print_id(int a, double b) { printf("int, double\n"); }
void print_di(double a, int b) { printf("double, int\n"); }
void print_dd(double a, double b) { printf("double, double\n"); }
void print_iii(int a, int b, int c) { printf("int, int, int\n"); }
void print_default(void) { printf("unknown arguments\n"); }
// declare as overloaded
#define print(...) OVERLOAD(print, (__VA_ARGS__), \
(print_ii, (int, int)), \
(print_id, (int, double)), \
(print_di, (double, int)), \
(print_dd, (double, double)), \
(print_iii, (int, int, int)) \
)
#define OVERLOAD_ARG_TYPES (int, double)
#define OVERLOAD_FUNCTIONS (print)
#include "activate-overloads.h"
int main(void) {
print(44, 47); // prints "int, int"
print(4.4, 47); // prints "double, int"
print(1, 2, 3); // prints "int, int, int"
print(""); // prints "unknown arguments"
}
Run Code Online (Sandbox Code Playgroud)
这可能是您将要获得的最简单的语法。
现在针对缺点/限制:
OVERLOADED_ARG_TYPES-O1)您还必须定义一个X_default不带参数的函数。不要将此添加到过载声明块中。这用于不匹配项(如果您想直接调用它,请使用任何不匹配的值(例如复合文字匿名结构之类的东西)调用重载)。
这里是activate-overloads.h:
// activate-overloads.h
#include <order/interpreter.h>
#define ORDER_PP_DEF_8dispatch_overload ORDER_PP_FN( \
8fn(8N, 8V, \
8do( \
8print( 8cat(8(static inline int DISPATCH_OVER_), 8N) ((int ac, int av[]) { return ) ), \
8seq_for_each_with_idx( \
8fn(8I, 8T, \
8let( (8S, 8tuple_to_seq(8tuple_at_1(8T))), \
8print( 8lparen (ac==) 8to_lit(8seq_size(8S)) ), \
8seq_for_each_with_idx(8fn(8I, 8T, 8print( (&&av[) 8I (]==) 8cat(8(K_), 8T) )), 0, 8S), \
8print( 8rparen (?) 8I (:) ) \
)), \
1, 8V), \
8print( ( -1; }) ) \
) ))
#define TYPES_TO_ENUMS(TS) ORDER_PP ( \
8do( \
8seq_for_each(8fn(8T, 8print( 8T (:) 8cat(8(K_), 8T) (,) )), \
8tuple_to_seq(8(TS))), \
8print( (default: -1) ) \
) \
)
#define ENUMERATE_TYPES(TS) enum OVERLOAD_TYPEK { ORDER_PP ( \
8seq_for_each(8fn(8V, 8print( 8V (,) )), 8types_to_vals(8tuple_to_seq(8(TS)))) \
) };
#define ORDER_PP_DEF_8types_to_vals ORDER_PP_FN( \
8fn(8S, 8seq_map(8fn(8T, 8cat(8(K_), 8T)), 8S)) )
ENUMERATE_TYPES(OVERLOAD_ARG_TYPES)
#define OVER_ARG_TYPE(V) _Generic((V), TYPES_TO_ENUMS(OVERLOAD_ARG_TYPES) )
#define OVERLOAD
ORDER_PP (
8seq_for_each(
8fn(8F,
8lets( (8D, 8expand(8adjoin( 8F, 8(()) )))
(8O, 8seq_drop(2, 8tuple_to_seq(8D))),
8dispatch_overload(8F, 8O) )),
8tuple_to_seq(8(OVERLOAD_FUNCTIONS))
)
)
#undef OVERLOAD
#define OVERLOAD(N, ARGS, ...) ORDER_PP ( \
8do( \
8print(8lparen), \
8seq_for_each_with_idx( \
8fn(8I, 8T, \
8lets( (8S, 8tuple_to_seq(8tuple_at_1(8T))) \
(8R, 8tuple_to_seq(8(ARGS))) \
(8N, 8tuple_at_0(8T)), \
8if(8equal(8seq_size(8S), 8seq_size(8R)), \
8do( \
8print( 8lparen (DISPATCH_OVER_##N) 8lparen 8to_lit(8seq_size(8R)) (,(int[]){) ), \
8seq_for_each(8fn(8A, 8print( (OVER_ARG_TYPE) 8lparen 8A 8rparen (,) )), 8R), \
8print( (-1}) 8rparen (==) 8I 8rparen (?) 8N 8lparen ), \
8let( (8P, 8fn(8A, 8T, \
8print( (_Generic) 8lparen 8lparen 8A 8rparen (,) 8T (:) 8A (,default:*) 8lparen 8T (*) 8rparen (0) 8rparen ) \
)), \
8ap(8P, 8seq_head(8R), 8seq_head(8S)), \
8seq_pair_with(8fn(8A, 8T, 8do(8print((,)), 8ap(8P, 8A, 8T))), 8seq_tail(8R), 8seq_tail(8S)) \
), \
8print( 8rparen (:) ) \
), \
8print(( )) ) \
)), \
1, 8tuple_to_seq(8((__VA_ARGS__))) \
), \
8print( 8cat(8(N), 8(_default)) (()) 8rparen) \
) \
)
Run Code Online (Sandbox Code Playgroud)
这需要Vesa K出色的Order预处理程序库。
它实际上是如何工作的:OVERLOAD_ARG_TYPES声明用于构建一个枚举,该枚举列出所有用作常量的参数类型。然后,可以在调用程序代码中用对所有实现(正确的参数编号)之间进行的大型三元操作来替换对重载名称的每次调用。调度通过使用_Generic从参数类型生成枚举值,将其放入数组中并具有自动生成的调度程序函数来返回该类型组合的ID(原始块中的位置)来进行工作。如果ID匹配,则调用该函数。如果参数的类型错误,则会为未实现的调用生成伪值,以避免类型不匹配。
从技术上讲,这涉及“运行时”分派,但是由于每个类型ID都是常量,而分派器函数是static inline,所以整个过程对于编译器来说很容易优化,除了所需的调用(GCC确实确实对其进行了优化) 。
这是对以前在此处发布的技术的改进(相同的想法,现在具有漂亮而超轻的语法)。
| 归档时间: |
|
| 查看次数: |
5551 次 |
| 最近记录: |