使用两个名称在C中调用相同函数的首选方法

时间:2012-04-02 20:52:21

标签: c function embedded

我知道至少有三种流行的方法可以使用多个名称来调用相同的函数。我实际上没有听说有人为此目的使用第四种方法。

1)。可以使用#defines:

int my_function (int);


#define my_func my_function

OR

#define my_func(int (a)) my_function(int (a))

2)。嵌入式函数调用是另一种可能性:

int my_func(int a) {
    return my_function(a);
}

第3)。在链接器中使用弱别名:

int my_func(int a) __attribute__((weak, alias("my_function")));

4)。功能指针:

int (* const my_func)(int) = my_function;

我需要多个名称的原因是数学库具有相同方法的多个实现。

例如,我需要一种有效的方法来计算标量浮点数的平方根。所以我可以使用math.h的sqrt()。这不是很有效。所以我写了一两个其他方法,比如使用牛顿方法。问题是每种技术在某些处理器(在我的情况下是微控制器)上更好。所以我希望编译过程选择最好的方法。

我认为这意味着最好使用宏或弱别名,因为这些技术可以很容易地在头文件中的几个#ifdef语句中进行分组。这简化了维护(相对)。也可以使用函数指针,但它必须在源文件中,头文件中包含常规函数的extern声明。

您认为哪种方法更好?


修改

从提议的解决方案中,似乎有两个重要的问题我没有提到。

问。用户主要使用C / C ++吗?

A。所有已知的开发都将使用C / C ++或汇编语言。我正在设计这个库供我个人使用,主要用于裸机项目。将没有或最小的操作系统功能。在完整的操作系统中使用它的可能性很小,这需要考虑语言绑定。由于这是为了个人成长,因此在流行的嵌入式操作系统上学习库开发将是有利的。

问。用户是否需要/想要一个公开的库?

A。到目前为止,是的。由于它只是我,我想对测试后使用的每个处理器进行直接修改。这是测试套件有用的地方。所以一个暴露的图书馆会有所帮助。另外,特定功能的每个“最佳实现”可能具有失败的条件。此时,必须决定谁解决了问题:用户或图书馆设计师。用户需要一个公开的库来解决失败的情况。我既是“用户”又是“图书馆设计师”。两者都允许几乎更好。然后非实时应用程序可以让库解决所有稳定性问题,但实时应用程序可以考虑算法速度/空间与算法稳定性。

5 个答案:

答案 0 :(得分:7)

根据您图书馆的目标受众,我建议您选择两种选择:

  1. 如果图书馆的消费者保证C是,请使用#define sqrt newton_sqrt以获得最佳可读性

  2. 如果你的图书馆的某些消费者是的C类(想想绑定到Dephi,.NET,等等),试着避免消费者可见#defines。这是绑定的主要PITA,因为宏在二进制文件上不可见 - 嵌入式函数调用是最具约束力的。

答案 1 :(得分:7)

另一种替代方法是将功能移动到针对每个不同体系结构优化的单独编译的库中,然后在编译期间链接到该库。这将允许项目代码保持不变。

答案 2 :(得分:4)

你能做的就是这个。在头文件(.h)中:

 int function(void);

在源文件(.c)中:

static int function_implementation_a(void);
static int function_implementation_b(void);
static int function_implementation_c(void);

#if ARCH == ARCH_A
int function(void)
{
    return function_implementation_a(); 
}
#elif ARCH == ARCH_B
int function(void)
{
    return function_implementation_b();
}
#else
int function(void)
{
    return function_implementation_c();
}
#endif // ARCH

调用一次的静态函数通常由实现内联。例如,gcc默认情况下就是这种情况:即使在-finline-functions-called-once中也启用了-O0。未调用的静态函数通常也不包含在最终的二进制文件中。

请注意,我没有将#if#else放在单个function正文中,因为当#if指令位于函数体外时,我发现代码更具可读性

请注意,这种方式可以更好地处理嵌入式代码,其中库通常以源代码形式分发。

答案 3 :(得分:3)

我通常喜欢在头文件中使用单个声明解决此问题,每个体系结构/处理器类型都有不同的源文件。然后我只需要构建系统(通常是GNU make)选择正确的源文件。

我通常将源代码树拆分为单独的目录,用于公共代码和特定于目标的代码。例如,我当前的项目有一个顶级目录Project1,下面是includecommonarmhost目录。对于armhost,Makefile根据目标在适当的目录中查找源。

我认为这使得导航代码更容易,因为我不必查找弱符号或预处理器定义来查看实际调用的函数。它还避免了函数包装器的丑陋以及函数指针的潜在性能损失。

答案 4 :(得分:0)

您可以为所有算法创建一个测试套件并在目标上运行它以确定哪个算法性能最佳,然后让测试套件自动生成必要的链接器别名(方法3)。

除此之外,一个简单的#define(方法1)可能是最简单的,并且不会和任何潜在的开销。然而,它确实向库用户暴露可能存在多个实现,这可能是不合需要的。

就个人而言,由于每个函数只有一个实现可能在任何特定目标上都是最优的,我会使用测试套件来确定每个目标所需的版本,并为每个目标构建一个单独的库,只有那个版本每个函数直接正确的函数名称。