2013-12-10 32 views
3

在编写C代码(上下文是科学计算)时遇到了很多情况,我将拥有具有完全相同体模的次要类型差异的函数。我意识到C++提供了模板特性和函数重载,它允许只有一个所述函数的副本,并让编译器找出你构建时使用的签名。尽管这是C++中的一个很棒的功能,但我的项目使用C语言,而且我不需要模板的全部功能。到目前为止,我所尝试的是候选源文件上的m4宏,并且这会为每个我需要的不同类型使用恰当的名称修改相应的.c文件。预处理器因此也可以实现这一点,但我试图避免以复杂的方式使用它(我的代码因可重复性原因需要可理解)。我对m4不太好,因此所有的文件都只能在特定情况下使用,并且在新情况下不适用。为不同签名的功能生成C代码,但实现相同

当需要时,其他人在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情况下,我只需找到/用必需的类型替换一个宏类型,并使用一个宏MANGLE()来修改函数,然后将输出指向一个替代的.c文件。虽然我的m4技能缺乏。

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

+1

非常模糊和不精确。你能提供[** SSCCE **](http://sscce.org/)吗? – abelenky

+1

复杂的宏是你在C中用于模板类行为的唯一真正的好解决方案。否则一般来说,如果你真的想要C++特有的功能,你需要编写C++代码 – Pyrce

+1

M4是一个很好的预处理器,你可以试着学习更多。但是对于更复杂的代码生成的情况,编写模板文件并编写一个脚本来替代项目需要的脚本通常会更好。 – rodrigo

回答

2

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

查看我的意思是qsort例程的定义/实现。 Qsort适用于各种数据,但透明地处理数据本身 - 您传递给qsort的唯一东西是每个条目的大小,以及指向函数的函数指针,以便进行真正的比较。

+1

+1中给出的类型来做'void my_generic_func(void * data,unsigned flags)';'并解释void *',但它会*使* OP的代码更难以阅读和维护,他正试图避免。但是,这可能是解决问题的最佳方法。 –

1

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

类型。高清

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提供一些条件代码,并可以打开和关闭条件(再次,不是问题)。

我倒是绝对尝试,看看是否有某种方式来概括算法,但这种方法可能有(从没有真正的基础类型例如性能问题)缺点。但是从评论中听到这种方法可能不适合你。

2

您似乎在寻求通用类型支持。尽管宏处理可以在受限域中工作,但您所做的事情很复杂。

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

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

+0

我以前使用预处理器和多个包含实际完成了此操作。它实际上等同于我拥有的m4解决方案,现在我赞成它,因为它吐出了可读的.c文件,然后将其编译。读过代码的人不必知道我的宏是什么意思,他们可以打开一个生成的文件,而忽略模板文件。 –

+0

@ Reid.Atcheson总是使用最高精度(可能保留一个整数和浮点版本)的性能受到如此大的影响,以至于值得维护多个实现?函数指针绝对不会内联,但可能并不昂贵。它们将导致您目前试图避免的同样的分布式复杂性。 – Pekka

+0

作为一般规则,我保留函数的指针,这些指针保留在内部循环之外。我的编码需求与其他人稍有不同,因为这是一项研究,而且我基本上只是从某人的抱怨中获得证明,因为我通过糟糕的编码风格先发制人地禁用了编译器优化,从而人为地使我实现了某人else的算法比我的算法慢(因此需要重新编码)。现在,如果人们更注重我的领域的可重复性并发布了他们的代码,我会像疯狂地编写接口。 –

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文件的本地,那么你不可能破坏任何东西,所以也不用担心。

此外,你可以使用参数化宏做更奇怪的事情,这可以让你更加简化代码重复。

+0

你需要一些更像'#define ADD(type,suffix)type add_ ## suffix(type x,type y){return x + y; }',它可以用#define ADD(unsigned int,ui)和#define ADD(double,db)和#define ADD(float,fl)等实例化。 –

相关问题