C语言实现Go的defer功能

之前笔者写了一篇博文C++实现Go的defer功能,介绍了如何在C++语言中实现Go的defer功能,那在C语言中是否也可以实现这样的功能呢?本文就将介绍一下如何在C语言中实现Go的defer功能。

我们还是使用C++实现Go的defer功能中的示例:

void test()
{FILE* fp = fopen("test.txt", "r");if (nullptr == fp)return;defer(fclose(fp));if (...){return;}if (...){return;}if (...){return;}
}

为了实现该功能,需要借助编译器的扩展功能,GCC/Clang的cleanup属性,微软目前的编译器不支持该扩展属性,所以本文介绍的方法不适用于微软编译器。

一、GCC编译器

GCC的cleanup属性,可以参见GCC官方文档var-attr-cleanup-cleanup_function或者文档index-cleanup-variable-attribute。

GCC下的cleanup属性用法:

static void foo(int *p) { printf("foo called\n"); }
static void bar(int *p) { printf("bar called\n"); }
void baz(void)
{int x __attribute__((cleanup(foo)));{ int y __attribute__((cleanup(bar))); }
}

GCC支持函数嵌套,cleanup属性也支持不带参数的函数调用:

void test()
{void ff() { printf("ff called\n"); }int z __attribute__((cleanup(ff)));
}

所以要实现前面示例中的写法也很简单,使用宏实现一个嵌套函数来执行自定义的表达式,再定义一个变量带上cleanup属性即可:

#define __CONCAT0__(a, b) a##b
#define __CONCAT__(a, b) __CONCAT0__(a, b)
#define __DEFER__(exp, COUNTER)                                                \void __CONCAT__(_DEFER_FUNC_, COUNTER)() { exp; }                            \int __CONCAT__(_DEFER_VAR_, COUNTER)                                         \__attribute__((cleanup(__CONCAT__(_DEFER_FUNC_, COUNTER))))
#define defer(exp) __DEFER__(exp, __COUNTER__)

二、Clang编译器

Clang的cleanup属性,可以参见Clang官方文档。由于Clang目前不支持函数嵌套,但提供了一种叫做Block类型的语法扩展,参见文档,有点类似C++的Lambda 表达式。

Clang的cleanup属性调用的函数,必须带有一个参数,否则会报错:

error: 'cleanup' function 'ff' must take 1 parameter

Clang需要写成:

void ff(int* p) { printf("ff called\n"); }
void test()
{int z __attribute__((cleanup(ff)));
}

但是前面示例的写法是:

defer(fclose(fp));

函数内部的表达式无法写到外部去,所以就需要借助Clang的Block类型语法扩展了:

//需要先定义一个外部函数,且必须带一个参数,这个参数的类型是一个`Block`类型
static inline void __cleanup__(void (^*fn)(void)) { (*fn)(); }
void test() {// 定义一个ff变量,该变量是一个`Block`类型void (^ff)() = ^{printf("ff called\n");};// 可以像函数一样使用,直接调用ff();// 定义一个`Block`类型的x变量,它的值一个是表达式,且拥有`cleanup`属性,调用外部函数__cleanup____attribute__((cleanup(__cleanup__))) void (^x)() = ^{printf("x called\n");};
}

编译时必须使用-fblocks参数进行编译,同时链接BlocksRuntime库。Ubuntu下可以使用下面的命令安装BlocksRuntime库:

sudo apt install libblocksruntime-dev

如果不安装也可以自己定义一下变量:

void *_NSConcreteGlobalBlock[32] = {0};
void *_NSConcreteStackBlock[32] = {0};

注意:MinGW下目前没有可用的BlocksRuntime库,在链接时会报错:

undefined reference to `__imp__NSConcreteGlobalBlock'
undefined reference to `__imp__NSConcreteStackBlock'

这就必须自己定义一下变量了:

void *__imp__NSConcreteGlobalBlock[32] = {0};
void *__imp__NSConcreteStackBlock[32] = {0};

三、跨GCC/Clang编译器

综上,为了让Clang与GCC都能支持前面示例中的写法,可以使用宏来分别处理:

