typesafe_cb

callback 回调函数

什么是callback function

typesafe_cb

如图(来自维基百科),回调函数提供了一种服务,可以由用户决定使用怎么样的服务(登记回调函数)。回调函数机制,提供了很大的灵活性,可以将库函数视为中间函数,借用回调函数完成个性化的服务。本文下面的内容会使用到 ccan中的一个安全回调函数库。

typesafe_cb.h

版权:

License: CC0 (Public domain)
Author: Rusty Russell <rusty@rustcorp.com.au>
</rusty@rustcorp.com.au>

该头文件是为实现安全的回调函数所编写的一系列的宏,包括

typesafe_cb

作者在 _info文件中写道:

If an expression exactly matches a given type, it is cast to the target type, otherwise it is left alone.

其根本目的在于,避免强制使用 void*指针破坏类型检查。

typesafe_cb_cast

/*
 * 当表达式匹配给定类型时,进行类型转换
 * dest_t:目标类型
 * allow_t : 允许的类型
 * expr : 需要转换类型的表达式
 */
#define typesafe_cb_cast(dest_t, allow_t, expr)         \
    __builtin_choose_expr(                      \
        __builtin_types_compatible_p(__typeof__(0?(expr):(expr)), \
                         allow_t),          \
        (dest_t)(expr), (expr))

此处使用了两个GNU扩展

int __builtin_types_compatible_p(type_a,type_b)

如果类型相同返回 1否则返回 0

type __builtin_choose_expr(const_expr,expr_a,expr_b)

如果常量表达式值非0,则生成 expr_a,否则生成 expr_b。 这都是编译期行为。

这两个扩展的结合,可以一定程度实现泛型编程。

因此这个宏 typesafe_cb_cast的功能便很显然了。

由此产生的相关宏如下:

/*
 * 三种允许的类型,typesafe_cb_cast 的三重嵌套
 */
#define typesafe_cb_cast3(dest_t,allow_t1,allow_t2,allow_t3,expr) \
        typesafe_cb_cast(dest_t,allow_t1,                         \
                        typesafe_cb_cast(dest_t,allow_t2,         \
                                        typesafe_cast(dest_t,allow_t3,(expr))))

typesafe_cb

/*
 * 如果回调函数参数匹配就进行类型转换
 * @ret_t: 回调函数返回值类型
 * @exp_t: 回调函数期待的(指针)类型
 * @func: 需要类型转换的回调函数
 * @arg: 传给回调函数的参数
 */
#define typesafe_cb(ret_t,exp_t,func,arg) \
        typesafe_cb_cast(ret_t(*)(exp_t), \
                         ret_t(*)(__typeof__(arg)), \
                         (func))

此处调用的 typesafe_cb_cast中的类型为函数指针

ret_t(*)(exp_t) :指向返回值为ret_t类型,参数为exp_t类型的函数的函数指针

这个宏在函数只有一个参数传入时能很好的工作。

if typeof(func)==ret_t(*)(__tyoeof__(arg))
then typeof(func)--->ret_t(*)(exp_t)

typesafe_cb_preargs

/*
 * 对于有多个参数的函数,针对的是在arg之前有多个参数
 * 比如 void(*fn)(int,void*arg)
 */
#define typesafe_cb_preargs(rtype, atype, fn, arg, ...)         \
    typesafe_cb_cast(rtype (*)(__VA_ARGS__, atype),         \
             rtype (*)(__VA_ARGS__, __typeof__(arg)),   \
             (fn))

仅仅是用了变参宏从而扩展了多参数回调函数的情况,举个例子

void _register_callback(void (*fn)(int, void *arg), void *arg);
#define register_callback(fn, arg)                                  \
        _register_callback(typesafe_cb_preargs(                     \
                            void, void *,(fn), (arg), int),         \
                   (arg))

在这个例子中:

ret_t :void
allow_t: void*
fn:     void (*fn)(int, void *arg)
arg:    (arg)
...:    int

