Как написать compile_time сортировку?

Недавно у меня возникла потребность написать compile time сортировку. Так как в нашем проекте используется с++17, std::sort не constexpr, да и 20 стандарт еще не до конца поддержен.

Я решил поделиться своим алгоритмом, и буду очень рад если его улучшат или скажат об ошибках


Ответы (2 шт):

Автор решения: Denver Toha

Так как сортировать большие массивы во время компиляции не придется (да это и не нужно), я реализовал самую простую сортировку вставками.

// енум нужен для примера, так как для моей задачи необходимо сортировать пары enum - строка
enum class MinLimValue : uint16_t
{
    NONE                 = 0,  // значение не задано
    // 0 - 3 бит - PPE
    MIN_BANNER_PPE       = 1 << 0,
    // 4 - 7 бит - cpm
    REQUEST_MIN_CPM      = 1 << 4,
    PAD_MIN_CPM          = 1 << 5,
    MIN_CPM_BY_ALIVE_CPM = 1 << 6,
};

namespace compile_time {

    // swapt для простых объектов и объектов с constexpr оператором присваивания
    template<typename T>
    constexpr void swap(T & aLeft, T & aRight) {
          T sTmp = aLeft;
          aLeft = aRight;
          aRight = sTmp;
    }
   
    // следующие 3 перегрузки нужны для свапа картежей
    template<size_t I, typename... T>
    constexpr void swap(std::tuple<T...> & aLeft, std::tuple<T...>& aRight)
    {
        auto& sLeft = std::get<I>(aLeft);
        auto& sRight = std::get<I>(aRight);
        auto sTmp = sLeft;
        sLeft = sRight;
        sRight = sTmp;
    }

    template<size_t... I, typename... T>
    constexpr void swap(std::tuple<T...> & aLeft, std::tuple<T...>& aRight,  std::index_sequence<I...>)
    {
        ([&]{ swap<I>(aLeft, aRight);}(), ...);
    }

    template<typename... T>
    constexpr void swap(std::tuple<T...> & aLeft, std::tuple<T...>& aRight)
    {
        constexpr auto sSize = std::tuple_size<std::tuple<T...>>{};
        swap(aLeft, aRight, std::make_index_sequence<sSize>{});
    }

    template<size_t I, typename T>
    constexpr void innerLoop(T& arr) {
        if constexpr(I == 0) {
            return;
        }
        for (size_t i = I; i > 0 && arr[i - 1] > arr[i]; i--) {
            compile_time::swap(arr[i - 1], arr[i]);
        }
    }

    template<typename T, std::size_t ...I>
    constexpr void outerLoop(T & arr, std::index_sequence<I...>)
    {
        ([&] {
            innerLoop<I>(arr);
        }(), ...);
    }

    template<size_t Size, typename T>
    constexpr void sort(T& arr)
    {
        outerLoop(arr, std::make_index_sequence<Size>());
    }


    // для примера, чтоб проверить работает ли наша сортировка в comp
    constexpr auto makeArr()
    {
        std::array<std::tuple<MinLimValue, int>, 5> sTest{std::make_tuple(MinLimValue::NONE, 5),
                                                                 std::make_tuple(MinLimValue::PAD_MIN_CPM,3),
                                                                 std::make_tuple(MinLimValue::MIN_CPM_BY_ALIVE_CPM,5),
                                                                 std::make_tuple(MinLimValue::MIN_BANNER_PPE,10),
                                                                 std::make_tuple(MinLimValue::REQUEST_MIN_CPM,89)};
        sort<5>(sTest);
        return sTest;
    }
}

int main() {

    constexpr auto sArr = compile_time::makeArr();

    return 0;
}

К сожалению, вложенный цикл, чтоб вложенный счетчик принимал переменное значение с compile_time у меня сделать не получилось, компилятор ругался, по этому для 2 циклов, которые есть в сортировке я реализовал функции.

→ Ссылка
Автор решения: HolyBlackCat

Просто пишете сортировку как обычно, и навешиваете на функцию constexpr.

#include <array>
#include <iostream>
#include <utility>

template <typename T>
constexpr void sort(T *begin, T *end)
{
    for (T *mid = end; mid > begin;)
    {
        mid--;
        for (T *cur = begin; cur < mid; cur++)
        {
            if (cur[0] > cur[1])
            {
                T tmp(std::move(cur[0]));
                cur[0] = std::move(cur[1]);
                cur[1] = std::move(tmp);
            }
        }
    }
}

constexpr std::array<int, 4> foo()
{
    std::array<int, 4> ret = {2,4,3,1};
    sort(ret.data(), ret.data() + ret.size());
    return ret;
}

int main()
{
    constexpr auto arr = foo();
    for (int x : arr)
        std::cout << x << '\n';
}
→ Ссылка