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;
}

 

细细品味推荐的链接. 专业没那么简单 ~ 

 

后记 - 简单并不简单

  下次再见, 有问题欢迎沟通交流指正 ~ 祝好运 

  音乐 : 约在春天相见 

posted on 2020-02-19 12:25  喜ω欢  阅读(1071)  评论(1编辑  收藏  举报