Arduino hangs on virtual method

I'm developing an abstract class having virtual methods:

class ControlElement{
  public:
    ControlElement(String id,int x,int y,int width,int height);
    virtual void paintElement();
    virtual int handleTouch(int x,int y)=0;
    ...
    protected:
    ...

};

class Button:public ControlElement{
  public:
    Button(String id,int x,int y,int width,int height,String text);
    void paintElement();
    int handleTouch(int x,int y);
  private:
    String text;
};

Calling the paint method works fine:

void Gui::addElement(ControlElement *controlElement){
    if(elementCount==-1){
        elementCount=0;
    }
    this->controlElements[elementCount]=controlElement;
    elementCount++;

    controlElement->paintElement();
}

Whereas controlElements is defined as: ControlElement* controlElements[20];

Calling handleTouch crashes the Arduino:

int retVal=controlElements[0]->handleTouch(x,y);

While all other non-virtual methods from the base class are working fine. I suppose that allways the base class is called and not the derived class. But how to make the derived class called - without calling it explicitly?

Nick

In your implementation of handleTouch(int x,int y)

Put a Serial.println(“1111111”);…Serial.println(“2222222”); etc after each line of code.

You will soon see what is causing this function to hang.

Already did it. It’s definitly the call of the method. If I give the method of the super class a return value it’s working.

You have a virtual method defined in the super class because you want the derived classes to implement it. That means that you should NEVER create an instance of the super class.

You should ONLY create instances of the derived class(es).

You are trying to call the virtual method using an instance of the super class THAT DOES NOT IMPLEMENT THE METHOD.

Obviously, you can't do that.

I think I can see your dilemma.....

You need to create an array of base class objects so that it can contain button objects and whatever other type of ui objects you are using.

But if you create your array like this in your container class then the pure virtual function in all the array elements is not implemented.

class Container
{
     ControlElement array[MAX];
}

You could declare your array like this instead:

class Container
{
     ControlElement* array[MAX];
}

I.E. As an array of pointers to ControElement. Then you could create Button objects etc dynamically and have an element in the array point to it. Thus the version of the virtual function in the derived object would be called instead of the unimplemented pure virtual function.

But dynamic creation of objects is not recommended for Arduino.

I guess one solution would be to do this:

class Container
{
     ControlElement* array[MAX];

     Button button1, button2, button3;
}

And then add pointers to button1, button2 and button3 to your array.

From then on you only refer to button1, button2 and button3 through the pointers in the array.

PaulS:
You should ONLY create instances of the derived class(es).

or put another way...

virtual int handleTouch(int x,int y)=0;

is a pure virtual method thus making your Base class an abstract class

as Paul pointed out, you don't have an implementation of the function in the base class

Whether array is an array of ControlElement objects or ControlElement pointers has NOTHING to do with the problem.

The ControlElement class does not implement the handleTouch() method, so you can NOT call the handleTouch() method using an instance of the ControlElement class or a ControlElement object.

OP claims that the method is declared virtual because the derived classes are supposed to implement it. So, the method can only be called for items in the array that are instances of the derived classes.

So far, there has been no proof that there ARE derived classes, or that the derived class(es) do indeed implement that method, or that the things in the array are instances or objects of the derived classes ONLY.

is a pure virtual method thus making your Base class an abstract class

I was under the impression that pure and abstract were used only when all the methods of a class are virtual.

Apparently not, so could you elaborate on the distinctions that pure and abstract bring to the party?

PaulS:
Whether array is an array of ControlElement objects or ControlElement pointers has NOTHING to do with the problem.

The ControlElement class does not implement the handleTouch() method, so you can NOT call the handleTouch() method using an instance of the ControlElement class or a ControlElement object.

OP claims that the method is declared virtual because the derived classes are supposed to implement it. So, the method can only be called for items in the array that are instances of the derived classes.

So far, there has been no proof that there ARE derived classes, or that the derived class(es) do indeed implement that method, or that the things in the array are instances or objects of the derived classes ONLY.

A pointer to a base class and an actual object created from the base class are two very different things.

The only way the OP can be calling the pure virtual function in his base class is if he has created an object from a base class.

Actually will the compiler even let you create an object from a base class with a pure virtual function? I have never tried myself.

If I do the following then all is good.

class foo
{
    virtual void doSomething() = 0;
}

class foo_derived: public foo
{
    virtual void doSomething()
    {
        Serial.println(F("XXXXXX"));
    }
}

foo_derived foo_derived_object;
foo* foo_object = &foo_derived_object;

foo_object->doSomething();

