LINUX.ORG.RU

Оптимизация лаконичности вызова сортировки. Как бы так сделать, что бы все было красиво.

 ,


0

1

У меня в кастомном списке есть такой метод:

template <typename T>
class MyList {
    T *buf = nullptr;
    ...

    void sort(const function<bool(const T& v1, const T& v2)> &compare) {
        if (p_count > 1) std::sort(buf, buf+p_count, compare);
    }
который я могу использовать вот так:
    listData.sort([&](auto &it1, auto &it2) -> bool {
        return tmplLess(
                    it1.field1, it2.field1,
                    it1.field2, it2.field2,
                    it1.field3, it2.field3);
    });
(tmplLess: https://github.com/victorprogrammist/snippets/blob/main/templateCompare.h)

Т.е. все просто, вот только в использовании нужно дублировать поля. Можно попробовать использовать tuple следующим образом:

    template <typename ...Args>
    void sort(const function<std::tuple<Args...>(const T& v)> &fields) {
        sort([&](const T &v1, const T &v2) -> bool {
            return fields(v1) < fields(v2); });
    }

Но тогда уже не задействуются auto при написании использования, и следующий вызов генерит ошибку компиляции:

    listData.sort([&](auto &it) {
        return std::tuple(it.field1, it.field2, it.field3); });

template argument deduction/substitution failed:
note: ‘myFunc::<lambda(auto:45&)>’ is not derived from...

Как бы так сделать, что бы можно было сделать чуть лучше чем есть?

void sort(const function<bool(const T& v1, const T& v2)> &compare) {

замени на

template <typename F>
void sort(F&& compare) {

С второй версией аналогично. Причины использовать здесь std::function нет. Элегантности здесь нет никакой. Под какой стандарт ты пишешь?

В чем смысл мучаться с туплом, я так и не понял. Пиши версию с обычным предикатом. Если юзеру нужно, для хранимого типа данных будет написан свой fields, и твой sort будет использоваться как-то так

sort([=](auto && a, auto && b) { return fields(a) < fields(b); });
Siborgium ★★★ ()
Последнее исправление: Siborgium (всего исправлений: 3)
Ответ на: комментарий от Siborgium

С второй версией аналогично. Причины использовать здесь std::function нет. Элегантности здесь нет никакой. Под какой стандарт ты пишешь?

Это спасибо, так заработало. Версия - какая скачается и запустится на Qt, сейчас c++2a.

В чем смысл мучаться с туплом, я так и не понял. Пиши версию с обычным предикатом. Если юзеру нужно, для хранимого типа данных будет написан свой fields, и твой sort будет использоваться как-то так sort([=](auto && a, auto && b) { return fields(a) < fields(b); });

Здесь я и лист делаю и пользую потом. И плюс у меня подобные конструкции в обходах сортированного списка, и каждый со своим набором сравнений. Короче много разных вариантов даже на одни список получается. А все сравнения в основном на равно или меньше. Т.е по сути использую этот список как таблицу.

И получается что я на каждый вариант пишу

[&](auto &it1, auto &it2) { return cmp(it1.field1, it2.field1, it1.field2, it2.field2); }
А хочется заменить на табличное\колоночное представление, без дублирования упоминаний полей:
// сортировка по первой и второй колонке
[&](auto &it1) { return tuple(it1.field1, it2.field2); }
И вроде как можно, но тогда нужно расписывать в шаблон вызова сортировки все задействованные типы.

victor79 ()
Ответ на: комментарий от Siborgium

С вашим советом получилось:

    template <typename F>
    void sort(F &&compare) {
        if (p_count > 1) std::sort(buf, buf+p_count, compare);
    }

    template <typename F>
    void sort2(F &&fields) {
        sort([&](const T &v1, const T &v2) -> bool {
            return fields(v1) < fields(v2); });
    }

И тогда можно:

    listData.sort2([](auto &&it) {
        return std::tuple(it.field1, it.field2); });

victor79 ()
Ответ на: комментарий от victor79

Версия - какая скачается и запустится на Qt, сейчас c++2a.

Можно писать в 2-4 раза короче, раз есть с++2а.

Кроме того, можно как-то так

template <typename T>      inline constexpr bool is_tuple_v = false;
template <typename ... Ts> inline constexpr bool is_tuple_v<std::tuple<Ts...>> = true;

template <typename F, typename ... Args>
concept returns_tuple = is_tuple_v<std::invoke_result_t<F, Args...>>;
 
void sort(returns_tuple auto && fields) {
    return sort([=](auto && a, auto && b) { return fields(a) < fields(b); });
}
void sort(auto && compare) {
    if (p_count > 1) std::sort(buf, buf + p_count, compare);
}
Siborgium ★★★ ()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от Siborgium

Кроме того, можно как-то так

Ограничение на tuple это уже лишнее. Без него для случаев когда колонка для сравнения одна, то можно писать просто:

list.sort2([&](auto &&it) { return it.field1; });

Но да, тогда нужно sort2, а не просто sort.

victor79 ()
Ответ на: комментарий от Radjah

Как бы так сделать, что бы все было красиво.
https://chtoby-pravilno.ru/chtoby/

Совершенство не достижимо, остается только пытаться приблизиться.

victor79 ()
Последнее исправление: victor79 (всего исправлений: 1)
Ответ на: комментарий от victor79

std::tie() надо использовать вместо std::tuple, он создаст тупл ссылок, а не тупл значений. std::function надо передавать по значению, либо инстанциировать то, что передаешь и это должна быть std::function, если ты хочешь передать по ссылке. Надо както создать условия для того чтобы инстанс лямбды скопировался либо протез как параметр функции.

anonymous ()