为不同签名的函数生成C代码,但实现相同

时间:2013-12-10 18:56:54

标签: c templates

我在编写C代码(上下文是科学计算)时经常遇到的情况是,我将拥有具有完全相同的主体模型次要类型差异的函数。我意识到C ++提供了模板功能和函数重载,它允许只有一个所述函数的副本,让编译器找出你在构建时要使用的签名。

虽然这是C ++中的一个很棒的功能,但我的项目是在C中,而且我不需要模板的全部功能。到目前为止,我所尝试的是候选源文件中的m4宏,并且这会针对我需要的不同类型使用适当的名称修改相应的.c文件。因此预处理器也可以实现这一点,但我试图避免以复杂的方式使用它(我的代码需要因可重复性原因而易于理解)。我对m4不太满意,所以所有文件都是黑客,只能在特定情况下使用,并且在新情况下不适用。

如果有必要,其他人在C中编程会怎么做?手动生成和维护功能签名的不同排列?我希望这不是最好的答案,或者存在一个工具来自动化这个沉闷且容易出错的任务。

对于含糊不清的道歉,让我举一个玩具示例。假设我需要添加两个数字。该函数可能如下所示:

float add(float x,float y){
   return x+y;
}

好的,对于花车很好,但是如果我需要它可用于各种类型的运算,那该怎么办呢?好我可以做到这一点

float add_f(float x,float y){...}
double add_lf(double x,double y){...}
unsigned int add_ui(unsigned int x, unsigned int y){...}

等等。如果由于某些(可能是愚蠢的)原因我决定我还需要将参数的内容写入二进制文件,我现在必须在每个函数中添加必需的文件I / O代码。是否有一个简单的方法/工具来获取添加功能并吐出不同的名称,以避免这种烦人的情况?

基本上在我的m4情况下,我只是找到/替换一个具有必需类型的宏TYPE,并且有一个宏MANGLE()来破坏函数,然后我将输出指向备用.c文件。我的m4技能很缺乏。

函数指针可以帮助我的代码的终极接口,但最终这些指针必须指向某些东西,然后我们再次枚举所有可能性。我也不清楚这可能如何影响短函数的潜在内联。

4 个答案:

答案 0 :(得分:2)

我唯一能想到的是:让算法本身独立于类型,让你的函数的用户创建自己的函数来处理特定于类型的部分,并使其中一个参数你的函数指向“处理函数”的指针。

请参阅qsort例程的定义/实现。 Qsort适用于所有类型的数据,但透明地处理数据本身 - 传递给qsort的唯一内容是每个条目的大小,以及指向执行真正比较的函数的函数指针。

答案 1 :(得分:2)

您似乎要求提供通用类型支持。虽然宏处理可以在受限制的域中工作,但您正在做的事情很复杂。

如果变体非常相似,只需键入和命名修改就足够了,那么在同一个源片段的多个包含中的每一个包含允许预处理器执行替换之前,您是否可以不使用常规C #defines?这样,至少只有一个环境可以管理。

或者,如果性能影响不大,您是否可以为每个专业化准备多个存根函数,并将这些函数映射到可以从存根调用的通用版本?

答案 2 :(得分:1)

我使用GNU autogen进行代码生成任务,这听起来有点像您当前的m4解决方案,但可能更有条理。例如:

<强> type.def

autogen definitions type;

type = { name="int"; mangle="i"; };
type = { name="double"; mangle="lf"; };
type = { name="float"; mangle="f"; };
type = { name="unsigned int"; mangle="ui"; };

<强> type.tpl

[+ autogen5 template 
c=%s.c
(setenv "SHELL" "/bin/sh") +]/*
[+ (dne "* " "* ") +]
*/
[+
 FOR type "\n" +][+name+] add_[+mangle+]([+name+] x, [+name+] y) { ... }[+ENDFOR+]

或类似的东西。这应该为type.def中的每个类型吐出一个函数,如下所示:

unsigned int add_ui(unsigned int x, unsigned int y) { ... }

如果需要,您也可以在特定位置插入特定于类型的代码等。您可以输出上述添加功能以及I / O版本。您必须计算mangle的文本而不是我所拥有的文本,但这不是问题。你还有一些I / O的条件代码和一种打开和关闭条件的方法(再次,不是问题)。

肯定尝试看看是否有某种方法来推广算法,但这种方法可能也有缺点(例如,由于没有真正的底层类型而导致性能问题)。但是从评论中可以看出这种方法可能不适合你。

答案 3 :(得分:1)

我知道大多数C开发人员都很害怕它,但您是否考虑过使用宏?

特定于您的示例:

// floatstuff.h
float add_f(float x,float y);
double add_lf(double x,double y);
unsigned int add_ui(unsigned int x, unsigned int y);

结合:

// floatstuff.c
#define MY_CODE \
  return x + y

float
add (float x, float y)
{
  MY_CODE;
}

double
add_lf (double x, double y)
{
  MY_CODE;
}

unsigned int
add_ui (unsigned int x, unsigned int y)
{
  MY_CODE;
}

如果您使用的每个功能的代码完全相同,那么这可能是您正在寻找的解决方案。它避免了大多数代码重复,保持一定程度的可读性并且对运行时没有影响。 此外,如果你将宏保存在.c文件的本地,你不可能破坏任何东西,所以也不用担心。

此外,您可以使用参数化宏来做更奇怪的事情,这可以为您提供更多减少的代码重复。