But if I do this then the $hit will hit the fan - I am de-referencing a NULL pointer for all intents and purposes.

class foo
{
    virtual void doSomething() = 0;
}

class foo_derived: public foo
{
    virtual void doSomething()
    {
        Serial.println(F("XXXXXX"));
    }
}

foo foo_object;

foo_object.doSomething();

I can only assume that the OP is doing something like the above.

How else can he get that error?

PaulS:
I was under the impression that pure and abstract were used only when all the methods of a class are virtual.

Apparently not, so could you elaborate on the distinctions that pure and abstract bring to the party?

classes are abstract if they have at least one pure virtual function.

pure virtual functions are defined like this:

virtual int handleTouch(int x,int y)=0; \\ notice the zero

while you cannot create an Object with a pure virtual function:

class Shape{
  public:
    virtual void getArea() = 0;
  private:
    
};

class Circle : public Shape{
  public:
    void getArea(){return (2*PI*radius*radius);}
  private:
    int radius;
};

class Square : public Shape{
  public:
    void getArea(){return (width * width);}
  private:
    int width;
};

Shape shape;


void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

you CAN create a pointer to it:

class Shape{
  public:
    virtual int getArea() = 0;
  private:
    
};

class Circle : public Shape{
  public:
    int getArea(){
      return (2*PI*radius*radius);
    }
  private:
    int radius;
};

//class Square : public Shape{
//  public:
//    void getArea(){return (width * width);}
//  private:
//    int width;
//};

Circle circle;

Shape* shapePtr;


void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

Abstract classes require the definition of their pure virtual functions in the derived classes.

A picture equals a thousand words....perhaps this might help the OP.

So in the Shape class those two functions would be pure virtual '=0'

And then the Circle Square and Triangle classes would implement those two functions according to the specifics of each shape.

Once you create your square, triangle and circle objects you can put them all in a collection, e.g. an array, because they are all derived from Shape.

The compiler implements a table (forget precisely what the correct term is) in the Shape class.

So calculateArea() = 0 is one entry in that table.

In the case of Triangle it would 'point' to Triangle::calculateArea().
In the case of Square calculateArea() = 0 would 'point' to Square::calculateArea().
And so on

So perhaps that helps the OP why

Circle circle;
Square square;
Triangle triangle;

Shape* pShape = &square
pShape->calculateArea();
pShape = &circle
pShape->calculateArea();
pShape = &triangle
pShape->calculateArea();

Always yields the correct value for arrea.

pure virtual functions are defined like this:

virtual int handleTouch(int x,int y)=0; \\ notice the zero

I want to make sure that I have this straight. The virtual keyword, without the =0, says that a derived class CAN override the method, but does not have to, while the virtual keyword, with the =0, says that a derived class MUST implement the method. Is that right?

PaulS:
I want to make sure that I have this straight. The virtual keyword, without the =0, says that a derived class CAN override the method, but does not have to, while the virtual keyword, with the =0, says that a derived class MUST implement the method. Is that right?

yes... If you take out the getArea() method from the Circle class in the example... compiler complains:

cannot declare variable 'circle' to be of abstract type 'Circle'

or in other words "you've inherited a pure virtual function and you haven't defined it, you big dope"

As I'm not too deep into C++ a short Java Example what was mit Original intent:

