samedi 7 novembre 2020

Dynamic conversion from integer value to type (C++11 template metaprogramming?)

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 :)

Aucun commentaire:

Enregistrer un commentaire