C++: How do I cast an int to an unsigned long and not change any bits? I want to pack and unpack values into memory. The word size is 64 bits.
This snippet illustrates the problem:
int v1 = -2; // 0xfe
unsigned long v2=(unsigned long)v1; // 0xfffe, I want 0x00fe
The simple solution is:
unsigned long v2=(unsigned int)v1; // 0x00fe
However, this code is in a template where the targe开发者_如何学Pythont type is a parameter, so I had to resort to this:
uint64 target = mem[index] & mask;
uint64 v;
if (value < 0) {
switch (bits) {
case 8:
v = (uint8)value;
break;
case 16:
v = (uint16)value;
break;
case 32:
v = (uint32)value;
break;
}
} else {
v = value;
}
v = v << lShift;
target |= v;
mem[index] = target;
Assume, for example, the type for "value" is an int (16 bits) and bits=16. The goal is to mask the bits in memory for value and replace them.
Does anyone know an easier way?
Assuming you have C++0x support:
#include <type_traits>
v= static_cast<std::make_unsigned<decltype(value)>::type>(value);
I'm assuming that you are parameterizing on the type of value
, otherwise this doesn't make any sense.
EDIT: making it more C++-ish by using static_cast
instead of a C cast. I suppose that's what got me a downvote.
If you don't mind the typing, a trait class comes to mind:
template <typename IType> struct ToULong;
template <> struct ToULong<signed char>
{
static inline unsigned long int get(signed char c) { return (unsigned char)(c); }
};
template <> struct ToULong<signed short int>
{
static inline unsigned long int get(signed short int c) { return (unsigned short int)(c); }
};
/* ... signed int, signed long int, signed long long int ... */
Usage:
template <typename IType>
struct Foo
{
unsigned lont int get_data() const { return ToULong<IType>::get(m_data); }
private:
IType m_data;
}
Update: Even simpler, you could just make a bunch of overloads:
unsigned long int toULong( char c) { return (unsigned char)(c); }
unsigned long int toULong(signed char c) { return (unsigned char)(c); }
unsigned long int toULong(signed short int c) { return (unsigned short int)(c); }
unsigned long int toULong(signed int c) { return (unsigned int)(c); }
unsigned long int toULong(signed long int c) { return (unsigned long int)(c); }
2nd update: You should probably say static_cast<T>(x)
rather than (T)(x)
if you want to be even more C++-like.
How about union?
union u1 {
short int si;
unsigned long int uli;
unsigned long int stub;
operator unsigned long int () {return uli;};
public:
u1(short int nsi) : stub(0) {si = nsi;}
};
I believe that you could use a bitwise-AND to get the desired result.
unsigned long v2 = 0;
v2 = v2 | v1;
Using the idea put forth by "Kerrek SB" I came up with a solution.
template <typename Tint> uint64 ToMemdata(Tint value) {
return (uint64)value;};
template <> uint64 ToMemdata<int8>(int8 value) {
return (uint64)((uint8)value);};
template <> uint64 ToMemdata<int16>(int16 value) {
return (uint64)((uint16)value);};
template <> uint64 ToMemdata<int32>(int32 value) {
return (uint64)((uint32)value);};
template <> uint64 ToMemdata<int64>(int64 value) {
return (uint64)((uint64)value);};
template <typename Tint> void packedWrite(Tint value, int vectorIndex, uint64* pData) {
uint64 v = ToMemdata(value);
// This call eliminates a run time test for minus and a switch statement
// Instead the compiler does it based on the template specialization
uint64 aryix, itemofs;
vectorArrayIndex(vectorIndex, &aryix, &itemofs); // get the memory index and the byte offset
uint64 mask = vectorItemMask(itemofs); // get the mask for the particular byte
uint64 aryData = pData[aryix]; // get the word in memory
aryData &= mask; // mask it
uint64 lShift = (uint64)(itemofs * sizeof(Tint) * 8);
uint64 d = v << lShift; // shift the value into the byte position
aryData |= d; // put the value into memory
pData[aryix] = aryData;
}
Using this concept I was able to make other improvements to the code. For example, the call to vectorItemMask() is now templateized also.
To cast without changing bits, take a reference and then dereference with appropriate type:
int v1 = -2; // 0xfe
unsigned long v2=*(unsigned long *)&v1;
This assumes the sizes are the same. It has undefined behavior if sizeof(int) != sizeof(unsigned long). You probably want unsigned int.
Edit: realized i answered wrong question.
Boost type_traits has something (i believe it is make_unsigned) to convert an int type to the unsigned version (if it is signed) and do nothing if it is unsigned.
精彩评论