Захотел результат <=> запихнуть в switch и сделал враппер
От: Sm0ke Россия http://ksi.ru.net/
Дата: 28.01.22 08:04
Оценка:
Вот они сделали, что std::strong_ordering::equal и прочие нельзя было запихнуть в switch case.
Но это не проблема, пишем враппер!

#include <compare>

enum class compare_strict {
    unordered = -2,

    less = -1,
    greater = +1,

    equal_strong = 0,
    equal_weak = 2,
    equal_partial = 3
};

template <typename Result>
constexpr Result cast_ordering(std::strong_ordering from) {
    using t_from = decltype(from);
    return from == t_from::less ? Result::less : (
        from == t_from::greater ? Result::greater : Result::equal_strong
    );
}

template <typename Result>
constexpr Result cast_ordering(std::weak_ordering from) {
    using t_from = decltype(from);
    return from == t_from::less ? Result::less : (
        from == t_from::greater ? Result::greater : Result::equal_weak
    );
}

template <typename Result>
constexpr Result cast_ordering(std::partial_ordering from) {
    using t_from = decltype(from);
    return from == t_from::less ? Result::less : (
        from == t_from::greater ? Result::greater : (
            from == t_from::unordered ? Result::unordered : Result::equal_partial
        )
    );
}


Теперь можно просто вот так:
int main() {
    switch( cast_ordering<compare_strict>(0.0/0.0 <=> 1.1) ) {
        case compare_strict::less :
        std::cout << "less\n"; break;

        case compare_strict::greater :
        std::cout << "greater\n"; break;

        case compare_strict::equal_partial :
        std::cout << "equal_partial\n"; break;

        case compare_strict::equal_weak :
        std::cout << "equal_weak\n"; break;

        case compare_strict::unordered :
        std::cout << "unordered\n"; break;

        default:
        std::cout << "equal_strong\n";
    }
    return 0;
}


Но если вы не хотите различать equal_partial, equal_weak, equal_strong — для этого пишем ещё один enum, чтобы подставить его как Result в cast_ordering<>() :
enum class compare_simple {
    less = -1,
    equal_any = 0,
    greater = +1,

    equal_strong = equal_any,
    equal_weak = equal_any,
    equal_partial = equal_any,

    unordered = less
};

int main() {
    // Вы могли заметить, что в compare_simple ещё и unordered работает как less !
    switch( cast_ordering<compare_simple>(0.0/0.0 <=> 1.1) ) {
        case compare_strict::less :
        std::cout << "less\n"; break;

        case compare_strict::greater :
        std::cout << "greater\n"; break;

        case compare_strict::equal_any :
        std::cout << "equal\n"; break;

        default:
        std::cout << "произошла фигня!\n";
    }
    return 0;
}


Короче таким макаром можно делать свои кастомные енумы результатов сравнения со своими значениями underlying тайпа.
Одно меня смущает однако что функция cast_ordering<>() кушает накладные расходы на трансформацию, но я думаю это не всегда критично.
cpp switch cast comparison
Re: Захотел результат <=> запихнуть в switch и сделал враппер
От: Alexander G Украина  
Дата: 28.01.22 10:28
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Вот они сделали, что std::strong_ordering::equal и прочие нельзя было запихнуть в switch case.


Ага, было http://rsdn.org/forum/cpp/7733292
Автор: Шахтер
Дата: 18.05.20


S>Но это не проблема, пишем враппер!


S>

S>template <typename Result>
S>constexpr Result cast_ordering(std::strong_ordering from) {
S>    using t_from = decltype(from);
S>    return from == t_from::less ? Result::less : (
S>        from == t_from::greater ? Result::greater : Result::equal_strong
S>    );
S>}

S>template <typename Result>
S>constexpr Result cast_ordering(std::weak_ordering from) {
S>    using t_from = decltype(from);
S>    return from == t_from::less ? Result::less : (
S>        from == t_from::greater ? Result::greater : Result::equal_weak
S>    );
S>}


В таком врапере следует постараться, чтобы значения энамов совпадали с внутреним представлением std::strong_ordering / std::weak_ordering, чтобы касты выродились максимально в пустышку.
Как-то подобрать underlying type равный по размеру std::strong_ordering / std::weak_ordering и бит кастами получить значения.
Русский военный корабль идёт ко дну!
Re[2]: Захотел результат <=> запихнуть в switch и сделал враппер
От: Sm0ke Россия http://ksi.ru.net/
Дата: 28.01.22 11:25
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>В таком врапере следует постараться, чтобы значения энамов совпадали с внутреним представлением std::strong_ordering / std::weak_ordering, чтобы касты выродились максимально в пустышку.

AG>Как-то подобрать underlying type равный по размеру std::strong_ordering / std::weak_ordering и бит кастами получить значения.

Или ещё лучше пропихнуть идею в стандарт, чтобы они добавили non-static поля strict и simple в эти все ордеренги.
Или вообще просто позволили наконец ставить их в switch case.
Re: Через sign<>()
От: Sm0ke Россия http://ksi.ru.net/
Дата: 05.02.22 11:18
Оценка:
Ещё один подход.

#include <concepts>
#include <iostream>

template <typename T, typename ... U>
concept c_any_of = ( std::same_as<T, U> || ... );

template <typename Result, c_any_of<std::strong_ordering, std::weak_ordering> T>
constexpr inline Result sign(T val) {
    return (val > 0) - (val < 0);
}

template <typename Result>
constexpr inline Result sign(std::partial_ordering val, Result on_unordered) {
    return (val != std::partial_ordering::unordered) ? (val > 0) - (val < 0) : on_unordered;
}

int main() {
    switch( sign<int>(0.0 <=> 0.0 / 0.0, 2) ) {
    case -1: std::cout << "less\n";    break;
    case +1: std::cout << "greater\n"; break;
    case  0: std::cout << "equal\n";   break;
    default: std::cout << "unordered\n";
    }
}


Кто разбирается в том, во что это компилируется? Вам не кажется, что оверхедов с этими ордерингами дожопы? Я не разбираюсь просто.

https://gcc.godbolt.org/z/z463TGrdP

https://gcc.godbolt.org/z/cMjc6YoG9
Re: Захотел результат <=> запихнуть в switch и сделал враппер
От: Sm0ke Россия http://ksi.ru.net/
Дата: 08.02.22 05:32
Оценка:
Здравствуйте, Sm0ke, Вы писали:

Для своих типов можно просто определять результат оператора <=> как int
Остальные операторы при этом будут сгенерированы автоматически, как и с ордерингами.
И да, в этом случае просто помещаем в switch( a <=> b ) { case -1: break; } без всяких врапперов!
Однако тогда так не получится: friend int operator <=> (/* cut */) = default;
И придётся прописывать operator == () для автогенерации !=

Такие дела
comparison
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.