0

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

3
  • 1
    Do you want template instantiations depending on run-time values? Did I understand that correctly? If so, you can't. Commented Nov 8, 2020 at 0:14
  • There's a finite and very small set of types in our template function (4): int,float,long,double. I'm looking to convert 2 integers at runtime to 2 types to specialize my template function call iterate_function_from_CSC_helper<type1, type2>(args...) as we expose IterateFunctionFromCSC in the API for the user (that is our entry point). This is what you mean by template instantiation? And that it is impossible? Commented Nov 8, 2020 at 0:27
  • 1
    Non-type parameters, like m in dummy_IterateFunctionFromCSC<m>() need to be constexpr, so if(m == 2) dummy_IterateFunctionFromCSC<2>(); would work. Commented Nov 8, 2020 at 0:35

1 Answer 1

2

Turning runtime value to compile time value requires indeed some if/switch like you did.

You might avoid some duplication by additional split:

C++17 might help reduce verbosity with std::variant, some utilities:

template <typename T> struct type_identity { using type = T; };

// type should be an enum
std::variant<type_identity<int32_t>, type_identity<int64_t>> to_compile_int_type(int type)
{
    switch (type) {
        case C_API_DTYPE_INT32: return type_identity<int32_t>{};
        case C_API_DTYPE_INT64: return type_identity<int64_t>{};
        default:
            Log::Fatal("Unknown int data type");
            throw "unknown type";
    }
}

// type should be an enum
std::variant<type_identity<float>, type_identity<double>> to_compile_float_type(int type)
{
    switch (type) {
        case C_API_DTYPE_FLOAT32: return type_identity<float>{};
        case C_API_DTYPE_FLOAT64: return type_identity<double>{};
        default:
            Log::Fatal("Unknown float data type");
            throw "unknown type";
    }
}

And then

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);
    std::visit(
        [&](auto intvar, auto floatvar){
            using inttype = typename decltype(intvar)::type;
            using floattype = typename decltype(floatvar)::type;

            return iterate_function_from_CSC_helper<floatype, inttype>(col_ptr, indices, data, col_idx);
        },
        to_compile_int_type(col_ptr_type),
        to_compile_float_type(data_type)
    );
}
Sign up to request clarification or add additional context in comments.

4 Comments

I'm limited to c++11, is this possible in it as well with some alternative to variant?
@Alberto there's boost::variant. In fact most new C++ standard functions begin their life from boost, so just check the boost equivalent
@phuclv: and generic lambda has to be written the old way too :(
another alternative: github.com/mpark/variant

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.