Partial template deduction in template argument list - c++

I have the following structs:
template<typename T1, typename T2>
struct A {};
template<typename T>
struct B {};
template<typename A, typename B>
struct C {};
and want to use them as follows:
C<B<int>, A<double, B<int>>> c;
Is there any way to deduce the second template parameter for A, such that I can use it like this?
C<B<int>, A<double>> c;
This should work for any template arguments of C and not just for a particular one (therefore default arguments don't seem to work).
Furthermore a solution for variadic templates would be even better, so instead of
C<B<int>, A<double, B<int>>, A<float, A<double, B<int>>>> c;
something like this would be nice:
C<B<int>, A<double>, A<float>> c;

With a bit of template metaprogramming fun, I was able to solve your problem both for the two-parameter and the variadic case (although in a somewhat narrow way):
// TypeList stuff
template<class... Args>
struct List {
template<class Arg>
using Add = List<Args..., Arg>;
};
template<class List> struct TailI;
template<class A, class... Ar> struct TailI<List<A, Ar...>> {
using type = typename TailI<List<Ar...>>::type;
};
template<class A> struct TailI<List<A>> {
using type = A;
};
template<class List>
using Tail = typename TailI<List>::type;
template<template<class...> class OP, class List> struct rename_impl;
template<template<class...> class OP, class... ListArgs>
struct rename_impl<OP, List<ListArgs...>> {
using type = OP<ListArgs...>;
};
template<template<class...> class OP, class List>
using rename = typename rename_impl<OP, List>::type;
// Actual code solving problem at hand
template<class T1, class T2> struct A{};
template<class... Args> struct C{};
template<class Built, class Next, class... Rest>
struct builder {
using NewBuilt = typename Built::template Add<A<Next, Tail<Built>>>;
using type = typename builder<NewBuilt, Rest...>::type;
};
template<class Built, class Next>
struct builder<Built, Next> {
using NewBuilt = typename Built::template Add<A<Next, Tail<Built>>>;
using type = rename<C, NewBuilt>;
};
template<class First, class... Rest>
using c_builder = typename builder<List<First>, Rest...>::type;
using t = c_builder<int, double, float>;
// Test driver
#include <utility>
static_assert(std::is_same_v<t, C<int, A<double, int>, A<float, A<double, int>>>>, "");

For the simpler case of
template<typename A, typename B> struct C {};
you can use a helper class to provide you the type you need.
template<typename T1, typename T2>
struct C_Helper
{
using type = C<B<T1>, A<T2, B<T1>>>;
};
and use
using C_Type = typename C_Helper<int, double>::type;
C_Type c;
I haven't thought through how that can be extended to support a variadic class template C,

Related

How to replace a template parameter in a given type?

Given that a template parameter called B is of type C<D> (on instantiation), how can I construct C<A> from B?
Here is a minimalized extract from my code:
template<typename A>
class foo {
template<typename B> // B is guaranteed to always be of the form C<D>
// How to write a function here with return type C<A>?
}
you can do this with template specialization of a template template parameter
template<typename, typename>
struct meta {};
template<typename A, template<typename> typename C, typename B>
struct meta<A, C<B>> {
using type = C<A>;
};
meta<A, C<B>>::type will be C<A>
if you want to have handling of default argument in basic cases
template<typename...>
struct meta {};
template<typename A, template<typename...> typename C, typename B, typename ... Ts>
struct meta<A, C<B, Ts...>> {
using type = C<A, Ts...>;
};
First we write some machinery to replace a type in a template type instance:
template<class In, class Replace>
struct replace_first_type;
template<template<class...>class Z, class T0, class...Ts, class Replace>
struct replace_first_type<Z<T0, Ts...>, Replace> {
using type=Z<Replace, Ts...>;
};
template<class In, class Replace>
using replace_first = typename replace_first_type<In,Replace>::type;
replace_first< Z, X > takes Z, pattern matches it against any template<class...>, grabs the first argument, and replaces it with X.
This won't work for std::array<int, 7> as 7 isn't a type, but will work for std::vector<int>; vector is actually <T, A> with a default argument. The template<class...> pattern matches templates that take 0 or more classes.
We then apply it:
template<typename A>
class foo {
template<typename B>
using result = replace_first<B, A>;
};
now foo<int>::template result< std::vector<double> > is a std::vector<int, std::alloctor<double>>. Which admittedly is a pretty stupid type.
If you have a function:
template<class B>
result<B> do_something() {
return {};
}
actually returns a value of that type.
We can fix the problem with std::allocator<double> above by recursively replacing the type we replaced...
template<class X, class Src, class Dest>
struct subst_type {
using type=X;
};
template<class X, class Src, class Dest>
using subst_t = typename subst_type<X, Src, Dest>::type;
template<template<class...>class Z, class...Ts, class Src, class Dest>
struct subst_type<Z<Ts...>, Src, Dest> {
using type=Z<subst_t<Ts, Src, Dest>...>;
};
template<class Src, class Dest>
struct subst_type<Src, Src, Dest> {
using type=Dest;
};
and apply to replace first:
template<class In, class Replace>
struct replace_first_type;
template<template<class...>class Z, class T0, class...Ts, class Replace>
struct replace_first_type<Z<T0, Ts...>, Replace> {
using type=Z<Replace, subst_t<Ts, T0, Replace>...>;
};
and now when we foo<int>::result<std::vector<double>> we get std::vector<int, std::allocator<int>> instead of std::vector<int, std::allocator<double>> (which is a useless type).
Live example.
There is some possibility that we'll get something horribly wrong from recursively replacing A with B in the rest of the arguments of a template, but as noted we definitely get something horrible with common templates if we don't do it.

Expanding with pack of templates

Define template_pack as in the following example. Given
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;
then
template_pack<std::tuple<char, bool, double>, A, B, C>::type
is to be
std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
i.e. always reading from left to right in the tuple, so as to get enough types to fit each template.
Here's my solution so far. But it only works for templates that take up to 3 types, and I don't see how I can generalize to templates of any number of types:
#include <type_traits>
#include <tuple>
template <template <typename...> class Template, typename... Ts> struct use_template;
template <template <typename> class Template, typename A, typename... Rest>
struct use_template<Template, A, Rest...> {
using type = Template<A>;
};
template <template <typename, typename> class Template, typename A, typename B, typename... Rest>
struct use_template<Template, A, B, Rest...> {
using type = Template<A,B>;
};
template <template <typename, typename, typename> class Template, typename A, typename B, typename C, typename... Rest>
struct use_template<Template, A, B, C, Rest...> {
using type = Template<A,B,C>;
};
template <typename Pack, template <typename...> class... Templates> struct template_pack;
template <template <typename...> class P, typename... Ts, template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
using type = P<typename use_template<Templates, Ts...>::type...>;
};
// Testing
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;
int main() {
static_assert (std::is_same<
template_pack<std::tuple<char, bool, double>, A, B, C>::type,
std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
>::value, "");
}
How to generalize the above? Is it even possible?
This is "find shortest prefix".
Utilities:
// pack that is easy to append to
template<class... Ts>
struct pack{
template<class... T1s>
using append = pack<Ts..., T1s...>;
};
template<class...> using void_t = void;
The meat:
// find the shortest proper prefix Ts... of [T, Rest...]
// such that A<Ts...> is well-formed, and return it.
template<template<class...> class A, // template we are going to look at
class AlwaysVoid, // for void_t
class Current, // pack containing the types we are testing next
class T, // next type to add to pack if test fails
class... Rest> // remaining types
struct find_viable
: find_viable<A, AlwaysVoid, typename Current::template append<T>, Rest...> {};
// picked if A<Ts...> is well-formed
template<template<class...> class A, class... Ts, class T, class...Rest>
struct find_viable<A, void_t<A<Ts...>>, pack<Ts...>, T, Rest...> {
using type = A<Ts...>;
};
// Finds the shortest prefix of Ts... such that A<prefix...> is well formed.
// the extra void at the end is slightly hackish - find_viable only checks
// proper prefixes, so we add an extra dummy type at the end.
template<template<class...> class A, class... Ts>
using find_viable_t = typename find_viable<A, void, pack<>, Ts..., void>::type;
Then use it:
template <typename Pack, template <typename...> class... Templates> struct template_pack;
template <template <typename...> class P, typename... Ts,
template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
using type = P<find_viable_t<Templates, Ts...>...>;
};
I won't write it all, but I'll do a walkthrough.
template<template<class...>class Z>
struct z_template{
template<class...Ts>using result=Z<Ts...>;
};
this lets you manipulate templates like types.
template<template<class...>class Z, class...Ts>
using can_apply = /* ... */;
this asks the question "is Z<Ts...> valid? If so, return true_type".
template<class...>struct types{using type=types;};
this is a light-weight template pack.
template<template<class...>class Z, class types>
using apply = /* ... */;
this takes a template Z, and a types<Ts...>, and returns Z<Ts...>.
template<class Z, class types>
using z_apply = apply<typename Z::template result, types>;
this does it with a z_template argument instead of a template one.
Working on types as often as possible makes many kinds of metaprogramming easier.
Next we write
template<class types>using pop_front = // ...
template<class types>using reverse = // ...
template<class types>using pop_back = reverse<pop_front<reverse<types>>>;
which remove elements from the front and back of types.
template<class Result>
struct z_constant {
template<class...Ts>using result=Result;
};
This finds the longest prefix of types that can be passed to Z::template result<???> validly. Some work to avoid passing too-short sequences is done, in case they fail "late":
template<class Z, class types>
struct z_find_longest_prefix :
std::conditional_t<
can_apply< z_apply, Z, types >{},
z_constant<types>,
z_template<longest_prefix>
>::template result<Z, pop_back<types>>
{};
template<class Z, class types>
using z_find_longest_prefix_t = typename z_find_longest_prefix<Z,types>::type;
struct empty_t {};
template<class Z>
struct z_find_longest_prefix<Z,types<>>:
std::conditional_t<
can_apply< Z::template result >,
types<>,
empty_t
>
{};
find_longest_prefix will fail to compile if there is no valid prefix.
template<class Z, class types>
using z_apply_longest_prefix_t =
z_apply< Z, z_find_longest_prefix_t<Z, types> >;
template<class src, template<class...>class... Zs>
struct use_template;
template<class src, template<class...>class... Zs>
using use_template_t = typename use_template<src, Zs...>::type;
template<template<class...>class Z0, class...Ts, template<class...>class... Zs>
struct use_template< Z0<Ts...>, Zs... > {
using type=Z0<
z_apply_longest_prefix_t<
z_template<Zs>, types<Ts...>
>...
>;
};
and up to typos and similar problems, done.
Can be cleaned up lots. Probably the entire z_ subsystem isn't needed. I just tend to run towards it whenever doing serious metaprogramming, because suddenly any metafunction I write that applies to types now can be used on templates.
reverse takes a bit of work to do efficiently.
can_apply is available in many of my posts.
pop_front is trivial.
apply should be easy.
This looked fun so I tried my hand at it; I'm not claiming this is in any way better than the existing answers, but maybe some people will find it clearer:
namespace detail {
template<typename...>
using void_t = void;
template<template<typename...> class, typename, typename, typename EnableT = void>
struct fill_template;
template<template<typename...> class TemplateT, typename TupleT, std::size_t... Is>
struct fill_template<
TemplateT, TupleT, std::index_sequence<Is...>,
void_t<TemplateT<std::tuple_element_t<Is, TupleT>...>>
>
{
using type = TemplateT<std::tuple_element_t<Is, TupleT>...>;
};
template<
template<typename...> class TemplateT, typename TupleT,
std::size_t I, std::size_t N
>
struct fill_template_dispatcher
{
template<typename T, typename = typename T::type>
static constexpr std::true_type test(int) { return {}; }
template<typename T>
static constexpr std::false_type test(long) { return {}; }
using candidate_t = fill_template<TemplateT, TupleT, std::make_index_sequence<I>>;
using type = typename std::conditional_t<
test<candidate_t>(0),
candidate_t,
fill_template_dispatcher<TemplateT, TupleT, I + 1, I != N ? N : 0>
>::type;
};
template<template<typename...> class TemplateT, typename TupleT, std::size_t I>
struct fill_template_dispatcher<TemplateT, TupleT, I, 0>
{
static_assert(I != I, "tuple contains insufficient types to satisfy all templates");
};
} // namespace detail
template<typename TupleT, template<typename...> class... TemplateTs>
struct template_pack
{
static_assert(std::tuple_size<TupleT>::value, "tuple must not be empty");
using type = std::tuple<typename detail::fill_template_dispatcher<
TemplateTs, TupleT, 0, std::tuple_size<TupleT>::value
>::type...>;
};
template<typename TupleT, template<typename...> class... TemplateTs>
using template_pack_t = typename template_pack<TupleT, TemplateTs...>::type;
Online Demo
Note that for variadic class templates this will pass as few types as possible (see D in the test); for an implementation that passes as many types as possible instead, see that variation of the code here (only two lines are changed in the implementation).

