I'm trying to write good readable code for a small embedded project which I think can be reused for (many?) other projects. The idea is to build a uint8_t array with variadic templates on compile time, something like this:
static uint8_t* read1;
I2C::CmdQueue<0x0f,
I2C::Read<&read1, 3>,
I2C::Write<7, 7, 7, 7>> u;
In this case 0x0f is an I2C address, I2C::Read
reads 3 bytes which can be read by pointer read1
, I2C::Write
writes 7, 7, 7, 7 to the I2C bus. This can (or should) be archived with the following code:
namespace I2C {
template<uint8_t** POS, uint8_t COUNT>
struct Read {
Read() { *POS = data; }
uint8_t type = 0x80 | COUNT;
uint8_t data[COUNT] = { COUNT };
};
template<uint8_t ... Cs>
struct Write {
uint8_t type = 0x7f & sizeof...(Cs);
uint8_t data[sizeof...(Cs)] = { Cs... };
};
template<typename ...> // primary template, used for termination,
struct Cmds { // no element - but has size 1 in c++, in c size is 0!
uint8_t make_me_size_zero[0];
};
template<typename T, typename ... Ts> // al least one element
struct Cmds<T, Ts ...> {
T t;
struct Cmds<Ts...> cmd;
};
template<uint8_t** POS, uint8_t COUNT> // specialization
struct Cmds<struct Read<POS, COUNT>> {
Read<POS, COUNT> read;
};
template<uint8_t ... Cs> // specialization
struct Cmds<struct Write<Cs ...>> {
Write<Cs ...> write;
};
template<uint8_t DEVICE, typename ... Ts>
struct CmdQueue {
uint8_t data[0];
uint8_t status;
const uint8_t device = DEVICE;
struct Cmds<Ts...> cmds;
};
template<uint8_t DEVICE, typename ... Ts>
union CmdQueueU {
CmdQueueU() {}
uint8_t data[sizeof(CmdQueue<DEVICE, Ts...>)];
CmdQueue<DEVICE, Ts...> cmds;
};
}
With this code above I ran into the following problems:
- depending on the compiler and optimization (e.g. avr-c++ (GCC) 12.1.0) only the space for the array is reserved and no data (I2C address, Read, Write, ...) is written
- to bypass this problem I added the
union CmdQueueU
which should - from my point of view - "show" the compiler that the whole data from the template is used. Did not work, even worse, nothing is initialized when accessing and dumpingdata
formCmdQueueU
- clang++ (13.0.1) is on strike with
non-type template argument refers to object 'read1' that does not have linkage
even when declared static. gcc does not complain. I think it has something to do with clang error: non-type template argument refers to function that does not have linkage -- bug? and/or https://github.com/llvm/llvm-project/issues/17404 Please note: I want to use gcc for my project, clang was used only for testing...
The big question - for me - is, does anybody see a chance to get this code running? Perhaps even without make_me_size_zero[0]
to avoid compiler extensions?