Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I check if an std::variant can hold a certain type

Tags:

c++

c++17

I have a class with an std::variant in it. This std::variant type is only allowed to hold a specific list of types.

I have template functions which allow the user of the class to insert various values into an std::unordered_map, the map holds values of this variant type. I.e., the user is only allowed to insert values if it's type is in the specific list of types. However, I don't want the user to be able to define this list of types themselves.

class GLCapabilities { public:     using VariantType = std::variant<GLint64>;  // in future this would have other types      template <typename T>     std::enable_if_t<???> AddCapability(const GLenum parameterName)     {         if(m_capabilities.count(parameterName) == 0)         {             /*... get correct value of type T ... */             m_capabilities.insert(parameterName,value);         }     }      template<typename T>     std::enable_if_t<???,T> GetCapability(const GLenum parameterName) const     {         auto itr = m_capabilities.find(parameterName);         if(std::holds_alternative<T>(*itr))             return std::get<T>(*itr);          return T;     }  private:     std::unordered_map<GLenum,VariantType> m_capabilities; }; 

You'll see in the above where there's the ???, how can I check? Some combination of std::disjunction with std::is_same?

Like

std::enable_if<std::disjunction<std::is_same<T,/*Variant Types???*/>...>> 

To make it clear, I'd prefer to not have to check against each allowed type manually.

like image 985
NeomerArcana Avatar asked Aug 26 '17 05:08

NeomerArcana


People also ask

Does std :: variant allocate?

According to cppreference ::std::variant must not allocate dynamic memory.

Does STD variant require RTTI?

Since this function is specific to a given type, you don't need RTTI to perform the operations required by std::any .

How does STD variant work?

std::variant (C++17)An instance of std::variant has a value from one of its types. The value must not be a reference, C-array or void. A std::variant can have one type more than once. A default-initialized std::variant will be initialized with its first type.


1 Answers

Edit: I actually dig your std::disjunction idea, and it absolutely works. You just have to extract the type list using template specialization.

My entire old-school recursive mess becomes simply:

template<typename T, typename VARIANT_T> struct isVariantMember;  template<typename T, typename... ALL_T> struct isVariantMember<T, std::variant<ALL_T...>>    : public std::disjunction<std::is_same<T, ALL_T>...> {}; 

Original answer: Here's a simple template that accomplishes this. It works by returning false for empty type lists. For non-empty lists, it returns true if the first type passes std::is_same<>, and recursively invokes itself with all but the first type otherwise.

#include <vector> #include <tuple> #include <variant>  // Main lookup logic of looking up a type in a list. template<typename T, typename... ALL_T> struct isOneOf : public std::false_type {};  template<typename T, typename FRONT_T, typename... REST_T> struct isOneOf<T, FRONT_T, REST_T...> : public    std::conditional<     std::is_same<T, FRONT_T>::value,     std::true_type,     isOneOf<T, REST_T...>   >::type {};  // Convenience wrapper for std::variant<>. template<typename T, typename VARIANT_T> struct isVariantMember;  template<typename T, typename... ALL_T> struct isVariantMember<T, std::variant<ALL_T...>> : public isOneOf<T, ALL_T...> {};  // Example: int main() {   using var_t = std::variant<int, float>;    bool x = isVariantMember<int, var_t>::value; // x == true   bool y = isVariantMember<double, var_t>::value; // y == false    return 0; } 

N.B. Make sure you strip cv and reference qualifiers from T before invoking this (or add the stripping to the template itself). It depends on your needs, really.

like image 77
Frank Avatar answered Oct 12 '22 13:10

Frank