I'm reading the source code of spconv, a sparse convolution library wrote by c++ (and cuda), in the source code I found a complicate template usage, and I summarize it as a minimum working example below:
#include <iostream>
#include <sstream>
template<class... T>
struct mp_list {
};
template<class T, T... I> using mp_list_c = mp_list<std::integral_constant<T, I>...>;
template<class... Ts, class F>
constexpr F mp_for_each_impl(mp_list<Ts...>, F &&f) {
return (void) (std::initializer_list<int>{(f(Ts()), 0)...}), std::forward<F>(f);
}
template<class A, template<class...> class B>
struct mp_rename_impl {
};
template<template<class...> class A, class... T,
template<class...> class B>
struct mp_rename_impl<A<T...>, B> {
using type = B<T...>;
};
template<class A, template<class...> class B> using mp_rename = typename mp_rename_impl<A, B>::type;
template<class L, class F>
constexpr F mp_for_each(F &&f) {
return mp_for_each_impl(mp_rename<L, mp_list>(), std::forward<F>(f));
}
template<int... Is, typename F>
bool dispatch_int_noexcept(int idx, F &&f) {
static_assert(sizeof...(Is) > 0, "you need to provide at least one candidate");
bool notFound = true;
mp_for_each<mp_list_c<int, Is...>>([=, ¬Found, &f, &idx](auto I) {
if (decltype(I)::value == idx && notFound) {
std::forward<F>(f)(I);
notFound = false;
}
});
return !notFound;
}
template<int ... Is, typename F>
void dispatch_int(int idx, F &&f) {
if (!dispatch_int_noexcept<Is...>(idx, std::forward<F>(f))) {
std::stringstream ss;
mp_for_each<mp_list_c<int, Is...>>([=, &ss](auto I) { ss << decltype(I)::value << " "; });
std::cout << "unknown value " << idx << ", available: " << ss.str() << std::endl;
}
}
int main() {
int ndim;
std::cin >> ndim;
dispatch_int<2, 3, 4>(ndim, [&](auto I) {
constexpr int NDim = decltype(I)::value;
std::cout << "using ndim= " << NDim << std::endl;
// blablabla ... ...
});
return 0;
}
In main()
, the function dispatch_int<2, 3, 4>(ndim, [&](auto I) {})
reveives an argument ndim
, and checks if ndim
is in a predefined list <2, 3, 4>
, if it is, then execute the following code compiled with the specific ndim
.
However, I can't understand how this works, the templates defined above are really confusing. After a lot of searching, I guess it is a kind of template metaprogramming to dispatch argument, but still can't figure out the details, could someone explain how the above code works?