C 语言中的模板编程

在 C 语言中,模板编程通常用预处理器进行,以避免代码冗余并提高可维护性。本文将介绍几种常见的 C 语言模板实现方法,并通过示例代码进行说明。

1. 简单的 C 宏

使用 C 语言宏是一种常见的模板方法,通过预处理器将重复代码封装。在下面的示例中,我们定义了一个宏 DO_RANDOM_STUFF,用于处理不同类型的数组。


#define DO_RANDOM_STUFF(type) do {               \int i;                                       \type *p = buf;                               \for (i = 0; i < len; i++)                    \p[i] = p[i] * k;                         \
} while (0)int func(void *buf, int len, float k, int request) {if (request == INT8) DO_RANDOM_STUFF(int8_t);else if (request == INT16) DO_RANDOM_STUFF(int16_t);else if (request == INT32) DO_RANDOM_STUFF(int32_t);
}

2. 完整函数的简单 C 宏

除了宏,还可以使用宏来声明完整的函数。例如,下面的代码使用 DECLARE_FUNC 来生成不同位数的乘法函数。


#define DECLARE_FUNC(n)                          \
static void func_##n(int##n##_t *p, int len, float k) { \int i;                                       \for (i = 0; i < len; i++)                    \p[i] = p[i] * k;                         \
}DECLARE_FUNC(8)
DECLARE_FUNC(16)
DECLARE_FUNC(32)

这段代码将会创建 func_8(),func_16() 和 func_32() 函数。

3. 替代函数创建

在某些情况下,通过模板减少输入的冗余是出于性能考虑。以下是一个具体的例子:


int process_image(void *img, int width, int height, const int n) {int x, y;for (y = 0; y < height; y++) {for (x = 0; x < width; x++) {if (n == 0) foo(img, x, y);else if (n == 1) bar(img, x, y);else baz(img, x, y);}}
}

我们可以通过使用 inline 函数创建特定逻辑的 wrapper,避免在每次像素处理时都进行函数调用。该方法可以让编译器更容易优化代码:

static inline int process_image(void *img, int width, int height, const int n)
{int x, y;for (y = 0; y < height; y++) {for (x = 0; x < width; x++) {if      (n == 0) foo(img, x, y);else if (n == 1) bar(img, x, y);else             baz(img, x, y);}}
}int process_image_foo(void *img, int width, int height)
{return process_image(img, width, height, 0);
}int process_image_bar(void *img, int width, int height)
{return process_image(img, width, height, 1);
}int process_image_baz(void *img, int width, int height)
{return process_image(img, width, height, 2);
}

4. 混合完整函数机制和宏

如果发现前面的例子中冗余过多,可以通过使用宏来减轻负担:


#define DECLARE_PROCESS_IMAGE_FUNC(name, n)      \
int process_image_##name(void *img, int width, int height) { \return process_image(img, width, height, n); \
}DECLARE_PROCESS_IMAGE_FUNC(foo, 0)
DECLARE_PROCESS_IMAGE_FUNC(bar, 1)
DECLARE_PROCESS_IMAGE_FUNC(baz, 2)

这种方法结合了宏和函数,减少了冗余,同时又保持了可读性。

5. 使用外部文件

另一种方法是将模板代码放在外部文件中。以下示例展示了如何通过包括不同的预处理器指令,生成不同类型的函数:

逻辑很简单;让我们从caller.c的内容开始:


#include <stdint.h>
#define TEMPLATE_U16
#include "evil_template.c"
#undef TEMPLATE_U16
#define TEMPLATE_U32
#include "evil_template.c"
#undef TEMPLATE_U32
#define TEMPLATE_FLT
#include "evil_template.c"
#undef TEMPLATE_FLT
#define TEMPLATE_DBL
#include "evil_template.c"
#undef TEMPLATE_DBL

在 evil_template.c 中,可以定义不同类型的模板:

