My gut instinct is to be worried about that because compiler extensions cause various issues (mainly related to portability, backwards compatibility and compiler bugs), but I think creating an 24-bit type manually would be quite a bit of effort so it’s probably the easiest option for now.
It’s late here so I’ll spend more time thinking about it tomorrow.
My immediate thoughts are are mainly:
-
init
should beinitialise
- (And possibly include
initialize
as a synonym to appease Oxfordians)
- (And possibly include
-
readWord
should be something likeread16
orreadUint16
.- The size of a processor word varies between CPU models, but a
uint16_t
is universally and unambiguously an unsigned 16 bit integer.
- The size of a processor word varies between CPU models, but a
-
read
should perhaps be eitherread8
,readUint8
orreadByte
to avoid any ambiguity
There’s probably a contradiction of their etymologies,
but not a contradiction of their definition within the scope of the language.
(Technically the variable can still vary in value, just not at runtime. :P
)
Perhaps, but I can’t think of a single good reason to prefer a macro over constexpr
for constants,
and I trust the words of the standard C++ foundation about what’s good and what’s bad.
Currently it’s still in the source.
I’m not sure whether it’s worthwhile or not, I’d have to think about it,
but I know for definite that a type alias would be better than a macro.
Theoretically it’s possible that uint24_t
could be added to stdint.h
as part of the standard,
in which case #ifndef uint24_t
wouldn’t actually do anything because uint24_t
wouldn’t exist during the preprocessor stage (because it would be a type, not a macro).
Worse still, if other code was using uint24_t
and it was expecting the type to behave differently to __uint24
at some point then the code could still compile but introduce some silent bugs.
With a type alias, at least you’d get an immediate error because the type uint24_t
would already exist and thus cause a name clash.
(Fail fast, fail early, fail at compile time rather than runtime, blah blah blah.)
Good to know.
Mainly I’m thinking of adding template functions so that instead of:
uint8_t someBuffer[16];
Cart::readBytes(someBuffer, sizeof(someBuffer));
You can just write:
uint8_t someBuffer[16];
Cart::readBytes(someBuffer);
(And the same for readDataBlock
and readSaveBlock
.)
I’d also like to add a template version of writeSavePage
that checks that the array you feed to it is large enough to prevent buffer overruns.
E.g.
// Large enough, runs fine
uint8_t bufferA[256];
Cart::writeSavePage(page, bufferA);
// Buffer not large enough, get a 'static assertion failed' error
uint8_t bufferB[128];
Cart::writeSavePage(page, bufferB);
Also maybe some templates for reading classes/structures like EEPROM’s get
.
E.g.
// Read an array of 16 points
// and the compiler takes care of all the hassle for you
Point points[16];
readData(points, pageAddress);
(Now I see that, I’m thinking it makes more sense to have the page address first.)
And also making sure to mark the buffer on writeSavePage
as const
,
I.e. static void writeSavePage(uint16_t page, const uint8_t* buffer);
(const
effectively means “I promise not to modify this”.)
Only if it has static storage duration (i.e. it’s a global).
There’s not much that can be done about that,
the compiler handles that sort of thing.
I’m not sure what this is supposed to be demonstrating.
(Also, do people actually use memset
?)
Wrap what exactly?
See my suggestion about templates earlier in the comments.
readData(state, pageAddress);
is certainly doable.
However, it would technically only work for certain objects.
I’m not sure what the full set of requirements would be,
but I’m pretty sure the class would have to be TriviallyCopyable or PODType.
It might be possible.
I don’t know enough about the flash chip and SPI to make any guesses as to what the performance increase would be like (or if there would even be a performance increase).
One problem is that there’s only one flash chip and potentially multiple streams.
Sharing resources can be very hard without threads, locking, mutexes, semaphores et cetera.
Either way it definitely needs to be more OOP:
FXStream stream { pageAddress };
stream.write(data);
uint8_t value = stream.read8Unsafe();