简体   繁体   English

正确施放签名到未签名

[英]Properly casting signed to unsigned

I'm using a C library that uses unsigned integers as index to some data. 我正在使用一个C库,它使用无符号整数作为某些数据的索引。 But sometimes, functions return those indices as signed in order to return -1 if the function fails to return an index.* 但有时,函数会将这些索引返回为signed,以便在函数无法返回索引时返回-1 。*

How do I prevent implicit conversion changes signedness warnings and instead, throw runtime errors if the conversion isn't possible? 如何防止implicit conversion changes signedness警告,而是在无法进行转换时抛出运行时错误? 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
}

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: 如果您不喜欢为这些微不足道的事情抛出异常,您也可以返回一个optional

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);
}

If you want a range check at runtime (ie permitting the conversion between types iff the value held can be maintained), Boost has numeric_cast that achieves this. 如果你想在运行时进行范围检查(即允许类型之间的转换, 如果保持的值可以保持),Boost有numeric_cast来实现这一点。

And if you don't want to use Boost, your approach looks decent enough. 如果你不想使用Boost,你的方法看起来不错。

Edit: I missed that you were using C++, my previous answer assumed C only. 编辑:我错过了你使用C ++,我之前的回答只假设C。

The simplest and most standard way is to use 最简单和最标准的方法是使用

std::optional<unsigned int> index;

instead of using -1 or some other sentinel value to represent invalid index. 而不是使用-1或其他一些sentinel值来表示无效索引。 If the index is invalid, you just don't set the optional. 如果索引无效,则只需设置可选项。 Then you can query it with 然后你可以查询它

index.has_value() index.has_value()

to find out if it is valid or not. 找出它是否有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM