关于C函数,它们的唯一区别是某些参数的数据类型

时间:2011-12-01 00:14:56

标签: c

假设我有两个不同的函数(C),它们之间的唯一区别是它们的一些参数具有不同的数据类型(我现在正在考虑CBLAS)。例如:

void cblas_dgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,
             const enum CBLAS_TRANSPOSE TransB, const int M, const int N,
             const int K, const double alpha, const double *A,
             const int lda, const double *B, const int ldb,
             const double beta, double *C, const int ldc);


void cblas_sgemm(const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,
             const enum CBLAS_TRANSPOSE TransB, const int M, const int N,
             const int K, const float alpha, const float *A,
             const int lda, const float *B, const int ldb,
             const float beta, float *C, const int ldc);

不是将此函数定义两次,只是某些数据类型不同,是否有更聪明的方法来拥有这两个函数?像编译器指令或什么?

编辑(不确定stackoverflow是否允许这个问题):我正在考虑编译后这些函数的外观。我是否认为这是正确的? “由于单精度加法和双精度加法是硬件级别的不同指令,即使C编译器被修改为允许问题所讨论的那种函数,最终的二进制文件也会类似,因为我们需要有两个不同的函数在二进制文件中呢?“

3 个答案:

答案 0 :(得分:4)

不,那是不可能的。在C中,每个函数名称对应一个全局唯一函数。这就是为什么C库,包括标准库,充满了以f / l / ul / ull等结尾的函数变体。

这个限制在C ++的设计中得到了认可,其中包括重载决策;然而,这是一个相当复杂的过程,除其他外,表明C ++没有通用的ABI,这与C在实践中不同。因此,通用库仍然符合C接口(并且C ++中的extern "C"函数不能有重载。)

C是一种简单的语言,易于实现,这意味着并非所有相关内容都适合。

更新:例如,考虑运行时链接àladlsym()GetProcAddress()。这些完全由 name 完成,由简单的C ABI提供。动态链接(或者只是链接,就此问题而言)不是C标准的一部分,尽管如此,这是一个非常有用的工具,它不能很好地与重载一起使用。

答案 1 :(得分:0)

< hideous monstruosity>:

#define cblass_gemm(type_prefix, type) void cblass_##type_prefix##gemm(       \
            const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA,  \
            const enum CBLAS_TRANSPOSE TransB, const int M, const int N,      \
            const int K, const type alpha, const type *A,                     \
            const int lda, const type *B, const int ldb,                      \
            const type beta, type *C, const int ldc)                          \
{                                                                             \
    type var;                                                                 \
}

cblass_gemm(d,double)
cblass_gemm(s,float)

那头野兽生出了:

$ gcc -E blass.c
# 1 "blass.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "blass.c"
# 11 "blass.c"
void cblass_dgemm( const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_TRANSPOSE TransB, const int M, const int N, const int K, const double alpha, const double *A, const int lda, const double *B, const int ldb, const double beta, double *C, const int ldc) { double var; }
void cblass_sgemm( const enum CBLAS_ORDER Order, const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_TRANSPOSE TransB, const int M, const int N, const int K, const float alpha, const float *A, const int lda, const float *B, const int ldb, const float beta, float *C, const int ldc) { float var; }

当然,编辑器的语法突出显示已经变得不那么有用了。

答案 2 :(得分:0)

正如评论所指出的那样,C中没有函数重载,这可能是一件好事。但我有一个建议。假设我们有:

void foo(double *);

当然我们无法编译以下片段:

float f;
foo(&f);

可能的解决方案是将foo()包装在同名的宏中:

#define foo(p) do {        \
   double _ = *(p);        \
   foo(&_);                \
   *(p) = _; } while(0)

然后foo(&f);double d; foo(&d);都会编译。

一些注意事项:

  • do {...} while (0)代码为'swallow semi-colons'

  • 此宏需要临时双精度的名称;我选择_。这有望避免 gcc 的'影子'警告。另一种方法是使用a_very_long_name

  • 这有'宏语义',所以(不太可能)double *d; foo(d++);不起作用。但是,如果在您的环境中可以实现这一点,则应遵循约定,并将宏命名为FOO而不是foo