-1

I want to use the higher half of an integer to store some value. Now out of the higher half, i want to again store two different values as below.

If long is 32 bits, i want to use first 8 bits and the next 8 bits(from left).

If long is 64 bits, i want to use first 16 bits and the next 16 bits(from left). I have come up with something like below.

unsigned long foo;
unsigned long bar = 1UL; // i'll ensure this value fits in 2 bits to handle 16 bits integer.
unsigned long baz = 1UL; // i'll ensure this value fits in 2 bits.
int BitsInLong = CHAR_BIT * sizeof(unsigned long);
foo |= bar << (BitsInLong / 2 + BitsInLong / 4);
foo |= baz << (BitsInLong / 2);

Though this seems to work, are there some situations where this can fail or may be is there a better solution.

user3819404
  • 611
  • 6
  • 18
  • Did you mean 2 bytes? – Mad Physicist Sep 07 '18 at 05:21
  • You'll probably want a mask at some point: `(1<<(sizeof(long)/4)) - 1` – Mad Physicist Sep 07 '18 at 05:22
  • You'd be better off doing this in the preprocessor if you can – Mad Physicist Sep 07 '18 at 05:24
  • 1
    Why you don't leave it up to compiler by using bit fields? https://en.cppreference.com/w/cpp/language/bit_field – Öö Tiib Sep 07 '18 at 05:31
  • well, an unsigned long can have padding bits ;) Or bit number not divisible by 4 ;) – Antti Haapala -- Слава Україні Sep 07 '18 at 05:32
  • IMHO, "left" and "right" are bad to address certain bits in a number. (Who knows how the storage cells are located in the RAM?) Better is "most significant" and "least significant". – Scheff's Cat Sep 07 '18 at 05:56
  • 1
    Your solution is OK, but very C-style. If you need this `foo` and `bar` values to be sent to some kind of microcontroller as a telegram, it's fine. But if you store some value in the half part of the `unsigned long` for no apparent reason, consider just writing an appropriate class for that. – pptaszni Sep 07 '18 at 10:04
  • you said higher bits, which means the higher half. And that's correct considering you said two 8-bit values in a 32-bit one, and two 16-bit values per 64-bit. But then again in the snippet you're said those twos are 2 bits?!? Please edit the question to make it clear, and choose only C or C++ – phuclv Sep 07 '18 at 15:16

2 Answers2

0

There is only one issue - field overflow. If the (near) middle field is set with a big value, that value may overflow to the next field.

Here is a generic solution that will work well with types whose number of bits is divisible by 4 (true for all regular types on standard systems ):

template <typename T, unsigned Field>
constexpr T make_field(unsigned value)
{
  static_assert(std::numeric_limits<T>::is_integer, 
                  "works only with integers");
  static_assert(Field <4, "a field can be 0,1,2,3 ");
  using UT = typename std::make_unsigned<T>::type;
  constexpr unsigned bits = std::numeric_limits<UT>::digits;
  static_assert(bits % 4 == 0, "number of bits in return type must be divisible by 4");
  auto mask = ((UT)1 << (bits / 4)) - 1;
  return (value & mask) << ((3 - Field) * bits / 4);
}

And the usage

int main()
{   
     // will print 700, since upper bits are truncated.
    std::cout << std::hex << (make_field<int12_t, 1>(0x127) << '\n';

    // will print 7000
    std::cout  << (make_field<int12_t, 0>(0x127) << '\n';

    // prints 1234
    std::cout << (make_field<int, 0>(0x12) | make_field<int, 1>(0x34)) << "\n";
}                           

Note: signed integer types may work in unpredictable ways if they are not in two's complement format, and the upper bit is set.

Michael Veksler
  • 8,217
  • 1
  • 20
  • 33
-1

How about a templated union?

template <typename T>
union segmentedStorage {
  std::array<unsigned char, sizeof(T)> byteRepr;
  T     fullRepr;
  static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value);
  static_assert(sizeof(T) % 4 == 0);
};

Godbolt example: https://godbolt.org/z/R0vtfT

This requires C++17 for the ::value types. Beware of strict aliasing rules. I imagine you'll want to write the byte representation, then read the full representation, but that's technically undefined behavior. GCC guaranatees, I think, that this will behave as expected. Clang may not. More details on how GCC allows this undefined behavior can be found here and here.

Dr. Watson
  • 128
  • 2
  • 11
  • Isn't this type punning? Isn't type punning declared as [UB](https://stackoverflow.com/a/4105123/1505939) in C++? Found [SO: Unions and type-punning](https://stackoverflow.com/a/25672839/7478597) – Scheff's Cat Sep 07 '18 at 06:15
  • Yes, reading the inactive member is type punning UB. However, per my link, GCC guarantees the C behavior, i.e. the expected behavior. This is not a cross-platform solution. – Dr. Watson Sep 07 '18 at 06:18
  • Actually, OP's solution to use shift operators is the preferred. It has the extra advantage that it's immune against [endianess](https://en.wikipedia.org/wiki/Endianness) issues. (Writing this, I must admit that I'm actually a fan of type punning in the rare cases I need them.) ;-) – Scheff's Cat Sep 07 '18 at 06:29
  • @Scheff Type punning is a good way to access the first, second etc. byte. Bit shifting is a good way to access the lowest value byte, second-lowest, etc. Neither approach has a problem with endianness by itself, it's only when you combine the two ways that you get issues. –  Sep 07 '18 at 06:55
  • @hvd I had something else in mind e.g. "overlaying" an `float elems[2][2];` with `float comps[2 * 2];` and `struct { float _00, _01, _10, _11; };` offers "any imaginable" kind of accessing a 2x2 matrix. Endianess is not an issue here (but still type punning is UB according to C++ standard). For what OP seems to intend to achieve (as far as I understood it), shift operators seems to be the better alternative (I believe). – Scheff's Cat Sep 07 '18 at 07:02
  • @Scheff Yeah, one could use a bit shifting solution, but I think it's nice to be able to wrap this behavior in a type. What if I want to write a functon that only accepts `segmentedStorage` objects? – Dr. Watson Sep 07 '18 at 07:07