Как написать compile_time сортировку?
Недавно у меня возникла потребность написать compile time сортировку. Так как в нашем проекте используется с++17, std::sort не constexpr, да и 20 стандарт еще не до конца поддержен.
Я решил поделиться своим алгоритмом, и буду очень рад если его улучшат или скажат об ошибках
Ответы (2 шт):
Так как сортировать большие массивы во время компиляции не придется (да это и не нужно), я реализовал самую простую сортировку вставками.
// енум нужен для примера, так как для моей задачи необходимо сортировать пары 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 циклов, которые есть в сортировке я реализовал функции.
Просто пишете сортировку как обычно, и навешиваете на функцию 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';
}