C++ Polymorphism vs. Arduino

There’s a subtlety in C++’s virtual mechanism that I’ve run across twice now. Many other languages don’t have this problem, but C++ is close enough to the bare metal that this is a problem.

In the world of the small, where you have kilobytes of memory to work with, how you program changes dramatically. It’s highly advisable to avoid dynamic memory operations if at all possible. Global variables and class instances are very common.

For a project I wrote a simple, minimal coöperative multitasking core for the ATmega328P, the microcontroller in the Arduino Uno. Being pleased that it worked well enough, I turned the scheduler and process classes into Arduino libraries. The classes were renamed from “Scheduler” and “Process” to “SCMScheduler” and “SCMProcess”, where SCM stands for Simple Coöperative Multitasking.

The intended idea was that each process would descend from SCMProcess, and the scheduler would be initialized with an array of process class instances. The scheduler is very simple and only calls SCMProcess::execute() to pass control over to the next process.

In the original implementation, the scheduler received an array of Process class instances to operate on. (Each process executed identical code, so only one class was necessary.)

#define PROCESS_COUNT 10
Process processList[PROCESS_COUNT];
Scheduler scheduler(processList, PROCESS_COUNT);

The Scheduler’s constructor looked like:

class Scheduler
{
public:
Scheduler(Process* aProcessList, byte aProcessCount) …

There was no class inheritance involved, so the main loop worked without a hitch:

void Scheduler::run()
{
selectNextProcess();
processes[current].execute();
}

When I abstracted the Process class into a parent (SCMProcess) and a descendent (Process), this broke terribly. (If you’ve had your head in C++ for a long time, you’re probably cringing already.)

The old Scheduler class was removed entirely, and its code lifted to the new generic SCMScheduler class. The constructor was changed to:

class SCMScheduler
{
public:
SCMScheduler(SCMProcess* aProcessList, byte aProcessCount) …

The process class was split into two pieces, the generic base class and the application-specific class:

class Process : public SCMProcess …

The man body of code was only changed to use the new SCMScheduler class.

#define PROCESS_COUNT 10
Process processList[PROCESS_COUNT];
SCMScheduler scheduler(processList, PROCESS_COUNT);

In most other languages, declaring an array of descendents (Process[]) and passing it to a function that expected an array of parents (SCMProcess*), this is not a problem.

In C++, the symptom I saw was code execution that seemingly wandered though memory at random, resetting the machine over and over. At first it was terribly frustrating until I remembered back to something similar biting me the 1990’s.

For polymorphism to work in C++, you have to pass pointers to the parent class type. You can’t pass references or arrays of class instances. The nitty-gritty details of C++ make this impossible.

The correct constructor for the scheduler is:

class SCMScheduler
{
public:
SCMScheduler(SCMProcess** aProcessList, byte aProcessCount) …

The main body of code required me to conceded to using the new operator as a balance between machine space constraints and human labour constraints. (Note that I altered the constructor of the Process class for unrelated reasons of convenience.) I may rework the code so that I’m not using new, but for the time being it’s acceptable.

#define PROCESS_COUNT 9
SCMProcess* processList[PROCESS_COUNT] =
{
new Process(13, Catalogue, CATALOGUE_COUNT),
new Process(5, Catalogue, CATALOGUE_COUNT),
new Process(6, Catalogue, CATALOGUE_COUNT),
new Process(7, Catalogue, CATALOGUE_COUNT),
new Process(8, Catalogue, CATALOGUE_COUNT),
new Process(9, Catalogue, CATALOGUE_COUNT),
new Process(10, Catalogue, CATALOGUE_COUNT),
new Process(11, Catalogue, CATALOGUE_COUNT),
new Process(12, Catalogue, CATALOGUE_COUNT)
};

SCMScheduler scheduler(processList, PROCESS_COUNT);

…

void loop()
{
scheduler.run();
}
This entry was posted in Micro & Hardware, Programming and tagged , , , , . Bookmark the permalink.

Leave a Reply