Earlier quoted context omitted.
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…
Implementing a class with void*
41–50 of 78 posts
Re: Implementing a class with void*
#42Yes, as others have said, using an opaque class is preferable to using a void pointer. class Impl; This isn't just for hiding, it also gives you faster compilation.
> This isn't just for hiding, it also gives you faster compilation. That seems unlikely. What are you basing this on?
Re: Implementing a class with void*
#43Earlier quoted context omitted.
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 i…
Declaring a private and opaque forward decl is very different to VOID* all the things.
Re: Implementing a class with void*
#44I'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…
Re: Implementing a class with void*
#45Earlier quoted context omitted.
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…
You don't need to cast anything - just placement-new it inside the array. So long as the array is properly aligned, this is fine. After that, it would be UB to peek at the bytes of the object via the array because of aliasing issues, but I don't see why it would be improper to use the pointer returned by new.
I don't think in this specific case there is an UB involved, but I'm not language standard lawyer so I'm not sure. I feel the standard's specification on what is allowed to reinterpret_cast and what isn't is arcane (or at least far from straightforward to understand).
Re: Implementing a class with void*
#46Earlier quoted context omitted.
The usual solution is to out Impl in a separate implementation header, so that implementation headers of derived classes can include it, but the rest of the world doesn't need to see it.
I mean, yes, but that's just the start of it. Suddenly you need to add contortions (like, say, 2-phase initialization) if you need to e.g. add virtual methods to Impl. You can keep adding workarounds after workarounds until everything works; my general point is just that supporting subclasses now becomes more painful, and you end up having to (in some sense) fight the language. It's nowhere near as free of a lunch as…
But yes, I don't think anybody claims is a free lunch. It is annoying, verbose, repetitive, but often necessary to keep the cose base complexity under check.
Re: Implementing a class with void*
#47Re: Implementing a class with void*
#48Earlier quoted context omitted.
> 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*
#49This 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…
Can't you just use a static assert in the implementation file?
Re: Implementing a class with void*
#50Earlier quoted context omitted.
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…
You don't need to cast anything - just placement-new it inside the array. So long as the array is properly aligned, this is fine. After that, it would be UB to peek at the bytes of the object via the array because of aliasing issues, but I don't see why it would be improper to use the pointer returned by new.
Do you have a source for this? IIRC char and std::byte have a specific aliasing exception. I.e. char* and std::byte are allowed to alias anything.
You obviously aren't allowed to modify the char or std::byte array (because that would violate the struct/class's aliasing rule).