Search code examples
c++delete-operatorfreestanding

Handling required delete-operator in virtual-destructors in freestanding environments


I maintain a library designed for resource-restricted environments on bare-metal microcontrollers: emio. This C++20 library uses virtual inheritance and therefore has virtual destructors.

A user encountered a new issue in a freestanding, no-heap environment:

undefined reference to `operator delete(void*, unsigned int)’ #99

The root cause of the issue is explained here: Why is delete operator required for virtual destructors.

My question is:

Can I solve the problem of the user somehow? Maybe by providing a destroying deleter for all classes? Or does the user needs to implement his own global deleter (e.g., calls std::terminate) which fails if he uses use new/delete expressions accidentally?

Additionally, why does the linker not drop this function even though it is never called?

Side note: I do not want to exclude users which use new/delete and are running on non freestanding environments.


Solution

  • Let the user decide themselves how they want to handle the global replaceable ::operator delete.

    If they are not on a freestanding implementation, then the defaults work and new/delete can be used with your classes as expected. The user can still replace the global replaceable ::operator delete and have your class work with the intended allocation semantics.

    If they are using a freestanding implementation, then they have to think about ::operator delete anyway, because it is incorporated in the core language. They need to implement ::operator delete to properly use the core language. The choice also has effect program-wide and a library shouldn't preempt the user choice. This isn't the only non-trivial issue that a user of a freestanding environment needs to consider either, so it doesn't seem like a big additional burden to me.

    The the proposal you linked in the question comment was also accepted in a later revision for C++26 and so in the future a compiler might also be helpful in defining a freestanding environment in which ::operator delete is defined as a noop so that using heap allocation won't work, but virtual destructors will continue to work as expected, simplifying the matter for the user. Still, the responsibility lies with the compiler and user, not the library.


    The linker retains a reference to ::operator delete in the deleting destructor emitted for the virtual destructor. Compilers typically do not compile in such a way that linkers could remove individual unused functions unless the functions are inline or implicit template instantiations. (I am excluding Windows here. I don't know how MSVC's linking works.)

    You generally need something like the -ffunction-sections -Wl,--gc-sections flags to give the linker the ability to do so, but it is practically impossible for the linker to really identify what can be removed, so the flags given above can incorrectly remove stuff and do not behave standard-conforming.