public abstract class ControlElement{
     public abstract int handleTouch(int x,int);
}
public class Button extrends ControlElement{
public int handleTouch(int x,int y){
...
}

ControlElement[] controlElements=new ControlElement[10];
...
controlElements[0]=new Button(...);

controlElements[0].handleTouch(...);

NickAmSee:
As I'm not too deep into C++ a short Java Example what was mit Original intent:

well, polymorphism is about as deep as it gets with C++

it's all about the ++ parts.

I also used the =0 (infact that's the case when it's crashing) without it's returning a random number (as the base class is not implemented) So I'm shure it allways calls the base class

NickAmSee:
I also used the =0 (infact that's the case when it's crashing) without it's returning a random number (as the base class is not implemented) So I'm shure it allways calls the base class

Again, dereferencing a Base Class pointer and accessing a pure virtual function is possible, but (as you've discovered) since the method is not implemented, you can expect undefined behavior.

And then how to call the method properly?

int retVal=controlElements[0]->handleTouch(x,y);

NickAmSee:
And then how to call the method properly?

int retVal=controlElements[0]->handleTouch(x,y);

well… it seems that you have created an array of pointers to the base class. Instead, try creating a base class array and filling it with derived class pointers. On Arduino, what you are trying to do is a bit of a challenge.

You could do it using the ArduinoSTL library and vectors (lots of memory to use this), or using an array. This is an example using the my example above for the sake of continuity (and you never posted all of your code)

EDIT: ADDED CLASS MEMBER Vector and Array method…

#include <ArduinoSTL.h>
#include <vector>

constexpr int MAX_INSTANCES = 5;

class Shape{
  public:
    Shape(Shape* instance){
      instances.push_back(instance);
      inst[instanceCount++] = instance;
    }
    virtual float getArea() = 0; 
    static std::vector<Shape*> instances; 
    static Shape* inst[MAX_INSTANCES];
    static size_t instanceCount;
  private:
    
};

class Circle : public Shape{
  public:
   Circle(int r) : radius(r), Shape(this){}
    float getArea(){
      return (2*PI*radius*radius);
    }
  private:
    int radius = 1;
};

class Square : public Shape{
  public:
    Square(int w) : width(w) , Shape(this){}
    float getArea(){
      return (float(width) * width);
    }
  private:
    int width;
};

std::vector<Shape*> Shape::instances;
Shape* Shape::inst[MAX_INSTANCES] = {nullptr};
size_t Shape::instanceCount = 0;

Circle circle(3);
Square mySquare(4);  

std::vector<Shape*> s;

Shape* shapeArray[MAX_INSTANCES] = {nullptr};

void setup() {
  Serial.begin(9600);

  Serial.println(F("\nUsing Class Vector:"));
  for (auto& i : Shape::instances){
    Serial.println(i->getArea());
  }

  Serial.println(F("\nUsing Class Array:"));
  for (size_t i = 0; i < Shape::instanceCount; i++){
    Serial.println(Shape::instances[i]->getArea());
  }
  
  Serial.println(F("\nUsing vector:"));
  s.push_back(&circle);
  s.push_back(&mySquare);
  for(auto i : s){
    Serial.println(i->getArea());
  }

  Serial.println(F("\nUsing array:"));
  shapeArray[0] = &circle;
  Serial.println(shapeArray[0]->getArea());
  shapeArray[1] = &mySquare;
  Serial.println(shapeArray[1]->getArea());
}

void loop() {

}

Now it’s getting interesting. To provide a proper laboratory example I wrote the following code:

class ControlElement{
  public:
    ControlElement(String id){
      this->id=id;  
    }
    virtual void paintElement()=0;
    virtual int handleTouch(int x,int y)=0;

    String getID(){
      return id;
    }
  protected:
  String id;

};



class Gui{
  public:
  Gui(){
    elementCount=-1;  
  };
  void addElement(ControlElement *controlElement){
    if(elementCount==-1){
      elementCount=0;
    }
    this->controlElements[elementCount]=controlElement;
    elementCount++;

    controlElement->paintElement();
  }
  String handleTouch(int x,int y){
  Serial.println("Element count="+String(elementCount));
  
  if(elementCount>-1){
    Serial.println("If entered");
   
    for(int i=0;i<elementCount;i++){
      
      Serial.println("For loop entered");
  
      Serial.println("Touch handling button "+controlElements[i]->getID());
 
      int retVal=controlElements[i]->handleTouch(x,y);
      Serial.println("Touch handled button "+controlElements[i]->getID());
  
      Serial.println(retVal);

    }
  }
  return "";
  }
  private:
  ControlElement* controlElements[20];
  int elementCount;

};

class Button:public ControlElement{
  public:
    Button(String id);
  void paintElement() override{
    Serial.println("Paint button");
  }
  int handleTouch(int x,int y) override{
    return 1;
  }

};

Button::Button(String id):ControlElement(id){
  
}

Gui gui;

void setup() {
  Serial.begin(9600);
  Button button1=Button("A");
  Serial.println("Button1 created");
  gui.addElement(&button1);
  Serial.println("Button1 added");
  
  
  Button button2=Button("B");
  Serial.println("Button2 created");
  gui.addElement(&button2);
  Serial.println("Button2 added");
  
  gui.handleTouch(1,1);
  Serial.println("Touch handled setup");
  
  
}

void loop() {
  delay(1000);
  Serial.println("Touch handling loop");
  gui.handleTouch(1,1);
  Serial.println("Touch handled loop");
  

}

handleTouch in the setup method is working - in loop it is crashing

Button1 created
Paint button
Button1 added
Button2 created
Paint button
Button2 added
Element count=2
If entered
For loop entered
Touch handling button A
Touch handled button A
1
For loop entered
Touch handling button B
Touch handled button B
1
Touch handled setup
Touch handling loop
Element c

The “Element c” is caused from a hangout. If I add some ms of delay it is exactly crashing at handleTouch