Hi Cristian,
do you mean end users of the Arduino core, or providers of 3rd party frameworks? I can agree with you about the first: the typical Arduino end user shouldn't have to know or care about template use. However, I don't agree about the second. If someone decides to start providing code to a possibly wide user base, then he should know what he's doing, and that includes knowing about design concepts and the language itself. This is especially true for 3rd party libs that are commercial. Even then, there are ways, as I show below.
Here, we're talking about designing a templated base that will provide usability of specific types to the user. Authors of other specific types should be able to make use of this. We're not talking about pushing templated usage to the user, which is completely different.
As example, use of STL for specific types is as simple as any other C++ objects. Consider the following:
You (Arduino) want to provide specific containers to your user base. Let's say a vector of ints and a vector of floats. You could provide this:
using intvector = std::vector<int>;
using floatvector = std::vector<float>;
Then the user does this:
intvector myInts;
myInts.push_back(1);
myInts.push_back(2);
floatvector myFloats;
myFloats.push_back(3.);
myFloats.push_back(4.);
There are no templates on the user side, and your implementation was 2 lines of code.
Let's say that you don't like the STL object interface, i.e.: method names. You could do composition, e.g.: provide something like this:
class intvector
{
private:
std::vector<int> mVector;
public:
void append(int val) {mVector.push_back(val);}
...
};
Then the user does this:
intvector myInts;
myInts.append(1);
myInts.append(2);
Again, no template usage on the user side. With such composition, the templated part is pushed even further away from the user. You can also decide what to expose from std::vector, as well as what to hide.
Let's say now that some 3rd party lib provider wants to offer his end user base a way to print Arduino-provided vectors out to Serial. He could implement and offer this:
template <typename vectorType>
void printVector(const vectorType &vec)
{
for(auto elem : vec)
{
Serial.print(elem);
Serial.print(' ');
}
}
Alternatively, if templates are "not simple" for this 3rd party lib provider, he can use overloads:
void printVector(const intvector &vec)
{
for(auto elem : vec)
{
Serial.print(elem);
Serial.print(' ');
}
}
void printVector(const floatvector &vec)
{
for(auto elem : vec)
{
Serial.print(elem);
Serial.print(' ');
}
}
In either case, the user then does this:
intvector myInts;
myInts.push_back(1);
myInts.push_back(2);
floatvector myFloats;
myFloats.push_back(3.);
myFloats.push_back(4.);
printVector(myInts);
printVector(myFloats);
It's a templated design base, but there is no end-user template usage. Also, no inheritance, no vtables, no pointers to some base class, no object slicing, no overhead.
Now, let's say that somebody comes up with a brand new vector type: a vector of Strings! The extension for the templated version above would look like this:
using stringvector = std::vector<String>;
That's it.
For the overloaded version, you'd need in addition this:
void printVector(const stringvector &vec)
{
for(auto elem : vec) {
Serial.print(elem);
Serial.print(' ');
}
}
For the filesystem design, the approach is similar. I was going to put an example here, but I decided to not jump the gun yet. I would like to try and show something that, while maybe only a skeleton and very incomplete, can at least serve as a starting point.
About the C++ guru, if you're going to implement a good design, you think twice about that design, so that the end users have to think only half about its usage.
- Dev