Search code examples
c++boostboost-hana

Ordering multiple function calls during compile time


I have a function:

template<typename... Args>
auto CallItemsInOrder(std::tuple<Args...> items)
{
    // Magic here!
}

Where each item of the tuple has two qualities: A priority for which it should be called, and a function overload to call it with:

// DoWork calls are NOT constexpr
template<> void DoWork(int item)         { ... }
template<> void DoWork(std::string item) { ... }

// std::string is higher priority than int, gets called first
// Priorities don't have to be unique or sequential, to allow future customization
template<> struct WorkPriority<int>         { static constexpr int = 0; }
template<> struct WorkPriority<std::string> { static constexpr int = 10; }

Such that, when CallItemsInOrder is called with that tuple, it should be able to be optimized down to this at compiletime:

template<>
auto CallItemsInOrder(std::tuple<int, std::string> items)
{
    DoWork(std::get<1>(items));
    DoWork(std::get<0>(items));
}

This is something I could imagine is easy to do without a hundred lines of code using Boost.Hana, but frankly I'm having trouble understanding the docs. Any help would be appreciated!


Solution

  • "Just" sort the indexes by priority (thanks to constexpr function):

    template<typename... Args>
    auto CallItemsInOrder(std::tuple<Args...> items)
    {
        [&]<std::size_t ... Is>(std::index_sequence<Is...>){
            constexpr auto arr = [](){
                // priority, index pairs
                std::array<std::pair<int, std::size_t>, sizeof...(Is)> res{{
                    {WorkPriority<std::tuple_element_t<Is, std::tuple<Args...>>>::value, Is}...
                }};
                std::sort(res.begin(), res.end(), std::greater{}); // std::partial_sort, custom comparer, ...
                return res;
            }();
    
            // now just call DoWork(std::get<arr[Is].index>(items)) in "loop".
            (DoWork(std::get<std::get<1>(arr[Is])>(items)), ...);
        }(std::index_sequence_for<Args...>());
    }
    

    Demo