#define __CONCAT0__(a, b) a##b
#define __CONCAT__(a, b) __CONCAT0__(a, b)#if defined(__clang__)
#if defined(__MINGW32__) || defined(__MINGW64__)
void *__imp__NSConcreteGlobalBlock[32] = {0};
void *__imp__NSConcreteStackBlock[32] = {0};
#elif defined(__LINUX__)
void *_NSConcreteGlobalBlock[32] = {0};
void *_NSConcreteStackBlock[32] = {0};
#endif
static inline void __cleanup__(void (^*fn)(void)) { (*fn)(); }
#define defer(exp)                                                             \__attribute__((cleanup(__cleanup__))) void (                                 \^__CONCAT__(_DEFER_VAR_, __COUNTER__))(void) = ^{                        \exp;                                                                       \}
#elif defined(__GNUC__)
#define __DEFER__(exp, COUNTER)                                                \void __CONCAT__(_DEFER_FUNC_, COUNTER)() { exp; }                            \int __CONCAT__(_DEFER_VAR_, COUNTER)                                         \__attribute__((cleanup(__CONCAT__(_DEFER_FUNC_, COUNTER))))
#define defer(exp) __DEFER__(exp, __COUNTER__)
#else
#error "compiler not support!"
#endif

下面给出完整代码:

main.c:

#include <stdio.h>
#include <stdlib.h>#define __CONCAT0__(a, b) a##b
#define __CONCAT__(a, b) __CONCAT0__(a, b)#if defined(__clang__)
#if defined(__MINGW32__) || defined(__MINGW64__)
void *__imp__NSConcreteGlobalBlock[32] = {0};
void *__imp__NSConcreteStackBlock[32] = {0};
#elif defined(__LINUX__)
void *_NSConcreteGlobalBlock[32] = {0};
void *_NSConcreteStackBlock[32] = {0};
#endif
static inline void __cleanup__(void (^*fn)(void)) { (*fn)(); }
#define defer(exp)                                                             \__attribute__((cleanup(__cleanup__))) void (                                 \^__CONCAT__(_DEFER_VAR_, __COUNTER__))(void) = ^{                        \exp;                                                                       \}
#elif defined(__GNUC__)
#define __DEFER__(exp, COUNTER)                                                \void __CONCAT__(_DEFER_FUNC_, COUNTER)() { exp; }                            \int __CONCAT__(_DEFER_VAR_, COUNTER)                                         \__attribute__((cleanup(__CONCAT__(_DEFER_FUNC_, COUNTER))))
#define defer(exp) __DEFER__(exp, __COUNTER__)
#else
#error "compiler not support!"
#endifvoid ff(int *p) { printf("ff\n"); }void myfree(char **p) {if (*p) {printf("myfree:%p\n", *p);free(*p);}
}int main(int argc, char *argv[]) {FILE *fp = fopen("test.txt", "r");defer(printf("close file\n"); if (fp) fclose(fp));char *__attribute__((cleanup(myfree))) p = malloc(10);char *p1 [[gnu::cleanup(myfree)]] = malloc(10);int a [[gnu::cleanup(ff)]] = 10;defer(printf("call defer1, a = %d\n", a));a = 20;defer(printf("call defer2, a = %d\n", a));return 0;
}

CMakeList.txt

cmake_minimum_required(VERSION 3.25.0)
project(t)if(CMAKE_C_COMPILER_ID MATCHES "Clang")
add_compile_options(-gdwarf-4-fblocks
)
endif()
aux_source_directory(. SRC)
add_executable(${PROJECT_NAME} ${SRC})

运行结果如下:

在这里插入图片描述

有了编译器的这一扩展属性的支持,写C语言也可以减轻程序员释放资源的心智负担了,不用担心某个分支遗忘了释放资源了。而且也可以像C++那样写Lambda 表达式了,而且默认是全部捕获函数内的所有变量。

以上代码在MinGW下使用GCC 14.2/Clang 18.1.8、Ubuntu下使用GCC 11.4/Clang 17.0.6、MacOS下使用Clang 9.0编译运行通过。

