使用预处理命令实现C的范型编程

一、引言

Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters.

范型编程在1973年的ML语言中首次提出,是在C语言之后诞生的,你可以轻易的在C++/Java等语言里实现范型,但是C语言却没有提供实现范型的工具,使用C语言实现一组范型的函数需要使用一些技巧。

范型编程实现的一组函数会根据用户提供的不同类型进行实例化,在C语言中,我们能想到的就是使用预处理命令来达到这个效果。

二、预处理命令

在我的上一篇文章善用C语言预处理命令中,比较简单的介绍了预处理命令的一些使用技巧,我们在这里使用的是##符号。比如下面这个例子

#define GENERIC_FUNC(TYPE)            void func##TYPE() {...}

GENERIC_FUNC(int)
GENERIC_FUNC(float)

通过这种方式在代码中使用预处理的代码,根据不同的类型自动生成不同的代码,这就达到了范型编程的要求。

三、命名约定

使用这种技巧使得创建范型抽象数据类型成为可能,不过这种灵活性是需要付出代价的,使用者需要自己承担一些额外的责任。

  1. 采用一种命名约定,避免不同类型间的同样功能的函数发生冲突。
  2. 使用约定好的规则调用一组类型的函数,比如func_int() func_float()。
  3. 确保传入正确的数据类型。

四、一个范型的堆栈

/*
 * static array stack - generic implement
 */

#include <assert.h>

#define GENERIC_STACK(STACK_TYPE, SUFFIX, STACK_SIZE)     static STACK_TYPE stack##SUFFIX[STACK_SIZE];          static int top_element##SUFFIX = -1;                                                                        int is_empty##SUFFIX()                                {                                                         return top_element##SUFFIX == -1;                 }                                                                                                           int is_full##SUFFIX()                                 {                                                         return top_element##SUFFIX == STACK_SIZE - 1;     }                                                                                                           void push##SUFFIX(STACK_TYPE value)                   {                                                         assert(!is_full##SUFFIX());                           top_element##SUFFIX += 1;                             stack##SUFFIX[top_element##SUFFIX] = value;       }                                                                                                           void pop##SUFFIX()                                    {                                                         assert(!is_empty##SUFFIX());                          top_element##SUFFIX -= 1;                         }                                                                                                           STACK_TYPE top##SUFFIX()                              {                                                         assert(!is_empty##SUFFIX());                          return stack##SUFFIX[top_element##SUFFIX];        }

/*
 * test program for generic stack
 */

#include <stdio.h>
#include <stdlib.h>

GENERIC_STACK(int, _int, 10)
GENERIC_STACK(float, _float, 5)

int main()
{
    push_int(5);
    push_int(22);
    push_float(3.14);
    push_float(2.714);

    while(!is_empty_int()) {
        printf("Popping %d\n", top_int());
        pop_int();
    }

    while(!is_empty_float()) {
        printf("Popping %f\n", top_float());
        pop_float();
    }

    return EXIT_SUCCESS;
}

这种范型的堆栈对不同的类型创建了不同的静态数组用以表示堆栈空间,也可以看到代码中调用函数的约定规则,这些都是用户需要做的额外的工作。

五、结语

以上提供了一种在C中实现范型编程的解决方案,这种方案要求编写者与使用者提前约定好命名规则、函数调用规则,这些是获得充分的灵活性外必须要付出的代价。

原文地址:https://www.cnblogs.com/trav/p/9572042.html

时间: 2024-07-31 22:17:12

使用预处理命令实现C的范型编程的相关文章

C++范型编程 -- &lt;type_traits&gt;头文件

在type_traits头文件中定义了许多非常好玩的东西,这里对着 c++ reference 翻译一丢丢 一,helper class , std::intergral_constant template< class T, T v > struct integral_constant; 我们知道对在模板参数中的非类型参数必须为常量,所以这个东西就是可以为类型T的任意一个常量v,做出来一个特定的类型,即integral_constant<T, v>的实例.通常用来保存int 和

JDBC简介,MySQL连接,PreparedStatement 预处理命令,通配符

何须浅碧轻红色,自是花中第一流. -李清照的<鹧鸪天·桂花> JDBC 简介 我实验的MySQL数据库 配置连接MySQL驱动 数据库连接工具类 JDBC API Driver 接口 Connection 接口 DriverManager 类 Statement 接口 PreparedStatement 接口 CallableStatement 接口 ResultSet 接口 JDBC 数据库操作 测试连接示例 添加数据 查询信息 修改数据 删除数据 批处理 调用存储过程 JDBC 简介 JD

C语言预处理命令详解

一  前言 预处理(或称预编译)是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作.预处理指令指示在程序正式编译前就由编译器进行的操作,可放在程序中任何位置. 预处理是C语言的一个重要功能,它由预处理程序负责完成.当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译. C语言提供多种预处理功能,主要处理#开始的预编译指令,如宏定义(#define).文件包含(#include).条件编译(#ifdef)等.合理使用预处理功能编

C/C++预处理命令

1.预处理概述和文件包含命令 前面各章中,已经多次使用过#include命令.使用库函数之前,应该用#include引入对应的头文件.这种以#号开头的命令称为预处理命令. C语言源文件要经过编译.链接才能生成可执行程序: 1) 编译(Compile)会将源文件(.c文件)转换为目标文件.对于VC/VS,目标文件后缀为 .obj:对于GCC,目标文件后缀为 .o. 编译是针对单个源文件的,一次编译操作只能编译一个源文件,如果程序中有多个源文件,就需要多次编译操作. 2) 链接(Link)是针对多个

七周七语言:理解多种编程范型pdf

下载地址:网盘下载 作者简介  · · · · · · 作者简介: Bruce A. Tate RapidRed公司总裁,该公司主要为Ruby轻量级开发提供咨询.他曾任职于IBM公司,并担任过多家公司的客户解决方案总监和CTO.著作有十余本,包括荣获Jolt大奖的Better, Faster, Lighter Java. 译者简介: 戴玮 80后宅男,中科院自动化所在读博士,热爱机器学习与计算机视觉.编程喜C#.Ruby.Haskell而厌Java. 白明 Neusoft某开发部技术总监,拥有多

C与C++之预处理命令与用typedef命名已有类型(三)

阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 预处理命令 主要是改进程序设计环境,以提高编程效率,不属于c语言本身的组成部分,不能直接对它们进行编译,必须在对 程序编译之前,先对程序中的这些特殊命令进行“预处理”.比如头文件. 有以下三类:宏定义,文件包含,条件编译. 宏定义(分为带参数与不带参数两种) 宏定义是用宏名代替一个字符串,也是简单的置换,不作正确性检查. 宏定义不是C语句,不必在行未加分号: d

#pragma预处理命令

#pragma预处理命令 #pragma可以说是C++中最复杂的预处理指令了,下面是最常用的几个#pragma指令: #pragma comment(lib,"XXX.lib") 表示链接XXX.lib这个库,和在工程设置里写上XXX.lib的效果一样. #pragma comment(linker,"/ENTRY:main_function") 表示指定链接器选项/ENTRY:main_function #pragma once 表示这个文件只被包含一次 #pra

由Cannot create a generic array of ArrayList&lt;xx&gt;引出的学习--Java范型

最近在用Java写某个程序时希望写出一个包括ArrayList<xx>的数组 自己定义如下: ArrayList<edge>[] edges = new ArrayList<edge>()[10]; 然后编译器报错如下: Cannot create a generic array of ArrayList<edge>; 在C++里可以这样比较方便的定义:  vector<edge> a[100]; 在Java里报错,查询了一下,见已经有人在sta

C语言第十一回合:预处理命令的集中营

  [学习目标]   1.         宏定义 2.         文件包括"处理 3.         条件编译 预处理命令:能够改进程序设计的环境.提高编程效率. 其功能主要有三种:宏定义.文件包括.文件编译. ANSI标准定义的C语言预处理指令预览表 A: 宏定义 (a)不带參数的宏定义 格式:#define标识符 字符串 如:#define PI 3.1415926 *标识符被称为:宏名 *在预编译时将宏名替换成字符串的过程为:宏展开. *#define 是宏定义命令 //求圆周长