Sorry if this is a double post. I'm almost sure this has been asked multiple times, but I'm unable to find it.
I mainly have a Java background. I haven't used C++ for a long time but I currently need to use it for microcontroller programming. In my main method, I have a display object for drawing to an e-paper display. I'd like to pass this object to a separate class that performs some drawing operations with it, but I'm struggling a little with this.
The display class is templated and gets initialized in the main method.
GxEPD2_BW<GxEPD2_154_D67, GxEPD2_154_D67::HEIGHT> display(GxEPD2_154_D67(SS, 17, 16, 4));
Now, my drawing class "Graph" has a constructor where display
is passed as a parameter:
class Graph {
public:
Graph(GxEPD2_BW *display);
...
That's not working as C++ is complaining about the missing argument list of the templated class GxEPD2_BW
.
The problem now is, I don't know what arguments to provide here. The template arguments GxEPD2_154_D67
and GxEPD2_154_D67::HEIGHT
define the specific display in use and I don't want my drawing class to rely on a specific display. It's supposed to take any kind of display class, no matter the parameterization, and work with it.
In Java, I would build the constructor like this:
public class Graph {
public Graph(GxEPD2_BW<?, ?> display) {
...
and henceforth, I could pass any display implementation to it without worrying about the underlying display characteristics.
How would I go about this in C++? Or am I forcing something upon C++ that it's not supposed to do?
For the sake of a simpler example consider this template and a class that expects an instance as parameter to the constructor:
template <typename T>
struct Foo {};
struct Bar {
Bar(Foo<??> ) {} // pseduo code. Doesn't work like this!
};
First you need to understand that C++ templates are nothing like Java generics. Java generics are based on type erasure. Thats what allows you to have a GxEPD2_BW<?, ?> display
. C++ templates are not based on type erasure. Foo<int>
and Foo<double>
are two completely different types. They have nothing in common (other than being instantiations of the same class template). C++ templates can be used for type erasure, but they don't do it out of the box. In C++ there is no type Foo<?>
that would be the equivalent to the generic in Java.
Basically you have two choices. You can introduce a common base class, such that Foo<int>
and Foo<double>
do have a common base:
struct Base {};
template <typename T>
struct Foo : Base {};
struct Bar {
Bar(std::unique_ptr<Base> b) {}
};
For runtime polymorphism you need pointers or references.
Alternatively you can consider to make Bar
itself a template:
template <typename T>
struct Foo {};
template <typename T>
struct Bar {
Bar(Foo<T> f) {}
};
Here, again, Bar<int>
and Bar<double>
are two completely different types, that have nothing in common (other than being instantiations of the same class template). And each has a concrete type (not a template) as argument for the constructor.
There are more variants and different ways. For example you could have a non-template Bar
and only its constructor is a template. However, the two above are what I would consider the most straightforward ways.