在C语言中,泛型函数与函数指针数组的区别是什么?

在C语言中,泛型函数与函数指针数组的区别是什么?,c,gcc,compiler-construction,function-pointers,C,Gcc,Compiler Construction,Function Pointers,我正在编写SciRuby的矩阵库部分,其中包含多种存储类型“stype”和多种数据类型“dtypes”。例如,矩阵的stype当前可能是密集的,也称为“csr”,或列表列表;其数据类型可以是int8、int16、int32、int64、float32、float64、complex64等 用Ruby或sed编写模板处理器非常简单,它采用了稀疏矩阵乘法等基本函数,并为每个可能的数据类型创建了自定义版本。我甚至可以编写这样一个模板来处理两种不同的数据类型,比如说,如果我们想将int32乘以float

我正在编写SciRuby的矩阵库部分,其中包含多种存储类型“stype”和多种数据类型“dtypes”。例如,矩阵的stype当前可能是密集的,也称为“csr”,或列表列表;其数据类型可以是int8、int16、int32、int64、float32、float64、complex64等

用Ruby或sed编写模板处理器非常简单,它采用了稀疏矩阵乘法等基本函数,并为每个可能的数据类型创建了自定义版本。我甚至可以编写这样一个模板来处理两种不同的数据类型,比如说,如果我们想将int32乘以float64

在某些情况下,也可以对不同的Stype执行相同的操作。但是,最终,您可能会得到一组非常大的函数,其中许多函数甚至在大多数人的使用过程中从未被使用过

使用函数指针数组访问这些函数也很容易,想象一个三维函数指针数组也不难:

MultFuncs[lhs->stype][lhs->dtype][rhs->dtype](lhs->shape[0], rhs->shape[1], lhs->data, rhs->data, result->data);
// This might point to some function like this:
// i32_f64_dense_mult(size_t, size_t, int32_t*, float64*, float64*);
当然,函数指针数组的极端替代方案是分层开关或if/else语句,它的编码和维护非常复杂:

switch(lhs->stype) {
case STYPE_SPARSE:
  switch(lhs->dtype) {
  case DTYPE_INT32:
    switch(rhs->dtype) {
    case DTYPE_FLOAT64:
      i32_f64_mult(lhs->shape[0], rhs->shape[1], lhs->ija, rhs->ija, lhs->a, rhs->a, result->data);
      break;
    // ... and so on ...
这似乎也是Osd2,其中s=Stype的数量,d=每个操作的数据类型的数量,而函数指针数组将是Or,其中r=数组中的维度数量

但还有第三种选择

第三个选项是使用函数指针数组进行常见操作,例如,从一种未知类型复制到另一种类型:

SetFuncs[lhs->dtype][rhs->dtype](5, // copy five consecutive items
 &to, // destination
 dtype_sizeof[lhs->dtype], // dtype_sizeof is a const size_t array giving sizeof(int8_t), sizeof(int16_t), etc.
 &from, // source
 dtype_sizeof[rhs->dtype]);
然后从一个通用的稀疏矩阵乘法函数调用它,可以这样声明:

void generic_sparse_multiply(size_t* ija, size_t* ijb, void* a, void* b, int dtype_a, int dtype_b);
例如,这将使用SetFuncs[dtype_a][dtype_b]引用正确的赋值函数。因此,缺点是您可能需要实现一大堆函数——递增函数、递减函数、多重函数、添加函数、子函数等等——因为您永远不知道需要什么类型

最后,我的问题是:

拥有庞大的多维常量函数指针数组的成本(如果有的话)是多少?大型库还是可执行库?慢加载时间?等 使用诸如IncrementFuncs、SetFuncs等可能都依赖于memcpy或typecast的泛型是否会阻碍编译时优化? 如果使用如上所述的switch语句,现代编译器会优化这些语句吗?还是每次都要进行评估? 我意识到这是一系列极其复杂的问题


如果你能简单地向我推荐一个好的资源,而不是直接回答,那就很好了。在发布这篇文章之前,我广泛使用了谷歌搜索引擎,但不太确定使用什么搜索词。

首先,尝试降低功能的复杂性。你应该可以有这样的声明

Result_t function (Param_t*);
其中,Param_t是一个结构,包含您传递的所有内容。要使用泛型类型,请在结构中包含一个枚举,告诉使用的是哪种类型,并在该类型中包含一个void*

1.拥有庞大的多维常量函数指针数组的成本(如果有)是多少?大型库还是可执行库?缓慢的 装载时间?等等

绝对是更大的可执行文件。加载时间取决于代码用于哪个系统。如果是基于RAM的系统PC等,则加载时间可能会增加,但不会对性能产生任何重大影响。当然,这取决于它有多大:

2.使用诸如IncrementFuncs、SetFuncs等可能都依赖于memcpy或typecast的泛型是否会阻碍 编译时优化

可能不会,编译器可以优化的内容太多了。在C中处理泛型数据类型时,通常最终归结为memcpy,它本身有望实现得与复制一样快

3.如果使用上述switch语句,现代编译器会优化这些语句吗?或者他们会被评估 每一次


具有讽刺意味的是,编译器可能会将其优化为类似于函数指针数组的内容。但是,编译器可能无法预测数据的性质,尤其是在运行时设置数据时。

首先,尝试降低函数的复杂性。你应该可以有这样的声明

Result_t function (Param_t*);
其中,Param_t是一个结构,包含您传递的所有内容。要使用泛型类型,请在结构中包含一个枚举,告诉使用的是哪种类型,并在该类型中包含一个void*

1.拥有庞大的多维常量函数指针数组的成本(如果有)是多少?大型库还是可执行库?缓慢的 装载时间?等等

绝对是更大的可执行文件。加载时间取决于代码用于哪个系统。如果是基于RAM的系统PC等,则加载时间可能会增加,但不会对性能产生任何重大影响。公司的想法 当然,这取决于它有多大:

2.使用诸如IncrementFuncs、SetFuncs等可能都依赖于memcpy或typecast的泛型是否会阻碍 编译时优化

可能不会,编译器可以优化的内容太多了。在C中处理泛型数据类型时,通常最终归结为memcpy,它本身有望实现得与复制一样快

3.如果使用上述switch语句,现代编译器会优化这些语句吗?或者他们会被评估 每一次


具有讽刺意味的是,编译器可能会将其优化为类似于函数指针数组的内容。但是,编译器可能无法预测数据的性质,尤其是在运行时设置数据时。

您是否可以通过决定在int64、float64和complex64上实现操作,然后编写将int8转换为int64的转换器(如果需要)来减少问题空间,从iT64转换为It8?我认为您应该分别考虑API和实现。首先考虑一下API,它更重要——您希望它看起来怎么样,这样它才是可用的。然后考虑如何实现它。您可以通过决定在操作上执行操作(例如,In64、FulAT64和Fiel64),然后编写将转换为ITC8到It64的转换器,如果需要的话,将其从It64转换为It8?我认为您应该分别考虑API和实现。首先考虑一下API,它更重要——您希望它看起来怎么样,这样它才是可用的。然后思考如何实施它。