那么对于多的参数在 arg之后也就有相应的情况了

#define typesafe_cb_postargs(rtype, atype, fn, arg, ...)        \
    typesafe_cb_cast(rtype (*)(atype, __VA_ARGS__),         \
             rtype (*)(__typeof__(arg), __VA_ARGS__),   \
             (fn))

应用

ccan_info中,作者给出了这样一个例子

#include "typesafe_cb.h"
#include
#include

/*callback函数链*/
struct callback{
    struct callback *next;
    int value;
    int(*callback)(int value,void *arg);
    void *arg;
};

static struct callback *callbacks;

/*给定值与参数注册回调函数*/
static void _register_callback(int value,int(*cb)(int,void*),void *arg)
{
    struct callback *new=malloc(sizeof(*new));
    new->next=callbacks;
    new->value=value;
    new->callback=cb;
    new->arg=arg;
    callbacks=new;
}

/*
 * exp_t:int(*)(__VA_ARGS__,void*)
 * allow_t:int(*)(__VA_ARGS__,__typeof__(arg))
 */
#define register_callback(value,cb,arg)\
    _register_callback(value,\
            typesafe_cb_preargs(int ,void*,(cb),(arg),int),(arg))

/*通过value,查找callback函数*/
static struct callback *find_callback(int value)
{
    struct callback * i;
    for(i=callbacks;i;i=i->next){
        if(i->value==value){
            return i;
        }
    }
    return NULL;
}

/* 定义回调函数的宏,注意此处并没有用void*指针
 * 回调函数的类型
 * int(*cb)(int,int*arg)
 * */
