How can I curry variadic template template parameters?

前端 未结 2 1584
旧巷少年郎
旧巷少年郎 2021-02-08 18:46

Variadic template template parameters accept any template:

template
struct Test1 {
    using type = int;
};

template

        
相关标签:
2条回答
  • 2021-02-08 19:36

    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

    0 讨论(0)
  • 2021-02-08 19:39

    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.

    0 讨论(0)
提交回复
热议问题