Reading List

The Selfish Gene
The Psychopath Test: A Journey Through the Madness Industry
Bad Science
The Feynman Lectures on Physics
The Theory of Everything: The Origin and Fate of the Universe


ifknot's favorite books »

Tuesday 28 April 2020

Undefined reference to operator delete(void*) C++ AVR pure virtual and __cxa_pure_virtual()

 __cxa_pure_virtual()
&
Undefined reference to operator delete(void*)


C++ Programming for MCUs such as the Atmel AVR family can be both very productive and also very frustrating as one learns to work without libstdc++ when using certain MCU toolchains e.g.
  • C++ AVR Studio 
  • C++ avr-gcc 
  • C++ Arduino 
Whilst it is generally well known that the MCU programmer must provide their own versions of new and delete[1] it is less well known that the C++ programmer using pure virtual member functions must provide an implementation for __cxa_purevirtual() or face some very confusing errors!

Depeding on toolchain the programmer may be lucky and be notified:

undefined reference to `__cxa_pure_virtual'

What is __cxa_purevirtual()?[2]


Pure virtual functions can get called during object construction/destruction. If that happens, __cxa_pure_virtual() gets called to report the error. 

Importantly, __cxa_pure_virtual()is defined in most toolchains as part of libstdc++. However, MCU compiler toolchains do not compile against libstdc++ and so have no definition for new, delete or __cxa_pure_virtual()

Why do I have to define  __cxa_purevirtual()?[1][2]

If just writing a C++ interface using pure virtual class member function, then it is not necessary to define new/delete. However...

  • Remove the virtual modifier from the base class destructor.
  • Ensure that the derived class has an implementation of __cxa_pure_virtual().
Here is a compilable example provided by Mike Two [1] on Stack Overflow
In PacketWriter.h
class PacketWriter {
public:
    virtual void nextByte() = 0;
protected:
    ~PacketWriter() {}
};
In StringWriter.h
#include "PacketWriter.h"

class StringWriter : public PacketWriter {
public:
    StringWriter(const char* message);
    void nextByte();
};  
In StringWriter.cpp
#include "StringWriter.h"

// Definition of the error function to call if the constructor goes bonkers
extern "C" void __cxa_pure_virtual() { while (1); }

StringWriter::StringWriter(const char* message){
    // constructor code here
}

void StringWriter::nextByte() {
...
}
Compile with avr-g++ StringWriter.cpp

Why is an infinite loop a good choice?[2]

  • safe
  • predictable
  • obvious a.k.a. "loud error"
An infinite loop would be awkward to debug unless using a debugger that can interrupt execution and give a stack backtrace. It would be more useful to abort the program and report the error but this may well be non-optional in a MCU setting.

References:

No comments:

Post a Comment