如果本文对你有帮助,欢迎点赞收藏!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/457557.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

医院信息化与智能化系统(9)

医院信息化与智能化系统(9) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应的…

改进YOLOv8系列:引入低照度图像增强网络Retinexformer | 优化低光照目标检测那题

改进YOLOv8系列:引入低照度图像增强网络Retinexformer | 优化低光照目标检测那题 🚀论文研究概括🚀加入到网络中的理论研究🚀需要修改的代码1 🍀🍀Retinexformer 代码2🍀🍀tasks里引用🚀创建yaml文件🚀测试是否创建成功前言:这篇论文提出了一种用于低光图像…

STM32应用详解(10)I2C总线初始化

文章目录 前言一、I2C总线初始化二、程序源码与详解1.I2C初始化2.I2C端口初始化及设置IO端口工作模式3.函数I2C_Init4.函数I2C_Cmd5.使能APB1外设时钟6.I2C通信时序图 前言 介绍STM32的I2C总线初始化&#xff0c;给出了代码详解。《i2c.h》文件&#xff0c;由用户编写。定义了…

系统聚类比较——最短距离法、最长距离法、重心法和类平均法

系统聚类概述 系统聚类&#xff0c;又称分层聚类法&#xff0c;是一种用于分析数据的统计方法&#xff0c;在生物学、分类学、社会网络等领域有广泛应用。以下是对系统聚类的详细概述&#xff1a; 一、基本思想 系统聚类的基本思想是将每个样品&#xff08;或数据点&#xf…

OAK相机的RGB-D彩色相机去畸变做对齐

▌低畸变标准镜头的OAK相机RGB-D对齐的方法 OAK相机内置的RGB-D管道会自动将深度图和RGB图对齐。其思想是将深度图像中的每个像素与彩色图像中对应的相应像素对齐。产生的RGB-D图像可以用于OAK内置的图像识别模型将识别到的2D物体自动映射到三维空间中去&#xff0c;或者产生的…

深入理解Python异常处理机制

在Python编程中&#xff0c;异常处理是一个非常重要的概念。它可以帮助我们捕获程序运行过程中出现的错误&#xff0c;防止程序崩溃&#xff0c;并提供友好的错误信息。本文将详细讲解Python的异常处理机制&#xff0c;并提供丰富的代码示例&#xff0c;帮助您更好地理解和应用…

【Spring MVC】响应结果和设置

​ 我的主页&#xff1a;2的n次方_ 1. 返回静态页面 先创建一个 html 页面 ​ 如果还按照之前的方式进行返回的话&#xff0c;返回的并不是一个 html 页面 RequestMapping("/response") RestController public class ResponseController {RequestMapping(&quo…

React基础使用教程

初识JSX React主要使用的就是jsx语法来编写dom&#xff0c;所以我们要先认识jsx&#xff0c;然后再开始学习两者相结合jsx其实就是在JS中编写HTML的一种写法编写jsx要注意以下几个规则&#xff1a; 定义虚拟DOM时&#xff0c;不要写引号标签中混入JS表达式时要用{}样式的类名指…

2024 Rust现代实用教程:1.3获取rust的库国内源以及windows下的操作

文章目录 一、使用Cargo第三方库1.直接修改Cargo.toml2.使用cargo-edit插件3.设置国内源4.与windows下面的rust不同点 参考 一、使用Cargo第三方库 1.直接修改Cargo.toml rust语言的库&#xff1a;crate 黏贴至Cargo.toml 保存完毕之后&#xff0c;自动下载依赖 拷贝crat…

形态学-闭运算

目录 依赖库显示图像的函数读取图像转换为灰度图像应用二值化阈值处理创建结构元素应用形态学闭运算显示结果 依赖库 首先&#xff0c;我们需要导入必要的库&#xff1a; import cv2 import numpy as npcv2 是OpenCV的Python接口&#xff0c;用于图像处理。numpy 是一个用于科…

在时间敏感网络中启用网络诊断:协议、算法和硬件

