C ++: как создать массив для указателей на функции в цикле - PullRequest
0 голосов
/ 27 ноября 2018

Учитывая

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

Возможно ли это?Как я могу это сделать?

Ответы [ 2 ]

0 голосов
/ 27 ноября 2018

Не совсем уверен, что он подходит для вашего варианта использования, но я столкнулся с довольно похожей проблемой при создании шаблонной оболочки 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 - это размер stat ic (известный во время компиляции), который должен позволить компилятору выполнять сложные оптимизации (векторизация цикла ...).

Теперь у нас также есть dyn amic fallback, когда 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());
}

Затем определите ваш API C:

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);
}
0 голосов
/ 27 ноября 2018

Попробуйте так:

#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;};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...