Live data from Hacker News

Implementing a class with void*

web.eecs.utk.edu

11–20 of 78 posts

Re: Implementing a class with void*

#11
post #4

I'm surprised he doesn't refer to it by name (Pimpl). In C++ it has the advantage that you can change your implementation and retain binary compatibility because the size of your class doesn't change as you add/remove member variables (and maybe more surprisingly, add/removing virtual functions). If you're going to use C++, seems like it would be better to avoid the void* by class C { protected: struct Impl; std::uni…

This also significantly hurts readability, and I would hate the person who writes code like this unless you really need binary compatibility (if you do, it is a sign that you should improve your release schedule).

Mostly, you just want to decouple the interface of the class from the implementation details.

In that case do just that - define an interface and implement it elsewhere.

You can always just static link all binaries.

Re: Implementing a class with void*

#12

> This is not an "industry-standard" way to program in C++. It absolutely is a de facto industry standard way to program C++, and has a name: PIMPL (Pointer to IMPLementation). It has that name, because it's famous. It's probably less fashionable in newer code bases; probably someone whose head is up in C++20 will probably scoff at this, and certainly at any version where the secret is hidden by void *. It provides a…

It is not an industry standard way. It is not a sensible way. It is not even a C way.

As others have said, there's no reason whatsoever to use void* here. Just declare the struct without defining it in the header. Then only define it in the C file. C is fine with pointers to structs which are merely declared but not defined.

Re: Implementing a class with void*

#13
post #4

I'm surprised he doesn't refer to it by name (Pimpl). In C++ it has the advantage that you can change your implementation and retain binary compatibility because the size of your class doesn't change as you add/remove member variables (and maybe more surprisingly, add/removing virtual functions). If you're going to use C++, seems like it would be better to avoid the void* by class C { protected: struct Impl; std::uni…

> and maybe more surprisingly, add/removing virtual functions

I don't have the spec handy but fairly certain that v-table implementation is compiler specific and while it may work it isn't guaranteed. However if you declare the function symbols as dynamic then you can leverage the linker to dynamically resolve the right symbols with the matching opaque data and achieve binary compatibility(assuming you use the same compiler or a compatible compiler ABI and all other caveats around C++ binary compatibility).

Re: Implementing a class with void*

#14
post #4

I'm surprised he doesn't refer to it by name (Pimpl). In C++ it has the advantage that you can change your implementation and retain binary compatibility because the size of your class doesn't change as you add/remove member variables (and maybe more surprisingly, add/removing virtual functions). If you're going to use C++, seems like it would be better to avoid the void* by class C { protected: struct Impl; std::uni…

It's kind of ironic that you declared it protected given that the poor subclasses won't actually have a definition to work with... which is incidentally one of the reasons not to do this.

Re: Implementing a class with void*

#15
post #12

> This is not an "industry-standard" way to program in C++. It absolutely is a de facto industry standard way to program C++, and has a name: PIMPL (Pointer to IMPLementation). It has that name, because it's famous. It's probably less fashionable in newer code bases; probably someone whose head is up in C++20 will probably scoff at this, and certainly at any version where the secret is hidden by void *. It provides a…

It is not an industry standard way. It is not a sensible way. It is not even a C way. As others have said, there's no reason whatsoever to use void* here. Just declare the struct without defining it in the header. Then only define it in the C file. C is fine with pointers to structs which are merely declared but not defined.

It is a de facto standard in that it is fairly widely documented and actually done. It's not in any ISO or IEEE standard or anything of the sort.

There is almost no reason whatsoever to use void * anywhere other than to write a declaration that is compatible with another one which uses it.

(A pointer to any object is better implemented as a typedef for unsigned char *. This requires a cast in both directions, thus it is safer. At the same time, it is more convenient when you actually want to work with the memory as such: you have bytewise arithmetic and dereferencing.)

> C is fine with pointers to structs which are merely declared but not defined.

Particularly if the secret object is a struct/class then this certainly improves the code (and when the secret isn't such a thing, it can probably be made into one: e.g. a secret array of integers can probably just be a a struct containing an array, possibly a flexible one). Numerous unsafe casts are thereby eliminated.

But it makes no material difference to its organization or semantics. It's still the same PIMPL pattern.

Re: Implementing a class with void*

#16
post #9

Back in the 1990s, the video game company I worked at would hang a couple of extra void pointers in every class just to, you know, store extra things that are needed as time goes on…

This was common in the 90s, BeOS had these everywhere in its API classes

Re: Implementing a class with void*

#17
post #4

I'm surprised he doesn't refer to it by name (Pimpl). In C++ it has the advantage that you can change your implementation and retain binary compatibility because the size of your class doesn't change as you add/remove member variables (and maybe more surprisingly, add/removing virtual functions). If you're going to use C++, seems like it would be better to avoid the void* by class C { protected: struct Impl; std::uni…

> and maybe more surprisingly, add/removing virtual functions I don't have the spec handy but fairly certain that v-table implementation is compiler specific and while it may work it isn't guaranteed. However if you declare the function symbols as dynamic then you can leverage the linker to dynamically resolve the right symbols with the matching opaque data and achieve binary compatibility(assuming you use the same c…

vtable layout is defined by the ABI, which is (mostly) consistent across major compilers everywhere except MSVC, however if MSVC ever broke vtable layout then everything that relies on COM would break on Windows. Which is basically all of Windows user space.

Re: Implementing a class with void*

#18

This is a well-known way to achieve separation of definition and implementation. The disadvantage of the method proposed in the article is that, now everything needs a pointer indirection. A solution that fixes the drawback is used by lz4's library implementation: instead of storing a void star, store a char[] array of same size as the real struct. (of course, now you have to manually make sure the struct size are in…

Or don't do that (the char array trick) in C++ because implementers and standardizers are not clear about when/if you are allowed to store other objects in an array of chars, and even if you are it is tricky because you need to manually manage the alignment, or they are attempting to replace char with std::byte in the long term but don't really have a comprehensive and detailed plan to do so, etc.

The implementation should probably provide you with std::aligned_storage which may involve some magic to handle some of those concerns, although merely the easiest ones, so I would say probably still don't use that either, unless you are already quite a C++ expert and/or are prepared to dig into the standard with no clear response about what you are attempting to do is even formally possible (the implementers/standardizers do not even know some things they make impossible for quite a long time, see for example the insanity of std::launder, or if you want to loose your mind forever the semantic of pointer provenance analysis that compilers are maybe already using to "optimize" but that what the semantic should even be is still being debated.)

Re: Implementing a class with void*

#19
post #18

This is a well-known way to achieve separation of definition and implementation. The disadvantage of the method proposed in the article is that, now everything needs a pointer indirection. A solution that fixes the drawback is used by lz4's library implementation: instead of storing a void star, store a char[] array of same size as the real struct. (of course, now you have to manually make sure the struct size are in…

Or don't do that (the char array trick) in C++ because implementers and standardizers are not clear about when/if you are allowed to store other objects in an array of chars, and even if you are it is tricky because you need to manually manage the alignment, or they are attempting to replace char with std::byte in the long term but don't really have a comprehensive and detailed plan to do so, etc. The implementation…

Yes, strict aliasing (or type-based alias analysis?) is quite crazy, and there are some murky dark corners where the specification is different between C and C++..

I think they have a std::launder thing exactly for this purpose of "safely casting an array of bytes into an object".

However, in this particular case (of using char array to hide real implementation), the implementation resides in another translation unit, so I don't think anything is going to break if LTO is not enabled. With LTO I have no idea..

Re: Implementing a class with void*

#20
post #4

I'm surprised he doesn't refer to it by name (Pimpl). In C++ it has the advantage that you can change your implementation and retain binary compatibility because the size of your class doesn't change as you add/remove member variables (and maybe more surprisingly, add/removing virtual functions). If you're going to use C++, seems like it would be better to avoid the void* by class C { protected: struct Impl; std::uni…

The downside of unique_ptr used this way is that you have to define ~C in the same translation unit that the declaration of C::Impl lives.
Post reply on HN