Using an index in variadic function template - c++

template <std::size_t N, class... Args>
using type = typename std::tuple_element<N, std::tuple<Args...>>::type;
template<typename ...Args, typename Func>
typename std::result_of<Func(Args...)>::type functionOnParams(Func fp, Args&&...args)
{
using Indices = std::make_index_sequence<sizeof...(Args)>;
int ints[2] = {1,2};
return fp(std::forward<Args>( static_cast<type<index_of_args, Args...>>(ints[index_of_args++]) )...);
}
Hi,
I have spent a number of hours trying to figure to how to get the index_of_args for the above code. Any help would be appreciated. My searches keep going around in circles. index_of_args does not exist. I need to generate this index for each element of the std::forward.
Thank you

you can get make an std::index_sequence from Args and pass them to a helper function (by template arguments deduction). and then do anything you want in the helper function.
template<typename Func, typename... Args, size_t... Is, typename RealArgs>
std::invoke_result_t<Func&&, Args&&...> helper(Func&& func, std::index_sequence<Is...>* indicesTag, std::tuple<Args...>* argsTypeTag, RealArgs&& args){
return std::forward<Func>(func)(std::forward<Args>(std::forward<RealArgs>(args)[Is])...);
}
template<typename Func, typename... Args>
std::invoke_result_t<Func&&, Args&&...> functionOnParams(Func&& func, Args&&... args){
int a[] = { 0, 1 };
return helper(std::forward<Func>(func), (std::index_sequence_for<Args...>*)nullptr, (std::tuple<Args...>*)nullptr, a /* anything to replace the arguments */);
}
int main(){
auto x = functionOnParams(std::plus<>(), 0, 1.0f);
std::cout << x;
return 0;
}
in the other hand, you can also use class template specialization to pass the std::index_sequence and Args.
template<typename, typename>
struct Helper;
template<typename... Args, size_t... Is>
struct Helper<std::index_sequence<Is...>, std::tuple<Args...>>{
template<typename Func, typename RealArgs>
static std::invoke_result_t<Func&&, Args&&...> call(Func&& func, RealArgs&& args){
return std::forward<Func>(func)(std::forward<Args>(std::forward<RealArgs>(args)[Is])...);
}
};
template<typename Func, typename... Args>
std::invoke_result_t<Func&&, Args&&...> functionOnParams(Func&& func, Args&&... args){
int a[] = { 0, 1 };
return Helper<std::index_sequence_for<Args...>, std::tuple<Args...>>::call(std::forward<Func>(func), a /* anything to replace the arguments */);
}

Related

unpack each element of an array into a template parameter

So I have a constexpr array, is there a way to templatize each of its elements as a template parameter, the purpose is that so I can call a function of each of them, but instead of a loop which is runtime, I'd like to call it at compile time, also be able to use static_assert on it? So far I tried the following but it didn't work, the compiler couldn't deduce the proper parameter to the func. I'm using version c++17.
template <auto param, typename Function, typename... Args>
void invoke_param(Function func, Args... args)
{
func(param, args...);
}
template <auto... params, typename Function, typename... Args>
void invoke_params(Function func, Args... args)
{
(invoke_param<params>(func, args...), ...);
}
template <auto& Array, std::size_t... Indices, typename Function, typename... Args>
auto for_each_array_impl(std::index_sequence<Indices...>, Function func, Args... args)
{
invoke_params<Array[Indices]...>(func, args...);
}
template <auto& Array, typename Function, typename... Args>
auto for_each_array(Function func, Args... args)
{
return for_each_array_impl<Array>(
std::make_index_sequence<std::size(Array)>(), func, args...);
}
static constexpr std::array<int, 5> ARR{1,2,3,4,5};
int main() {
auto fun = []<int E>() {
static_assert(E > 0);
//...;
};
for_each_array<ARR>(func);
}
You need to pass param as a template parameter if you want it to be a template parameter
template <auto param, typename Function, typename... Args>
void invoke_param(Function func, Args... args)
{
func.template operator()<param>(args...);
}
As an aside, you need C++20 for []<int E>(), before then you'd have to write it longhand.
See it on coliru
In C++20 we can condense all the boilerplate down into a single function thanks to template lambdas and expanded auto parameters:
Live Demo
template<auto& arr, auto F>
consteval auto for_each_array()
{
[]<auto... indices>(std::index_sequence<indices...>)
{
(F.template operator()<arr[indices]>(), ...);
}(std::make_index_sequence<arr.size()>{});
}
static constexpr std::array<int, 5> ARR{1,2,3,4,5};
int main() {
constexpr auto fun = []<int E>()
{
static_assert(E > 0);
//...
};
for_each_array<ARR, fun>();
}
for_each_array creates a helper lambda and immediately invokes it, making use of a fold-expression to call your function for each array value.
EDIT:
C++17 version
template<auto& arr, class F, auto... indices>
constexpr auto for_each_array_impl(F callable, std::index_sequence<indices...>)
{
(callable.template operator()<arr[indices]>(), ...);
}
template<auto& arr, class F>
constexpr auto for_each_array(F&& callable)
{
for_each_array_impl<arr>(std::forward<F>(callable),
std::make_index_sequence<arr.size()>{});
}
Called like so:
struct fun
{
template <int E>
void operator()()
{
static_assert(E > 0);
//...
}
};
static constexpr std::array<int, 5> ARR{1,2,3,4,5};
int main() {
for_each_array<ARR>(fun{});
}

