I am trying to reduce code duplication through templates. I already moved most code to this helper iterate_function_from_CSC_helper which is now a template. However, this function still repeats a lot of code just to call a different specialization of a template:
std::function<std::pair<int, double>(int idx)>
IterateFunctionFromCSC(const void* col_ptr, int col_ptr_type, const int32_t* indices, const void* data, int data_type, int64_t ncol_ptr, int64_t , int col_idx) {
CHECK(col_idx < ncol_ptr && col_idx >= 0);
if (data_type == C_API_DTYPE_FLOAT32) {
if (col_ptr_type == C_API_DTYPE_INT32) {
return iterate_function_from_CSC_helper<float, int32_t>(col_ptr, indices, data, col_idx);
} else if (col_ptr_type == C_API_DTYPE_INT64) {
return iterate_function_from_CSC_helper<float, int64_t>(col_ptr, indices, data, col_idx);
}
} else if (data_type == C_API_DTYPE_FLOAT64) {
if (col_ptr_type == C_API_DTYPE_INT32) {
return iterate_function_from_CSC_helper<double, int32_t>(col_ptr, indices, data, col_idx);
} else if (col_ptr_type == C_API_DTYPE_INT64) {
return iterate_function_from_CSC_helper<double, int64_t>(col_ptr, indices, data, col_idx);
}
}
Log::Fatal("Unknown data type in CSC matrix");
return nullptr;
}
I'd like to automatically map the integers data_type and col_ptr_dtype which are received at runtime to the types float/double and int32_t/int64_t respectively and calling the template with those. Something like this:
std::function<std::pair<int, double>(int idx)>
IterateFunctionFromCSC(const void* col_ptr, int col_ptr_type, const int32_t* indices, const void* data, int data_type, int64_t ncol_ptr, int64_t , int col_idx) {
CHECK(col_idx < ncol_ptr && col_idx >= 0);
if (<TTag<data_col>::invalid_type || TTag<col_ptr_type>::invalid_type) {
Log::Fatal("Unknown data type in CSC matrix");
return nullptr;
}
return iterate_function_from_CSC_helper<TTag<data_type>::type, TTag<col_ptr_type>::type>(col_ptr, indices, data, col_idx);
}
Is that possible? I assumed with some metaprogramming one could eliminate this.
I tried the following but cannot make dummy_IterateFunctionFromCSC consume a non const input (which will be the case at runtime):
#include <cstdint>
#include <stdio.h>
#include <iostream>
#include <type_traits>
#define C_API_DTYPE_FLOAT32 (0) /*!< \brief float32 (single precision float). */
#define C_API_DTYPE_FLOAT64 (1) /*!< \brief float64 (double precision float). */
#define C_API_DTYPE_INT32 (2) /*!< \brief int32. */
#define C_API_DTYPE_INT64 (3) /*!< \brief int64. */
struct TTagInvalidType {}; //! Meant for invalid types in TTag.
template <int C_API_DTYPE>
struct TTag {
using type = TTagInvalidType;
};
template<>
struct TTag<C_API_DTYPE_FLOAT32> {
using type = float;
};
template <>
struct TTag<C_API_DTYPE_FLOAT64> {
using type = double;
};
template <>
struct TTag<C_API_DTYPE_INT32> {
using type = int32_t;
};
template <>
struct TTag<C_API_DTYPE_INT64> {
using type = int64_t;
};
template <typename T>
void example_f () {
T x = 3.6;
std::cout << x << "\n";
}
template <>
void example_f<TTagInvalidType>() {
std::cout << "Abort!\n";
}
template<int x>
void dummy_IterateFunctionFromCSC() {
f<typename TTag<x>::type>();
}
int main() {
const int m = 2; // Doesn't work for non const integers (true at runtime)
dummy_IterateFunctionFromCSC<m>();
}
This compiles but only with constant m, not with an integer received from the user for instance.
Is this impossible because the type-dispatching must be computed at compile time? Or is it possible and how? :D
Thank you :)
iterate_function_from_CSC_helper<type1, type2>(args...)as we exposeIterateFunctionFromCSCin the API for the user (that is our entry point). This is what you mean by template instantiation? And that it is impossible?mindummy_IterateFunctionFromCSC<m>()need to beconstexpr, soif(m == 2) dummy_IterateFunctionFromCSC<2>();would work.