C 基础 _Generic 泛型应用
引言 - _Generic 用法简介
#include <stdio.h> #define TYPENAME_CASE(type) \ type: #type, #define TYPENAME_POINTER_CASE(type) \ TYPENAME_CASE(type) \ TYPENAME_CASE(type *) \ #define TYPENAME_UNSIGNED_CASE(type) \ TYPENAME_POINTER_CASE(type) \ TYPENAME_POINTER_CASE(unsigned type) \ #define TYPENAME(x) _Generic((x), \ TYPENAME_POINTER_CASE(_Bool) \ TYPENAME_UNSIGNED_CASE(char) \ TYPENAME_UNSIGNED_CASE(short) \ TYPENAME_UNSIGNED_CASE(long) \ TYPENAME_UNSIGNED_CASE(long long) \ TYPENAME_POINTER_CASE(float) \ TYPENAME_POINTER_CASE(double) \ TYPENAME_POINTER_CASE(long double) \ TYPENAME_POINTER_CASE(float _Complex) \ TYPENAME_POINTER_CASE(double _Complex) \ TYPENAME_CASE(void *) \ default: "other" \ ) int main(int argc, char * argv[]) { double _Complex c; double _Complex * p = &c; puts(TYPENAME(p)); return 0; }
{ warning 点睛呀, GCC 亲自指出自己的 BUG}
正文 - _Generic 实现函数重载
#include <stdio.h> void foo_void(void) { printf("void\n"); } void foo_int(int c) { printf("int: %d\n", c); } void foo_char(char c) { printf("char: %c\n", c); } void foo_double(double c) { printf("double: %f\n", c); } void foo_double_int(double c, int d) { printf("double: %f, int: %d\n", c, d); } #define foo(...) \ SELECT(__VA_ARGS__)(__VA_ARGS__) #define CONCAT(X, Y) CONCAT_(X, Y) #define CONCAT_(X, Y) X ## Y #define SELECT(...) \ CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__) #define SELECT_0() \ foo_void #define SELECT_1(_1) _Generic((_1), \ int: foo_int, \ char: foo_char, \ double: foo_double \ ) #define SELECT_2(_1, _2) _Generic((_1), \ double: _Generic((_2), \ int: foo_double_int \ ) \ ) #define ARGN(...) ARGN_(__VA_ARGS__) #define ARGN_(_0, _1, _2, /*...*/ N, ...) N #define NARG(...) \ ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0) #define HAS_COMMA(...) \ ARGN(__VA_ARGS__, 1, 1, 0) #define SET_COMMA(...) , #define COMMA(...) SELECT_COMMA( \ HAS_COMMA(__VA_ARGS__), \ HAS_COMMA(__VA_ARGS__ ()), \ HAS_COMMA(SET_COMMA __VA_ARGS__), \ HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \ ) #define SELECT_COMMA(_0, _1, _2, _3) \ SELECT_COMMA_(_0, _1, _2, _3) #define SELECT_COMMA_(_0, _1, _2, _3) \ COMMA_ ## _0 ## _1 ## _2 ## _3 #define COMMA_0000 , #define COMMA_0001 #define COMMA_0010 , #define COMMA_0011 , #define COMMA_0100 , #define COMMA_0101 , #define COMMA_0110 , #define COMMA_0111 , #define COMMA_1000 , #define COMMA_1001 , #define COMMA_1010 , #define COMMA_1011 , #define COMMA_1100 , #define COMMA_1101 , #define COMMA_1110 , #define COMMA_1111 , int main(int argc, char** argv) { foo(); foo(7); foo(10.12); foo(12.10, 7); foo((char)'s'); return 0; }
{
代码脉络可以详细参照下面 1和 2 讨论. 思索大师的思维 ~
1. https://stackoverflow.com/questions/479207/how-to-achieve-function-overloading-in-c?rq=1
2. https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
}
对于上面 put.c 演示的 COMMA 宏, 我们用下面一段 demo (define.c)来简单拆解其中一部分思路
#include <stdio.h> // https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/ #define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) _16 #define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) // // _TRIGGER_PARENTHESIS_ __VA_ARGS__ (/* empty */) // #define _TRIGGER_PARENTHESIS_(...) , #define HAS_COMMA_EMPTY(...) HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__(/* empty */)) int main(int argc, char * argv[]) { printf("%d\n", HAS_COMMA()); printf("%d\n", HAS_COMMA(1)); printf("%d\n", HAS_COMMA(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)); printf("%d\n", HAS_COMMA(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)); printf("%d\n", HAS_COMMA(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)); printf("%d\n", HAS_COMMA(,)); printf("%d\n", HAS_COMMA_EMPTY()); return 0; }
细细品味推荐的链接. 专业没那么简单 ~
后记 - 简单并不简单
下次再见, 有问题欢迎沟通交流指正 ~ 祝好运
音乐 : 约在春天相见