我必须使用返回 int32_t 的 API。但实际值可能是较小的有符号/无符号类型。要知道 API 返回 ENUM 值的确切类型。它看起来像这样:
typedef enum Int{
Int8,
Int16,
Int32,
Uint8,
Uint16,
Uint32
}IntT;
typedef struct{
IntT (*getType)();
void (*getInt)(int32_t* out);
}IntegerT;
我想通过知道枚举值将值从 int32_t 转换为实际类型。有时我什至想在我的代码中将 unsigned int32 分配给 unsigned int64 变量。知道如果值足够大,无符号 int32 作为 int32_t 返回,那么它在这种类型中表示为负值,如果我只是将其静态转换为 uint64_t,则符号位被扩展以填充 uint64_t 中的所有高位这会产生与预期完全不同的无符号值。
因此,我编写了一个转换函数,该函数应该负责将更大的 int 类型或较小的 int 类型转换为正确的值。但是,我觉得这可能是一个已知问题,并且可能已经存在解决方案。下面是函数。请告知您是否认为这可以改进或者是否有更好的解决方案(尽管我已经使这个函数比我真正需要的用例更通用)。
编辑:就字节顺序而言,它是可移植的。
编辑:删除了关于有符号/无符号比较的编译器警告。
#include <limits>
#include <boost/static_assert.hpp> //BOOST_STATIC_ASSERT
#include <stdexcept>
#include <cstring>
#include <boost/type_traits/make_unsigned.hpp>
namespace Detail
{
/** This a implementation helper class for bitwise_int_cast function */
template<bool IsToTypeSigned, bool IsFromTypeSigned>
class Converter
{
public:
template<typename ToIntType, typename FromIntType>
ToIntType convert(FromIntType from) {
BOOST_STATIC_ASSERT(sizeof(from) == 0); //This prevents this generic implementation being compiled
return from;
}
};
/** From signed to signed */
template<>
template<typename ToIntType, typename FromIntType>
ToIntType Converter<true, true>::convert(FromIntType from)
{
BOOST_STATIC_ASSERT(std::numeric_limits<ToIntType>::is_signed && std::numeric_limits<FromIntType>::is_signed);
if((from < std::numeric_limits<ToIntType>::min()) ||
(from > std::numeric_limits<ToIntType>::max())
) {
throw std::runtime_error("Integer overflow in casting from large signed rvalue into smaller signed lvalue");
}
return static_cast<ToIntType>(from);
}
/** From signed to unsigned */
template<>
template<typename ToIntType, typename FromIntType>
ToIntType Converter<false, true>::convert(FromIntType from)
{
BOOST_STATIC_ASSERT(!std::numeric_limits<ToIntType>::is_signed && std::numeric_limits<FromIntType>::is_signed);
typedef typename boost::make_unsigned<FromIntType>::type unsignedType;
unsignedType unsignedIn = from;
if(std::numeric_limits<FromIntType>::digits < std::numeric_limits<ToIntType>::digits) {
if(from < 0) {
return unsignedIn;
}
} else {
if(from > 0) {
if (unsignedIn > std::numeric_limits<ToIntType>::max()) {
throw std::runtime_error("Integer overflow in casting from large signed rvalue into smaller unsigned lvalue");
}
} else if (from < 0) {
throw std::runtime_error("Integer overflow in casting from large signed rvalue into smaller unsigned lvalue");
}
}
return unsignedIn;
}
/** From unsigned to signed */
template<>
template<typename ToIntType, typename FromIntType>
ToIntType Converter<true, false>::convert(FromIntType from)
{
BOOST_STATIC_ASSERT(std::numeric_limits<ToIntType>::is_signed && !std::numeric_limits<FromIntType>::is_signed);
if(std::numeric_limits<ToIntType>::digits < std::numeric_limits<FromIntType>::digits) {
typename boost::make_unsigned<ToIntType>::type allBitsSet = -1; //e.g. 0xFFFF
if( from > allBitsSet) {
throw std::runtime_error("Integer overflow in casting from large unsigned rvalue into smaller signed lvalue");
}
}
return static_cast<ToIntType>(from);
}
/** From unsigned to unsigned */
template<>
template<typename ToIntType, typename FromIntType>
ToIntType Converter<false, false>::convert(FromIntType from)
{
if(from > std::numeric_limits<ToIntType>::max()) {
throw std::runtime_error("Integer overflow in casting from large unsigned rvalue into smaller unsigned lvalue");
}
return static_cast<ToIntType>(from);
}
}
/**
* This cast only cares about integer sizes not sign mismatch
* works only on two's complement (Big or Little Endian) Machines
*/
template<typename ToIntType, typename FromIntType>
inline ToIntType bitwise_int_cast(FromIntType from)
{
BOOST_STATIC_ASSERT(std::numeric_limits<ToIntType>::is_integer && std::numeric_limits<FromIntType>::is_integer);
Detail::Converter<std::numeric_limits<ToIntType>::is_signed, std::numeric_limits<FromIntType>::is_signed> converter;
return converter.template convert<ToIntType>(from);
}