In embedded systems, you often have a memory location which is not within the program memory itself but which points to some hardware registers. Most C SDKs provide these as #define statements. According to the following article, https://arne-mertz.de/2017/06/stepping-away-from-define/ one method of transitioning from #define statements (as used by C SDKs) to something more C++ friendly, is to create a class which forces reinterpret_cast to occur at runtime.
I am trying to go about this in a slightly different way because I want to be able to create "type traits" for the different pointers. Let me illustrate with an example.
#define USART1_ADDR 0x1234
#define USART2_ADDR 0x5678
template <typename T_, std::intptr_t ADDR_>
class MemPointer {
public:
static T_& ref() { return *reinterpret_cast<T_*>(ADDR_); }
};
class USART {
public:
void foo() { _registerA = 0x10; }
private:
uint32_t _registerA;
uint32_t _registerB;
};
using USART1 = MemPointer<USART, USART1_ADDR>;
using USART2 = MemPointer<USART, USART2_ADDR>;
template <typename USART_>
class usart_name;
template <>
class usart_name<USART1> {
public:
static constexpr const char* name() { return "USART1"; }
};
template <>
class usart_name<USART2> {
public:
static constexpr const char* name() { return "USART2"; }
};
Each USART "instance" in this example is its own, unique type so that I am able to create traits which allow compile-time "lookup" of information about the USART instance.
This actually seems to work, however, I wanted to create some test code as follows
static USART testUsart;
#define TEST_USART_ADDR (std::intptr_t)(&testUsart);
using TEST_USART = MemPointer<USART, TEST_USART_ADDR>;
Which fails with the following error:
conversion from pointer type 'USART*' to arithmetic type 'intptr_t' {aka 'long long int'} in a constant expression
I believe I understand the source of the problem based upon Why is reinterpret_cast not constexpr?
My question is, is there a way to make my MemPointer template work for test code like above as well?
EDIT
One solution is to have a separate class for each "instance" has follows
class USART1 : public USART {
public:
static USART& ref() { return *reinterpret_cast<USART*>(USART1_ADDR); }
};
class USART2 : public USART {
public:
static USART& ref() { return *reinterpret_cast<USART*>(USART2_ADDR); }
};
I would prefer some sort of template + using combination though so that I don't need to write a bunch of classes. But perhaps this is the only option.