不确定它是否适合您的用例,但我遇到了一个非常相似的问题来创建模板化 C++ 函数 C 包装器。该演示展示了如何创建(在编译时)和重用函数指针的std::array。
假设您有一个计算平方和的基本 C++ 函数
template <std::size_t N>
double sum2_stat(const double* p)
{
double s = 0;
for (size_t i = 0; i < N; i++)
{
s += p[i] * p[i];
}
return s;
}
出于效率原因,N 是一个 static 大小(在编译时已知),应该允许编译器进行棘手的优化(向量化循环...)。
现在我们还有一个 dynamic 回退,当 N 太大或在编译时未知时
double sum2_dyn(const double* p, const std::size_t n)
{
double s = 0;
for (size_t i = 0; i < n; i++)
{
s += p[i] * p[i];
}
return s;
}
现在您要创建一个 C API。一种天真的方法是定义如下内容:
extern "C" {
double sum2_naive(const double* p, const std::size_t n)
{
assert(n >= 0);
switch (n)
{
case 0:
return sum2_stat<0>(p);
case 1:
return sum2_stat<1>(p);
case 2:
return sum2_stat<2>(p);
case 3:
return sum2_stat<3>(p);
case 4:
return sum2_stat<4>(p);
case 5:
return sum2_stat<5>(p);
case 6:
return sum2_stat<6>(p);
case 7:
return sum2_stat<7>(p);
case 8:
return sum2_stat<8>(p);
default:
return sum2_dyn(p, n);
}
}
}
但是这种方法很繁琐,因为你必须重复很多次,而且你不能自动更改Nmax=8 的值。
现在我建议一个更优雅的解决方案。首先定义一些帮助器,在编译时自动创建一个静态函数指针数组:
template <std::size_t... I>
constexpr auto sum2_call_helper(std::index_sequence<I...>)
{
return std::array<double (*)(const double* p), sizeof...(I)>({&sum2_stat<I>...});
}
template <std::size_t N, typename Indices = std::make_index_sequence<N>>
constexpr auto sum2_call_helper()
{
return sum2_call_helper(Indices());
}
然后定义你的 C API:
extern "C" {
double sum2(const double* p, const std::size_t n)
{
constexpr auto N_Max = 8;
constexpr auto indirections = sum2_call_helper<N_Max + 1>();
assert(N_Max >= 0);
if (n <= N_Max)
{
return indirections[n](p);
}
return sum2_dyn(p, n);
}
}
有明显的优势,你有一个干净的代码,你可以很容易地改变Nmax的值,而不需要进一步修改代码。另请注意,您使用std::array 而不要使用std::function,这会将性能损失的风险降至最低。
我希望这部分回答了您的问题。要使其适应您的问题,您必须将a() 函数(a<0>(), a<1>(), ...) 编入索引,如下所示:
template <std::size_t INDEX>
... a(...)
而不是(你的例子)
... a0(...)
... a1(...)
... a2(...)
如果不是这样,我担心您将不得不编写问题中提到的胶水代码:
a[0] = a0; a[1] = a1;
完整的工作示例:
#include <array>
#include <cassert>
#include <iostream>
#include <utility>
#include <vector>
template <std::size_t N>
double sum2_stat(const double* p)
{
double s = 0;
for (size_t i = 0; i < N; i++)
{
s += p[i] * p[i];
}
return s;
}
template double sum2_stat<10>(const double*);
double sum2_dyn(const double* p, const std::size_t n)
{
double s = 0;
for (size_t i = 0; i < n; i++)
{
s += p[i] * p[i];
}
return s;
}
template <std::size_t... I>
constexpr auto sum2_call_helper(std::index_sequence<I...>)
{
return std::array<double (*)(const double* p), sizeof...(I)>({&sum2_stat<I>...});
}
template <std::size_t N, typename Indices = std::make_index_sequence<N>>
constexpr auto sum2_call_helper()
{
return sum2_call_helper(Indices());
}
extern "C" {
double sum2(const double* p, const std::size_t n)
{
constexpr auto N_Max = 8;
constexpr auto indirections = sum2_call_helper<N_Max + 1>();
assert(N_Max >= 0);
if (n <= N_Max)
{
return indirections[n](p);
}
return sum2_dyn(p, n);
}
double sum2_naive(const double* p, const std::size_t n)
{
assert(n >= 0);
switch (n)
{
case 0:
return sum2_stat<0>(p);
case 1:
return sum2_stat<1>(p);
case 2:
return sum2_stat<2>(p);
case 3:
return sum2_stat<3>(p);
case 4:
return sum2_stat<4>(p);
case 5:
return sum2_stat<5>(p);
case 6:
return sum2_stat<6>(p);
case 7:
return sum2_stat<7>(p);
case 8:
return sum2_stat<8>(p);
default:
return sum2_dyn(p, n);
}
}
}
int main()
{
std::vector<double> buffer(100, 2);
std::cout << "\n" << sum2(buffer.data(), 5);
std::cout << "\n" << sum2(buffer.data(), 10);
std::cout << "\n" << sum2_naive(buffer.data(), 5);
std::cout << "\n" << sum2_naive(buffer.data(), 10);
}