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
 •  0 comments  •  flag
Share on Twitter
Published on September 25, 2016 11:46
No comments have been added yet.


Herb Sutter's Blog

Herb Sutter
Herb Sutter isn't a Goodreads Author (yet), but they do have a blog, so here are some recent posts imported from their feed.
Follow Herb Sutter's blog with rss.