gpt4 book ai didi

c - 如何用纯C优雅地实现不同类型版本的一系列功能?

转载 作者:太空狗 更新时间:2023-10-29 16:53:06 26 4
gpt4 key购买 nike

我想编写几个仅参数类型不同的函数。我知道 C++ 有 template 来很好地处理这个问题(虽然还不是很好,很少有编译器支持 export 关键字,并且查询此关键字以提高效率)。举个简单的例子,我想要:

template <typename T>
T add(T a, T b){
return a+b;
}

但是,在纯C中(有时我不得不选择纯C,因为某些平台没有C++编译器),不同版本必须有不同的函数名称,如

double addDouble(double a, double b){
return a+b;
}

int addInt(int a, int b){
return a+b;
}

嗯,当有两个版本时,我似乎可以在源文件中进行复制和粘贴工作;但是,在实践中,函数中会有很多行,而不仅仅是一个 return 语句,并且会有更多版本。 那么,我的问题是,如何优雅地实现不同类型版本的一系列功能?

到目前为止,我已经尝试了以下一些解决方案,但我认为它们远非理想。我需要您的建议,谢谢!

解决方案一:

#define mytype int
mytype addInt(mytype a, mytype b){
return a+b;
}
#undef mytype

#define mytype float
mytype addFloat(mytype a, mytype b){
return a+b;
}
#undef mytype

方案一的缺点:重复内容太多,如果要修改功能,必须修改所有版本。

解决方案 2:

func.h

#ifndef FUNC_H
#define FUNC_H

#define add(a, b, typename) functionAdd##typename(a,b)

/* function declarations */
#define declared(typename) \
typename functionAdd##typename(typename, typename)

declared(int);
declared(float);

#endif

func.c

#include "func.h"

/* function code */
#define functionAdd(a, b, typename) \
typename functionAdd##typename(typename a, typename b){ \
return a+b; \
}

/* function bodies (definitions) */
functionAdd(a, b, int)
functionAdd(a, b, float)

ma​​in.c

#include <stdio.h>
#include "func.h"

int main()
{
int x1 = add(1, 2, int);
float x2 = add(3.0, 4.0, float);
printf("%d %f\n", x1, x2);
return 0;
}

方案二的不足之处:因为函数是在define中写的,调试起来比较困难。此外,\ 符号很烦人。虽然,添加新版本很方便,只需将 declared(double) 插入 func.h 并将 functionAdd(a, b, double) 插入 func.c 即可实现此目的瞄准。

最佳答案

在许多(如果不是大多数)情况下,在 C 中模拟 C++ 模板的最佳方法是解决方案 3:参数化头文件和参数化实现文件。在您的情况下,它将按如下方式工作

  1. 创建一个元头文件,我们将其命名为 add.dec,如下所示

    TYPE_ CONCAT(add, SUFFIX_)(TYPE_ a, TYPE_ b);
    TYPE_ CONCAT(sub, SUFFIX_)(TYPE_ a, TYPE_ b);
  2. 创建一个元实现文件,我们将其命名为add.def,如下所示

    TYPE_ CONCAT(add, SUFFIX_)(TYPE_ a, TYPE_ b){
    return a + b;
    }

    TYPE_ CONCAT(sub, SUFFIX_)(TYPE_ a, TYPE_ b){
    return a - b;
    }

这两个文件由两个宏参数化:TYPE_SUFFIX_,而CONCAT是宏连接的传统实现

#define CONCAT_(a, b) a##b
#define CONCAT(a, b) CONCAT_(a, b)

现在,假设您想为 intdouble 类型实例化您的"template"函数。在“真正的”头文件 add.h 中,您只需做

#define TYPE_ int
#define SUFFIX_ Int
#include "add.dec"
#undef TYPE_
#undef SUFFIX_

#define TYPE_ double
#define SUFFIX_ Double
#include "add.dec"
#undef TYPE_
#undef SUFFIX_

在一个“真正的”实现文件add.c

#define TYPE_ int
#define SUFFIX_ Int
#include "add.def"
#undef TYPE_
#undef SUFFIX_

#define TYPE_ double
#define SUFFIX_ Double
#include "add.def"
#undef TYPE_
#undef SUFFIX_

就是这样。通过这样做,您实例化了(声明和定义)addIntaddDoublesubIntsubDouble

当然,您可以对声明进行更多参数化。如果需要,您可以添加一个 DECLSPEC_ 参数,以便能够将您的太阳函数声明为 static。您可以为参数和返回值指定不同的类型(例如,ARG_TYPE_RET_TYPE_)。你可以参数化很多其他的东西。基本上,您可以参数化的内容没有限制。通过一些相当简单的宏技术,您甚至可以参数化函数期望的参数数量。

这实际上类似于您的解决方案 1 和解决方案 2 的组合。这基本上充分利用了您的两种方法。我想说这是模拟 C++ 模板实例化行为的最忠实尝试。

请注意,每个函数的主体仅显式键入一次(与您的解决方案 1 中的多个显式副本相反)。实际的函数体也很容易编辑,因为不必担心每行末尾那些讨厌的 \ (就像解决方案 2 中的情况一样)。

这种方法还有另一个有趣的好处:add.def 中的代码将保持“可调试”,即普通的交互式调试器通常能够进入这些实现(这在您的解决方案中是不可能的2).

关于c - 如何用纯C优雅地实现不同类型版本的一系列功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7186103/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com