Reputation: 24719
I've recently been implementing a specialized parser for a slightly modified Abstract Syntax Notation. The specification says that integers are encoded as an array of octets which are to be interpreted as a binary two's-complement integer.
So, at first I thought the best way to unserialize this into an actual C++ int
would be to simply start with a value of 0, and then OR each octet with the value like:
uint64_t value = 0;
int shift = 0;
std::vector<uint8_t> octets = { /* some values */ };
for (auto it = octets.rbegin(); it != octets.rend(); ++shift, ++it)
{
value |= uint64_t(*it) << (shift * 8);
}
This would leave me with a bit pattern stored in value
, which I could then interpret as a signed (two's-complement) integer by casting it:
int64_t signed_value = static_cast<int64_t>(value);
But it occurred to me that this is really relying on implementation-defined behavior. C++ doesn't guarantee that signed integers are represented as two's complement. So, to get the actual value of the encoded integer as a C++ int64_t
, I'd need to actually calculate the summation of 2^N for each Nth bit in the bit pattern, taking into account the sign bit. This seems kind of silly when I know that casting should just work most of the time.
Is there a better solution here that would be both portable and efficient?
Upvotes: 4
Views: 3639
Reputation: 2965
If your solution works, I think you can use a bit of metaprogramming to test whether your platform is one's complement or two's complement.
struct is_ones_complement {
static const bool value = ( (1 & -1) == 0);
}
And then, you can write an inlinable conversion function:
template<bool is_ones_complement>
uint64_t convert_impl(const std::vector<uint8_t>& vec);
template<>
uint64_t convert_impl<true>(const std::vector<uint8_t>& vec) {
// Your specialization for 1's-complement platforms
}
template<>
uint64_t convert_impl<false>(const std::vector<uint8_t>& vec) {
// Your specialization for 2's-complement platforms
}
inline uint64_t convert(const std::vector<uint8_t>& vec) {
return convert_impl<is_ones_complement::value>(vec);
}
Untested, but it should work.
Upvotes: 1