For example, say I have the following:
template<typename ...FunctionTypes>
static void MainFunction(FunctionTypes... functions)
{
constexpr Uint32_t NumFunctions= sizeof...(FunctionTypes);
std::array<double, NumFunctions> myArray;
double arg1 = 4.2;
int arg2= 9;
for_each_tuple(myArray, FigureOutThisPart(myFunctions, arg1, arg2)...);
}
Where for_each_tuple
takes a tuple or array of size N
as well as N
functions to apply to each tuple entry. This part was tricky to implement, but works! It is defined as such:
namespace detail {
template <typename Tuple, std::size_t ...Indices, typename ...FunctionTypes>
constexpr void for_each_tuple_impl(Tuple&& tuple, std::index_sequence<Indices...>, FunctionTypes&&... functionsIn) {
std::tuple<FunctionTypes...> functions = std::tie(functionsIn...);
using swallow = int[];
(void)swallow{
1, // Make sure array has at least one element
(std::get<Indices>(functions)(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...
};
}
}
template <typename Tuple, typename ...Functions>
void for_each_tuple(Tuple&& tuple, Functions&&... f) {
constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
static_assert(N == sizeof...(Functions), "Need one function per tuple entry");
detail::for_each_tuple_impl(
std::forward<Tuple>(tuple),
std::make_index_sequence<N>{},
std::forward<Functions>(f)...);
}
The idea is that I have set of myFunctions
, each of which is operating on a different entry of myArray
. Each function in myFunctions
would take arg1
, arg2
, and the current array entry as arguments.
My goal is to be able to pass arg1
and arg2
into each of myFunctions
so that those values can be used in the operation on the current array entry. Is there a sane way in which this can be done? Ideally, I would like the solution to be a constexpr so everything can get resolved at compile time.
CodePudding user response:
I think something like this could work:
#include <array>
#include <functional>
template<typename ...FunctionTypes>
constexpr void MainFunction(FunctionTypes... functions)
{
constexpr auto NumFunctions= sizeof...(FunctionTypes);
std::array<double, NumFunctions> myArray{};//Zero-init for now.
double arg1 = 4.2;
int arg2= 9;
std::size_t i=0;
(std::invoke(functions, arg1,arg2, myArray[i ]),...);
}
#include <iostream>
void foo(double a1, int a2, int a){
std::cout<<"Called foo("<<a1<<','<<a2<<','<<a<<")\n";
}
int main()
{
MainFunction(foo,foo);
return 0;
}
It requires C 17 for the fold expression and the sequence points for the comma.
It can be evaluated at compile-time:
constexpr void bar(double a1, int a2, int a){
}
int main()
{
constexpr auto x = (MainFunction(bar,bar),1); // Force compile-time evaluation.
return 0;
}
CodePudding user response:
I would just make a tuple out of functors at very beginning and match an element of the tuple to a corresponding element of the array:
template<std::size_t I, typename E, typename ...F, typename ...Args>
constexpr void apply(const std::array<E, sizeof...(F)>& elements,
const std::tuple<F...>& functions,
Args... args) {
std::get<I>(functions)(std::get<I>(elements), args...);
}
template<std::size_t ...I, typename E, typename ...F, typename ...Args>
constexpr void for_each(std::index_sequence<I...>,
const std::array<E, sizeof...(I)>& elements,
const std::tuple<F...>& functions,
Args... args) {
(apply<I>(elements, functions, args...),...);
}
template<typename ...F>
constexpr static void MainFunction(F... functors)
{
constexpr std::size_t size = sizeof...(F);
constexpr std::array<double, size> elements{};
constexpr double arg1 = 4.2;
constexpr int arg2 = 9;
for_each(std::make_index_sequence<size>(),
elements,
std::forward_as_tuple(functors...),
arg1, arg2);
}
The client code will look something like that:
void function(double val, double arg1, int arg2) {
std::cout << arg1 arg2 8 * val << std::endl;
}
int main() {
MainFunction(function, [](double val, double arg1, double arg2) {
std::cout << val / arg1 arg2 << std::endl;
});
return 0;
}
As long as argument functions are constexpr
, the implementation is also constexpr
:
template<typename ...F>
constexpr static auto MainFunction(F... functors) { ... return elements; }
constexpr void function(double val, double arg1, int arg2) { ... }
int main() {
// Retrieves the constexpr elements array from the function
constexpr auto val = MainFunction(function, [](double, double, int){ ... });
}
CodePudding user response:
You can use std::bind_front
to construct a function that will have placeholder arguments, which is a solution a little closer to what you originally intended. This, however, has a drawback of not being runnable in constexpr
context:
template <std::size_t N, typename T, typename... Functions>
auto for_each_tuple(std::array<T, N> const& array, Functions... functions) {
auto index = 0uz;
(std::invoke(functions, array[index ]), ...);
}
template<typename ...FunctionTypes>
static void MainFunction(FunctionTypes... functions)
{
constexpr std::uint32_t NumFunctions= sizeof...(FunctionTypes);
std::array<double, NumFunctions> myArray{};
double arg1 = 4.2;
int arg2= 9;
for_each_tuple(myArray, std::bind_front(functions, arg1, arg2)...);
}
auto main() -> int {
auto print_thrice = [](double d1, int i2, double arr) {
std::cout << d1 << ' ' << i2 << ' ' << arr << '\n';
};
MainFunction(print_thrice, print_thrice, print_thrice);
}