Problem
I'm working on a video game emulator using C++.
The memory is represented by an array of uint8_t values. Often, I also need to access the contents of this memory as 16-bit values (two consecutive 8-bit values).
I have something that looks like this:
struct Memory {
uint8_t map[0xFFFF];
// Let's say that [] is for accessing 8-bit values
uint8_t& operator[](uint16_t address) {
return map[address];
}
// () is for 16-bit values
uint16_t& operator()(uint16_t address) {
return ???; // Looking for a good implementation here
}
};
For instance, if the memory contains [0xC0, 0xFF, 0x66, 0xAA...] then []
would return:
mem[0] -> 0xC0
mem[1] -> 0xFF
mem[2] -> 0x66
and ()
would return (depending on the system endianness):
mem(0) -> 0xFFC0
mem(1) -> 0x66FF
mem(2) -> 0xAA66
Those access methods are going to get called a lot. I would like to leverage pointers in ()
for fast access. I don't want to compute 16-bit values by shifting and |'ing 8-bit pairs (and I cannot as the method must return a reference).
My question: Is there a way in C++ to have different views on the same buffer? A 8-bit view and a 16-bit view pointing at the same data would be ideal.
(For example, the JS ArrayBuffer API provides DataView, which can do just that. Maybe there is a clever trick to achieve that cleanly with pointers in C++?)
Attempt #1
My first guess was to use some sort of union:
union mem_t {
uint8_t bytes[0xFFFF];
uint16_t words[0x8000]
}
But then only 16-bit values at even bytes can be accessed:
mem.words[0] -> 0xFFC0
mem.words[1] -> 0xAA66 // Should be 0x66FF :(
Attempt #2
A working solution is to have two additional 16-bit pointers for even and odd addresses, to handle overlapping:
uint16_t* even16 = (uint16_t*) map;
uint16_t* odd16 = (uint16_t*) (map + 1);
uint16_t& Memory::operator()(uint16_t address) {
return address % 2 ? odd16[address / 2] : even16[address / 2];
}
It works fine but it seems very convoluted and I'm sure there are more elegant solutions.
Thanks.