chHeapAlloc with polymorphic classes

Discussions and support about ChibiOS/RT, the free embedded RTOS.
elasticdoor
Posts: 8
Joined: Sun Nov 14, 2021 8:44 pm
Has thanked: 2 times
Been thanked: 1 time

chHeapAlloc with polymorphic classes

Postby elasticdoor » Thu Jan 20, 2022 2:09 pm

Hello,

I am writing a program using the Arduino environment and the ChRt library https://github.com/greiman/ChRt, developing for the Teensy MicroMod.

As you may know, the Arduino environment allows for programming in C++, so this is the language that I'm using. I am implementing a dynamic list of objects derived from a common base class. I have a helper function to append items to the list. The base class features a pure virtual function that must be overridden by all subclasses. The code looks something like this:

Code: Select all

class Base {
public:
  Base *next = NULL;
  Base *prev = NULL;
  virtual void doWork() = 0;
};

class Child1 : public Base {
public:
  void doWork() { /* Child1 version of doWork() */ };
};

class Child2 : public Base {
public:
  void doWork() { /* Child2 version of doWork() */ };
};

Base *head = NULL;
Base *tail = NULL;

void append(Base *p) {
  if (head == NULL) {
    head = p;
    tail = p;
  }
  else {
    tail->next = p;
    p->prev = tail;
    tail = p;
  }
}

void main() {
  Child1 *c = (Child1 *)chHeapAlloc(NULL, sizeof(Child1));
  append(c);
  head->doWork(); /*Segmentation fault*/
}


As I mentioned in the code comment, calling the doWork function on an element of the list (here called on "head" for simplicity) gives a segmentation fault. The interesting thing is that if I skip explicitly instantiating c and instead I do

Code: Select all

append(new Child1());


the program runs no problem, the Child1 version of doWork is correctly called. So I have the suspicion that somehow, chHeapAlloc breaks polymorphism in some way. I could also be way off and any C++ advice would be welcome :)

Thank you all in advance for your help

User avatar
Giovanni
Site Admin
Posts: 14020
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 988 times
Been thanked: 832 times
Contact:

Re: chHeapAlloc with polymorphic classes

Postby Giovanni » Thu Jan 20, 2022 2:33 pm

Hi,

That function behaves just like malloc(), I doubt it causes polymorphism to break. You need to look elsewhere.

If in doubt then try to replace chHeapAlloc() with the pool allocator, make a pool of your objects and allocate from there.

Giovanni

elasticdoor
Posts: 8
Joined: Sun Nov 14, 2021 8:44 pm
Has thanked: 2 times
Been thanked: 1 time

Re: chHeapAlloc with polymorphic classes

Postby elasticdoor » Thu Jan 20, 2022 3:25 pm

Thank you Giovanni.

For anybody who feels like taking a closer look at the code, I think it's relevant to specify that the Base pointers that are being initialized are actually contained into a container class, as follows

Code: Select all

class Base {
public:
  Base *next = NULL;
  Base *prev = NULL;
  virtual void doWork() = 0;
};

class Child1 : public Base {
public:
  void doWork() { /* Child1 version of doWork() */ };
};

class Child2 : public Base {
public:
  void doWork() { /* Child2 version of doWork() */ };
};

class Container {
  Base *head = NULL;
  Base *tail = NULL;
  void append(Base *p);
};

void Container::append(Base *p) {
  if (head == NULL) {
    head = p;
    tail = p;
  }
  else {
    tail->next = p;
    p->prev = tail;
    tail = p;
  }
}

void main() {
  Container *cont = new Container();
  Child1 *c = (Child1 *)chHeapAlloc(NULL, sizeof(Child1));
  cont->append(c);
  head->doWork(); /*Segmentation fault*/
}

elasticdoor
Posts: 8
Joined: Sun Nov 14, 2021 8:44 pm
Has thanked: 2 times
Been thanked: 1 time

Re: chHeapAlloc with polymorphic classes

Postby elasticdoor » Thu Jan 20, 2022 3:39 pm

Also, yes, you were absolutely right. I tried substituting chHeapAlloc with malloc and the problem is the same .

elasticdoor
Posts: 8
Joined: Sun Nov 14, 2021 8:44 pm
Has thanked: 2 times
Been thanked: 1 time

Re: chHeapAlloc with polymorphic classes

Postby elasticdoor » Thu Jan 20, 2022 4:13 pm

Final update:

As I discovered, neither chHeapAlloc nor malloc (which behave in the same way) preserve polymorphism, because they are just raw memory allocators and they do not actually properly initialize the object.

I solved my problem by allocating memory using chHeapAlloc and subsequently using placement new. The difference between placement new and "regular" new is that placement new skips the memory allocation step that "regular" new does. So I can safely allocate memory using chibiOS memory management functions and still have a properly initialized object. Code below:

Code: Select all

#include <new>

class Base {
public:
  Base *next = NULL;
  Base *prev = NULL;
  virtual void doWork() = 0;
};

class Child1 : public Base {
public:
  void doWork() { /* Child1 version of doWork() */ };
};

class Child2 : public Base {
public:
  void doWork() { /* Child2 version of doWork() */ };
};

class Container {
  Base *head = NULL;
  Base *tail = NULL;
  void append(Base *p);
};

void Container::append(Base *p) {
  if (head == NULL) {
    head = p;
    tail = p;
  }
  else {
    tail->next = p;
    p->prev = tail;
    tail = p;
  }
}

void main() {
  Container *cont = new Container();
  Child1 *c = (Child1 *)chHeapAlloc(NULL, sizeof(Child1));
  Child1 *child = new(c) Child1();
  cont->append(child);
  head->doWork(); /* Works properly*/
}


Return to “ChibiOS/RT”

Who is online

Users browsing this forum: No registered users and 2 guests