Accessing template arguments from specialization

template<typename T, typename U>
struct A;
template<std::size_t I>
struct A<int, char[I]> {
using pointer = T*; // does not compile
};
int main() {
A<int, char[3]> a;
}
Is there any way to access the type T (= int) from inside the class template specialization A<int, char[I]>, without explicitly writing the type that is has in the specialization?
Something like this:
template<class T, class U, class=T, class=U>
struct A;
template<std::size_t I, class T, class U>
struct A<int, char[I], T, U> {
using pointer = T*;
};
works. If someone actually passes a type for the second T and U problems develop, but...
Another approach is:
template<class T, class U>
struct A;
// get args from an instance!
template<class A>
struct A_Args;
template<class T_, class U_>
struct A_Args<A<T_,U_>> {
using T = T_; using U = U_;
};
template<class A>
using A_T = typename A_Args<A>::T;
template<class A>
using A_U = typename A_Args<A>::U;
// reflect on our troubles:
template<std::size_t I>
struct A<int, char[I]> {
using pointer = A_T<A>*;
};
where we have a using alias that extracts args from a generic A which we use within the specialization.
This version can be made generic with an interface like:
template<std::size_t I, class Instance>
struct nth_template_arg;
template<std::size_t I, class Instance>
using nth_template_arg_t=typename nth_template_arg<I, Instance>::type;
with the note it will only work with templates that only take type arguments. (implementation left as an exercise. I'd probably use tuple_element for a first pass; using tuples has the downside that they are heavy types, and metaprogramming with heavy types sucks performance and sometimes causes other problems.)
Simply,
#include <type_traits>
template<class T, class U>
struct foo;
template<class T, std::size_t Index>
struct foo<T, char[Index]>
{
using type = T;
};
int
main()
{
static_assert(std::is_same<foo<int, char[3]>::type, int>::value, "");
}
You can try this:
template<typename T, typename U, std::size_t I>
struct A{
using pointer = T*;
};

Template template and partial specialization: a puzzle

Consider the following code:
template<typename>
struct S { };
template<typename, typename>
struct B;
template <typename R, typename... Args, template<class> class C>
struct B<R(Args...), C<R>> {
void f() { }
};
int main() {
B<void(), S<void>> b;
b.f();
}
It compiles and has no problem.
Anyway, whenever one decides to use B, it has to provide two types.
What I'd like to achieve is to default somehow the second parameter (I know, partial specializations do not accept a default for their parameters) and let an user define it's type as B<void()> instead of B<void(), S<void>>.
Unfortunately, because of template template, partial specialization and the dependency existent between the parameters, all together they lead to a puzzle against which I'm struggling since a couple of hours.
Is there any clever solution to do that?
So far, I have been able to solve it with intermediate structures, but I don't like it so much...
Partial specializations don't accept default parameters, but the primary does. You can just add it there:
template<typename Sig, typename X = S<return_type_t<Sig>>>
struct B;
Then all you need to do is implement a return type metafunction for a signature. Something like:
template <class Sig>
struct return_type;
template <class Sig>
using return_type_t = typename return_type<Sig>::type;
template <class R, class... Args>
struct return_type<R(Args...)> {
using type = R;
};
You may create an helper class for that:
template <typename T> struct default_arg;
template <typename R, typename... Args>
struct default_arg<R(Args...)>
{
using type = S<R>;
};
template<typename Sign, typename T = typename default_arg<Sign>::type>
struct B;
Demo
Here we change B into a template alias.
B_t does the default arg work.
B_impl is the implementation of B without any default args.
B is a using alias that gets the result of B_t.
template<class> struct S {};
template<class, class>
struct B_impl;
template<class R, class... Args, template<class...> class C>
struct B_impl<R(Args...), C<R>> {
void f() { }
};
template<class, class=void>
struct B_t;
template<class R, class...Args>
struct B_t<R(Args...), void>:
B_t<R(Args...),S<R>>
{};
template<class R, class... Args, template<class...> class C>
struct B_t<R(Args...), C<R>> {
using type=B_impl<R(Args...), C<R>>;
};
template<class Sig, class Z=void>
using B=typename B_t<Sig,Z>::type;
The downside is that pattern-matching on B won't work well.

How to instantiate template from mpl::vector

I have a mpl::vector & want to instantiate a template using the vector elements as template arguments. How is this done? Can a argument pack be used to consolidate extra mpl::vector elements?
For example:
struct A; struct B; struct C; struct D;
using args = mpl::vector<A, B, C, D>;
template<typename argA, typename argB, typename argC...>
struct derived_type;
using type_from_vector = derived_type<args>;
What is the best way to approach something like this?
Thanks.
[Full disclosure: I am Boost.Hana's developer]
I know this question is about Boost.MPL, but let me answer using the Boost.Hana
library (which is still in development). If you are using a recent Clang, you
might want to give this library a try; it can do everything Boost.MPL can
do, but it tries very hard to make it less painful. Here you go:
#include <boost/hana/type_list.hpp>
#include <boost/hana/type.hpp>
#include <type_traits>
namespace hana = boost::hana;
struct A; struct B; struct C; struct D;
constexpr auto args = hana::type_list<A, B, C, D>;
template<typename argA, typename argB, typename ...argC>
struct derived_type;
using type_from_vector = decltype(
hana::unpack(args, hana::template_<derived_type>)
)::type;
static_assert(std::is_same<
type_from_vector,
derived_type<A, B, C, D>
>{}, "");
You could either use boost::mpl::fold or std::make_index_sequence.
Both these code snippets assume using namespace boost::mpl;.
Using boost::mpl::fold:
template <typename TList, typename T> struct ExtendTList;
template<typename T, typename... Ts>
struct ExtendTList<derived_type<Ts...>, T>
{
using type = derived_type<Ts..., T>;
};
using type_from_vector = fold<args, derived_type<>, ExtendTList<_1, _2>>::type;
Using std::make_index_sequence:
template <typename V, template<typename...> T, typename Seq>
struct MakeFromTypesAtIndices;
template <typename V, template<typename...> T, size_t ... Indices>
struct MakeFromTypesAtIndices<V, T, std::integer_sequence<size_t, Indices...>>
{
using type = T< at<V, Indices>... >;
};
using type_from_vector = MakeFromTypesAtIndices<args, derived_type, std::make_index_sequence<size<args>::value>>::type;