假设我有两个不同的函数(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编译器被修改为允许问题所讨论的那种函数,最终的二进制文件也会类似,因为我们需要有两个不同的函数在二进制文件中呢?“
答案 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
。