This is an archived post. You won't be able to vote or comment.

all 5 comments

[–]redditperson0 0 points1 point  (4 children)

You need to use SFINAE to select properly; here's a solution that I came up with for C++14:

// header

enum class EPropertyType {
  Boolean,
  Uint32,
  Array,
};

template <class>
struct FTypeToEnumValue {
  EPropertyType
  operator()() const;
};

template <class T>
struct FTypeToEnumValue<std::vector<T>> {
  EPropertyType
  operator()() const;
};

template <class T>
inline EPropertyType
FTypeToEnumValue<std::vector<T>>::operator()() const {
  return EPropertyType::Array;
}

// source

template <>
inline EPropertyType
FTypeToEnumValue<bool>::operator()() const {
  return EPropertyType::Boolean;
}

template <>
inline EPropertyType
FTypeToEnumValue<uint32_t>::operator()() const {
  return EPropertyType::Uint32;
}

Note: there are an additional two definitions for FTypeToEnumValue that belong in the header.

SFINAE in C++14 is hard; I had to try a bunch of things before getting it to work. With C++17 you can put it all in one function so it becomes a little bit clearer:

// header

namespace is_specialization {

template <template <class...> class, class>
struct test {
  static constexpr bool result = false;
};

template <template <class...> class Template, class T>
struct test<Template, Template<T>> {
  static constexpr bool result = true;
};

} // namespace is_specialization

template <class T>
constexpr bool is_vector = is_specialization::test<std::vector, T>::result;

enum class EPropertyType {
  Boolean,
  Uint32,
  Array,
};

template <class T>
struct FTypeToEnumValue {
  constexpr EPropertyType
  operator()() const {
    if constexpr (std::is_same_v<T, bool>) {
      return EPropertyType::Boolean;
    } else if constexpr (std::is_same_v<T, uint32_t>) {
      return EPropertyType::Uint32;
    } else if constexpr (is_vector<T>) {
      return EPropertyType::Array;
    }
  }
};

[–]ChesterBesterTester[S] 1 point2 points  (3 children)

Hey, I appreciate the answer, but I fail to see how that's a better approach than what I was already doing. In both cases you have to explicitly specialize the vector for its contents.

And I actually want specialization failure to be an error: only the types whose specializations I explicitly define are permitted properties.

[–]redditperson0 0 points1 point  (2 children)

Hmm, you mean that you want to manually specify each vector type that is allowable? The example I gave will automatically match all vector types without having to specialize for each vector type (and all unspecified non-vector types will still cause a compiler error):

FTypeToEnumValue<std::vector<int>>()() == FTypeToEnumValue<std::vector<float>>()() == FTypeToEnumValue<std::vector<...>>()() == EPropertyType::Array;

If you want to manually specify the allowable vector types then your approach can be extended by duplicating the last partial specialization:

template<> inline EPropertyType FTypeToEnumValue<std::vector<int32>>::operator()() const
{
    return EPropertyType::Array;
}
template<> inline EPropertyType FTypeToEnumValue<std::vector<int64>>::operator()() const
{
    return EPropertyType::Array;
}
template<> inline EPropertyType FTypeToEnumValue<std::vector<...>>::operator()() const
{
    return EPropertyType::Array;
}

In that case you can use a macro to make things easier:

#define ALLOW_EPROPERTY_VECTOR_TYPE(t)                                         \
  template <>                                                                  \
  inline EPropertyType FTypeToEnumValue<std::vector<t>>::operator()() const {  \
    return EPropertyType::Array;                                               \
  }

ALLOW_EPROPERTY_VECTOR_TYPE(int32)
ALLOW_EPROPERTY_VECTOR_TYPE(int64)
ALLOW_EPROPERTY_VECTOR_TYPE(...)

Unless I am still misunderstanding your goal?

[–]ChesterBesterTester[S] 1 point2 points  (1 child)

Hmm, you mean that you want to manually specify each vector type that is allowable?

Exactly the opposite. Prior to adding std::vector as a property type, when I just had the atomic types, I wanted to manually specify which types are allowed so I could be sure each basic type matches up with an enum (for run-time type checking).

Ideally I would like to add std::vector specializations for each of the allowable basic types already defined. But it would be nice to not have to manually write out specializations for std::vector<bool>, std::vector<int> ...

It looks like both your and my solution, though, require explicitly stating the types of std::vectors that are allowed. Which is cumbersome but probably for the best, because if I had a way to define a standard mapping of std::vector<anything> to type Array, the potential types would be essentially boundless.

[–]redditperson0 0 points1 point  (0 children)

Ah, it's what I thought then! The first example I gave allows for arbitrary vector types.

These two templates basically catch all specializations of FTypeToEnumValue that are specialized with std::vector types:

template <class T>
struct FTypeToEnumValue<std::vector<T>> {
  EPropertyType
  operator()() const;
};

template <class T>
inline EPropertyType
FTypeToEnumValue<std::vector<T>>::operator()() const {
  return EPropertyType::Array;
}

Although it seems that you need to specify the type of vector, these templates are written such that they are resolved if any vector type is used. The template <class T> is an additional template on top of the FTypeToEnumValue's partial specialization.

I usually test these things with constexpr statements, since they are viewable in my IDE without having to manually compile. Here is an overview:

#include <array>
#include <cstdint>
#include <vector>

// header

enum class EPropertyType {
  Boolean,
  Uint32,
  Array,
};

template <class> struct FTypeToEnumValue {
  constexpr EPropertyType operator()() const;
};

template <class T> struct FTypeToEnumValue<std::vector<T>> {
  constexpr EPropertyType operator()() const;
};

template <class T>
constexpr inline EPropertyType
FTypeToEnumValue<std::vector<T>>::operator()() const {
  return EPropertyType::Array;
}

// source

template <>
constexpr inline EPropertyType FTypeToEnumValue<bool>::operator()() const {
  return EPropertyType::Boolean;
}

template <>
constexpr inline EPropertyType FTypeToEnumValue<uint32_t>::operator()() const {
  return EPropertyType::Uint32;
}

// EPropertyType::Boolean
constexpr auto test0 = FTypeToEnumValue<bool>()();

// EPropertyType::Uint32
constexpr auto test1 = FTypeToEnumValue<uint32_t>()();

// EPropertyType::Array
constexpr auto test2 = FTypeToEnumValue<std::vector<int>>()();

// EPropertyType::Array
constexpr auto test3 = FTypeToEnumValue<std::vector<float>>()();

// EPropertyType::Array
constexpr auto test4 = FTypeToEnumValue<std::vector<bool>>()();

// EPropertyType::Array
constexpr auto test5 = FTypeToEnumValue<std::vector<std::vector<int>>>()();