5

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>>;
Jody Hagins
  • 27,943
  • 6
  • 58
  • 87
  • Consider posting this question to [Code Review SE](https://codereview.stackexchange.com/), since the code itself has no identifiable problem/bug. – Neervana Jan 06 '23 at 12:42
  • If you do post it to Code Review be sure to read [A Guide to Code Review for Stack Overflow Users](https://codereview.meta.stackexchange.com/questions/5777/a-guide-to-code-review-for-stack-overflow-users/5778#5778) first, there are a few edits necessary. – pacmaninbw Jan 06 '23 at 14:05

0 Answers0