#if defined(TEMPLATE_U16)#    define RENAME(N)   N ## _u16
#    define TYPE        uint16_t
#    define SUM_TYPE    uint32_t
#    define XDIV(x, n)  (((x) + ((1<<(n))-1)) >> (n))#elif defined(TEMPLATE_U32)#    define RENAME(N)   N ## _u32
#    define TYPE        uint32_t
#    define SUM_TYPE    uint64_t
#    define XDIV(x, n)  (((x) + ((1<<(n))-1)) >> (n))#elif defined(TEMPLATE_FLT)#    define RENAME(N)   N ## _flt
#    define TYPE        float
#    define SUM_TYPE    float
#    define XDIV(x, n)  ((x) / (float)(1<<(n)))#elif defined(TEMPLATE_DBL)#    define RENAME(N)   N ## _dbl
#    define TYPE        double
#    define SUM_TYPE    double
#    define XDIV(x, n)  ((x) / (double)(1<<(n)))#endifTYPE RENAME(func)(const TYPE *p, int n)
{int i;SUM_TYPE sum = 0;for (i = 0; i < 1<<n; i++)sum += p[i];return XDIV(sum, n);
}#undef RENAME
#undef TYPE
#undef SUM_TYPE
#undef XDIV

如果模板中包含一组函数,这种方法可能会比较方便,但对许多人来说却是万恶之源。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

C# 日志框架 NLog、log4net 和 Serilog对比

文章目录 前言NLog、log4net 和 Serilog 三个框架的详细对比:一、NLog优点:缺点:二、 log4net优点缺点三、Serilog优点缺点四、Serilog使用举例总结前言 NLog、log4net 和 Serilog 三个框架的详细对比: NLog、log4net 和 Serilog 是三个非常流行的 .NET 日志框架,它们各自…

本地缓存库分析(四):fastcache

文章目录 本系列前言设计索引和数组怎么判断是否被覆盖其他问题 源码走读数据结构setget 总结 本系列 本地缓存库分析&#xff08;一&#xff09;&#xff1a;golang-lru本地缓存库分析&#xff08;二&#xff09;&#xff1a;bigcache本地缓存库分析&#xff08;三&#xff0…

安科瑞5G基站直流叠光监控系统-安科瑞黄安南

基站现状和趋势 5G基站是专门提供5G网络服务的公用移动通信基站。5G基站主要用于提供5G空口协议功能&#xff0c;支持与用户设备、核心网之间的通信。按照逻辑功能划分&#xff0c;5G基站可分为5G基带单元与5G射频单元&#xff0c;二者之间可通过CPRI或eCPRI接口连接。 2019年…

Pr 视频效果:过渡

效果面板/视频效果/过渡 Video Effects/Transition Adobe Premiere Pro 的视频效果中&#xff0c;过渡 Transition效果组用于创建单个剪辑内过渡效果的一组视频效果。这些效果可以增强视频的视觉连贯性&#xff0c;添加创意性的视觉转换&#xff0c;为观众提供流畅的观看体验。…

DataX 的安装配置和使用 (详细版)

1&#xff0c;上传解压 1&#xff0c;开始上传安装包到你虚拟机上放置安装包的文件夹 2&#xff0c;开始解压 ,配置环境变量 1、上传 /opt/modules 2、解压 tar -zxvf datax.tar.gz -C /opt/installs 3、修改 vi /etc/profile 配置环境变量&#xff1a; export DAT…

zookeeper安装

安装之前&#xff1a;先关闭三台服务器的防火墙&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; systemctl stop firewalld systemctl disable firewalld 1)上传 /opt/modules下面 2&#xff09;解压 /opt/installs下面 tar -zxvf zookeeper-3.4.10.tar.gz …

Nature文章《deep learning》文章翻译

这篇文章是对Nature上《deep learning》文章的翻译。原作者 Yann LeCun, Yoshua Bengio& Geoffrey Hinton。 这篇文章的中心思想是深入探讨深度学习在机器学习中的革命性贡献&#xff0c;重点介绍其在特征学习、监督学习、无监督学习等方面的突破&#xff0c;并阐述其在图…

动态规划—整数拆分

