Template value class inheriting name from base

I think you are "mentally stuck" to sharing the labels through polymorphism of 2 things that are actually not so "related"... The "label" class already exists, it's just a String.

What you want is a Menu class.

A Menu holds

  • a label
  • a value
  • a pointer to a list of sub-menus
  • a 2D layout (width & height possibly to meet your need)

Your pointer can be NULL and then it's a leaf in your hierarchy (and w/h are 0)

The minute you want to use template to define different value types, you jump into messy territory for handling this dynamically. I would recommend against.

if you want to optimize memory, rather than making those instance variables, you can stuff them in a union and have a type entry (1 byte) and a small memory buffer (say 4 bytes, enough to represent float or int32_t) to store the value.

width & height are probably going to be small in a menu, so they could probably be 2 bits each (3 x 3 max)

so you end up with

  • a label
  • 1 bit for the type (menu or leaf)
  • 3 bit for the value type (8 possible types)
  • 3 bits for w/h (2 bits each)
  • 4 byte that could be either a pointer to submenus or 4 bytes value buffer

it will be pretty packed.

That is actually what I had originally, a single menu class with a void pointer and an enum to store the type, but felt it was too hacky. I was using dynamic allocation to store an array of Menu pointers, an empty list will just leave a pointer to null so it doesn't use much memory anyway.

As for the polymorphism, there were specific things I wanted to do with each class. For the base class acting as a label x and y would be the position on the lcd screen, such as centering a title for example, and would not be selectable while the menu class x and y would refer to size etc and would be selectable. A value item would be selectable to change the value and so on..

To put that all in one class means I have to start adding flags like ReadOnly and Selectable which I then have to specify for every instance either on initialization or afterwards and that is why I tried the polymorphic approach.

I feel your suggestion may be cramming a bit too much, there is more than enough memory to at least initialize variables inside the class.

I understand your point but there must be a way to contain the value in a separate class and access it, that's the whole point of a container class such as this, but I only need it to store its base and all its derivatives which I have done. All that is left is accessing it.

What if I want to create a menu that has a list of char* options like ON and OFF when you change the "value"? If I do it your way I will have to create a separate class anyway so I might as well use templates for the value at least; but I will get rid of the templates for size since as you pointed out that's ridiculous.

J-M-L:
I think you are "mentally stuck" to sharing the labels through polymorphism of 2 things that are actually not so "related"... The "label" class already exists, it's just a String.

Yes you're right. I seem to have a fundamental misunderstanding of polymorphism in this case. All objects concerned have names or "labels", that is why I put it in the base class. The other two classes should contain either just a value (or pointer to value), or a list of objects inheriting from the base class. Is this not a simple inheritance? The base class could be empty, but I am just trying to inherit a character string.

For a class to be considered polymorphic it must implement at least one virtual function, this separates polymorphism from simple inheritance by implying that a polymorphic class can have "many forms". i.e if I want the object to act as a label (base class), or a menu (list of pointers), or to store/track a value, then I can use the appropriate derived class.

What is the point of polymorphism if I can't group derived objects together under their base class? What's the point of inheritance if the base class must know everything about its derived members?

If you have a GameObject base class, and PlayerObject and EnemyObject inherit from GameObject, and you want to perform calculations on every GameObject, BUT PlayerObject has a float Mana variable, then the only way to resolve this is to put a virtual function inside the GameObject class or cast the GameObject to a PlayerObject every time you want to do anything with float Mana.

My case differs slightly in that float mana is actually a templated value. But even templates should work because they are just copies of the derived class.

So why does this line not work?

lcd.print((dynamic_cast<ValueItem<float>*>(Main.MenuList[0][1]))->GetValue());

I have specified the Template parameter, so why the hell does it not work and how are you supposed to resolve this?

syphex:
If you have a GameObject base class, and PlayerObject and EnemyObject inherit from GameObject, and you want to perform calculations on every GameObject, BUT PlayerObject has a float Mana variable, then the only way to resolve this is to put a virtual function inside the GameObject

"Resolve" what? Why is that a problem?

Polymorphism (among other things) allows you to refer to a derived class using a pointer to the base class. For example, most LCD libraries inherit from the Print class. And (on an Uno) Serial is an object of the HardwareSerial class. HardwareSerial inherits from Stream which, in turn, inherits from Print. This inheritance chain allows you to refer to both types of objects using a pointer to Print:

#include "Arduino.h"
#include <LiquidCrystal.h>

void sayHello(Print *printPtr);

const uint8_t rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {
 Serial.begin(115200);
 lcd.begin(16, 2);

 sayHello(&Serial);
 sayHello(&lcd);
}

void loop() {
}

void sayHello(Print *printPtr) {
 printPtr->print("Hello World");
}

