I'm having a bit of trouble getting my head around TypeLists or lists of templates. That is:
class nulltype{};
template <typename HEAD, typename TAIL>
struct tlist
{
typedef HEAD head;
typedef TAIL tail;
};
template <class TList>
class OutputClass
{
public:
void output(Type t)
{
std::cout << t << endl;
}
};
typedef tlist<double,tlist<float,NullType> > floatingPoints;
typedef tlist<std::string,tlist<char *,NullType> > text;
int main()
{
OutputClass<floatingPoints> floats = OutputClass<floatingPoints>();
floats.output(1.0f);
OutputClass<text> strings = OutputClass<text>();
floats.output("hello");
return 0;
}
Basically my goal is that I would like OutputClass.output to output the parameter passed to it, but only if that class instances typelist contains the type passed into the function. ie. referring to the above code: floats can only output float type and double type as defined by its typelist, "floatingPoints". Should a string or int get passed in, I would hope it would error.
I'm having one hell of a time trying to find any examples on how to do this, I've found the indexing examples and length examples a million times over, and they have helped me a lot, but i just can't seem to figure this last bit out. Any and all help will be appreciated.
We first need some helper templates. The first one checks if two types are the same:
template <typename T, typename U>
struct is_same
{
// Default case: T and U are not the same type
static const bool value = false;
};
template <typename T>
struct is_same<T, T>
{
// Specialization: both template arguments are of the same type
static const bool value = true;
};
We can now use the boolean is_same<T, U>::value to determine if the types T and U are equivalent.
Using is_same we can now write a template which checks if a specific type occurs in a type list:
template <typename TList, typename T>
struct contains
{
static const bool value =
is_same<typename TList::head, T>::value // Base case
|| contains<typename TList::tail, T>::value; // Recursion
};
template <typename T>
struct contains<nulltype, T>
{
// Termination condition
static const bool value = false;
};
This is a recursive template, using a specialization on nulltype to terminate the recursion.
One final helper template we need is enable_if:
template <bool Cond, typename T=void>
struct enable_if
{
// Default case: Cond assumed to be false, no typedef
};
template <typename T>
struct enable_if<true, T>
{
// Specialization: Cond is true, so typedef
typedef T type;
};
enable_if<true, T>::type yields T, whereas enable_if<false, T>::type is undefined. We exploit the SFINAE rule the enable or disable a function depending on its template argument, like so:
template <typename TList>
class OutputClass
{
public:
// Only enable the function if T is in TList (by SFINAE)
template <typename T>
typename enable_if<contains<TList, T>::value>::type
output(T t)
{
std::cout << t << std::endl;
}
};
Quite a trip, but if you understand all of this you're well on your way to master template metaprogramming. For an in-depth discussion of template metaprogramming I recommend you pick up a copy of C++ Template Metaprogramming (ISBN-13: 9780321227256).
Related
I get an error:
error: default template arguments may not be used in partial specializations
in the following code:
#include <iostream>
#include <type_traits>
#include <vector>
enum class MyEnum
{
aaa,
bbb,
};
template<class T>
struct is_vector_enum
{
using type = T ;
constexpr static bool value = false;
};
template<class T, class std::enable_if<std::is_enum<T>::value>::type* = nullptr> // Error ....
struct is_vector_enum<std::vector<T>>
{
using type = std::vector<T> ;
constexpr static bool value = true;
};
int main()
{
std::cout << "is_vector_enum: " << is_vector_enum<std::vector<MyEnum>>::value << std::endl;
}
The purpose is to detect whether a type is a vector of enum.
How should I fix this code?
Your primary template and your specialization need to have the same number of template parameters. At the moment, your primary has 1:
template<class T>
struct is_vector_enum
and your specialization has 2:
template<class T, class std::enable_if<std::is_enum<T>::value>::type* = nullptr>
struct is_vector_enum<std::vector<T>>
The typical way to do this in C++17 is to provide a dummy 2nd template parameter to the primary, that defaults to void, to then let you do the SFINAE in the second parameter:
template <class T, class Enable=void>
struct is_vector_enum { /* ... */ };
template <class T>
struct is_vector_enum<std::vector<T>, std::enable_if_t<std::is_enum_v<T>>> { /* ... */ };
A different way entirely to do this would be:
template <typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
std::true_type impl(std::vector<T> const&);
template <typename T>
std::false_type impl(T const&);
template <typename U>
using is_vector_enum = decltype(impl(std::declval<T>()));
Note that the impl functions here are not defined, and are not intended to be invoked.
Specializations are allowed to have a different number of template parameters than the primary. In fact, this happens quite often. However, as the error indicates, you are not allowed to give any of them default arguments.
That aside, I prefer simplicity, when possible.
template <typename T>
struct is_vector_enum : std::false_type { };
template <typename T>
struct is_vector_enum<std::vector<T>> : std::is_enum<T> { };
If I have a function, insert, within a templated class, and I overload it to take either a fill pattern:
void insert(const size_type N, const value_type &element);
or a range pattern:
template <class iterator_type>
void insert(const iterator_type begin, const iterator_type end)
When I specify the value_type of the class to be int, it results in the following call:
insert(500, 50);
being ambiguous, as it assumes 500 to be an int and therefore not a match for size_type N, and instead of calling the fill pattern function, calls the templated range function and fails.
This is a C++03-compatible library,- it can't use external libraries like boost, only builtin C++ code. The only workaround I've found that doesn't require C++11's enable_if would be creating additional overloads, replacing size_type with int, long int, char etc, and calling the fill function from them. Obviously this is problematic because you can have so many different types which match 500. Any suggestions?
Take advantage of SFINAE:
void insert(const size_type N, const value_type &element);
template <class iterator_type>
void insert(iterator_type begin, iterator_type end, char (*)[sizeof(*begin)] = NULL);
The extra dummy argument in the range version of insert will be optimized out by the compiler. Its only role is to eliminate the template function from overloading resolution for types that cannot be used with a dereference operator.
Clarification of the trick:
char (*)[sizeof(*begin)] stands for a pointer to an array of chars whose size is equal to sizeof(*begin). If the variable begin is not dereferenceable then this would be an error. However, in the context of considering a function template during function overload resolution, such an error doesn't stop compilation, but simply discards the template (Substitution Failure Is Not An Error - SFINAE).
Depending on the exact compilers you support, you may find enable_if as a builtin anyway- it's present in TR1.
However, the implementation of enable_if is trivial. You could simply copy it from Boost as well. In fact, it's so short I'm going to post it in this answer.
namespace boost
{
template <bool B, class T = void>
struct enable_if_c {
typedef T type;
};
template <class T>
struct enable_if_c<false, T> {};
template <class Cond, class T = void>
struct enable_if : public enable_if_c<Cond::value, T> {};
template <bool B, class T>
struct lazy_enable_if_c {
typedef typename T::type type;
};
template <class T>
struct lazy_enable_if_c<false, T> {};
template <class Cond, class T>
struct lazy_enable_if : public lazy_enable_if_c<Cond::value, T> {};
template <bool B, class T = void>
struct disable_if_c {
typedef T type;
};
template <class T>
struct disable_if_c<true, T> {};
template <class Cond, class T = void>
struct disable_if : public disable_if_c<Cond::value, T> {};
template <bool B, class T>
struct lazy_disable_if_c {
typedef typename T::type type;
};
template <class T>
struct lazy_disable_if_c<true, T> {};
template <class Cond, class T>
struct lazy_disable_if : public lazy_disable_if_c<Cond::value, T> {};
} // namespace boost
With help from Puppy's answer figured it out:
// Fill insert
void insert(const size_type N, const value_type &element);
// Enable_if template
template <bool condition, class T = void>
struct enable_if_c
{
typedef T type;
};
template <class T>
struct enable_if_c<false, T>
{};
// Range insert:
template <class iterator_type>
iterator insert (const typename enable_if_c<!std::numeric_limits<iterator_type>::is_integer, iterator_type>::type &first, const iterator_type &last);
numeric_limits is C++03-safe and works in all (tested) compilers including MSVC 2010. This checks to make sure the type supplied to range-insert is not an integer.
I have a class something like this:
template <typename T>
struct operation {
typedef T result_type;
typedef ::std::shared_ptr<operation<T> > ptr_t;
};
I have a functor that would match this ::std::function type:
::std::function<int(double, ::std::string)>
I want to create a functor that has a signature something like this:
operation<int>::ptr_t a_func(operation<double>::ptr_t, operation< ::std::string>::ptr_t);
I want to do this in an automated fashion so I can create a similar functor for any given ::std::function type.
Lastly, I would like to put this wrinkle in. This:
::std::function<int(operation<double>::ptr_t, ::std::string)>
should result in this:
operation<int>::ptr_t a_func(operation<double>::ptr_t, operation< ::std::string>::ptr_t);
Because if a functor already accepts an operation<T>::ptr_t that means it understands what they are and is willing to deal with their asynchronous nature itself.
How would I do this? I have a naive and partially working attempt here:
template <typename argtype>
struct transform_type {
typedef typename operation<argtype>::ptr_t type;
};
template <typename ResultType, typename... ArgTypes>
::std::function<typename transform_type<ResultType>::type(typename transform_type<ArgTypes...>::type)>
make_function(::std::function<ResultType(ArgTypes...)>)
{
return nullptr;
}
It doesn't detect arguments that are already of type std::shared_ptr<operation<T> > though. And this specialization of transform_type fails to compile:
template <typename argtype>
struct transform_type<typename operation<argtype>::ptr_t>
{
typedef typename stub_op<argtype>::ptr_t type;
};
template<template<typename...> class F, typename Sig>
struct transform;
template<template<typename...> class F, typename R, typename... A>
struct transform<F, R(A...)> {
using type = typename F<R>::ptr_t(typename F<A>::ptr_t...);
};
Usage looks like:
template<typename Sig>
void foo(std::function<Sig> f)
{
using transformed_type = typename transform<operation, Sig>::type;
std::function<transformed_type> g;
}
As for the specialization to avoid transforming types that are already in the desired form:
template<typename T>
struct operation<std::shared_ptr<T>> {
using ptr_t = std::shared_ptr<T>;
using result_type = ptr_t; // Or perhaps this needs to be T, you haven't said
};
I believe I have figured it out with R. Martinho Fernandez's help:
template <typename T>
struct is_op_ptr {
private:
// Returns false_type, which has a ::value that is false.
template <class AT>
static constexpr std::false_type is_it_a_ptr(...);
// Returns true_type (if enable_if allows it to exist).
template <class AT>
static constexpr typename ::std::enable_if<
::std::is_same<
AT,
typename operation<typename AT::element_type::result_type>::ptr_t>::value,
std::true_type>::type // note the true_type return
is_it_a_ptr(int); // no definition needed
public:
// do everything unevaluated
static constexpr bool value = decltype(is_it_a_ptr<T>(0))::value;
};
template <typename T>
struct transform_type
: ::std::conditional< is_op_ptr<T>::value, T, typename operation<T>::ptr_t>
{
};
This also allows me to query whether or not a type will be transformed in the construction of the wrapper function.
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.
Consider the following example:
struct Scanner
{
template <typename T>
T get();
};
template <>
string Scanner::get()
{
return string("string");
}
template <>
int Scanner::get()
{
return 10;
}
int main()
{
Scanner scanner;
string s = scanner.get<string>();
int i = scanner.get<int>();
}
The Scanner class is used to extract tokens from some source. The above code works fine, but fails when I try to get other integral types like a char or an unsigned int. The code to read these types is exactly the same as the code to read an int. I could just duplicate the code for all other integral types I'd like to read, but I'd rather define one function template for all integral types.
I've tried the following:
struct Scanner
{
template <typename T>
typename enable_if<boost::is_integral<T>, T>::type get();
};
Which works like a charm, but I am unsure how to get Scanner::get<string>() to function again. So, how can I write code so that I can do scanner.get<string>() and scanner.get<any integral type>() and have a single definition to read all integral types?
Update: bonus question: What if I want to accept more than one range of classes based on some traits? For example: how should I approach this problem if I want to have three get functions that accept (i) integral types (ii) floating point types (iii) strings, respectively.
struct Scanner
{
template <typename T>
typename boost::enable_if<boost::is_integral<T>, T>::type get()
{
return 10;
}
template <typename T>
typename boost::disable_if<boost::is_integral<T>, std::string>::type get()
{
return "string";
}
};
Update "What if I want to accept more than one range of classes based on some traits?"
struct Scanner
{
template <typename T>
typename boost::enable_if<boost::is_integral<T>, T>::type get()
{
return 10;
}
template <typename T>
typename boost::enable_if<boost::is_floating_point<T>, T>::type get()
{
return 11.5;
}
template <typename T>
std::string get(
typename boost::disable_if<boost::is_floating_point<T>, T>::type* = 0,
typename boost::disable_if<boost::is_integral<T>, T>::type* = 0)
{
return std::string("string");
}
};
Defer to another template. Here's the general pattern for what you want:
template <typename T, bool HasTrait = false>
struct scanner_impl;
template <typename T>
struct scanner_impl
{
// Implement as though the trait is false
};
template <typename T>
struct scanner_impl<true>
{
// Implement as though the trait is true
};
// This is the one the user uses
template <typename T>
struct scanner : scanner_impl<T, typename has_my_trait<T>::value>
{
};