Can we use variadic template function to filter parameters of specific type, then pass the rest to another function?

for example
// we have a variadic function
void print(...);
// I need such a function to filter parameters of specific type
template<typename... Args>
void print_filter(const Args&... args)
{
// filter non-integral type
print(integral args);
}
// my goal, all non-integral type can be ignored:
print_filter(1.0, 2, "abc", 3) == print(2, 3)
I have used up my knowledge to do that... can you help? or just to prove it's impossible, which also be very helpful. Thanks
A neat trick is to convert the arguments you want into a 1-element forwarding tuple, the arguments you don't want into an empty tuple, tuple_cat the results, then apply (C++17) the resulting tuple to the function you want to invoke:
template<typename... Args>
void print_filter(Args&&... args) {
std::apply(
[](auto&&... args) { return print(std::forward<decltype(args)>(args)...); },
std::tuple_cat(
std::get<std::is_integral<typename std::decay<Args>::type>::value ? 0 : 1>(
std::make_tuple(
[](Args&& arg) { return std::tuple<Args&&>{std::forward<Args>(arg)}; },
[](Args&&) { return std::tuple<>{}; }))(
std::forward<Args>(args))...));
}
Note that this employs another trick, which is to use get to conditionally apply one of two functions to an argument.
Example.
#include <cstddef>
#include <type_traits>
#include <utility>
#include <tuple>
template <typename S, typename M, typename O = std::index_sequence<>>
struct filter : O {};
template <std::size_t I, std::size_t... Is, std::size_t... Js, std::size_t... Ks>
struct filter<std::index_sequence<I, Is...>, std::index_sequence<0, Js...>, std::index_sequence<Ks...>>
: filter<std::index_sequence<Is...>, std::index_sequence<Js...>, std::index_sequence<Ks...>> {};
template <std::size_t I, std::size_t... Is, std::size_t... Js, std::size_t... Ks>
struct filter<std::index_sequence<I, Is...>, std::index_sequence<1, Js...>, std::index_sequence<Ks...>>
: filter<std::index_sequence<Is...>, std::index_sequence<Js...>, std::index_sequence<Ks..., I>> {};
template <template <typename T> class F, typename... Args>
using Filter = filter<std::make_index_sequence<sizeof...(Args)>, std::index_sequence<F<Args>{}...>>;
template <typename... Args, std::size_t... Is>
void print_filter_impl(std::tuple<Args...>&& tuple, std::index_sequence<Is...>)
{
print(std::get<Is>(std::move(tuple))...);
}
template <typename... Args>
void print_filter(Args&&... args)
{
print_filter_impl(std::forward_as_tuple(std::forward<Args>(args)...), Filter<std::is_integral, std::decay_t<Args>...>{});
}
DEMO
Here's one way to do it. Start by creating a dummy separator class:
class SEP { };
Then, a helper function that discards all non-integral arguments by pushing the other arguments to the end of the argument list:
template <class T, class... R>
void print_filter_(T x, R... a) {
if (std::is_integral<T>::value) {
print_filter_(a..., x);
} else {
print_filter_(a...);
}
}
After going through all the initial arguments, it is left with only the integral ones:
template <class... T>
void print_filter_(SEP, T... a) {
print(a...);
}
Finally, call the helper function:
template <class... T>
void print_filter(T... a) {
print_filter_(a..., SEP());
}
#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>
template <template <typename> class Predicate, std::size_t N, typename Output, typename... Args> struct find_indices_h;
template <template <typename> class Predicate, std::size_t N, std::size_t... Is, typename First, typename... Rest>
struct find_indices_h<Predicate, N, std::index_sequence<Is...>, First, Rest...> : std::conditional_t<
Predicate<First>::value,
find_indices_h<Predicate, N+1, std::index_sequence<Is..., N>, Rest...>,
find_indices_h<Predicate, N+1, std::index_sequence<Is...>, Rest...>
> {};
template <template <typename> class Predicate, std::size_t N, typename Sequence>
struct find_indices_h<Predicate, N, Sequence> {
using type = Sequence;
};
template <template <typename> class Predicate, typename... Args>
using find_indices = typename find_indices_h<Predicate, 0, std::index_sequence<>, Args...>::type;
template <typename... Args>
void print (Args&&... args) {
const int a[] = {(std::cout << args << ' ', 0)...};
static_cast<void>(a);
std::cout << '\n';
}
template <typename F, typename Tuple, std::size_t... Is>
void partial_apply (F f, Tuple&& tuple, std::index_sequence<Is...>) {
f(std::get<Is>(std::forward<Tuple>(tuple))...);
}
template<typename... Args>
void print_filter (const Args&... args) {
const auto partial_print = [](auto&&... ps) { return print(std::forward<decltype(ps)>(ps)...); };
partial_apply(partial_print, std::forward_as_tuple(args...), find_indices<std::is_integral, Args...>{});
}
int main() {
std::cout << std::boolalpha;
print_filter(1, "hello", 'a', true, 1.3, 1000); // 1 a true 1000
}