What is the point of polymorphism if I can't group derived objects together under their base class? What's the point of inheritance if the base class must know everything about its derived members?

C++ polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that receive the method call. Indeed it involves inheritance.

Typical example would be the Shape class which would have 3 subclasses: Circle, Rectangle and Triangle. A (closed) shape defines an area, so you could have a virtual method area() defined at the shape class level and implemented differently in each subclass. That’s polymorphism. And because you have a true purpose for inheritance, it’s not that you need to know everything but just conform to the fact that a shape needs to know how to calculate its area.

From a compiler perspective, defining a virtual function in a base class , with other implementation in derived classes, tells the compiler that you don't expect static linkage for this function but dynamic linkage (aka late binding): we want the selection of the function to be called at any given point in the program to be based on the kind of object for which it is called.

for example if you run this code (without the area() method being virtual):

class Shape {
  protected:
    int xPos, yPos;
    int boundingBoxWidth, boundingBoxheight;

  public:
    Shape(int x = 0, int y = 0, int w = 0, int h = 0): xPos(x), yPos(y), boundingBoxWidth(w), boundingBoxheight(h) {}
   float area() {
      Serial.print(F("@Shape: "));
      return 0;
    }
};

class Rectangle: public Shape {
  public:
    Rectangle(int x = 0, int y = 0,  int w = 0, int h = 0): Shape(x, y, w, h) { }

    float area () {
      Serial.print(F("@Rectangle: "));
      return (boundingBoxWidth * boundingBoxheight);
    }
};

class Triangle: public Shape {
  public:
    Triangle(int x = 0, int y = 0, int w = 0, int h = 0): Shape(x, y, w, h) { }

    float area () {
      Serial.print(F("@Triangle: "));
      return (boundingBoxWidth * boundingBoxheight / 2.0);
    }
};

Rectangle aRectangle(10, 10, 10, 20);
Triangle  aTriangle(0, 10, 20, 5);
Shape* shapes[] = {&aRectangle, &aTriangle};
uint8_t shapesCount = sizeof(shapes) / sizeof(shapes[0]);

void setup()
{
  Serial.begin(115200);
  for (byte i = 0; i < shapesCount; i++)
    Serial.println(shapes[i]->area());
}

void loop() {}

it won't work as expected because of early binding to the array type Shape and you'll see in the console

[color=purple]@Shape: 0.00
@Shape: 0.00
[/color]

Now if you take the exact same code and add virtual in front of the area() method in the base class, then the compiler sees the functions implemented in the subclasses and will use late binding. so this code

class Shape {
  protected:
    int xPos, yPos;
    int boundingBoxWidth, boundingBoxheight;

  public:
    Shape(int x = 0, int y = 0, int w = 0, int h = 0): xPos(x), yPos(y), boundingBoxWidth(w), boundingBoxheight(h) {}
    virtual float area() {
      Serial.print(F("@Shape: "));
      return 0;
    }
};

class Rectangle: public Shape {
  public:
    Rectangle(int x = 0, int y = 0,  int w = 0, int h = 0): Shape(x, y, w, h) { }

    float area () {
      Serial.print(F("@Rectangle: "));
      return (boundingBoxWidth * boundingBoxheight);
    }
};

class Triangle: public Shape {
  public:
    Triangle(int x = 0, int y = 0, int w = 0, int h = 0): Shape(x, y, w, h) { }

    float area () {
      Serial.print(F("@Triangle: "));
      return (boundingBoxWidth * boundingBoxheight / 2.0);
    }
};

Rectangle aRectangle(10, 10, 10, 20);
Triangle  aTriangle(0, 10, 20, 5);
Shape* shapes[] = {&aRectangle, &aTriangle};
uint8_t shapesCount = sizeof(shapes) / sizeof(shapes[0]);

void setup()
{
  Serial.begin(115200);
  for (byte i = 0; i < shapesCount; i++)
    Serial.println(shapes[i]->area());
}

void loop() {}

will generate in the console:

[color=purple]@Rectangle: 200.00
@Triangle: 50.00
[/color]

which is what we expected.

gfvalvo:
"Resolve" what? Why is that a problem?

Polymorphism (among other things) allows you to refer to a derived class using a pointer to the base class.

Yes but the base class doesn't know about members initialized in its derived classes. I see your code is equivalent to:

