C++ Safely and Efficiently Casting std::weak_ordering to int

Viewed 291

C++20 is introducing a new comparison type: std::weak_ordering.

It allows for representing less than, equal to, or greater than.

However, some older functions use an int for a similar purpose. Such as qsort, which uses the signature

int compar (const void* p1, const void* p2);

How can I cast std::weak_ordering to int for the use in a function such as qsort?

Here is an example situation:

#include <compare>
#include <iostream>

int main() {
    long a = 2354, b = 1234;
    std::weak_ordering cmp = a <=> b;
    
    if (cmp > 0)  std::cout << "a is greater than b" << std::endl;
    if (cmp == 0) std::cout << "a is equal to b" << std::endl;
    if (cmp < 0)  std::cout << "a is less than b" << std::endl;

    int equivalent_cmp = cmp; // errors
}

In testing, I noticed that using a reinterpret_cast to int8_t type does work, but I am not sure if this would be portable.

int equivalent_cmp = *(int8_t *)&cmp;

or equivalently,

int equivalent_cmp = *reinterpret_cast<int8_t*>(&cmp);

Is this safe?

Furthermore, there are some other solutions that can work, but are inefficient compared this "unsafe" method. All of these would be slower than the above solutions

    int equivalent_cmp = (a > b) - (a < b);

or

    int equivalent_cmp;
    if (cmp < 0)       equivalent_cmp = -1;
    else if (cmp == 0) equivalent_cmp =  0;
    else               equivalent_cmp =  1;

Is there a better solution that would be guaranteed to work?

2 Answers

Is there a better solution that would be guaranteed to work?

No.

The standard does not specify the contents or representation of the ordering classes. Barry's answer is based on reasonable assumptions, that are likely to hold, but they are not guaranteed.

Should you need it, your best bet is to write something like your last snippet

constexpr int ordering_as_int(std::weak_ordering cmp) noexcept {
    return (cmp < 0) ? -1 : ((cmp == 0) ? 0 : 1);
}

How can I cast std::weak_ordering to int for the use in a function such as qsort?

The easy answer is: don't use qsort, use std::sort, it'll perform better anyway.


That said, we know that std::weak_ordering has to have some integral type member, and C++20 does come with a mechanism to pull it out: std::bit_cast:

static_assert(std::bit_cast<int8_t>(0 <=> 1) == -1);

The rule is that the type you're casting to (in this case int8_t) has to be the same size as the type you're casting from (in this case std::strong_ordering). That's a constraint on bit_cast, so it's safe - if the implementation actually stores an int instead of an int8_t, this won't compile.

So more generally, you'd have to write a short metaprogram to determine the correct signed integer type to cast into.


Note that while weak_ordering and strong_ordering will just be implemented as storing an integer (though not int as illustrated in the standard), partial_ordering will probably not be implemented as storing an int and a bool - it will likely still be implemented as a single integer. So this trick won't work.

Related