Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looping over a templated C++ function with int type

Is there a solution like this to loop over a function with a templated int parameter that doesn't require creating a new struct with a body() function any time forIdx is needed with a new function? Templated lambdas in C++20 seemed promising, but it didn't seem possible to specify template parameters that aren't automatically deduced.

struct LoopFunc {
    template <int i>
    void body() {
        std::cout << i;
    };
};

template<int i>
struct forIdx {
    template<typename T>
    static void loop(T&& func) {
        func.body<i>();
        forIdx<i - 1>::loop(func);
    }
};

template<>
struct forIdx<-1> {
    template<typename T>
    static void loop(T&& func) {};
};

int main() {
    forIdx<10>::template loop(LoopFunc{});
}

The function is used to create a cartesian product of tuple elements. DirectProduct contains elements that all have a static generateAllElements() function.

    struct CrossProduct {
        std::tuple<MockElement...> vals;
        std::set<DirectProduct> result;
        template <int num>
        void body() {
            if (result.empty()) {
                for (const auto& e2 : std::get<num>(vals).generateAllElements()) {
                    DirectProduct tmp;
                    std::get<num>(tmp.vals) = e2;
                    result.insert(tmp);
                }
            }
            else for (const DirectProduct& e1 : result)
                for (const auto& e2 : std::get<num>(vals).generateAllElements()) {
                    DirectProduct tmp = e1;
                    std::get<num>(tmp.vals) = e2;
                    result.insert(tmp);
                }
        };
    };

DirectProduct uses the CrossProduct in its own generateAllElements() function

    std::set<DirectProduct> generateAllElements() const {
        CrossProduct crossProduct{ };
        forIdx<std::tuple_size<std::tuple<MockElement...>>::value - 1>::template loop(crossProduct);
        return crossProduct.result;
    };
like image 325
Joy McGibbon Avatar asked Jun 04 '26 23:06

Joy McGibbon


2 Answers

"Templated lambdas in C++20" have you said?

Do you mean something as follows?

#include <iostream>
#include <type_traits>

template <std::size_t I>
void loop_func()
 { std::cout << I << ' '; };

int main ()
 {
    []<std::size_t ... Is>(std::index_sequence<Is...>)
    { (loop_func<sizeof...(Is)-Is-1u>(), ...); }
    (std::make_index_sequence<11u>{});
 }

That prints

10 9 8 7 6 5 4 3 2 1 0
like image 50
max66 Avatar answered Jun 07 '26 11:06

max66


template<auto x>
using value_t=std::integral_constant<std::decay_t<decltype(x)>,x>;
template<auto x>
constexpr value_t<x> value={};
template<std::size_t...Is>
using indexes_t=std::tuple<value_t<Is>...>;
template<std::size_t>
constexpr indexes_t<Is...> indexes={};

some compile time values.

template<std::size_t N>
constexpr auto indexes_upto=[]<std::size_t...Is>(std::index_sequence<Is...>){ return indexes<Is...>; }( std::make_index_sequence<N>{} );

now we are almost done.

void do_foreach_arg(auto f){
  return [&](auto&&...args){
    ((void)(f(std::forward<decltype(args)>(args))),...);
  };
}
template<std::size_t N>
auto do_foreach_index_upto( auto f ){
  std::apply( do_foreach_arg(std::move(f)), indexes_upto<N> );
}

your main now looks like

do_foreach_index_upto<N>([](auto I){ LoopFunc{}.body<I>(); });

but the LoopFunc class is really not needed. You can just call some_func<I>() directly.

What we do here is we make stateless compile time values representing the integers up to 10. We stuff them in a tuple, unpack them with std apply, and unpack that with do_foreach_arg.

We could probably skip the tuple "step" here, but more advanced use could find it useful.

like image 26
Yakk - Adam Nevraumont Avatar answered Jun 07 '26 12:06

Yakk - Adam Nevraumont



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!