I have some code that looks like this:
class Writable {
public:
virtual void putc(const char ch) = 0;
protected:
virtual ~Writable() {};
};
class Readable {
public:
virtual char getc() = 0;
protected:
virtual ~Readable() {};
};
Notice the two virtual functions. Compiling this (along with my other code) using arm-none-eabi-gcc
, and linking with -fno-exceptions
produces this output:
arm-none-eabi-size --format=berkeley bareCortexM.elf
text data bss dec hex filename
108948 2304 2372 113624 1bbd8 bareCortexM.elf
Running it again with method stubs in place of pure virtual functions yields:
arm-none-eabi-size --format=berkeley bareCortexM.elf
text data bss dec hex filename
47340 2296 304 49940 c314 bareCortexM.elf
This huge difference seems to be due to exceptions. Is there any way that I can prevent this from happening?
A pure virtual function is declared, but not necessarily defined, by a base class. A class with a pure virtual function is "abstract" (as opposed to "concrete"), in that it's not possible to create instances of that class.
You may call a virtual function as long as it is not a pure virtual function, and as long as you know that what you are calling is the method from the class itself and not expecting any polymorphism. Calling a pure virtual function from a constructor is undefined behaviour even if it has an implementation.
¶ Δ A pure virtual function is a function that must be overridden in a derived class and need not be defined.
A virtual function can be private as C++ has access control, but not visibility control. As mentioned virtual functions can be overridden by the derived class but under all circumstances will only be called within the base class. Example: C++
This is described by this blog post: Smaller binary size with C++ on baremetal (g++)
Provide a
__cxa_pure_virtual()
implementationIf you use pure virtual functions anywhere but have disabled exceptions, you may notice your code suddenly inflate again.
This happened to me, and it took a while to track down, whoops!
Inspecting assembly listing of the final binary (fromobjdump -h -C -S
), it looked like exceptions were coming back!One thing I tried was linking with
-nostdlib
, completely pulling libstdc++ out of the picture. I provided dummy implementations of malloc, realloc, free, and a few other stdlib functions I used, but thenavr32-g++
complained about something I hadn’t seen before: I was missing__cxa_pure_virtual()
.“Aha,” I thought, “this has to be it!” In the source of that particular function, found in libstdc++, is a call to
std::terminate()
, seen here. That call threw a lovely party all over my poor AVR32′s flash memory, trampling on-fno-exceptions
on their way in.Anyway,
__cxa_pure_virtual()
is what actually gets called when you call a pure virtual function. Likenew
anddelete
, this is probably something you want to override anyway so your own debug/trace code can give you useful feedback. The implementation is straightforward, just be sure to make itextern "C"
so the name doesn’t get mangled:extern "C" void __cxa_pure_virtual() { while(1); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With