【问题标题】:C++: How to generate array to function pointers in a loopC++:如何在循环中生成指向函数指针的数组
【发布时间】:2018-11-27 07:46:21
【问题描述】:

给定

// from an external C api
void f(int i, void (*g)());

const int n = ...
void a0(); void a1(); ...
void (*a[n])();

int main()
{
  a[0] = a0; a[1] = a1; ...
  for (int i = 0; i != n; ++i)
    f(i, a[i]);
  ...
}

我不想生成每个函数a0, a1, ... 并将其分别分配给a。相反,我想生成函数并在循环中将它们分配给a,就像这样(对于可怕的代码抱歉,它不会编译):

for (int i = 0; i != n; ++i)
{
    void b() { cout << i; };
    a[i] = b;
}

这可能吗?我该怎么做?

【问题讨论】:

  • 最方便的方法可能是使用更面向文本的语言编写的外部脚本生成函数(和数组)。
  • @molbdnilo:我的用例是:函数必须用 C++ 而不是其他语言编写。
  • 我的意思是你生成 C++ 代码。代码是你还是你的电脑写的重要吗?

标签: c++ arrays function pointers


【解决方案1】:

试试这样:

#include <iostream>
#include <vector>
using namespace std;

// from an external C api
void f(int i, void(*g)())
{
    //g();
}

const int n = 100;
using fnType = void(*)();
vector<fnType> a(n);

template <int N>
void func()
{
    cout << N << endl;
}

template <int N>
void init()
{
    a[N - 1] = func<N - 1>;
    init<N - 1>();
}

template <>
void init<0>()
{
}


int main()
{
    init<n>();

    for(int i = 0; i < n; ++i)
        a[i]();
    for(int i = 0; i < n; ++i)
        f(i, a[i]);   
}

另外,你可以定义

vector<std::function<void()>> funcs(n);

并从 f 调用它:

template <int N>
void func()
{
    //cout << N << endl;
    funcs[N]();
}

所以,你可以简单地定义它:

for(int k = 0;k < n;k++)
    funcs[k] = [k](){cout << k << endl;};

【讨论】:

  • 很有趣:n越大,构建的时间越长,可执行文件就越大。在我的电脑上,n 必须
  • 是的,它是已知模板的属性
【解决方案2】:

不确定它是否适合您的用例,但我遇到了一个非常相似的问题来创建模板化 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&lt;0&gt;(), a&lt;1&gt;(), ...) 编入索引,如下所示:

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);
}

【讨论】:

  • 抱歉,我不明白这与我的问题有什么关系。
  • @JohannesFlügel 我在回答的末尾添加了精确度。是的,不确定您是否可以调整解决方案,您必须已索引 a&lt;index&gt;(...) 函数。
猜你喜欢
  • 1970-01-01
  • 2018-05-12
  • 2014-05-13
  • 1970-01-01
  • 2021-12-07
  • 1970-01-01
  • 2011-04-03
  • 2015-09-11
相关资源
最近更新 更多