c++ - 按位整数转换

标签 c++

我必须使用返回 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 返回,那么它在此类型中表示为负值,如果我只是将其 static_cast 为 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);
}

最佳答案

您可以使用 union :

union mergeint_t
{
  int8_t int8;
  int16_t int16;
  int32_t int32;
  int64_t int64;
  //..
};

将值赋给int32:

  mergeval.int32=yourFunctionReturningInt32();

然后从 union 体的适当成员访问该值。

if (type==INT8_T) {
  int8val = mergeint.int8;
}

//编辑:

我测试了这个方法,因为我不确定这里是否能正确保留字节顺序。在我的 Linux 上,下面的程序运行正常:

int main() {
  union mergeint_t mergeval;
  mergeval.int64=11;
  printf("mergeval int8=[%d]\n\n", mergeval.int8);
}

给出输出:mergeval int8=[11]

//编辑2:

是的,此方法可能不适用于 Big Endian 机器。我没有,所以无法测试。抱歉之前没有提到。我认为,如果我在上面的疑问中提到了字节顺序,那么或多或少就会清楚地表明,这可能是一个有限的解决方案。

关于c++ - 按位整数转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16736505/

相关文章:

c++ - 两个 C++/CX Platform::Array:s 之间的高效复制方法?

c++ - 从函数中寻找最小值的启发式算法

c++ - 如何在 C++ 中为堆栈集声明 Iterator

C++ 面向对象设计

c++ - 控制台程序中的奇怪行为

c++ - CUDA 函数仅适用于某些元素

c++ - 如何在音轨中找到无声部分

c++ - random_shuffle() 对象 vector 时出现大量错误?

c++ - 如何仅插入数字和字母应该显示一条消息或其他内容

c++ - 斜齿轮结构 : Sweep Profile with Spin (twist)