I have been using my own TMP library for quite some time. However, I want to start using Boost.MP11. The interface is quite nice, and, to give myself some motivation to learn it, I did the 2022 Advent of Code as much as I could at compile time, using Boost.MP11.
However, there were some things that I had to do, which I did not like. I went back to clean some things up, and one place where I am still a bit confused, is how to prevent unnecessary instantiations. I'm sure some use of mp_valid is the way to go, but I could use some guidance.
For example, consider the following implementation of push_heap, using Boost.MP11 (which I implemented as part of implementing Dijkstra's algorithm for one of the challenges).
It is a bit verbose in order to make it obvious what I want to achieve, namely the instantiation of templates only when they are true.
I must be misunderstanding something about Boost.MP11, so I would like to see an elegant Boost.MP11 implementation of push_heap.
template <typename IndexT>
using parent = mp_constant<(IndexT::value - 1) / 2>;
template <typename ListT, typename IndexT> struct BubbleUp;
template <
typename ListT,
typename IndexT,
typename CondT = mp_less<
mp_at<ListT, parent<IndexT>>,
mp_at<ListT, IndexT>>>
struct BubbleUpImpl
: BubbleUp<
mp_replace_at<
mp_replace_at<ListT, IndexT, mp_at<ListT, parent<IndexT>>>,
parent<IndexT>,
mp_at<ListT, IndexT>>,
parent<IndexT>>
{ };
template <typename ListT, typename IndexT>
struct BubbleUpImpl<ListT, IndexT, mp_false>
{
using type = ListT;
};
template <typename ListT, typename IndexT>
struct BubbleUp
: BubbleUpImpl<ListT, IndexT>
{ };
template <typename ListT>
struct BubbleUp<ListT, mp_size_t<0>>
{
using type = ListT;
};
// Not same as std::push_heap - it pushes and heapifies
template <typename ListT, typename T>
using push_heap = typename BubbleUp<
mp_push_back<ListT, T>,
mp_size<ListT>>::type;
EDIT
Here is my attempt with Boost.MP11, but I am less than enamored with it, and still searching for a better alternative, and especially interested in learning how best to do these kinds of things with Boost.MP11. Note that the semantics here are the same as std::push_heap - the difference above was to try and simplify.
namespace heap_detail {
template <typename ListT, typename Index1T, typename Index2T>
using swap_at = mp_replace_at<
mp_replace_at<ListT, Index1T, mp_at<ListT, Index2T>>,
Index2T,
mp_at<ListT, Index1T>>;
template <typename CmpQ>
class PushHeap
{
template <typename T>
using heap = mp_first<T>;
template <typename T>
using child = mp_second<T>;
template <typename T>
using parent = mp_if<
mp_less<mp_size_t<0>, child<T>>,
mp_constant<(child<T>::value - 1) / 2>>;
template <typename T>
using bubble_up = mp_list<
mp_if<
mp_invoke_q<
CmpQ,
mp_at<heap<T>, parent<T>>,
mp_at<heap<T>, child<T>>>,
swap_at<heap<T>, parent<T>, child<T>>>,
parent<T>>;
public:
template <typename ListT>
using fn = mp_back<mp_iterate<
mp_list<ListT, mp_minus<mp_size<ListT>, mp_int<1>>>,
mp_first,
bubble_up>>;
};
} // namespace heap_detail
template <typename ListT, typename CmpQ>
using push_heap_q = mp_invoke_q<heap_detail::PushHeap<CmpQ>, ListT>;
template <typename ListT, template <typename...> class Cmp = mp_less>
using push_heap = push_heap_q<ListT, mp_quote<Cmp>>;
Also, for what it's worth, here is the rest of the heap interface. Maybe these will show more opportunity for learning how better to use Boost.MP11.
make_heap
template <typename ListT, typename CmpQ>
using make_heap_q = mp_fold_q<
ListT,
mp_list<>,
mp_compose_q<mp_quote<mp_push_back>, mp_bind_back<push_heap_q, CmpQ>>>;
template <typename ListT, template <typename...> class Cmp = mp_less>
using make_heap = make_heap_q<ListT, mp_quote<Cmp>>;
And something to push additional items onto the heap
template <typename ListT, typename CmpQ, typename... Ts>
using heap_push_back_q = mp_fold_q<
mp_list<Ts...>,
ListT,
mp_compose_q<mp_quote<mp_push_back>, mp_bind_back<push_heap_q, CmpQ>>>;
And pop_heap, which seems way overly complicated - specialization seems much easier to read and write, so, I believe I am missing something fundamental with Boost.MP11.
template <typename CmpQ>
class PopHeap
{
template <typename T>
using heap = mp_first<T>;
template <typename T>
using parent = mp_second<T>;
template <typename T>
using left_child = mp_constant<parent<T>::value * 2 + 1>;
template <typename T>
using right_child = mp_constant<parent<T>::value * 2 + 2>;
template <typename T>
using child_to_swap = mp_eval_or_q<
left_child<T>,
mp_quote<mp_if>,
mp_invoke_q<
CmpQ,
mp_at<heap<T>, left_child<T>>,
mp_at<heap<T>, right_child<T>>>,
right_child<T>,
left_child<T>>;
template <typename T>
using bubble_down = mp_list<
mp_if<
mp_invoke_q<
CmpQ,
mp_at<heap<T>, parent<T>>,
mp_at<heap<T>, child_to_swap<T>>>,
swap_at<heap<T>, parent<T>, child_to_swap<T>>>,
child_to_swap<T>>;
public:
template <typename ListT>
using fn = mp_push_back<
mp_back<
mp_iterate<
mp_list<
mp_rotate_right<mp_pop_front<ListT>, mp_int<1>>,
mp_size_t<0>>,
mp_first,
bubble_down>>,
mp_first<ListT>>;
};
template <typename ListT, typename CmpQ>
using pop_heap_q = mp_invoke_q<heap_detail::PopHeap<CmpQ>, ListT>;
template <typename ListT, template <typename...> class Cmp = mp_less>
using pop_heap = pop_heap_q<ListT, mp_quote<Cmp>>;
Related
This question already has answers here:
effective way to select last parameter of variadic template
(8 answers)
Closed 2 years ago.
Note:
My concern here is about compilation speeds.
I assume that recursive type traits are typically slower than the alternatives (when possible). If I'm wrong about this then please let me know.
We can access the types at the front of a variadic list without recursion, like so:
#include <iostream>
#include <type_traits>
template <typename T>
struct this_type
{
using type = T;
};
template <typename T1, typename ...>
struct front : this_type<T1> {};
template <typename ... Ts>
using front_t = typename front<Ts...>::type;
template <typename ... Ts>
void Foo ()
{
std::cout << std::is_same_v<front_t<Ts...>, int> << std::endl;
}
int main ()
{
Foo<int, char, bool>();
}
However, I can't think of a generic way to access the back type without recursion. Intuitively I'd want to do something like this:
template <typename ...>
struct pack;
template <typename ...>
struct back;
template <typename Tn, typename ... Ts>
struct back <pack<Ts..., Tn>> : this_type<Tn> {};
template <typename ... Ts>
using back_t = typename back<pack<Ts...>>::type;
... but the variadic template needs to be the last argument in the specialisation.
We can use a bit of code bloat to manage accessing elements up to a certain amount. eg, 3:
template <typename ...>
struct back;
template <typename T1>
struct back <T1> : this_type<T1> {};
template <typename T1, typename T2>
struct back <T1, T2> : this_type<T2> {};
template <typename T1, typename T2, typename T3>
struct back <T1, T2, T3> : this_type<T3> {};
template <typename ... Ts>
using back_t = typename back<Ts...>::type;
template <typename ... Ts>
void Foo ()
{
std::cout << std::is_same_v<back_t<Ts...>, bool> << std::endl;
}
int main ()
{
Foo<int, char, bool>();
}
...But is there a way to do it generically without recursion?
Motivation:
Often we can change the way that we solve a problem so that we access from near the front of a variadic list, but sometimes it's unavoidable to access from the back. I don't want to create useless work for the compiler if there's some language feature that I'm not taking advantage of.
I'd leverage std::tuple to do most of the heavy lifting.
template<typename... Ts>
using back_t = std::tuple_element_t<std::tuple<Ts...>, sizeof...(Ts) - 1>;
I have the following code:
template <template <typename, typename...> typename trait_t, typename arg_t>
struct BindFirst
{
template <typename... arg_ts>
using result_t = trait_t<arg_t, arg_ts...>;
};
#define BIND_FIRST(trait_t, arg_t) BindFirst<trait_t, arg_t>::template result_t
you can use it to bind the first argument of a trait like this:
BIND_FIRST(std::is_same, double)
The result is equivalent to:
template <typename T>
struct IsInt : std::is_same<double, T> { };
The difference is, that you can use it inline. For example like this:
using result_t = find_t<type_list, BIND_FIRST(std::is_same, double)>;
This works but i like to avoid the define. I tried to use an alias. But I have no idea how to apply it. Is there any way to replace the define?
You can use templates with using to create an alias template, just like you did for result_t.
template <typename... Args>
using IsDouble = BindFirst<std::is_same, double>::template result_t<Args...>;
You can limit the Args... to a single type T as well, it doesn't have to be variadic.
Edit: If your goal is to reduce boilerplate, you may want to opt for something like this
template <typename T, typename U>
using IsSameAs = std::is_same<T, U>;
template <typename T>
using IsDouble = IsSameAs<double, T>;
Is there any way one can alias a nested template class with a using keyword? Something like this
template <typename... Types>
struct Something {
template <typename... TypesTwo>
struct Another {};
};
template <typename... Types>
template <typename... TypesTwo>
using Something_t = typename Something<Types...>::template Another<TypesTwo...>;
int main() {
Something_t<int><double>{};
return 0;
}
This answer template template alias to a nested template? shows a way to do that but that will no longer work if both the parameter packs are variadic, as the compiler will not know where to start and where to end the type lists.
Not exactly what you asked but... if you can wrap your variadic type lists as arguments of tuples (or similar classes)...
#include <tuple>
template <typename ... Types>
struct Something
{
template <typename ... TypesTwo>
struct Another {};
};
template <typename, typename>
struct variadicWrapper;
template <typename ... Ts1, typename ... Ts2>
struct variadicWrapper<std::tuple<Ts1...>, std::tuple<Ts2...>>
{ using type = typename Something<Ts1...>::template Another<Ts2...>; };
template <typename T1, typename T2>
using Something_t = typename variadicWrapper<T1, T2>::type;
int main()
{
Something_t<std::tuple<int>, std::tuple<double>>{};
}
Not a standalone answer, but an addition to max66's answer:
You could have tried this:
template<typename ... TT, typename ... TTT>
using Alias = typename Something<TT...>::Another<TTT...>;
Looks really nice at first, doesn't it?
Problem then, however will already be with one single template parameter:
Alias<int> a;
Which one is it now? Something<int>::Another<> or Something<>::Another<int>? And if you have more parameters, how to distribute? No chance to get a meaningful solution. So no, you can't do that directly, you have to help yourself with tricks such as max66 proposed...
How can I retrieve the template a type was originally instantiated from?
I'd like to do the following:
struct Baz{};
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
using Template = get_template<SomeType>::template type<T>;
static_assert(std::is_same<Foo<Baz>, Template<Baz>>::value, "");
I know I can achieve this through partial specialization, but this forces me to specialize get_template for every template I want to use it with:
template <typename T>
struct get_template;
template <typename T>
struct get_template<Foo<T>>
{
template <typename X>
using type = Foo<X>;
};
live example
Is there a way around this limitation?
You could do something like that, using a template template parameter (should work for templates with any number of type arguments):
template <typename T>
struct get_template;
template <template <class...> class Y, typename... Args>
struct get_template<Y<Args...>> {
template <typename... Others>
using type = Y<Others...>;
};
Then to get the template:
template <typename T>
using Template = typename get_template<SomeType>::type<T>;
As mentioned by #Yakk in the comment, the above only works for template that only have type arguments. You can specialize for template with specific pattern of type and non-type arguments, e.g.:
// Note: You need the first size_t to avoid ambiguity with the first specialization
template <template <class, size_t, size_t...> class Y, typename A, size_t... Sizes>
struct get_template<Y<A, Sizes...>> {
template <class U, size_t... OSizes>
using type = Y<U, OSizes...>;
};
...but you will not be able to specialize it for arbitrary templates.
DEMO (with Foo and std::pair):
#include <type_traits>
#include <map>
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
struct get_template;
template <template <class...> class Y, typename... Args>
struct get_template<Y<Args...>> {
template <typename... Others>
using type = Y<Others...>;
};
template <typename T>
using Template = typename get_template<SomeType>::type<T>;
static_assert(std::is_same<SomeType, Template<Bar>>::value, "");
static_assert(std::is_same<Foo<int>, Template<int>>::value, "");
using PairIntInt = std::pair<int, int>;
using PairIntDouble = std::pair<int, double>;
template <typename U1, typename U2>
using HopeItIsPair =
typename get_template<PairIntDouble>::type<U1, U2>;
static_assert(std::is_same<PairIntDouble, HopeItIsPair<int, double>>::value, "");
static_assert(std::is_same<PairIntInt, HopeItIsPair<int, int>>::value, "");
Not sure I got the question. Would this work?
#include<type_traits>
#include<utility>
template<typename V, template<typename> class T, typename U>
auto get_template(T<U>) { return T<V>{}; }
struct Baz{};
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
using Template = decltype(get_template<T>(SomeType{}));
int main() {
static_assert(std::is_same<Foo<Baz>, Template<Baz>>::value, "");
}
You can generalize even more (SomeType could be a template parameter of Template, as an example), but it gives an idea of what the way is.
I have traits classes sprinkled about my code which follow the same basic idiom:
template<class Frame, typename = void>
struct frame_traits
{
typedef void base_frame_type;
};
template<class Frame>
struct frame_traits<Frame, typename std::void_t<
typename Frame::base_frame_type>::type>
{
typedef typename Frame::base_frame_type base_frame_type;
};
and I have a bunch of trait checkers which use them, which also follow a similar idiom:
template <typename T>
struct has_base_frame_type : std::integral_constant<bool,
!std::is_same<typename frame_traits<T>::base_frame_type, void>::value>::type {};
however, it turns out that has_base_frame_type has become useful to multiple concepts in my code, and I'd like to generalize it further so that I can pass the traits class as an additional parameter:
template <typename T, template<typename> class Traits = frame_traits>
struct has_base_frame_type : std::integral_constant<bool,
!std::is_same<typename Traits<T>::base_frame_type, void>::value>::type {};
This doesn't work though, since templates with default arguments cannot be used as template template parameters.
I know I could work around the problem if I always use a traits class in the template instantiation (and modify the trait checker to accept it), namely
has_base_frame_type<frame_traits<MyClass>>::value
but I don't want to do that, because it would be all too easy to forget and pass in a non-trait class. In fact, that's how I originally had the code written until I forgot the trait one too many times and refactored it.
Is there someway I can modify my trait class idiom to work around the template template parameter problem?
Framework:
#include <type_traits>
template <typename...>
using void_t = void;
template <typename AlwaysVoid, template <typename...> class Operation, typename... Args>
struct detect_impl : std::false_type {};
template <template <typename...> class Operation, typename... Args>
struct detect_impl<void_t<Operation<Args...>>, Operation, Args...> : std::true_type {};
template <template <typename...> class Operation, typename... Args>
using detect = detect_impl<void, Operation, Args...>;
Detectors:
template <class Frame>
using frame_traits = typename Frame::base_frame_type;
template <class Frame>
using other_frame_traits = typename Frame::other_frame_type;
Trait with a default detector:
template <typename T, template <typename...> class Traits = frame_traits>
using has_frame_type = detect<Traits, T>;
Test:
struct A
{
using base_frame_type = void;
};
struct B
{
using other_frame_type = void;
};
int main()
{
static_assert(has_frame_type<A>{}, "!"); // default
static_assert(!has_frame_type<B>{}, "!"); // default
static_assert(!has_frame_type<A, other_frame_traits>{}, "!"); // non-default
static_assert(has_frame_type<B, other_frame_traits>{}, "!"); // non-default
}
DEMO