Currently, I have a code like
#include <cstdint>
#include <iostream>
template<std::uint32_t idx>
void f() {
std::cout << "f(" << idx << ")\n";
}
template<std::uint32_t n>
void loop1() {
f<n>();
if constexpr (n > 0) {
loop1<n - 1>();
}
}
template<std::uint32_t idx>
void g() {
std::cout << "g(" << idx << ")\n";
}
template<std::uint32_t n>
void loop2() {
g<n>();
if constexpr (n > 0) {
loop2<n - 1>();
}
}
int main() {
loop1<4>();
loop2<2>();
}
It's necessary for f
, g
, loop1
and loop2
to be function templates. Now, I have to add more functions, but loop1
and loop2
will always be the same, a recursive template loop.
How can I pass the function template as (template) argument to loop
to achieve something like:
#include <cstdint>
#include <iostream>
template<std::uint32_t idx>
void f() {
std::cout << "f(" << idx << ")\n";
}
template<std::uint32_t idx>
void g() {
std::cout << "g(" << idx << ")\n";
}
template<std::uint32_t n, typename h>
void loop() {
h<n>();
if constexpr (n > 0) {
loop<n - 1, h>();
}
}
int main() {
loop<4, h>();
loop<2, h>();
}
I prefer to pass the function as template argument at compile time, but a solution to pass the function template as function argument at runtime would also solve my problem, e.g.
#include <cstdint>
#include <iostream>
template<std::uint32_t idx>
void f() {
std::cout << "f(" << idx << ")\n";
}
template<std::uint32_t idx>
void g() {
std::cout << "g(" << idx << ")\n";
}
template<std::uint32_t n, typename T>
void loop(T h) {
h<n>();
if constexpr (n > 0) {
loop<n - 1>(h);
}
}
int main() {
loop<4>(h);
loop<2>(h);
}
or
#include <cstdint>
#include <iostream>
template<std::uint32_t idx>
void f() {
std::cout << "f(" << idx << ")\n";
}
template<std::uint32_t idx>
void g() {
std::cout << "g(" << idx << ")\n";
}
template<std::uint32_t n, template<std::uint32_t> typename F>
void loop(F h) {
h<n>();
if constexpr (n > 0) {
loop<n - 1>(h);
}
}
int main() {
loop<4>(h);
loop<2>(h);
}
Is this even possible? I know, that all of my approaches are wrong syntax. It's just to illustrate, what I want to achieve.
CodePudding user response:
I would replace the template functions f
and g
by structs F
and G
respectively, each one with a template method invoke
:
struct F {
template<std::uint32_t idx>
void invoke() {
std::cout << "f(" << idx << ")\n";
}
};
struct G {
template<std::uint32_t idx>
void invoke() {
std::cout << "g(" << idx << ")\n";
}
};
template<std::uint32_t n, typename F>
void loop(F h) {
h.template invoke<n>();
if constexpr (n > 0) {
loop<n - 1>(h);
}
}
Nothing stops you from adding template functions f
and g
that call invoke
for F
and G
respectively:
template<std::uint32_t idx>
void f() {
F().invoke<idx>();
}
template<std::uint32_t idx>
void g() {
G().invoke<idx>();
}
CodePudding user response:
You cannot do this with free functions but you can do something similar to what you want to have with class template static member functions:
#include <cstdint>
#include <iostream>
template<std::uint32_t idx>
struct F {
static void function() {
std::cout << "f(" << idx << ")\n";
}
};
template<std::uint32_t idx>
struct G {
static void function() {
std::cout << "g(" << idx << ")\n";
}
};
template<std::uint32_t n, template<std::uint32_t> typename T>
void loop() {
T<n>::function();
if constexpr (n > 0) {
loop<n - 1, T>();
}
}
int main() {
loop<4, F>();
loop<2, G>();
}
Here's a demo.
CodePudding user response:
For this I would recommend a either a generic or a template lambda. The goal of our solution would be to send in a predicate that can receive the parameter idx
at compile time.
With template lambda (C 20):
#include <cstdint>
#include <iostream>
template<std::uint32_t idx>
void f() {
std::cout << "f(" << idx << ")\n";
}
template<std::uint32_t idx>
void g() {
std::cout << "g(" << idx << ")\n";
}
template<std::uint32_t n, typename F>
void loop(F h) {
h.template operator()<n>();
if constexpr (n > 0) {
loop<n - 1>(h);
}
}
int main() {
loop<4>([]<std::uint32_t idx>() {
return f<idx>();
});
loop<2>([]<std::uint32_t idx>() {
return g<idx>();
});
}
With generic lambda (compatible with C 14)
#include <cstdint>
#include <iostream>
template<std::uint32_t idx>
void f() {
std::cout << "f(" << idx << ")\n";
}
template<std::uint32_t idx>
void g() {
std::cout << "g(" << idx << ")\n";
}
template<std::uint32_t n, typename F>
void loop(F h) {
h(std::integral_constant<std::uint32_t, n>{});
if constexpr (n > 0) {
loop<n - 1>(h);
}
}
int main() {
loop<4>([](auto idx) {
return f<idx>();
});
loop<2>([](auto idx) {
return g<idx>();
});
}