Reputation: 2254
I wrote a variadic template which is executing a functor F
exactly N
times and accumulating the result. Now I wonder how to make this template able to process a variable amount of index (template) parameters currently named id
and in the desired functor x, y, z
.
What I have in mind was a functor like below which is executed as if you call it three for loops. I also wonder whether it could be solved by argument lists.
struct 3DFunctor {
template <int x, int y, int z>
static int run() {
return x*y*z;
}
};
Desired behavior should be something like:
accum_for<3,3,3>::get<3DFunctor>();
3DFunctor::run<0,0,0>(/*args ...*/) +
3DFunctor::run<0,0,1>(/*args ...*/) +
3DFunctor::run<0,0,2>(/*args ...*/) +
3DFunctor::run<0,1,0>(/*args ...*/) +
3DFunctor::run<0,1,1>(/*args ...*/) +
3DFunctor::run<0,1,2>(/*args ...*/) +
// ..
Example:
#include <iostream>
#include <string>
struct F {
template <int id>
static int run(int val) {
return id * val;
}
};
template<unsigned int n>
struct accum1d_for {
template <class Funct, class ... ArgTypes>
static auto get(ArgTypes ... args) -> decltype(Funct::template run<0>(args ...)) {
return (
accum1d_for<n-1>::template get<Funct>(args ...) +
Funct::template run<n>(args ...)
);
}
};
template<>
struct accum1d_for<0> {
template <class Funct, class ... ArgTypes>
static auto get(ArgTypes ... args) -> decltype(Funct::template run<0>(args ...)) {
return static_cast<decltype(Funct::template run<0>(args ...))>(0);
}
};
int main()
{
std::cout << accum1d_for<10>::get<F>(2.f) << std::endl;
}
Upvotes: 0
Views: 202
Reputation: 66230
I propose a C++14 solution (using std::index_sequence
and std::make_index_sequence()
; if you need a C++11 solution should be simple create a substitute).
I've added constexpr
to your get()
so you can use it to initialize constexpr
values.
And I've used std::size_t
instead int
because you wrote about "index", so it's a little simpler.
The example
#include <utility>
#include <iostream>
struct Functor3D
{
template <std::size_t x, std::size_t y, std::size_t z>
static constexpr std::size_t run ()
{ return x*y*z; }
};
template <std::size_t ... topIs>
struct accum_for
{
private:
template <std::size_t ... Is>
using IndS = std::index_sequence<Is...>;
template <typename Func, std::size_t ... Is>
static constexpr std::size_t h1 (IndS<Is...> const &)
{ return Func::template run<Is...>(); }
template <typename Func, std::size_t ... I0s,
std::size_t ... Ins, typename ... Ts>
static constexpr std::size_t h1 (IndS<I0s...>, IndS<Ins...>,
Ts const & ... ts)
{
using unused = int[];
std::size_t ret { 0 };
(void)unused { 0,
((void)(ret += h1<Func>(IndS<I0s..., Ins>{}, ts...)), 0)... };
return ret;
}
public:
template <typename Func>
static constexpr std::size_t get ()
{ return h1<Func>(IndS<>{}, std::make_index_sequence<topIs>()...); }
};
int main(void)
{
constexpr std::size_t val { accum_for<3U, 3U, 3U>::get<Functor3D>() };
std::cout << val << std::endl;
}
If you can use C++17, the generic version of the h1()
helper function (the one with the unused
array) can be simplified as follows
template <typename Func, std::size_t ... I0s,
std::size_t ... Ins, typename ... Ts>
static constexpr std::size_t h1 (IndS<I0s...>, IndS<Ins...>,
Ts const & ... ts)
{ return ( h1<Func>(IndS<I0s..., Ins>{}, ts...) + ... ); }
Upvotes: 1
Reputation: 275730
Given n tuples, generate their cross product (the tuple of one element from each, where every combination is covered).
Given a tuple of integral constants, invoke run with their values.
Given a value, generate a tuple of integral constants from 0 to n-1.
Given a tuple, execute a function object on each element.
3 and 1 defines the cube you want to run on.
Pass that cube to 4, together with a lambda that does 2.
While it woukd be amusing to write, not worth it to replace 9 lines of code. None of the above operations are prewritten in std; I assume most metaprogramming libraries would have them implemented. It would take less than 50 lines for each of the above steps to write from scratch using std (possibly much less).
Upvotes: 1
Reputation: 217810
With std::index_sequence
, you may do
template <std::size_t N1, std::size_t N2, std::size_t N3>
struct accum_for
{
private:
template <class Funct, std::size_t ... Is>
static int get(std::index_sequence<Is...>) {
int res = 0;
using int_array = int[];
static_cast<void>(int_array{0, (res += Funct::template run<Is / N3 / N2,
(Is / N3) % N2,
Is % N3>())...});
return res;
}
public:
template <class Funct>
static int get() {
return get<Funct>(std::make_index_sequence<N1 * N2 * N3>());
}
};
Upvotes: 3