To store a destructor
[edited to improve punctuation and add another note]
After my talk on Friday, a couple of people asked me how I was storing destructors in my gcpp library. Since several people are interested, I thought I’d write a note.
The short answer is to store two raw pointers: one to the object, and one to a type-erased destructor function that’s handy to write using a lambda.
In deferred_heap.h, you’ll see that the library stores a deferred destructor as:
struct destructor {
const void* p;
void(*destroy)(const void*);
};
So we store two raw pointers, a raw pointer to the object to be destroyed and a raw function pointer to the destructor. Then later we can just invoke d.destroy(d.p).
The most common question is: “But how can you get a raw function pointer to the destructor?” I use a lambda:
// Called indirectly from deferred_heap::make.
// Here, t is a T&.
dtors.push_back({
std::addressof(t), // address of object
[](const void* x) { reinterpret_cast(x)->~T(); }
}); // dtor to invoke
A non-capturing lambda has no state so it can be used as a plain function, and because we know T here we just write a lambda that performs the correct cast back to invoke the correct T destructor. So for each distinct type T that this is instantiated with, we generate one T-specific lambda function (on demand at compile time, globally unique) and we store that function’s address.
Notes:
The lambda gives a handy way to do type erasure in this case. One line removes the type, and the other line adds it back.
Yes, it’s legal to call the destructor on a const object. It has to be; we have to be able to destroy const objects. Const never applies to a constructor or destructor, it applies to all the other member functions.
Some have asked what the body of the wrapper lambda generates. I looked at the code gen in Clang a couple of weeks ago, and depending on the optimization level, the lambda is generated as either a one-instruction function (just a single jmp to the actual destructor) or as a copy of the destructor if it’s inlined (no run-time overhead at all, just another inline copy of the destructor in the binary if it’s generally being inlined anyway).
If people keep asking I might add this to the readme FAQ but I’m trying to keep that reasonably short.
Filed under: Uncategorized
Herb Sutter's Blog
- Herb Sutter's profile
- 32 followers

