I'm writing a C++ hardware abstraction layer (HAL), which needs to be as fast as possible.
Polymorphism offers the best API, but Virtual Table lookups really kill the speed of the code.
This lead me to using templates in conjunction with policies to get compile-time polymorphism. But because templates with different arguments get instantiated as completely different types, I can not use them interchangeably in function calls unless the function is a template as well.
However, I dont want to force the user of my HAL library to write all function as templates because I have used templates.
For illustration purposes, suppose this is my HAL:
template<typename T_POLICY>
class I2CManager {
public:
void send(uint8_t data) {
T_POLICY::send(data);
++sent_data;
}
private:
int sent_data; // Just to illustrate that I2CManager has state
};
class I2C1 {
static void send(uint8_t data) { /* Run some code here */ }
};
class I2C2 {
static void send(uint8_t data) { /* Run other code here */ }
};
// OTHER HW
template<typename T_POLICY>
class UARTManager { ··· };
class UART1 { ··· };
class UART2 { ··· };
template<typename T_POLICY>
class TIMERManager { ··· };
class TIMER1A { ··· };
class TIMER1B { ··· };
This works and I can now create a I2CManager with different policies, such as follows. I can even have several I2CManagers running with different policies at the same time.
I2CManager<I2C1> i2c1;
I2CManager<I2C2> i2c2;
i2c1.send(0x11); // This works
i2c2.send(0x11); // This also works
Now, i2c1 and i2c2 have the same public methods, yet they are not interchangeable. Consequently, the user of my HAL library is forced to use templates as well.
// THIS DOES NOT WORK
void foo(I2CManager m) { m.send(0x11); }
foo(my_manager_1);
// BUT THIS WORKS
template<typename T>
void foo(I2CManager<T> m) { m.send(0x11); }
foo(i2c1);
Can I somehow get compile-time polymorphism but allow the end-user to treat it as if it was normal polymorphism? I don't care if the inner code in my library gets ugly or difficult to read for the sake of speed, but the API has to be as simple and intuitive as possible.
Actually, I want foo() to be specialized (and replicated in code) for the different parameters as if it was a template, but I don't want the user of my library to notice it is a template function. Altought alternatives to templates are also welcome.
I don't know if this is even possible, but I have read something about concepts which will appear in the next C++ standard. I want compile-time polymorphism, but as userfrliendly as runtime polymorhism.
Considerations:
Because I'm interfacing HW, each instantiation of my HWManagers with different policies will be unique (i.e. There is only one HWManager instance, and one HWManager instance, and may or may not exist simultaneously).
All instances are created by the library as global variables, and are not heapable.
All policy methods are extremely short, so having multiple unique instances due to templates is preferable to Virtual Tables for the sake of execution speed.
Code size does not matter (its for embedded systems), but RAM usage and execution speed does. I need as much as possible to be solved during compile time. Again, I'm willing to have a over-bloated executable for the sake of avoiding run-time resolutions.
Only up to C++03 supported
Code example edited