I have a fairly simple variant class which supports a predefined set of types, and provides an enumerate to indicate which of the available types is currently active. Something like this:
class variant
{
enum class type { integer, real, string, etc };
type active_type() const;
/* ... */
};
I would like to make the class into a template where the supported types are supplied as template parameters:
template <typename... T>
class variant
{
const std::type_info& active_type() const; // logical, but can't switch on it
/* ... */
};
A key feature that I rely on for catching errors is that I can switch on the active type and the compiler will warn if any of the possible cases have been missed. This is not possible using the above design (nor using boost::variant).
My question is, is there any way for me to automatically generate an enum with the same number of enumerates as the number of arguments in the parameter pack?
The actual names/values of the enumerates would not matter, as they can be hidden behind constexpr functions used to map the type to the correct enumerate. I could imagine an eventual usage like this:
template <typename... T>
class variant
{
enum class type { T... }; // magic here
// specializations provided to map T into type (for use in case labels)
template <typename T>
static constexpr type type_enum();
type active_type() const;
/* ... */
};
typedef variant<int, float, std::string> myvar;
myvar var;
switch (var.active_type())
{
case myvar::type_enum<int>(): // static constexpr function
...
break;
case myvar::type_enum<float>():
...
break;
} // warning: enumeration for string not handled in switch
This question is two years old, but since there are no answers and I found no solutions elsewhere, here's a way how I have solved the problem.
First obvious problem is enum type with matching number of parameters (to let compiler do switch checking). The only way I see is a series of template specializations.
template <typename Tag, size_t size> struct Enum;
template <typename Tag> struct Enum<Tag, 0> {
enum class Type { };
};
template <typename Tag> struct Enum<Tag, 1> {
enum class Type {
VALUE0
};
};
template <typename Tag> struct Enum<Tag, 2> {
enum class Type {
VALUE0, VALUE1
};
};
template <typename Tag> struct Enum<Tag, 3> {
enum class Type {
VALUE0, VALUE1, VALUE2
};
};
template <typename Tag> struct Enum<Tag, 4> {
enum class Type {
VALUE0, VALUE1, VALUE2, VALUE3
};
};
Yes, it requires some manual input, but it's not a big problem, keeping in mind that number of types is usually limited and new specializations can be added as they are required (i.e. the approach is statically safe). The Tag parameter is used to distinguish enums with same number of values.
Secondly we need some recursive template magic to enumerate types:
template <typename EnumType, typename Type, typename... Types> struct TypeInfo;
template <typename EnumType, typename Type, typename... Types> struct TypeInfo<EnumType, Type, Type, Types...> {
static const EnumType value = EnumType::VALUE0;
};
template <typename EnumType, typename Type, typename FirstType, typename... Types> struct TypeInfo<EnumType, Type, FirstType, Types...> {
static const EnumType value = static_cast<EnumType>(static_cast<typename std::underlying_type<EnumType>::type>(TypeInfo<EnumType, Type, Types...>::value) + 1);
};
Finally a class to bring all pieces together:
template <typename Tag, typename... Ts> class TypedEnum {
private:
struct InternalTag;
public:
static const size_t NUM_TYPES = sizeof...(Ts);
typedef typename Enum<InternalTag, NUM_TYPES>::Type Type;
template <typename T> static constexpr decltype(TypeInfo<Type, T, Ts...>::value) getValue() { // SFINAE guard
return TypeInfo<Type, T, Ts...>::value;
}
};
Voila! You can switch! (Tested with clang)
struct Tag0;
struct Tag1;
typedef TypedEnum<Tag0, int, float, char> Enum0;
typedef TypedEnum<Tag0, bool, float, char> Enum1; // Incompatible with Enum0!
Enum0::Type tpy = Enum0::getValue<char>(); // 2
switch(tpy) {
case Enum0::getValue<int>():
break;
case Enum0::getValue<float>():
break;
case Enum0::getValue<char>():
break;
}
Summary of features:
Statically rejecting types that are not in enum
Statically warn about missing switch cases
Incompatible enum types for different tags and/or parameters in pack
Features missing:
Needs some template magic to reject parameter packs with duplicates
Get the index of the type in a variadic template and use that as the enum value. This way, you don't need to copy pasta all the code for different numbers of types. This doesn't handle duplicate types in the list, but the index will be the first occurrence in the variadic list. If there needs to be duplicate detection it can be added using std::is_same and looping over all the types for each type in a similar fashion to IndexOf.
#include <type_traits>
using std::size_t;
template <size_t Idx, class Q, class... Ts>
struct IndexOfImpl;
// base case
template <size_t Idx, class Q>
struct IndexOfImpl<Idx, Q>{};
template <size_t Idx, class Q, class T>
struct IndexOfImpl<Idx, Q, T>
: public std::conditional_t<
std::is_same<Q, T>::value, // Is Query the same as T?
// if so
std::integral_constant<size_t, Idx>, // inheret from Idx
void> // type not found
{
};
template <size_t Idx, class Q, class T, class... Ts>
struct IndexOfImpl<Idx, Q, T, Ts...>
: public std::conditional_t<
std::is_same<Q, T>::value, // Is Query the same as T?
// if so
std::integral_constant<size_t, Idx>, // inheret from Idx
IndexOfImpl<Idx + 1, Q, Ts...>> // else try the trailing types
{
};
// Initial case at Idx 0
template <class Q, class... Ts>
struct IndexOf : public IndexOfImpl<0, Q, Ts...>
{
};
// Base case
template <class Q>
struct IndexOf<Q>
{
};
// Store the type list in Ts...
template <class... Ts>
struct TypeEnum {
// Get index/enum value of type T
template <class T>
static constexpr decltype(IndexOf<T, Ts...>::value) getValue() {
return IndexOf<T, Ts...>::value;
}
};
template <>
struct TypeEnum<> {};
struct Unknown;
int main() {
using Tags = TypeEnum<Unknown, int, float, long long, unsigned int, double>;
size_t tag = Tags::getValue<double>();
switch(tag) {
case Tags::getValue<int>(): return 0;
case Tags::getValue<float>(): return 0;
case Tags::getValue<long long>(): return 0;
case Tags::getValue<unsigned int>(): return 0;
case Tags::getValue<double>(): return 0;
default: return 1;
}
}
On compiler explorer
Related
Problem.
I am new to template metaprogramming and am unsure of how to implement
a type filtering transformation on a tuple where a type is produced when provided
with a filtering description. I believe the following code snippet demonstrates
the behaviour I want.
enum : int {
INCLUDE,
EXCLUDE
};
template <int filter_val, class T>
struct filter {
};
int
main() {
struct A {};
struct B {};
struct C {};
typedef std::tuple<filter<INCLUDE, A>,
filter<EXCLUDE, B>,
filter<INCLUDE, C>> filters;
typedef filter_all<filters>::type filtered;
static_assert(std::is_same<filtered,
std::tuple<A, C>
>::value,
":(");
return 0;
}
What I've tried
To my knowledge, you cannot unpack more than 1 independent variadic templates so the way I thought about approaching the problem is to maintain two tuples during the recursive template specialization process where one of them represents where we are in the recursion while the other represents the so-far included T's.
template <template <class ...> class, class ...Unfiltered_Ts>
struct filter_all {
private:
template <class Unfiltered_Ts_Tuple, class Included_Ts_Tuple>
struct filter_all_impl;
// CASE 1: Include T in the result
template <
template <int, class> class, int filter_val, class T, class ...Unfiltered_Ts_impl, // Unfiltered input
template <class ...> class, class ...Included_Ts // Result so far
>
struct filter_all_impl<std::tuple<filter<INCLUDE, T>,
Unfiltered_Ts_impl...>,
std::tuple<Included_Ts...>> {
typedef typename
filter_all_impl<std::tuple<Unfiltered_Ts_impl...>,
std::tuple<Included_Ts..., T> // Append T to result
>::type type;
};
// CASE 2: Don't include T in the result
template <
template <int, class> class, int filter_val, class T, class ...Unfiltered_Ts_impl, // Unfiltered input
template <class ...> class, class ...Included_Ts // Result so far
>
struct filter_all_impl<std::tuple<filter<EXCLUDE, T>,
Unfiltered_Ts_impl...>,
std::tuple<Included_Ts...>
> {
typedef typename
filter_all_impl<std::tuple<Unfiltered_Ts_impl...>,
std::tuple<Included_Ts...> // Don't append T to result
>::type type;
};
// CASE 3: Filtering finished - set the final type as the included T's
template <
template <int, class> class, int filter_val, class T, class ...Unfiltered_Ts_impl,
template <class ...> class, class ...Included_Ts
>
struct filter_all_impl<<>, // empty
std::tuple<Included_Ts...>
> {
// Final step, set type as a tuple of all included Ts
typedef std::tuple<Included_Ts...> type;
};
public:
typedef typename filter_all_impl<
std::tuple<Unfiltered_Ts...>, // Initially contains all unfiltered Ts
std::tuple<> // Initially empty filtered Ts which eventually becomes the return type
>::type type;
};
I hope there is a simpler way to do this transformation but this is what I have arrived at so far but it's far from compiling and complains about the template specializations not being valid. Any guidance is appreciated.
Firstly, define a pick<T> helper that returns an empty tuple for excluded items, and a tuple containing the item otherwise:
template <typename>
struct pick;
template <typename T>
struct pick<filter<INCLUDE, T>> { using type = std::tuple<T>; };
template <typename T>
struct pick<filter<EXCLUDE, T>> { using type = std::tuple<>; };
Then, implement your filtering logic in terms of std::tuple_cat:
template <typename>
struct filter_all;
template <typename... Ts>
struct filter_all<std::tuple<Ts...>>
{
using type = decltype(std::tuple_cat(typename pick<Ts>::type{}...));
};
Done!
live example on wandbox.org
If you can modify the implementation of filter to expose a constexpr bool, it can be even simpler:
template <int filter_val, class T>
struct filter
{
static constexpr bool pick = filter_val == INCLUDE;
using type = T;
};
template <typename T>
struct filter_all;
template <typename... Ts>
struct filter_all<std::tuple<Ts...>>
{
using type = decltype(std::tuple_cat(
std::conditional_t<Ts::pick,
std::tuple<typename Ts::type>, std::tuple<>>{}...
));
};
live example on wandbox.org
I am new to making my own type traits and I want to make a type trait that allows me to identify if the passed type is the expected container.
template<typename T, typename ... Types>
struct is_array
{
static constexpr bool value = false;
};
template<typename ... Types>
struct is_array<std::array<Types...>>
{
static constexpr bool value = true;
};
I've adopted the format above and it works for all container types except for array:
constexpr bool state = is_array<std::array<int, 5>>::value;
The above evaluates to false when it should be true. This only happens for the array class and I believe it is due to it having 2 template parameters that do not have default values. I am unable to figure out a fix for this.
First, your trait should have one, and only one template parameter: the expected array type:
template<typename T/*, typename ... Types*/>
struct is_array {
static constexpr bool value = false;
};
Second, std::array is defined as:
template<
class T,
std::size_t N
> struct array;
as first type parameter T and a non-type parameter N of type std::size_t.
Therefore, your true specialization should be:
template<typename T, std::size_t N>
struct is_array<std::array<T,N>>
{
static constexpr bool value = true;
};
Note, that instead of defining a member value, we prefer to inherit from std::true_type and std::false_type which provide some other member alias + conversion operator. The code then becomes:
template<typename T>
struct is_array : std::false_type {};
template<typename T, std::size_t N>
struct is_array<std::array<T,N>> : std::true_type {};
I want to implement a class template that:
behaves like a function
it's input and output variables are all shared.
relatively easy to use.
As a result, I construct the following:
// all input/output variable's base class
class basic_logic_parameter;
// input/output variable, has theire value and iterators to functions that reference to this variable
template <typename FuncIterator, typename ValueType>
class logic_parameter
:public basic_logic_parameter
{
private:
std::list<FuncIterator> _refedFuncs;
ValueType _val;
public:
};
// all `function`'s base class
class basic_logic_function
{
public:
virtual ~basic_logic_function() = 0;
};
// the function, has input/output variable
template <typename FuncIterator, typename R, typename... Args>
class logic_function_base
:public basic_logic_function
{
private:
std::shared_ptr<logic_parameter<FuncIterator, R>> _ret;
std::tuple<std::shared_ptr<logic_parameter<FuncIterator, Args>>...> _args;
public:
template <std::size_t N>
decltype(auto) arg()
{
return std::get<N>(_args);
}
template <std::size_t N>
struct arg_type
{
typedef std::tuple_element_t<N> type;
};
template <std::size_t N>
using arg_type_t = arg_type<N>::type;
decltype(auto) ret()
{
return _ret;
}
};
I wish to use as these like:
// drawing need color and a pen
struct Color
{
};
struct Pen
{
};
struct Iter
{
};
class Drawer
:public logic_function_base<Iter, void(Color, Pen)>
{
public:
void draw()
{
arg_type_t<0> pColor; // wrong
}
}
My compiler can not pass this code through, why? I just want convert a template parameter pack to std::tuple of std::shared_ptr of them.
for example:
Given struct A, int, struct C, I want to have:
std::tuple<
std::shared_ptr<logic_parameter<A>>,
std::shared_ptr<logic_parameter<int>>,
std::shared_ptr<logic_parameter<C>>,
>
The problem (once the small errors are fixed1) is that you instantiate:
logic_function_base<Iter, void(Color, Pen)>
...meaning that FuncIterator is Iter and R is void(Color, Pen), so Args is emtpy <>, so decltype(_args) is an empty std::tuple<>, and your code fails to obtain the type of the 0th element of an empty tuple, which is legit.
What you want is partial specialization of logic_function_base:
template <typename F, typename T>
class logic_function_base;
template <typename FuncIterator, typename R, typename... Args>
class logic_function_base<FuncIterator, R(Args...)>: public basic_logic_function {
};
1 Small mistakes in your current code:
template <std::size_t N>
struct arg_type
{
typedef std::tuple_element_t<N, decltype(_args)> type; // Missing the tuple type
};
template <std::size_t N>
using arg_type_t = typename arg_type<N>::type; // Missing a typename
This may not answer your whole question, but you could use the following trait to wrap tuple element types.
template <typename T> struct wrap;
template <typename... T>
struct wrap<std::tuple<T...>> {
using type = std::tuple<std::shared_ptr<logic_parameter<T>>...>;
}
template <typename T>
using wrap_t = typename wrap<T>::type;
You can then use it like this:
std::tuple<int,double,char> t1;
wrap_t<decltype(t)> t2;
The type of t2 is std::tuple<std::shared_ptr<logic_parameter<int>>,std::shared_ptr<logic_parameter<double>>,std::shared_ptr<logic_parameter<char>>>.
There are a number of ways to implement a has_type<T> template that deduces if T has a nested class or typedef named type. ie
namespace detail {
template<typename> struct tovoid { typedef void type; };
}
template<typename T, typename = void> struct has_type
: std::false_type { };
// this one will only be selected if C::type is valid
template<typename C> struct has_type<C, typename detail::tovoid<typename C::type>::type>
: std::true_type { };
Or
template <typename C> char test_for_type(...) { return '0'; }
template <typename C> double test_for_type(typename C::type const *) { return 0.0; }
template <typename T> struct has_type
{
static const bool value = sizeof(test_for_type<T>(0)) == sizeof(double);
};
however in either case, has_type<type>::value is true for this class:
struct type
{
};
Now the above type doesn't have another type nested within it, but it does have a constructor type::type().
But should that constructor 'trigger' the checks for the nested type? Or is it a compiler bug?
(I would like to think that typename type::type didn't apply to a constructor and/or that you couldn't take a pointer to a constructor, such as what would be produced by the second test method: typename type::type const *.
?
The name of a class is "injected" into the scope of the class, so type::type really is the name of a type, and it's the same type as ::type.
Currently, I'm trying to get some code to react differently to different types. This isn't the exact code, but it gets the message across.
template<class A, class B>
struct alpha {
enum { value = 0 };
};
template<class T, class... Args>
struct alpha<std::tuple<Args...>, T> {
enum { value = 1 };
};
// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., std::vector<T> >, T> {
enum { value = 2 };
};
// This gets ignored
template<class T, class... Args>
struct alpha<std::tuple<Args..., T>, T> {
enum { value = 3 };
};
template<class T, class... Args>
struct alpha<T, std::tuple<Args...> > {
enum { value = 4 };
};
template<class... LArgs, class... RArgs>
struct alpha<std::tuple<LArgs...>, std::tuple<RArgs...> > {
enum { value = 5 };
};
int main(int argc, char* argv[]) {
std::cout << alpha<std::tuple<int, double>, double>::value << std::endl; // prints 1
return 0;
}
I've tried more than this code shows, but nothing works so far and I ran across a problem with explicit specialization in a non-namespace scope. For reference, I'm working on gcc 4.6 (the one that comes with oneiric server), which I believe has complete variadic template support. I don't care how ugly it gets if the implementation works to detect the last argument of the parameter pack and the other types as well. Any suggestions?
EDIT:
I wanted to share the solution I used based on the answers (this is an example).
template<typename T> struct tuple_last;
template<typename T, typename U, typename... Args>
struct tuple_last<std::tuple<T,U,Args...>> {
typedef typename tuple_last<std::tuple<U,Args...>>::type type;
};
template<typename T>
struct tuple_last<std::tuple<T>> {
typedef T type;
};
namespace details {
// default case:
template<class T, class U>
struct alpha_impl {
enum { value = 1 };
};
template<class T>
struct alpha_impl<T, T> {
enum { value = 101 };
};
template<class T>
struct alpha_impl<T, std::vector<T>> {
enum { value = 102 };
};
// and so on.
}
template<class T, class... Args>
struct alpha<std::tuple<Args...>, T>
: details::alpha_impl<T, tuple_last<std::tuple<Args...>>;
If you compile using clang, it helpfully reports that (2) and (3) are unusable. The warning for (3), which you expect to be selected, is as follows:
warning: class template partial specialization contains a template parameter that can not be deduced; this partial specialization will never be used
struct alpha<std::tuple<Args..., T>, T> {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: non-deducible template parameter 'Args'
template<class T, class... Args>
^
Why is Args not deducible? The C++0x FDIS states at ยง14.8.2.5/9:
If the template argument list of [a type that is specified in terms of template parameters] contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context.
In your specialization, the type std::tuple<Args..., T> is a type that is specified in terms of template parameters Args and T. It contains a pack expansion (Args...), but that pack expansion is not the last template argument (T is the last template argument). Thus, the entire template argument list of the tuple (the entirety of <Args..., T>) is a non-deduced context.
The argument list of the std::tuple is the only place in the template specialization's argument list that Args appears; since it is not deducible from there, it is not deducible at all and the specialization will never be used.
Matthieu M. provides a clever workaround in his answer.
#James provided the why, now let's try to find an alternative.
I would suggest using another level of indirection.
1. Getting the last argument
template <typename T> struct Last;
template <typename T, typename U, typename... Args>
struct Last<std::tuple<T,U,Args...>>
{
typedef typename Last<std::tuple<U,Args...>>::type type;
};
template <typename T>
struct Last<std::tuple<T>>
{
typedef T type;
};
2. Introducing a specialized helper
template <typename T, typename U>
struct alpha_tuple
{
enum { value = 1 };
};
template <typename T>
struct alpha_tuple<T,T>
{
enum { value = 3 };
};
template <typename T>
struct alpha_tuple<std::vector<T>,T>
{
enum { value = 2; }
};
3. Hooking it up
template <typename T>
struct alpha<std::tuple<>, T>
{
enum { value = 1 };
};
template <typename T, typename U, typename Args...>
struct alpha<std::tuple<U, Args...>, T>
{
typedef typename Last<std::tuple<U, Args...>>::type LastType;
enum { value = alpha_tuple<LastType,T>::value };
};
Note that there is no last type for empty tuples, so I had to deal with them in a separate specialization.
If you like to find out whether a tuple as a specific last member, here's a type trait for that:
#include <type_traits>
#include <tuple>
template <typename ...Args> struct back;
template <typename T, typename ...Args> struct back<T, Args...>
{ typedef typename back<Args...>::type type; };
template <typename T> struct back<T>
{ typedef T type; };
template <typename...> struct tuple_has_last : public std::false_type {};
template <typename T, typename... Args> struct tuple_has_last<T, std::tuple<Args...>>
{
static const bool value = std::is_same<typename back<Args...>::type, T>::value;
};
Edit: Oh, I didn't see that Matthieu had already written the exact same thing. Never mind.