Info
-
Did you know that default template arguments can be explored with template template arguments?
Example
namespace detail {
template<class...>
struct type_defaults{};
template<template<class...> class T, class... Ts>
struct type_defaults<T<Ts...>>{
using type = std::tuple<Ts...>;
};
} // namespace detail
template<template<class...> class T>
using type_defaults_t = typename detail::type_defaults<T<>>::type;
template<class = int> struct foo{};
static_assert(std::is_same_v<std::tuple<int>, type_defaults_t<foo>>);
template<class TQuant = class Quant, class TLab = class Lab> struct bar{};
static_assert(std::is_same_v<std::tuple<Quant, Lab>, type_defaults_t<bar>>);
Puzzle
- Can you implement
rebind_defaults_t
type trait which rebinds template arguments depending on provided bindings... (bind<From, To>
)?
template<class...> struct bind;
template<template<class...> class T, class... Ts>
using rebind_defaults_t = /*TODO*/T<Ts...>;
template<class = int> struct foo{};
static_assert(std::is_same_v<foo<>, rebind_defaults_t<foo>>);
static_assert(std::is_same_v<foo<int>, rebind_defaults_t<foo>>);
static_assert(std::is_same_v<foo<double>, rebind_defaults_t<
foo,
bind<int, double>
>>);
template<class TQuant = class Quant, class TLab = class Lab> struct bar{};
static_assert(std::is_same_v<bar<Quant, Lab>, rebind_defaults_t<bar>>);
static_assert(std::is_same_v<bar<int, Lab>, rebind_defaults_t<
bar,
bind<Quant, int>
>>);
static_assert(std::is_same_v<bar<Quant, double>, rebind_defaults_t<
bar,
bind<class Lab, double>
>>);
static_assert(std::is_same_v<bar<int, double>, rebind_defaults_t<
bar,
bind<Quant, int>,
bind<Lab, double>
>>);
static_assert(std::is_same_v<bar<int, double>, rebind_defaults_t<
bar,
bind<Lab, double>,
bind<Quant, int>
>>);
Solutions
template <class T, class U> struct bind {};
namespace detail {
template <class... Ts> struct rebind_map : Ts... {};
template <class T, class U> constexpr auto rebind_lookup(const bind<T, U>&) -> U;
template <class T> constexpr auto rebind_lookup(...) -> T;
template <class TMap>
struct rebinder {
template <class...> struct rebind_types{};
template <template<class...> class T, class... Ts>
struct rebind_types<T<Ts...>> {
using type = T<decltype(rebind_lookup<Ts>(std::declval<TMap>()))...>;
};
};
} // namespace detail
template <template<class...> class T, class... Ts>
using rebind_defaults_t =
typename detail::rebinder<detail::rebind_map<Ts...>>::template rebind_types<T<>>::type;
namespace detail {
template<class...> struct type_defaults{};
template<template<class...> class T, class... Ts>
struct type_defaults<T<Ts...>>{
using type = std::tuple<Ts...>;
};
} // namespace detail
template<template<class...> class T>
using type_defaults_t = typename detail::type_defaults<T<>>::type;
template<class...> struct bind;
template<class L, class T > struct unbind;
template<class L, class T1,class T2 > struct unbind<L, bind<T1,T2>>
{
using type = boost::mp11::mp_replace<L,T1,T2>;
};
template<class L, class T > using unbind_t = typename unbind<L,T>::type;
template<template<class...> class T, class... Ts>
using rebind_defaults_t = boost::mp11::mp_apply<T,boost::mp11::mp_fold<boost::mp11::mp_list<Ts...>,type_defaults_t<T>,unbind_t>>;
namespace detail {
template<class...> struct type_defaults {};
template<template<class...> class T, class... Ts>
struct type_defaults<T<Ts...>> {
using type = T<Ts...>;
};
template<template<class...> class T>
using type_defaults_t = typename detail::type_defaults<T<>>::type;
template<class T> struct unhandled {
using type = T;
};
template<class T> struct handled {
using type = T;
};
template<class T>
using trait_t = typename T::type;
} // namespace detail
template<class From, class To> struct bind {
template<class T>
using fn = mp11::mp_if<
mp11::mp_same<T, detail::unhandled<From>>,
detail::handled<To>,
T>;
};
template<template<class...> class T, class... Ts>
using rebind_defaults_t = mp11::mp_transform_q<
mp11::mp_compose_q<
mp11::mp_quote<detail::unhandled>,
Ts...,
mp11::mp_quote<detail::trait_t>>,
detail::type_defaults_t<T>>;
namespace detail {
template<class...>
struct type_defaults{};
template<template<class...> class T, class... Ts>
struct type_defaults<T<Ts...>>{
using type = std::tuple<Ts...>;
};
template<template<class...> class, class...> struct rebind_defaults{};
template<template<class...> class T, class... TIfs, class... TImpls>
struct rebind_defaults<T, std::tuple<TIfs...>, TImpls...>{
template<class U, class> struct rebind { using type = U; };
template<class U, template<class, class> class TBind, class TIf, class TImpl> struct rebind<U, TBind<TIf, TImpl>> { using type = TImpl; };
using type = T<typename rebind<TIfs, boost::mp11::mp_map_find<boost::mp11::mp_inherit<TImpls...>, TIfs>>::type...>;
};
} // namespace detail
template<template<class...> class T>
using type_defaults_t = typename detail::type_defaults<T<>>::type;
template<template<class...> class T, class... Ts>
using rebind_defaults_t = typename detail::rebind_defaults<T, type_defaults_t<T>, Ts...>::type;
namespace detail {
template<class...> struct type_defaults{};
template<template<class...> class T, class... Ts>
struct type_defaults<T<Ts...>>{
using type = std::tuple<Ts...>;
};
} // namespace detail
template<template<class...> class T>
using type_defaults_t = typename detail::type_defaults<T<>>::type;
template<class...> struct bind;
template<class...> struct rebind_next;
template<class D> struct rebind_next<D> { using type = D; };
template<class D, class R, class ...Ms, class...Rs>
struct rebind_next<D, bind<D,R>, bind<Ms,Rs>...> { using type = R; };
template<class D, class M, class R, class ...Ms, class... Rs>
struct rebind_next<D, bind<M,R>, bind<Ms,Rs>...> {
using type = typename rebind_next<D, bind<Ms,Rs>...>::type;
};
template<class...> struct list;
template<class ...Fs, class D, class ...Ds, class...Ms, class ...Rs>
struct rebind_next< list<Fs...>, list<D, Ds...>, bind<Ms, Rs>...> {
using type = typename rebind_next< list<Fs..., typename rebind_next<D, bind<Ms, Rs> ...>::type >, list<Ds...>, bind<Ms,Rs>...>::type;
};
template<class ...Fs, class D, class...Ms, class ...Rs>
struct rebind_next< list<Fs...>, list<D>, bind<Ms,Rs>...> {
using type = list<Fs..., typename rebind_next<D, bind<Ms,Rs>...>::type>;
};
template<template<class...> class N, class O> struct rename;
template<template<class...> class N , template<class...> class O, class ...Es>
struct rename< N, O<Es...>> { using type = N<Es...>; };
template<class...> struct rebind_defaults;
template<template<class...> class T, class... Ds, class ...Ms, class... Rs>
struct rebind_defaults<T<Ds...>, bind<Ms, Rs>...> {
using rebound_list = typename rebind_next< list<>, list<Ds...>, bind<Ms,Rs>...>::type;
using type = typename rename< T, rebound_list>::type;
};
template<template<class...> class T, class... Ts>
using rebind_defaults_t = typename rebind_defaults<T<>, Ts...>::type;
namespace detail {
template<class...> struct type_defaults{};
template<template<class...> class T, class... Ts>
struct type_defaults<T<Ts...>>{
using type = std::tuple<Ts...>;
};
template<class T, class>
struct process_bind {
using type = T;
};
template<class T, template<class, class> class TBind, class TReplace>
struct process_bind<T, TBind<T, TReplace>> {
using type = TReplace;
};
template<class T, class TBind>
using process_bind_t = process_bind<T, TBind>::type;
template<template<class...> class, class...>
struct rebind {};
template<template<class...> class TContainer, template<class...> class TDefaultContainer, class... TDefaults, class... TBinds>
struct rebind<TContainer, TDefaultContainer<TDefaults...>, TBinds...> {
using binds_t = boost::mp11::mp_list<TBinds...>;
using type = TContainer<process_bind_t<TDefaults, boost::mp11::mp_map_find<binds_t, TDefaults>>...>;
};
namespace test {
template<class...> struct test {};
static_assert(std::is_same_v<process_bind_t<int, test<int, float>>, float>);
static_assert(std::is_same_v<process_bind_t<int, test<long, float>>, int>);
}
} // namespace detail
template<template<class...> class T>
using type_defaults_t = typename detail::type_defaults<T<>>::type;
template<class...> struct bind;
template<template<class...> class T, class... TBinds>
using rebind_defaults_t = detail::rebind<T, type_defaults_t<T>, TBinds...>::type;