Given
// 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]);
...
}
I don't want to generate each function a0
, a1
, ... and assign it to a
separately. Instead I want to generate the functions and assign them to a
in a loop, something like that (sorry for the hideous code, it won't compile):
for (int i = 0; i != n; ++i)
{
void b() { cout << i; };
a[i] = b;
}
Is this possible? How can I do it?
Try like this:
#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]);
}
Also, you can define
vector<std::function<void()>> funcs(n);
and call it from f:
template <int N>
void func()
{
//cout << N << endl;
funcs[N]();
}
So, you can simply define it:
for(int k = 0;k < n;k++)
funcs[k] = [k](){cout << k << endl;};
No really sure it fits your use case, but I encountered a quite similar problem to create a templated C++ functions C wrapper. The demo shows how to create (at compile time) and reuse a std::array
of function pointers.
Let imagine that you have a basic C++ function that computes sum of squares
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;
}
for efficiency reason N is a static size (known at compile time) that should allow compiler to do tricky optimizations (vectorize the loop...).
Now we also have a dynamic fallback, when N is too big or unknown at compile time
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;
}
Now you want to create a C API. A naive approach would be to define something like:
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);
}
}
}
However this approach is tedious because you have to repeat yourself a lot and you can not automatically change Nmax=8
value.
Now a more elegant solution I suggest. First define some helpers to automatically create, at compile time, a static array of function pointers:
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());
}
Then define your 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);
}
}
There are clear advantages, you have a clean code and you can easily change Nmax
value without further modifications of the code. Also note that you use a std::array
and do not use std::function
which minimize the risk of performance penalties.
I hope this partially answer your question. To adapt it to your problem you must have indexed a()
functions (a<0>(), a<1>(), ...)
as follows:
template <std::size_t INDEX>
... a(...)
and not (your example)
... a0(...)
... a1(...)
... a2(...)
If it is not the case I fear that you will have to write glue code as you mentioned in your question:
a[0] = a0; a[1] = a1;
Complete working example:
#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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With