class Solution {public int integerBreak(int n) {int[] dp new int[n1];dp[2] 1;for(int i 3; i< n; i){for(int j 1; j< i/2; j){//j拆i&#xff0c;只需要遍历到 i/2 就可以&#xff0c;后面没有必要遍历dp[i] Math.max(dp[i], Math.max(j*(i-j) , j*dp[i-j]));…

OceanBase V4.3.3,首个面向实时分析场景的GA版本发布

在10月23日举办的 OceanBase年度发布会 上&#xff0c;我们怀着激动之情&#xff0c;正式向大家宣布了 OceanBase 4.3.3 GA 版的正式发布&#xff0c;这也是OceanBase 为实时分析&#xff08;AP&#xff09;场景打造的首个GA版本。 2024 年初&#xff0c;我们推出了 4.3.0 版本…

儿童安全座椅行业全面深入分析

儿童安全座椅就是一种专为不同体重&#xff08;或年龄段&#xff09;的儿童设计&#xff0c;将孩子束缚在安全座椅内&#xff0c;能有效提高儿童乘车安全的座椅。欧洲强制性执行标准ECE R44/03的定义是&#xff1a;能够固定到机动车辆上&#xff0c;带有ISOFIX接口、LATCH接口的…

算法笔记:Day-09(初始动态规划)

509. 斐波那契数 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n) F(n - 1) F(n - 2)&#xff0c;其中 …

HTTP和HTTPS 的作用和应用场景 (python 爬虫简单入门)

HTTP和HTTPS HTTP HTTP协议&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;&#xff1a;是一种发布和接收 HTML页面的方法。 HTTP的端口号为80 HTTPS HTTPS&#xff08;Hypertext Transfer Protocol over Secure Socket Layer&#xff09;…

Java多线程编程(三)一>详解synchronized, 死锁,wait和notify

目录&#xff1a; 一.synchronized 的使用&#xff1a; 二. 常见死锁情况&#xff1a; 三 .如何避免死锁&#xff1a; 四.wait和notify 一.synchronized 的使用&#xff1a; 我们知道synchronized锁具有互斥的特点&#xff1a; synchronized 会起到互斥效果, 某个线程…

linux入门——“初识make”

make是linux中的自动化构建工具&#xff0c;一般来说系统会自带make&#xff0c;如果没有&#xff0c;那么可以使用命令“sudo apt install -y make”来安装。 1.初识make make使用的前提是维护makefile/Makefile文件&#xff0c;需要在自己的目录下自己创建。 我在此目录下创…

【K8S系列】Kubernetes 中 Pod 无法通过 Service 名称访问服务的 DNS 解析失败问题【已解决】

在 Kubernetes 中&#xff0c;Service 提供了一种稳定的方式&#xff0c;通过名称访问一组 Pod。当其他 Pod 无法通过 Service 名称访问服务&#xff0c;并且出现 DNS 解析失败时&#xff0c;通常会导致应用无法正常工作。本文将详细分析此问题的常见原因及其解决方案。 一、问…

关于分布式事务,你知道多少?如何落地?

很多人估计会说&#xff0c;我在项目中完全没有涉及到过分布式事务&#xff0c;而面试官老喜欢问&#xff0c;真TM烦&#xff01; 本文就来聊聊分布式事务&#xff0c;有哪些方案和实现。文章有点长&#xff0c;可以先收藏&#xff0c;有时间了慢慢看。 什么是事务&#xff1f;…

SIwave:释放 Resonant Mode Solver 的强大功能

SIwave 是一种电源完整性和信号完整性工具。本文的重点是 Resonant 模式求解器。 进行谐振计算的主要原因是确定 Powerplane 中 Cap 去耦的最佳位置。Powerplane 的大小由最大预期电流和允许的最大电压降决定。然而&#xff0c;即使是最好的设计也没有足够的电容来将宽带频谱的…

【VS+QT】联合开发踩坑记录

0. 写在前面 因为目前在做自动化产线集成软件开发相关的工作&#xff0c;需要用到QT&#xff0c;所以选择了VS联合开发&#xff0c;方便调试。学习QT的过程中也踩了很多坑&#xff0c;在此记录一下&#xff0c;提供给各位参考。 1. 环境配置 Win11Visual Studio 2019Qt 5.12…

【LeetCode】每日一题 2024_11_1 超级饮料的最大强化能量(DP)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 题目&#xff1a;超级饮料的最大强化能量 代码与解题思路 先读题&#xff1a; 题目给了两个数组&#xff0c;长度为 n&#xff0c;题目要求在 n 个小时内选择饮料&#xff0c;一个小时可以选一瓶&#x…

IBM服务器修改IMM的IP方法

服务器设备&#xff1a;IBM x3550 M4 Server IMM默认IP地址&#xff1a;192.168.70.125 用户名&#xff1a;USERID 密码&#xff1a;PASSW0RD&#xff08;注意是零0&#xff09; 1.服务器开机按F1进入BIOS界面 2.进入System Settings 3.进入Integrated Management Module 4.…