#define def_callback(name,op)\
    static int name(int val,int *arg)\
    {\
        printf("%s",#op);\
        return val op *arg;\
    }

def_callback(multiply,*);
def_callback(add, +);
def_callback(divide, /);
def_callback(sub, -);
def_callback(or,|);
def_callback(and,&);
def_callback(xor, ^);
def_callback(assign, =);

int main(int argc,char*argv[])
{
    int i,run=1,num= argc > 1 ? atoi(argv[1]) : 0 ;
    for (i = 1; i < 1024;) {
        /*
         * replacement: _register_callback(i++, (int (*)(int, void *)) (((add))), (&run))
         * exp_t:   int (*)(int, void *)
         * allow_t: int (*)(int,__typeof__(arg))--->int (*)(int,int*)
         * cb_t:    int (*)(int,int*)
         */
            register_callback(i++, add, &run);
            register_callback(i++, divide, &run);
            register_callback(i++, sub, &run);
            register_callback(i++, multiply, &run);
            register_callback(i++, or, &run);
            register_callback(i++, and, &run);
            register_callback(i++, xor, &run);
            register_callback(i++, assign, &run);
        }

        printf("%i ", num);
        while (run < 56) {
            struct callback *cb = find_callback(num % i);
            if (!cb) {
                printf("-> STOP\n");
                return 1;
            }
            num = cb->callback(num, cb->arg);
            printf("->%i ", num);
            run++;
        }
        printf("-> Winner!\n");
        return 0;
}

参考

  1. http://ccodearchive.net/

Original: https://www.cnblogs.com/oasisyang/p/14423422.html
Author: OasisYang
Title: typesafe_cb

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585685/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 三种移除list中的元素(可靠)

    /** * 直接使用foreach方法移除list中的元素会抛异常 * Exception in thread "main" java.util.Concurr…

    Linux 2023年6月7日
    088
  • 多线程/哈希slot/集群

    io多线程 以前的redis是单线程模型,其实就是多路复用机制,知道多路复用的来一波6,我们在架构师课程中讲过,那么netty也有,看过老师相关课程的也应该知道。这里不多说了。 R…

    Linux 2023年5月28日
    099
  • Canal.adapter报错

    Canal.adapter报错 报错如下: 2021-09-09 15:56:33.669 [Thread-12] ERROR c.a.o.canal.adapter.launch…

    Linux 2023年6月8日
    0102
  • 分布式中灰度方案实践

    让请求在导航的服务节上点执行; 一、背景简介 分布式系统中会存在这样的开发场景,不同需求可能涉及到对同一个服务的开发,那么该服务在研发期间就会存在多个版本并行的状态,为了保持不同版…

    Linux 2023年6月14日
    0113
  • 部署apache

    1、使用DockerHub镜像 [root@master ~]# mkdir httpd_dockerfile [root@master ~]# cd httpd_dockerfi…

    Linux 2023年6月13日
    0110
  • Linux基线加固

    bash;gutter:true; 1、修改vsftp回显信息 (1)检查办法 修改vsftp回显信息: 需在安装VSFTP的情况下检查,未安装可忽略或禁用该项。 查看ftpd_b…

    Linux 2023年6月13日
    084
  • Windows 下日志保存至Linux rsyslog日志服务器

    一、 下载安装 通过https://www.rsyslog.com/windows-agent/windows-agent-download/下载客户端后,按照默认安装完成后即进行…

    Linux 2023年6月6日
    098
  • 记一次从源码泄露到getshell(二)

    0x00 前言 文章所述漏洞已经提交至漏洞平台,且所有恶意操作均已复原 0x01 源码泄露 http://www.xxx.com.cn/www.zip 老规矩拿到源码先通关关键词找…

    Linux 2023年5月28日
    0101
  • MySQL主从复制的原理和实现

    垂直扩展: 横向扩展: 复制:使每一个节点都有相同的数据集 MySQL复制的实现:使用二进制日志来实现 提高性能(负载均衡)、 实现读写分离 实现数据备份的功能(实时备份) 高可用…

    Linux 2023年6月7日
    0100
  • JAVA设计模式-工厂模式

    JAVA设计模式-工厂模式 简单工厂模式 介绍 简单工厂模式就是定义一个工厂类,工厂类提供获取实例的方法,方法会根据传入的参数不同来返回不同的实例。不同的实例基本都有共同的父类。对…

    Linux 2023年6月6日
    093
  • 服务管理与通信,基础原理分析

    涉及轻微的源码展示,可放心参考; 一、基础简介 服务注册发现是微服务架构中最基础的能力,下面将从源码层面分析实现逻辑和原理,在这之前要先来看下依赖工程的基础结构,涉及如下几个核心组…

    Linux 2023年6月14日
    0136
  • 操作系统之虚拟内存总结

    前言 操作系统为每个进程提供了一个假象:它拥有属于自己的大量的私有内存,可以有巨大的连续地址空间放入自己的代码和数据。用户程序中访问的地址都是虚拟地址,需要经过操作系统和硬件的协同…

    Linux 2023年6月7日
    0140
  • [PYTHON][BAT][SHELL] 常见易忘 python、bat、shell 脚本操作汇总(持续更新)

    BAT 脚本 1、相互调用 1.1、bat 调用 python 1.2、bat 调用 bat 2、系统相关 2.1、不关闭 2.2、读取环境变量 3、文件操作 3.1、读取 3.2…

    Linux 2023年6月8日
    0167
  • 常用命令记录

    npm仓库查看和修改 npm config set registry https://registry.npm.taobao.org #设置使用淘宝提供的npm仓库 npm con…

    Linux 2023年6月14日
    070
  • Flink 如何分流数据

    场景 分流方式 如何分流 使用Filter分流 使用Split分流 使用Side Output分流 场景 获取流数据的时候,通常需要根据所需把流拆分出其他多个流,根据不同的流再去作…

    Linux 2023年6月7日
    0131
  • 20191223-Exp3-免杀原理

    Exp3-免杀原理 姓名:张俊怡 学号:20191223 课程:网络对抗技术 一、实践内容 方法 正确使用msf编码器,使用msfvenom生成如jar之类的其他文件 veil,加…

    Linux 2023年6月8日
    0122
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球