Serial.print("Hello World);
lcd.print("Hello World);

Because both Serial and lcd override their own print function from Print, probably using a virtual function.

The problem however is that I wanted to use Templates in a derived class to store a generic value. This means I need a virtual GetValue() function inside my base class as the base class pointer has no knowledge of the value in its derived class, but it does not know the type as it is defined by the template so I cant define it in the base class!

The other solution is to use dynamic_cast to cast the pointer to the base class, into a pointer to the derived class, that way the base class doesn't need to know what's in its derived classes. But as prophesied by J-M-L, I am running into issues when involving templates in a derived class.

J-M-L:
C++ polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that receive the method call. Indeed it involves inheritance.

Typical example would be the Shape class which would have 3 subclasses: Circle, Rectangle and Triangle. A (closed) shape defines an area, so you could have a virtual method area() defined at the shape class level and implemented differently in each subclass. That’s polymorphism. And because you have a true purpose for inheritance, it’s not that you need to know everything but just conform to the fact that a shape needs to know how to calculate its area.

From a compiler perspective, defining a virtual function in a base class , with other implementation in derived classes, tells the compiler that you don't expect static linkage for this function but dynamic linkage (aka late binding): we want the selection of the function to be called at any given point in the program to be based on the kind of object for which it is called.

Now if you take the exact same code and add virtual in front of the area() method in the base class, then the compiler sees the functions implemented in the subclasses and will use late binding.

Yes that's why I said for a class to be polymorphic it must implement at least one virtual function, infact I think dynamic_cast checks for at least one virtual function in p_obj:

*p_subclass = dynamic_cast< *>( p_obj );

My issue is that is a template class, so I replaced * with <ValueItem> *. Hence

lcd.print((dynamic_cast<ValueItem*>(Main.MenuList[0][1]))->GetValue());

But this gives a "'dynamic_cast' not permitted with -fno-rtti" error which I don't understand.

I found a workaround to access the value by creating a new pointer:

ValueItem *d = Main.MenuList[0][1];
lcd.print(d->GetValue());

But I don't like this workaround. Also when I tried to create a pointer to keep track of the current menu:

  MenuItem* Current_Menu;
  Current_Menu=&Main;

  ValueItem<float> *d = Current_Menu->MenuList[0][1];
  lcd.print(d->GetValue());

Then I get 'class MenuItem' has no member named 'MenuList' error.

So Perhaps J-M-L is right and I don't need polymorphism at all, I just need one class that holds a value and a list of other objects like itself. And then I can't use templates, because they will create a copy of the class and so neither class will be related.

So then without using templates to store the values I will have to just store Real numbers as floats and cast them to integers based on some enumerated type. I am just dissapointed that C++ doesn't appear to be flexible enough to do it the previous way, either that or there is something that I am misunderstanding.

I feel confident that I understand what everyone is saying, just not how it relates to what I am trying to achieve and my methods. I have provided examples and my thoughts alongside using them; If this is not enough to pinpoint the flaw in my understanding then I am not sure how to correct it.

I’m still not sure I totally understand your intended use case. But, suppose that you could have this magic ‘GetValue()’ function defined in the base class but whose return type is defined by the derived template-class. How would you then use pointers to the base class in a polymorphic sense? Meaning you could call baseClassPointer->GetValue(). But, the return type would be unknown at compile time since baseClassPointer can point to an object of any derived template-class.

You’d have to cast the return value to a known type in order to use it:

 uint32_t x;
  x = (uint32_t)baseClassPointer->GetValue();

But, that implies you KNOW the return type at compile time. This, in turn, implies that you know exactly which type of derived template-class object the pointer points to. So, you should just use a pointer to the correct template-class instead of the base class. You get no “polymorphic benefit”.

The above notwithstanding, you could have the GetValue() function return a ‘void *’. Then, it all hangs together, but you still need to know the type:
.ino file:

#include "Template.h"

Derrived <float> derrived1(3.14159);
Derrived <uint32_t> derrived2(1234567890);

void setup() {
  Base *ptr;

  Serial.begin(115200);
  delay(1000);
  Serial.println("Starting");

  ptr = &derrived1;
  Serial.print("derrived1: ");
  Serial.println(*((float *)ptr->getVal()), 5);

  ptr = &derrived2;
  Serial.print("derrived2: ");
  Serial.println(*((uint32_t *)ptr->getVal()));
}

void loop() {

}

Template.h:

class Base {
  public:
    virtual void *getVal() = 0;
};

template <typename VarType>
class Derrived : public Base {
  public:
    Derrived(VarType x) : privateData(x) {}
    virtual ~Derrived() {}
    virtual void *getVal() {
      return (void *)(&privateData);
    }

  private:
    VarType privateData;
};

But, that implies you KNOW the return type at compile time. This, in turn, implies that you know exactly which type of derived template-class object the pointer points to. So, you should just use a pointer to the correct template-class instead of the base class. You get no "polymorphic benefit".

that was my earlier point that using templates you actually instantiate different classes and you loose the benefit of virtual functions since you would be changing the virtual functions signature (returned type). So unless you document the type of data you have in your instance through some instance variable and return a generic void* pointer to a "data storage buffer" that you would cast to the real data type later, you are stuck