引言 - _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 ;
}

{ 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__) , , , )
#define HAS_COMMA(...) \
ARGN(__VA_ARGS__, , , ) #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();
foo(10.12);
foo(12.10, );
foo((char)'s'); return ;

{

代码脉络可以详细参照下面 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());
printf("%d\n", HAS_COMMA(, , , , , , , , , , , , , , ));
printf("%d\n", HAS_COMMA(, , , , , , , , , , , , , , , ));
printf("%d\n", HAS_COMMA(, , , , , , , , , , , , , , , , )); printf("%d\n", HAS_COMMA(,));
printf("%d\n", HAS_COMMA_EMPTY());
return ;
}

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

后记 - 简单并不简单

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

  音乐 : 约在春天相见 

C 基础 _Generic 泛型应用的更多相关文章

  1. [.net 面向对象编程基础] (18) 泛型

    [.net 面向对象编程基础] (18) 泛型 上一节我们说到了两种数据类型数组和集合,数组是指包含同一类型的多个元素,集合是指.net中提供数据存储和检索的专用类. 数组使用前需要先指定大小,并且检 ...

  2. 黑马程序员:Java基础总结----泛型(高级)

    黑马程序员:Java基础总结 泛型(高级)   ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 泛型(高级) 泛型是提供给javac编译器使用的,可以限定集合中的输入类型 ...

  3. Java基础:泛型

    Java的泛型是什么呢, 就是类型的參数化,这得类型包含方法參数和返回值.也就是原本该是确定类型的地方换成了变量,把类型的确定时间向后延迟了. 在之前,学过"重载"的概念,重载是什 ...

  4. java基础(9) - 泛型解析

    泛型 定义简单的泛型类 泛型方法 /** * 1.定义一个泛型类 * 在类名后添加类的泛型参数 <T> * 泛型类里面的所有T会根据创建泛型类时传入的参数确定类型 * 2.定义泛型方法 * ...

  5. Java基础之泛型

    泛型: (1)为什么会出现泛型? 因为集合存放的数据类型不固定,故往集合里面存放元素时,存在安全隐患, 如果在定义集合时,可以想定义数组一样指定数据类型,那么就可以解决该类安全问题. JDK1.5后出 ...

  6. 【Java基础】泛型

    Num1:请不要在新代码中使用原生类型 泛型类和接口统称为泛型.每种泛型定义一组参数化的类型,构成格式是:类或接口名称,接着用<>把对应于泛型形式类型的参数的实际参数列表括起来.比如:Li ...

  7. C#基础之泛型

    1.泛型的本质 泛型的好处不用多说,在.NET中我看到有很多技术都是以泛型为基础的,不过因为不懂泛型而只能对那些技术一脸茫然.泛型主要用于集合类,最主要的原因是它不需要装箱拆箱且类型安全,比如很常用的 ...

  8. 黑马程序员——【Java基础】——泛型、Utilities工具类、其他对象API

    ---------- android培训.java培训.期待与您交流! ---------- 一.泛型 (一)泛型概述 1.泛型:JDK1.5版本以后出现的新特性,用于解决安全问题,是一个类型安全机制 ...

  9. Java基础之泛型——使用二叉树进行排序(TryBinaryTree)

    控制台程序. 1.实现针对容器类的基于集合的循环 为了让容器类类型的对象能够在基于集合的for循环中可用,类必须并且只需要满足一个要求——必须实现泛型接口java.lang.Iterable<& ...

随机推荐

  1. 杭电-------2044一只小蜜蜂(C语言写)

    #include<stdio.h> ] = { }; long long divide(int n) { ) { ; } ) { return a[n]; } ) + divide(n - ...

  2. Day7前端学习之路——多栏布局

    该文章主要讨论两栏布局和三栏布局,三栏布局包括很著名的圣杯布局和双飞翼布局 一.两栏布局的七种方法(左边固定,右边自适应) 原理: block水平元素宽度能够跟随父容器调节的流动特性,block级别的 ...

  3. Node.js的__dirname,__filename,process.cwd(),./的一些坑

    参考博客:https://github.com/jawil/blog/issues/18

  4. Spring ioc(4)---如何解决循环依赖

    前面说到对象的创建,那么在创建的过程中Spring是怎么又是如何解决循环依赖的呢.前面提到有个三级缓存.就是利用这个来解决循环依赖.打个比方说实例化A的时候,先将A创建(早期对象)放入一个池子中.这个 ...

  5. Android中TimePicker时间选择器的使用和获取选择的时和分

    场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 将布局改 ...

  6. python+selenium自动化测试,浏览器最大化报错解决方法

    此处以谷歌浏览器为例 [问题1]缺少chrome驱动,webdriver调用谷歌浏览器的时候就报错了,如下图: [原因分析]缺少谷歌驱动程序 [解决办法] 1.查看本地安装chrome浏览器版本 2. ...

  7. C#中WinFrom保存文件SaveFileDialog类的使用方法

    C#中WinFrom保存文件SaveFileDialog类的使用方法 使用的命名空间是:System.Windows.Forms; 常用属性:   Title:保存对话框的标题,默认为"另存 ...

  8. 常用类String的总结

    /* String:字符串,使用一对""引起来表示. 1.String声明为final的,不可被继承 2.String实现了Serializable接口:表示字符串是支持序列化的. ...

  9. #《H.264和MPEG-4视频压缩》# 一. 色彩空间

    多数的数字视频应用需要播放彩色的视频信号,所以需要捕获和重现颜色信息.一幅黑白图像的每一个采样点只需要一个像素表示明暗或亮度,而在彩色图像中至少需要3个像素来表示每个像素的色彩.表示亮度和色彩的不同方 ...

  10. 建造者模式(Builder)——从组装电脑开始

    建造者模式(Builder)--从组装电脑开始 建造者模式概括起来就是将不同独立的组件按照一定的条件组合起来构成一个相对业务完整的对象.调用者无需知道构造的过程. 我们从组装电脑开始 让我们从买组装电 ...