英文论文标题&#xff1a;Enabling Network Diagnostics in Time-Sensitive Networking: Protocol, Algorithm, and Hardware 作者信息&#xff1a; Zeyu Wang, Xiaowu He, Xiangwen Zhuge, Shen Xu, Fan Dang, Jingao Xu, Zheng Yang所属机构&#xff1a;清华大学软件学院和…

Pytorch笔记--RuntimeError: NCCL communicator was aborted on rank 3.

1--分布式并行训练&#xff0c;出现以下bug&#xff1a; [E ProcessGroupNCCL.cpp:719] [Rank 3] Watchdog caught collective operation timeout: WorkNCCL(SeqNum1721483, OpTypeALLREDUCE, Timeout(ms)1800000) ran for 1805695 milliseconds before timing out. RuntimeErr…

Docker:安装 Syslog-ng 的技术指南

1、简述 Syslog-ng 是一种流行的日志管理工具&#xff0c;能够集中处理和分析日志。通过 Docker 安装 Syslog-ng 可以简化部署和管理过程。本文将介绍如何使用 Docker 安装 Syslog-ng&#xff0c;并提供一个 Java 示例来展示如何将日志发送到 Syslog-ng。 2、安装 2.1 创建…

[mysql]子查询的概述和分类及单行子查询

子查询引入 查询的基本结构已经给大家了,子查询里面也是有一些新的内容,子查询其实就是在查询中嵌套另一个查询,叫嵌套查询可能大家更容易理解一点..,类似与FOR循环和FOR循环的嵌套,这一章是我们查询的最难的部分,大家 难度是查询的顶峰,多表查询和子查询是非常重要,SQL优化里…

【CUDA代码实践02】矩阵加法运算程序

文章目录 main.cu代码工作流程 matrixSum.cuhmatrixSum.cu代码结构说明总体工作流程 近年来&#xff0c;人工智能&#xff08;AI&#xff09;技术&#xff0c;尤其是大模型的快速发展&#xff0c;打开了全新的时代大门。对于想要在这个时代迅速成长并提升自身能力的个人而言&am…

k8s可以部署私有云吗?私有云部署全攻略

k8s可以部署私有云吗&#xff1f;K8S可以部署私有云。Kubernetes是一个开源的容器编排引擎&#xff0c;能够自动化容器的部署、扩展和管理&#xff0c;使得应用可以在各种环境中高效运行。通过使用Kubernetes&#xff0c;企业可以在自己的数据中心或私有云环境中搭建和管理容器…

OpenSSH_8.7 无法使用密码登录问题解决

文章目录 前言SSH 版本 配置 SSH 密码登录其它总结个人简介 前言 最近使用 Crunchbits VPS&#xff08;virt.crunchbits.com&#xff09; 时&#xff0c;由于更换电脑导致认证的 ssh 秘钥丢失&#xff0c;尝试 SSH 密码登录时遇到如下错误提示&#xff1a;Permission denied (…

探索 Python 幽默之源:pyjokes 库全解析

&#x1f680; 探索 Python 幽默之源&#xff1a;pyjokes 库全解析 1. 背景介绍&#xff1a;为何选择 pyjokes&#xff1f; 在紧张的编程工作中&#xff0c;幽默是一种有效的缓解压力的方式。pyjokes 是一个专为程序员设计的 Python 库&#xff0c;它提供了丰富的单行笑话&am…

【Dv2Admin】Django配置线上ws反向代理

在 Web 应用程序的部署过程中,安全性、稳定性和实时通信是开发者们普遍关注的重点。Django 是一个非常流行的 Web 框架,常与 Nginx 配合使用,以便实现反向代理、负载均衡以及 SSL 加密等功能。除此之外,实时功能(如 WebSocket)也是现代应用中经常使用的技术。 在项目中实…

Fast Simulation of Mass-Spring Systems in Rust 论文阅读

参考资料&#xff1a; Fast Simulation of Mass-Spring Systems in Rust 论文阅读&#xff1a;Fast Simulation of Mass-Spring Systems 【论文精读】讲解刘天添2013年的fast simulation of mass spring system(Projective Dynamics最早的论文) Projective Dynamics笔记(一…