Why template instantiations go on forever here?

In the following code, I want to replace
template <typename T, typename... Args>
auto check (rank<1,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<2, Ts...>{}, args...))> {
return check(rank<2, Ts...>{}, args...); // Since rank<1,T> derives immediately from rank<2, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<2,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<3, Ts...>{}, args...))> {
return check(rank<3, Ts...>{}, args...); // Since rank<2,T> derives immediately from rank<3, Ts...>.
}
// etc... until rank<9>.
with the simple
template <std::size_t N, typename T, typename... Args>
auto check (rank<N,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<N+1, Ts...>{}, args...))> {
return check(rank<N+1, Ts...>{}, args...); // Since rank<N,T> derives immediately from rank<N+1, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<10, T>, Args... args) const { std::cout << "Nothing found.\n"; }
But when I do, the template instantiations go on forever despite the terminating check(rank<10, T>, Args... args) function. Here is the full code using the long version above. Sorry for not minimizing the problem because I don't think I can minimize it and show what the problem is. Jumping to main() will show you the simple task I'm after, but I want to solve it using sequence ranking and overload resolution.
#include <iostream>
#include <type_traits>
#include <tuple>
template <typename T, typename... Args>
constexpr auto has_argument_type_impl(int)
-> decltype(std::is_same<typename T::argument_type, std::tuple<Args...>>{}); // Checking both that T::argument_type exists and that it is the same as std::tuple<Args...>.
template <typename T, typename... Args>
constexpr auto has_argument_type_impl(long) -> std::false_type;
template <typename T, typename... Args>
constexpr bool has_argument_type() { return decltype(has_argument_type_impl<T, Args...>(0))::value; }
template <typename T, std::size_t N, typename... Args>
constexpr auto has_argument_type_n_impl(int)
-> decltype(std::is_same<typename T::template argument_type<N>, std::tuple<Args...>>{}); // Checking both that T::argument_type<N> exists and that it is the same as std::tuple<Args...>.
template <typename T, std::size_t N, typename... Args>
constexpr auto has_argument_type_n_impl(long) -> std::false_type;
template <typename T, std::size_t N, typename... Args>
constexpr bool has_argument_type_n() { return decltype(has_argument_type_n_impl<T, N, Args...>(0))::value; }
template <typename... Ts>
class Factory {
template <std::size_t, typename...> struct rank;
template <std::size_t N, typename First, typename... Rest>
struct rank<N, First, Rest...> : rank<N, Rest...> {};
template <std::size_t N, typename T> struct rank<N,T> : rank<N+1, Ts...> {};
template <typename T> struct rank<10, T> {}; // Need to end the instantiations somewhere.
public:
template <typename... Args>
decltype(auto) create (Args... args) const {
return check(rank<0, Ts...>{}, args...);
}
private:
template <typename T, typename... Rest, typename... Args>
auto check (rank<0, T, Rest...>, Args... args) const
-> std::enable_if_t<has_argument_type<T, Args...>(), decltype(T(args...))> {
return T(args...);
}
template <typename T, typename... Rest, typename... Args>
auto check (rank<0, T, Rest...>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<0, Rest...>{}, args...))> {
return check(rank<0, Rest...>{}, args...);
}
template <typename T, typename... Args>
auto check (rank<0,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T, Args...>(), decltype(check(rank<1, Ts...>{}, args...))> {
return check(rank<1, Ts...>{}, args...); // Since rank<0,T> derives immediately from rank<1, Ts...>.
}
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<has_argument_type_n<T, N-1, Args...>(), decltype(T(args...))> {
return T(args...);
}
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, N-1, Args...>(), decltype(check(rank<N, Rest...>{}, args...))> {
return check(rank<N, Rest...>{}, args...);
}
// I want to use the following instead of what's below it.
// template <std::size_t N, typename T, typename... Args>
// auto check (rank<N,T>, Args... args) const
// -> std::enable_if_t<!has_argument_type_n<T, N-1, Args...>(), decltype(check(rank<N+1, Ts...>{}, args...))> {
// return check(rank<N+1, Ts...>{}, args...); // Since rank<N,T> derives immediately from rank<N+1, Ts...>.
// }
//
// template <typename T, typename... Args>
// auto check (rank<10, T>, Args... args) const { std::cout << "Nothing found.\n"; }
template <typename T, typename... Args>
auto check (rank<1,T>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, 0, Args...>(), decltype(check(rank<2, Ts...>{}, args...))> {
return check(rank<2, Ts...>{}, args...); // Since rank<1,T> derives immediately from rank<2, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<2,T>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, 1, Args...>(), decltype(check(rank<3, Ts...>{}, args...))> {
return check(rank<3, Ts...>{}, args...); // Since rank<2,T> derives immediately from rank<3, Ts...>.
}
// etc... until rank<9>.
};
// Testing
struct Object {
template <std::size_t, typename = void> struct ArgumentType;
template <typename T> struct ArgumentType<0,T> { using type = std::tuple<int, bool, char, double>; };
template <typename T> struct ArgumentType<1,T> { using type = std::tuple<bool, char, double>; };
template <std::size_t N> using argument_type = typename ArgumentType<N>::type;
Object (int, bool, char, double) { print(); }
Object (bool, char, double) { print(); }
void print() const { std::cout << "Object\n"; }
};
struct Thing {
template <std::size_t, typename = void> struct ArgumentType;
template <typename T> struct ArgumentType<0,T> { using type = std::tuple<int, int, char>; };
template <typename T> struct ArgumentType<1,T> { using type = std::tuple<int, char>; };
template <typename T> struct ArgumentType<2,T> { using type = std::tuple<char>; };
template <std::size_t N> using argument_type = typename ArgumentType<N>::type;
Thing (int, int, char) { print(); }
Thing (int, char) { print(); }
Thing (char) { print(); }
void print() const { std::cout << "Thing\n"; }
};
struct Blob {
using argument_type = std::tuple<int, double>;
Blob (int, double) { print(); }
void print() const { std::cout << "Blob\n"; }
};
struct Widget {
using argument_type = std::tuple<int>;
Widget (double, double, int, double) { print(); }
Widget (int) { print(); }
void print() const { std::cout << "Widget\n"; }
};
int main() {
Factory<Blob, Object, Thing, Widget>().create(4,3.5); // Blob
Factory<Object, Blob, Widget, Thing>().create(2); // Widget
Factory<Object, Thing, Blob, Widget>().create(5); // Widget
Factory<Blob, Object, Thing, Widget>().create(4,true,'a',7.5); // Object
Factory<Blob, Thing, Object, Widget>().create(true,'a',7.5); // Object
Factory<Blob, Object, Thing, Widget>().create('a'); // Thing
}
I know that there are other ways of accomplishing this, but I'm trying to understand sequence ranking better, and would like to know why I cannot use the commented-out section. How to avoid the repetitive code that I need to put (to rank<9>, or even higher rank) that is currently making this code work? Thanks for your patience.
Note: I actually must NOT enter the repetitive part of the code manually as I currently have. Because the highest N value for rank<N, Ts...> used in the check overloads will be determined during compile-time as the highest N value such that a argument_type<N> member type exists among all the Ts.... Thus I HAVE to use the generic part that I commented out, and the rank<10,T> I'm using will have to have the 10 replaced by that specific N value. Thus, this is not just a matter of convenience. I have to solve this problem to continue developing the program.
Edit: Here is a more minimal example, showing the same problem:
#include <iostream>
#include <type_traits>
#include <tuple>
template <typename T>
constexpr auto has_argument_type_impl(int)
-> decltype(typename T::argument_type{}, std::true_type{});
template <typename T>
constexpr auto has_argument_type_impl(long) -> std::false_type;
template <typename T>
constexpr bool has_argument_type() { return decltype(has_argument_type_impl<T>(0))::value; }
template <typename... Ts>
class Factory {
template <std::size_t, typename...> struct rank;
template <std::size_t N, typename First, typename... Rest>
struct rank<N, First, Rest...> : rank<N, Rest...> {};
template <std::size_t N, typename T> struct rank<N,T> : rank<N+1, Ts...> {};
template <typename T> struct rank<10, T> {}; // Need to end the instantiations somewhere.
public:
template <typename... Args>
decltype(auto) create (Args... args) const {
return check(rank<0, Ts...>{}, args...);
}
private:
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<has_argument_type<T>(), decltype(T(args...))> {
return T(args...);
}
template <std::size_t N, typename T, typename... Rest, typename... Args>
auto check (rank<N, T, Rest...>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<N, Rest...>{}, args...))> {
return check(rank<N, Rest...>{}, args...);
}
template <typename T, typename... Args>
auto check (rank<0,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<1, Ts...>{}, args...))> {
return check(rank<1, Ts...>{}, args...); // Since rank<0,T> derives immediately from rank<1, Ts...>.
}
// I want to use the following instead of what's below it.
// template <std::size_t N, typename T, typename... Args>
// auto check (rank<N,T>, Args... args) const
// -> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<N+1, Ts...>{}, args...))> {
// return check(rank<N+1, Ts...>{}, args...); // Since rank<N,T> derives immediately from rank<N+1, Ts...>.
// }
//
// template <typename T, typename... Args>
// auto check (rank<10, T>, Args... args) const { std::cout << "Nothing found.\n"; }
template <typename T, typename... Args>
auto check (rank<1,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<2, Ts...>{}, args...))> {
return check(rank<2, Ts...>{}, args...); // Since rank<1,T> derives immediately from rank<2, Ts...>.
}
template <typename T, typename... Args>
auto check (rank<2,T>, Args... args) const
-> std::enable_if_t<!has_argument_type<T>(), decltype(check(rank<3, Ts...>{}, args...))> {
return check(rank<3, Ts...>{}, args...); // Since rank<2,T> derives immediately from rank<3, Ts...>.
}
// etc... until rank<9>.
};
// Testing
struct Object {};
struct Thing {};
struct Blob {
using argument_type = std::tuple<int, double>;
Blob (int, double) { std::cout << "Blob\n"; }
};
int main() {
Factory<Object, Thing, Blob>().create(4,3.5); // Blob
}
Partial ordering does not kick in until very late in the overload resolution process.
Ignoring all the ping-ponging amongst your various check overloads, eventually you end up with
template <std::size_t N, typename T, typename... Args>
auto check (rank<N,T>, Args... args) const
-> std::enable_if_t<!has_argument_type_n<T, N, Args...>(),
decltype(check(rank<N+1, Ts...>{}, args...))>;
template <typename T, typename... Args>
auto check (rank<10, T>, Args... args) const;
with a rank<10, something I frankly don't care about>. Deduction and substitution will be performed for both overloads; and as part of substitution into the return type of the first signature, you'll instantiate rank<11, Ts...>, which in turn bypasses the terminating specialization of rank, resulting in an infinite chain of template instantiations. You don't even get to the point where the partial ordering tiebreaker chooses the second overload.
Just constrain the first overload to N < 10. It will need to lexically precede the return type (so that when N >= 10 the compiler doesn't attempt to substitute into it), so put it in a default template argument.

How to apply an if at compile time in C++

I am trying to write a general static_for implementation that can accept bounds, an increment function & a comparison function to run a loop through. I have been using this construct with simple loops that increment by 1. In that case it is easy to stop the loop unrolling by simply specializing on the IDX & END being equal.
However when the increment could be with an arbitrary integer, it is not guaranteed that the IDX & END will always be equal. The if conditional is only evaluated at run time. In the code snippet below I was trying to specialize on the std::false_type which stops the recursion. The integral_constant is constructed by evaluating the std::less functional (which could be substituted by the user for any other evaluation). Unfortunately this comparator functional is also evaluated only at run time and therefore the compiler fails. Could someone advise on how to get this to work?
NOTE: Using C++11.
template <int idx, int end, typename eval, int count, typename comparator>
struct static_for_loop {
template <typename Lambda, typename... Args>
void operator()(const Lambda& function, Args... args) const {
if (comparator()(idx, end)) {
std::integral_constant<int, idx> i;
function(i, args...);
constexpr bool lesser = comparator()(idx + count, end);
static_for_loop<idx + count, end, std::integral_constant<bool, lesser>, count,
comparator>()(function, args...);
}
}
};
template <int idx, int end, int count, typename comparator>
struct static_for_loop<idx, end, std::false_type, count, comparator> {
template <typename Lambda, typename... Args>
void operator()(const Lambda& function, Args... args) const {}
};
template <int idx, int end, int count = 1, typename comparator = std::less<int>>
struct static_for {
template <typename Lambda, typename... Args>
void operator()(const Lambda& function, Args... args) const {
static_for_loop<idx, end, std::true_type, count, comparator>()(function, args...);
}
};
I find it easier to just wrap everything in an object:
template <int S, int E, int step>
struct iter {
auto next() { return iter<std::min(E, S+step), E, step>{}; }
};
And then you just have an overload for the case where it's done and the case where it's not:
template <int S, int E, int step, class F, class... Args>
void for_loop(iter<S, E, step> i, F func, Args... args) {
func(S, args...);
for_loop(i.next(), func, args...);
}
template <int E, int step, class F, class... Args>
void for_loop(iter<E, E, step>, F, Args... ) {
}
For instance:
// prints 0 4 8
for_loop(iter<0, 10, 4>{}, [](int i){std::cout << i << ' ';});
Alternatively, could use enable_if to differentiate the cases to avoid the need for min:
template <int S, int E, int step, class F, class... Args>
std::enable_if_t<(S<E)> for_loop(iter<S, E, step>, F func, Args... args)
{
func(S, args...);
for_loop(iter<S+step, E, step>{}, func, args...);
}
template <int S, int E, int step, class F, class... Args>
std::enable_if_t<!(S<E)> for_loop(iter<S, E, step>, F , Args... )
{
}
YMMV on which you prefer.
You can use sfinae to overcome the problem:
template <int idx, int end, typename eval, int count, typename Comparator>
struct static_for_loop {
template <typename Lambda, typename... Args>
auto operator()(Lambda&& function, Args&&... args) const
-> std::enable_if_t<Comparator{}(idx, end)> {
std::integral_constant<int, idx> i;
std::forward<Lambda>(function)(i, std::forward<Args>(args)...);
constexpr bool lesser = comparator{}(idx + count, end);
static_for_loop<
idx + count,
END,
std::integral_constant<bool, lesser>,
count,
Comparator
>()(std::forward<Lambda>(function), std::forward<Args>(args)...);
}
// do nothing when false
template <typename Lambda, typename... Args>
auto operator()(Lambda&& function, Args&&... args) const
-> std::enable_if_t<!Comparator{}(idx, end)> {
}
};
std::enable_if will select the right function with sfinae. It will act as a compile time if.
I used perfect forwarding too, as your code didn't work in all case, like passing non copiable or a mutable lambda. Now it will.
If you do not have c++14, you can write typename std::enable_if<...>::type instead.
Try to use less all uppercase name, it hurts lisibility.
Isn't the problem that you are underspecifying comparator? Just specify your API such that comparator<IDX>::type is std::true_type if the loop should continue for IDX, and stop when it's false_type. Your simple loop case then uses template<int IDX> using Comp = std::integral_constant<bool, (IDX < 5)>`.

Variadic template indexed pack expansion

Assume the following templates:
template<typename T, bool stack>
struct bind_argument
{
static inline T get_arg(Object& obj, u8 index)
{
return ...;
}
};
template<typename RT, typename Arg0, typename... Args>
inline RT call(Object& obj, RT(*function)(Arg0, Args...))
{
constexpr bool use_stack = ...;
return function(..., bind_argument<Args, use_stack>::get_arg(obj, 0)...);
}
For bind_argument I need to pass the index of the expansion. Another question regarding the indexed expansion showcased the use of the "indices trick" using another template, but in my case I also need to pass the expanded arguments to the call of the function in the call method. This seems to be quite a bit harder than I thought.
My original solution using the "indices trick" looked like this:
template<bool stack, typename... Args, u64... Indices>
struct bind_arguments
{
static inline Args get_args(CPU& cpu, indices<Indices...>)
{
return bind_argument<Args, stack>(cpu, Indices)...;
}
};
template<typename RT, typename Arg0, typename... Args>
inline RT call(Object& obj, RT(*function)(Arg0, Args...))
{
constexpr bool use_stack = ...;
Arg0 some_value = ...;
return function(some_value, bind_arguments<use_stack, Args...>::get_args(obj, build_indices<sizeof...(Args)>{}));
}
Unfortunately this is not going to compile. How would one perform template indexed pack expansion inside another template and after that passing the expanded values to the place intended for the expanded values? (in this case the function() call)
The intended call expansion would be as follows:
function(some_value, bind_argument<A1, use_stack>(obj, 0), bind_argument<A2, use_stack>(obj, 1), bind_argument<A3, use_stack>(obj, 2), ...)
You can do whatever you want in that other function, forwarding all necessary arguments; there's no reason to return anything but the final result:
#include <utility>
#include <cstddef>
template <typename RT, typename Arg0, typename... Args, std::size_t... Is>
inline RT call(Object& obj, RT(*function)(Arg0, Args...), std::index_sequence<Is...>)
{
return function(&obj, bind_argument<Args>::get_arg(obj, Is)...);
}
template <typename RT, typename Arg0, typename... Args>
inline RT call(Object& obj, RT(*function)(Arg0, Args...))
{
return call(obj, function, std::make_index_sequence<sizeof...(Args)>{});
}
DEMO