Creating an array of packed bytes is easy - you can just use an array (or vector) of uint8_t. What's tricky is treating 12 bits inside that array as a 12-bit int, since there is no C++ type for "12 bit integer".
We can, however, create a proxy type which approximates a reference to a 12-bit integer:
class TwelveBitInt {
public:
// Our 12-bit int starts at byte offset "byte", bit offset "bit"
TwelveBitInt( uint8_t* byte, int bit ) : ptr{byte}, start_position{bit} {}
operator uint16_t() const {
// return bit manipulation to extract your 12-bit number
}
TwelveBitInt& operator=( uint16_t new_value ) {
// use bit manipulation to assign new_value to the underlying array
}
private:
uint8_t* ptr;
int start_position;
};
This gives you a type that looks like a 12-bit integer as long as you don't look too closely. It's implicitly convertible to uint16_t, and it's assignable from uint16_t, which is good enough for most uses. As for whether it's portable, it depends on what assumptions you make in your bit manipulations, but that's up to you.
I would then write a container class for your array. For simplicity, I'll assume that the size of the array is known at construction time.
class Fat12Array {
public:
Fat12Array( std::size_t n_elems )
: bytes( (n_elems * 3 + 1) / 2, 0 ) {}
TwelveBitInt operator[]( std::size_t idx ) {
// Interpret underlying bytes as an array of 3-byte/2-elem
// Get address of the 3-byte structure
auto byte_ptr = bytes.data() + 3*(idx/2);
if( idx % 2 ) {
return TwelveBitInt{ byte_ptr + 1, 4 };
} else {
return TwelveBitInt{ byte_ptr, 0 };
}
}
private:
std::vector<uint8_t> bytes;
};
Depending on how fancy you want to make this, you could deal with const TwelveBitInts, add more methods to the container, perhaps iterators, etc., but this is the basic idea.