C++ Template Arduino investigation
I tried to simplify and refactor some of my code for a new Arduino project lastly and came up with some interessting programming concepts in C/C++ I never heard of before my Arduino adventures.
The Problem
At first I wanted to get rid of my String class coding. Its evil. As far as I know its not very efficient to work with in the Arduino (C/C++) programming environment especially in embedded systems like the small ATMegas and ATTinies. Its slow and allocates dynamic memory which could lead to memory heap fragmentation etc.. Short: they suck! 😉 Better use Chars but unfortunately they arent like the fluffy little Strings, they are spikey hedgehogs… Since Chars arent that friendly like Strings with all theire nice methods I needed to write some convenience methods by my own.
After a very nerdy weekend in my nerd cave, some experimentation and asking questions in the Arduino forum (best forum and community _ever_ 🙂 ) I learned about “C Template Programming”.
First approach
I wanted to write a function to split a Char Array by a delimiter and return it back to a specified Char Array. The problem was that a normal function in C needs to know the actuall size of the Array in the function definition.
Have a look at the last argument in the definition (char aBuf[][7]):
1 2 3 4 5 6 7 8 9 10 11 |
void splitCharArray(char *theBuffer,char *delimiter,char aBuf[][7]) { char *token = strtok(theBuffer, delimiter); int tokenCounter = 0; while (token != 0) { strcpy(aBuf[tokenCounter],token); token = strtok(0, delimiter); tokenCounter++; } } |
… and call it like this:
1 2 |
char cmdBuffer[3][7]; splitCharArray("123#2313#431","#",&cmdBuffer[0]); |
So every array I pass to the method to fill, needs to have a second dimension size of 7, otherwise the compiler will complain… 🙁 (Notice that “cmdBuffer” is passed as a reference to the method (“&”-sign) so it gets directly modified and does not get copied to the method).
Not very reusable.
The Solution – C Templates
A template function in C really helped me out while trying to solve the problem:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// DESC: Splits and copies a char array by delimiter into passed char array // WARN: THIS FUNCTION NEEDS TO BE ABOVE ANY CALL IN CODE // ARG: char *theBuffer = Source chars array // ARG: char *delimiter = Char delimiter eg. "$" // ARG: char (&aBuf[N][M]) = The output buffer char array specify dimensions in <N,M> before arguments // USAGE: char cmdBuffer[3][7] // USAGE: splitCharArray<3,7>("123#2313#431","#",cmdBuffer); template< size_t N, size_t M > void splitCharArray(char *theBuffer,char *delimiter,char (&aBuf)[N][M]) throw() { char *token = strtok(theBuffer, delimiter); int tokenCounter = 0; while (token != 0) { strcpy(aBuf[tokenCounter],token); token = strtok(0, delimiter); tokenCounter++; } } |
- The “template <size_t N, size_t M> “ directive above the method tells the method the dimensions of the passed, referenced (“&”-sign, remember? ;)) char array and will be specified when the method is called.
- Note the “throw()” call after the function definition…
- …and always write this method above any calls to it (this prevents some compiler issues that would occur)!
The call will look like this (you specify the dimensions in the <> brackets before the argument list):
1 2 |
char cmdBuffer[3][7]; splitCharArray<3,7>("123#2313#431","#",cmdBuffer); |
Actually you can even call the method without specifying the Array dimensions like this and it still works:
1 2 |
char cmdBuffer[3][7]; splitCharArray("123#2313#431","#",cmdBuffer); |
Woooowooo! This makes things waaay easier and very universal/generic! 🙂 The method adapts automatically to the needs of each project and therefore it could be integrated into a generic char utillity class.
A side note on packing the method in a class allthough: Its strange but you need to write the whole method into the header file of a class. I could not get it running by only defining the method signature in the header and writing it in the implementation like you would normally do. It does not work. Just write the whole method in the header and you can sleep at night. 😉
Some further reading about “C Templates”:
Wikipedia – Template (C++), English
Generische Programmierung (Templates), German
Templates and Template Classes in C++, English
Have fun! 🙂