Variadic template template parameters accept any template:
template
struct Test1 {
using type = int;
};
template
After some problems of my own, I came up with this solution which will work for any template class (also the ones you provide in your post).
The core of this solution is is_valid_specialization
which is used as the condition on whether or not the currying process may be considered complete:
#include <iostream>
#include <type_traits>
template<template<typename...> class C, typename... T>
struct is_valid_specialization {
typedef struct { char _; } yes;
typedef struct { yes _[2]; } no;
template<template<typename...> class D>
static yes test(D<T...>*);
template<template<typename...> class D>
static no test(...);
constexpr static bool value = (sizeof(test<C>(0)) == sizeof(yes));
};
namespace detail {
template<template<typename...> class BeCurry, bool = false, typename... S>
struct Currying {
template<typename... T>
using apply = Currying<BeCurry, is_valid_specialization<BeCurry, S..., T...>::value, S..., T...>;
};
template<template<typename...> class BeCurry, typename... S>
struct Currying<BeCurry, true, S...> {
template<typename... T>
using apply = Currying<BeCurry, is_valid_specialization<BeCurry, S..., T...>::value, S..., T...>;
using type = typename BeCurry<S...>::type;
};
}
template<template<typename...> class BeCurry>
using Currying = detail::Currying<BeCurry, is_valid_specialization<BeCurry>::value>;
template<typename T>
struct Test1 { using type = int; };
template<typename T1, typename T2>
struct Test2 { using type = char*; };
template<typename...>
struct Test3 { using type = double; };
using curry = Currying<Test1>;
using curry2 = Currying<Test2>;
using curry3 = Currying<Test3>;
template<typename T>
void pretty_print(T) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main() {
pretty_print(typename curry::apply<char>::type{});
pretty_print(typename curry2::apply<int>::apply<char>::type{});
pretty_print(typename curry3::type{});
}
Output on ideone
Simple solution is:
template
<
template <typename...> class BeCurry,
typename... Params
>
struct Currying
{
template <typename... OtherParams>
using curried = BeCurry<Params..., OtherParams...>;
template <typename... OtherParams>
using type = typename curried<OtherParams...>::type;
template <typename... NewParams>
using apply = Currying<curried, NewParams...>;
};
But it does not work with templates like Test1
and Test2
due to compilation errors (under gcc, at least). A workaround for this problem looks like this:
template
<
template <typename...> class BeCurry,
typename... Params
>
struct Curry
{
using type = BeCurry<Params...>;
};
template
<
template <typename...> class BeCurry
>
struct Curry<BeCurry>
{
using type = BeCurry<>;
};
And now lines
template <typename... OtherParams>
using curried = BeCurry<Params..., OtherParams...>;
should be replaced with lines
template <typename... OtherParams>
using curried = typename Curry<BeCurry, Params..., OtherParams...>::type;
Example of using:
#include <iostream>
#include <typeinfo>
template <typename T>
void print_type(T t)
{
std::cout << typeid(t).name() << std::endl;
}
// ...
print_type(Currying<Test1>::type<int>{});
print_type(Currying<Test1>::apply<int>::type<>{});
print_type(Currying<Test2>::type<int, char>{});
print_type(Currying<Test2>::apply<int>::type<char>{});
print_type(Currying<Test2>::apply<int>::apply<char>::type<>{});
print_type(Currying<Test2>::apply<int, char>::type<>{});
Full example at ideone.