Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Properly casting signed to unsigned

I'm using a C library that uses unsigned integers as index to some data. But sometimes, functions return those indices as signed in order to return -1 if the function fails to return an index.*

How do I prevent implicit conversion changes signedness warnings and instead, throw runtime errors if the conversion isn't possible? Would you recommend to wrap library functions to use exceptions for error handling and only return proper values? Is there a standard way to do this:

#include <stdlib.h>
#include <errno.h>
#include <limits.h>

// pointless c function to demonstrate the question
// parse the string to an unsigned integer, return -1 on failure
int atoui(char const* str) {
    char* pend;
    long int li=strtol(str, &pend, 10);
    if ( errno!=0 || *pend!='\0' || li<0 || li>INT_MAX ) {
        return -1;
    } else {
        return li;
    }
}

// --8<---

#include <stdexcept>

// How to do this properly?
unsigned int unsign(int i) {
    if(i<0) {
        throw std::runtime_error("Tried to cast negative int to unsigned int");
    } else {
        return static_cast<unsigned>(i);
    }
}

int main() {
    unsigned int j=unsign(atoui("42")); // OK
    unsigned int k=unsign(atoui("-7")); // Runtime error
}
like image 228
Gamification Avatar asked Dec 17 '22 18:12

Gamification


1 Answers

The standard library has no such function, but it's easy enough to write such a template:

template<typename SInt, typename = std::enable_if_t<std::is_integeral_v<SInt> && std::is_signed_v<SInt>>>
constexpr auto unsigned_cast(Sint i)
{
  if(i < 0) throw std::domain_error("Outside of domain");
  return static_cast<std::make_unsigned_t<SInt>>(i);
}

You can also return an optional if you don't like throwing exceptions for such trivial matters:

template<typename SInt, typename = std::enable_if_t<std::is_integeral_v<SInt> && std::is_signed_v<SInt>>>
constexpr std::optional<std::make_unsigned_t<SInt>> unsigned_cast_opt(Sint i)
{
  if(i < 0) return std::nullopt;
  return static_cast<std::make_unsigned_t<SInt>>(i);
}
like image 131
Nicol Bolas Avatar answered Jan 07 '23 10:01

Nicol Bolas