引言 - _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. springboot 基于Tomcate的自启动流程

    Springboot 内置了Tomcat的容器,我们今天来说一下Springboot的自启动流程. 一.Spring通过注解导入Bean大体可分为四种方式,我们主要来说以下Import的两种实现方法: ...

  2. 微信小程序仿朋友圈功能开发(发布、点赞、评论等功能)

    微信小程序仿朋友圈功能开发(发布.点赞.评论等功能) 1.项目分析 项目整体分为三个部分 发布 展示 详情页 graph LR 朋友圈发布 --内容发布--> 内容展示 内容展示 --点击展示卡 ...

  3. 在React中使用WebUploader实现大文件分片上传的踩坑日记!

    前段时间公司项目有个大文件分片上传的需求,项目是用React写的,大文件分片上传这个功能使用了WebUploader这个组件. 具体交互是: 1. 点击上传文件button后出现弹窗,弹窗内有选择文件 ...

  4. vuex 状态管理 入门

    vuex是什么 是一个对 数据状态实现集中式管理 的工具.可以解决组件之间传递的问题 多组件共享状态(变量),有一个数据好多组件都用 组件数大于2,任何一个组件修改其他组件都要改变 实现组件通信 St ...

  5. Centos7没有IP地址,查看网络状态显示No suitable device found for this connection (devint match))

    今天打开虚拟机,使用 ifconfig 命令时,没有显示出 IP 地址 (更好的阅读体验可访问 这里 ) 使用 systemctl status network 命令查看网络状态 显示没有合适的网络装 ...

  6. You (oracle) are not allowed to use this program (crontab)

      检查一台ORACLE数据库服务器的crontab作业(用户为oracle,实际环境中可能为oracle.也有可能是其它用户)时,发现出现下面提示信息: $ crontab -l You (orac ...

  7. 【idea激活码】,【WebStorm激活码】,【DataGrip激活码】,【IntelliJ 全家桶系列】,【定期更新】,【第一期】

    IntelliJ IDEA.PyCharm.PhpStorm.WebStorm.RubyMide.AppCode.CLion.GoLand.DataGrip.Rider.Android Studio可 ...

  8. JS事件流、DOM事件流、IE事件处理、跨浏览器事件处理、事件对象与类型

    事件的移除 removeEventListener() 第二个参数需要指定要移除的事件句柄,不能是匿名函数,因为无法识别 想要移除成功,那么三个参数必须跟addEventListener中的三个完全一 ...

  9. mssql sqlserver 如何将一个日期数据转换为"年份-月份"的格式呢?

    摘要: 下文讲述在sqlserver数据库中,将日期数据转换为指定格式的方法分享,如下所示: 实验环境:sqlserver 2008 R2 实现思路: 实现方法1: 使用year函数和month函数获 ...

  10. Jetty配置虚拟目录,实现把web项目发布到自定义目录,指定指定上下文访问;jetty编码修改

    2019-12-31     17:28:38 一般只需要把Java站点文件夹或*.war文件拷贝到Web Server的webapps文件夹下,即可启动运行该站点,但更多时候,我们并不想拷贝站点,而 ...