I'm in the process of coding a reusable C++ module for an ARM Cortex-M4 processor. The module uses a lot of storage to accomplish its task and it's time-critical.
To allow the users of my module to customize its behavior, I'm using different backend-classes to allow for different low-level task implementations. One of these backends is a storage backend, that is meant to be a way to store the actual data in different types of volatile/non-voltile RAM. It consists mostly of set/get functions that are very fast to execute and they will be called very frequently. They are mostly in this form:
uint8_t StorageBackend::getValueFromTable(int row, int column, int parameterID)
{
return table[row][column].parameters[parameterID];
}
uint8_t StorageBackend::getNumParameters() { return kNumParameters; }
The underlying tables and arrays have a sizes and datatypes that depend on the user-defined functionality, so there is no way for me to aviod using a storage backend. One primary concern is the need to put the actual data into a certain section of the RAM address space (e.g. for using an external RAM) and I don't want to limit my module to a specific storage option.
Now I'm wondering what design pattern to choose for separating the storage aspects from my main module.
- A class with virtual functions would be a simple yet powerful option. However, I fear the cost of calling virtual set/get functions very often in a time-critical environment. Especially for a storage backend this can be a serious problem.
- Supplying the modules main class with template parameters for its different backends (maybe even with the CRTP-pattern?). This would avoid virtual functions and would even allow to inline the set/get-functions of the storage backend. However, it would require the whole main class to be implemented in the header file which isn't particularly tidy...
- Use simple C-style functions to form the storage backend.
- Use macros for the simple set/get functions (after compilation this should roughly be the same as option 2 with all set/get-functions inlined.)
- Define the storage datastructures myself and allow customization by using macros as the datatypes. E.g.
RAM_UINT8 table[ROWSIZE][COLSIZE]
with the user adding#define RAM_UINT8 __attribute__ ((section ("EXTRAM"))) uint8_t
The downside of this is that it requires all data to sit in the same, continuous section of RAM - which is not always possible on an embedded target.
I wonder if there are more options? Right now, I'm leaning towards option 4, as its tidy enough yet it has zero influence on the actual run-time performance.
To sum it up: What's the best way to implement a low/zero-overhead storage abstraction layer on a Cortex-M4?