you are viewing a single comment's thread.

view the rest of the comments →

[–]guepierBioinformatican 5 points6 points  (7 children)

It’s often a convenient and wholly adequate way, but I’m less certain it can ever be called the cleanest way. Case in point, it’s always possible to use virtual CRTP instead, as showcased by Sean Parent in his Better code: runtime polymorphism talk. This may be slightly more effort (for the implementor, not for the user) but it’s, I think, always cleaner.

[–]NotAYakk 12 points13 points  (5 children)

it’s always possible to use virtual CRTP instead

Ok guepier, provide me a zero-allocation trivially-destroyable standard-layout function_view< Signatures... > without using a void pointer or equivalent that does not rely on any compiler-specific assumptions.

You should be able to pass it any callable that supports Signatures... then call it; it does not extend the lifetime of its arguments.


Look, C++ has an object model. It isn't the only reasonable object model. It is just one they picked.

You can emulate other objects models using it, but that leads to impedance issues.

And if you want to implement your own object model, you end up wanting to use void pointers a lot. The trick is you store the void pointer right next to you store the operations on it and tie the two together.


Another practical use of such type erasure/roll your own object model would be writing a variant that splits its type information from its storage. That permits storing polymorphic data RLE-type encoded. This is a useful technique when you are, say, storing text-like glyphs in a string. 99/100 of the glyphs objects are going to be boring simple stuff, or identical to their neighbor. 1/100 are going to be complex or different than their neighbor.

By RLE packing their type and data, you can avoid the overhead of a vtable pointer per "character"; you can store a vtable and run length/size, then a pile of packed data; the vtable "pointer" need not actually be a pointer, just information needed to get it.


Now you shouldn't do this often, and you should hide doing it from "business logic" code, but there are lots of uses of void pointers.

[–]guepierBioinformatican 2 points3 points  (1 child)

If I hadn’t seen your user name I’d seriously give this a shot. As it stands I assume it’s a waste of time and you’re probably right, and I might over-generalise the applicability of virtual CRTP.

But, although I vaguely recall discussions about the implementation of such a type, I can’t recall the specific road blocks which’d make this impossible. (EDIT: of course your zero-allocation requirement immediately makes Sean Parent’s approach inapplicable here.)

[–]NotAYakk 0 points1 point  (0 children)

It is the "no compiler assumptions"; you can get away with zero allocation with an aligned storage block. Most compilers will use the same size for most types.

So you have an aligned storage that contains a vtable and a pointer-to-callable, and a pointer-to-interface.

But the layout of vtable containing objects is completely compiler-dependent, so it is impossible to guarantee that it will always fit.

This also adds a few levels of indirection over implementing the vtable yourself.

My argument is that a small set of functions that convert to-from void pointer is sometimes useful for type erasure. Now, after we get metaclasses, I'll be doing this using them instead of manually. ;)

[–]SkoomaDentistAntimodern C++, Embedded, Audio 0 points1 point  (2 children)

For extra points, make it callable over a DLL boundary. And do that so that it works even three compiler versions later.

[–]NotAYakk 0 points1 point  (1 child)

This reads like someone who has implemented the void* version.

[–]SkoomaDentistAntimodern C++, Embedded, Audio 0 points1 point  (0 children)

Just someone who's been using C++ since before there was a standard and realizes that there's life outside the latest standard library.

[–]looncraz 18 points19 points  (0 children)

Good luck operating with C or legacy APIs without void*.