How to get the closest signed type which covers all the range of current unsigned type?

53 Views Asked by At

In the template I have a type for index (IndexType) which is unsigned type and need to create a member for offset which could be negative but should contain all the range for the initial unsigned type. In practice this means that it must be the next larger integer signed size.

At the same time I don't want to allocate the largest possible signed type like std::ptrdiff_t to save space, since signed short could be much better fit for unsigned char in terms of memory.

I can make very complex std::conditional with sizeofs for all possible combinations, but this will be too verbose. Is there any straightforward solution for this?

At the moment I don't need sizes larger than std::ptrdiff_t, so the quest "what if you need the size larger than all build-in primitive integer types?" could be postponed.

template <typename IndexType> // Here I get unsigned type of index
struct Offset {
    IndexType offs; // Here I need a type which will be signed, but enough to keep unsigned values of IndexType, but not unnecessary large
};
2

There are 2 best solutions below

0
NathanOliver On BEST ANSWER

Since you aren't worried about the case when the unsigned type is as large as the largest signed type there is really only three cases you need to check. If the size is 1, then get a 2 size type, If the size is 2, get a 4 size type, otherwise just go with the max signed type. One way to do that is to leverage a function to get the type like:

auto promote_type(std::unsigned_integral auto type)
{
    if constexpr (sizeof(type) == 1)      return int16_t();
    else if constexpr (sizeof(type) == 2) return int32_t();
    else                                  return int64_t();
}
    
template <typename T>
using promoted_type = decltype(promote_type(T{}));
    
int main()
{
    static_assert(std::is_same_v<promoted_type<unsigned>, int64_t>);
}
0
John Zwinck On

In practice the number of cases is really not so large on the usual platforms:

template <std::unsigned_integral T>
using Offset = std::conditional_t<sizeof(T) == 1, int16_t,
    std::conditional_t<sizeof(T) == 2, int32_t, int64_t>>;

You mentioned this would be "too verbose" but it really is quite concise.