I have a template function:
template <class IndexType, class DataType, class EntityType>
void my_func(...)
I need to instantiate it for many combinations of IndexType
/DataType
/EntityType
. Is there an efficient way to do this?
Currently, I am instantiating the function explicitly for every combination of IndexType
/DataType
/EntityType
, and it takes a long list of instantiations.
I need to instantiate it for many combinations of
IndexType
/DataType
/EntityType
. Is there an efficient way to do this?
C++ doesn't provide a built-in mechanism to automatically generate explicit instantiations for all type combinations, requiring you to handle this manually. The language lacks a built-in mechanism to "loop over" type lists and force instantiations without additional work.
You can do this:
Either by relying on the compiler's implicit instantiation, which creates template instances as needed;
Or by using macros to streamline the process of creating explicit instantiations in specific translation units.
For example, you could write:
#define INSTANTIATE_MY_FUNC(INDEX, DATA, ENTITY) \
template void my_func<INDEX, DATA, ENTITY>(/* function arguments */);
INSTANTIATE_MY_FUNC(int, double, MyEntity)
INSTANTIATE_MY_FUNC(int, float, MyEntity)
INSTANTIATE_MY_FUNC(unsigned, double, MyEntity)
// ...and so on for each combination.
Both approaches help reduce boilerplate code while maintaining the necessary flexibility for template instantiation.
That being said, if you want to just get all the possible combinations of IndexType
/DataType
/EntityType
types been instatiated by the compiler, there might be a non-macro approch.
(I assume that you have some special reasons for this, instead of letting the compiler do it automatically, whenever is required.)
Lets say:
// Define your type lists (using parameter packs or tuples)
template<typename... Ts> struct TypeList { };
using IndexTypes = TypeList<int, unsigned>; // types for "IndexType"
using DataTypes = TypeList<double, float>; // types for "DataType"
using EntityTypes = TypeList<MyEntity, OtherEntity>; // types for "EntityType"
Then you might able to write:
// A helper that forces instantiation by taking the function's address.
template<typename IndexType, typename DataType, typename EntityType>
inline constexpr void instantiate_one() noexcept
{
[[maybe_unused]] auto ptr = &my_func<IndexType, DataType, EntityType>;
}
// Combined function using template lambdas to instantiate all combinations
template<typename... Is, typename... Ds, typename... Es>
constexpr void instantiate_all_combos(TypeList<Is...>, TypeList<Ds...>, TypeList<Es...>) noexcept
{
// Use C++20 template lambdas with immediate invocation for each type level
(([&]<typename I>() {
(([&]<typename D>() {
(instantiate_one<I, D, Es>(), ...);
}.template operator()<Ds>()), ...);
}.template operator()<Is>()), ...);
}
and just:
instantiate_all_combos(IndexTypes{}, DataTypes{}, EntityTypes{});