0

I'm trying to concisely define a variable template with these effective values:

// (template<typename T> constexpr T EXP = std::numeric_limits<T>::max_exponent / 2;)

// float and double scalar definitions:
const double huge = std::scalbn(1, EXP<double>);
const float huge = std::scalbn(1, EXP<float>);
// SIMD vector definitions:
const Vec8f huge = Vec8f(huge<float>); // vector of 8 floats
const Vec8d huge = Vec8d(huge<double>); // vector of 8 doubles
const Vec4f huge = Vec4f(huge<float>); // vector of 4 floats
// Integral types should fail to compile

The VecXX vector definitions (SIMD vectors) need to use the corresponding scalar type as shown (e.g. huge<float> for vector of floats). This is available as VecXX::value_type or through a type traits-style template class (VectorTraits<VecXX>::value_type).

Ideally I think I'd have something like:

// Primary. What should go here? I want all other types to not compile
template<typename T, typename Enabler = void>
const T huge = T{ 0 };

// Scalar specialization for floating point types:
template<typename T>
const T huge<T> = std::enable_if_t<std::is_floating_point<T>::value, T>(std::scalbn(1, EXP<T>));

// Vector specialization, uses above declaration for corresponding FP type
template<typename T>
const T huge<T> = std::enable_if_t<VectorTraits<T>::is_vector, T>(huge<VectorTraits<T>::scalar_type>);

but I can't quite figure out a working version (above fails with "redefinition of const T huge<T>"). What's the best way to do this?

ZachB
  • 13,051
  • 4
  • 61
  • 89

2 Answers2

2

Not exactly what you asked but I hope the following example can show you how to use SFINAE to specialize a template variable

template <typename T, typename = void>
constexpr T huge = T{0};

template <typename T>
constexpr T huge<T, std::enable_if_t<std::is_floating_point<T>{}>> = T{1};

template <typename T>
constexpr T huge<std::vector<T>> = T{2};

You can check it with

std::cout << huge<int> << std::endl;
std::cout << huge<long> << std::endl;
std::cout << huge<float> << std::endl;
std::cout << huge<double> << std::endl;
std::cout << huge<long double> << std::endl;
std::cout << huge<std::vector<int>> << std::endl;
max66
  • 65,235
  • 10
  • 71
  • 111
  • 1
    That might be as good as it gets, and I guess I could add another specialization with `std::enable_if*is vector*/>` to restrict it to vector types. – ZachB Dec 31 '18 at 00:25
  • @ZachB - not sure to understand what do you want but... added specialization – max66 Dec 31 '18 at 00:31
  • sorry, I meant that I could add a specialization with `std::enable_if* is Vec4f or Vec8f or VecXX... */>`. I know how to do that, was just thinking out loud. Thanks! – ZachB Dec 31 '18 at 03:41
  • I've (hopefully) clarified the question and posted the specific solution so far, pointing out the remaining flaws, in case you have any ideas for improvements. – ZachB Dec 31 '18 at 09:29
0

Leaving @max66's answer as the accepted one for the credit, but here's the specific solution I wound up with:

struct _VectorTraits { static constexpr bool is_vector = true; };
template<class T> struct VectorTraits : _VectorTraits { static constexpr bool is_vector = false; };
template<> struct VectorTraits<Vec4f> : _VectorTraits { typedef float value_type; };
template<> struct VectorTraits<Vec8f> : _VectorTraits { typedef float value_type; };
template<> struct VectorTraits<Vec4d> : _VectorTraits { typedef double value_type; };

template<typename T> using EnableIfFP = std::enable_if_t<std::is_floating_point<T>::value>;
template<typename T> using EnableIfVec = std::enable_if_t<VectorTraits<T>::is_vector>;

template<typename T> constexpr T EXP = std::numeric_limits<T>::max_exponent / 2;

// Actual variable template, finally:
template<typename T, typename Enabler = void> const T huge = T{ 0 };
template<typename T> const T huge<T, EnableIfFP<T> > = std::scalbn(1, EXP<T>);
template<typename T> const T huge<T, EnableIfVec<T> > = T{ huge<typename VectorTraits<T>::value_type> };

This could still be improved I think:

  • It's verbose. T appears 4 times in each specialization's left-hand side.
  • Integral types (e.g. huge<uint32_t>) still compile with a nonsense 0 value. I'd rather them not compile.
ZachB
  • 13,051
  • 4
  • 61
  • 89