Live data from Hacker News

Implementing a class with void*

web.eecs.utk.edu

71–78 of 78 posts

Re: Implementing a class with void*

#71
post #69

Earlier quoted context omitted.

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…

> You can always just static link all binaries. Not always you can't. There are many reasons to use dynamically linked libraries all of which are applicable whether you're using PIMPL or not.

> Not always you can't

What's an example of this (assuming you don't care about binary size)?

Re: Implementing a class with void*

#72
post #18

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…

This is incorrect. I participated in the committee discussions around launder, byte, pointer provenance, and implicit object creation. None of those issues show up here. This is a very simple case of using placement new into a properly aligned char buffer to create an object at that location. This has worked just fine since C++98 and is not impacted by the many other object/lifetime/pointer issues that are being disc…

Thanks for clarification. It's nice to know that this approach is fully standard compliant.

(and that's exactly my point: the standard is hard to access for normal devs like me, so I can guess "some reinterpret_cast hack seems ok", but never be 100% certain until some standard expert confirms)

Re: Implementing a class with void*

#73
post #69

Earlier quoted context omitted.

> You can always just static link all binaries. Not always you can't. There are many reasons to use dynamically linked libraries all of which are applicable whether you're using PIMPL or not.

> Not always you can't What's an example of this (assuming you don't care about binary size)?

You care about binary size is probably the first one. In my experience, increases in binary size cause an exponential increase in link time. You may have licensing restrictions (LGPL for example), or be using a binary thay was provided to you. Your source code might be set up in such a way that a monolithic build doesn't make sense.

Re: Implementing a class with void*

#74
post #58

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…

That breaks the ABI-compatibility part of this trick.

Yes, it's a tradeoff. The upside is now you can put the object on the stack without invoking memory allocators.

Re: Implementing a class with void*

#75
post #41

Earlier quoted context omitted.

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.

After the object is constructed by placement-new, the class methods still needs to reinterpret_cast the char array to an object pointer to access the object. 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 unders…

You will get the properly typed pointer to object from new, so if you want to play it completely safe wrt UB, it can be stashed away alongside the array in the public class; this adds sizeof(T*) to the latter, but avoids casts entirely.

But, yes, you do technically need std::launder to get it directly via the array.

Re: Implementing a class with void*

#76
post #41

Earlier quoted context omitted.

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.

> After that, it would be UB to peek at the bytes of the object via the array because of aliasing issues 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).

I'm not so sure anymore, after looking at the standard again. It says that it's okay to access any object via a glvalue of type char, unsigned char, or std::byte; note that this does not include arrays of the same. If you have a field of array type, and you subscript it, the expression does involve a glvalue of that array type as one of the operands - but does this constitute "access"?

Re: Implementing a class with void*

#77
post #54

Consider the rule of zero.

Interesting thing indeed. Would you care to expand? Edit: This is interesting from the case of "beginning of the calendar" - coincidentally just read this from a reddit thread (linked): > Let me start with a quote you may hear in a lot of history classrooms: "Jesus Christ has been born 7 years Before Christ", sounds a bit weird doesn't it. Yep, historically the idea was to mark his birth as a dividing point, however,…

I was refering to this: https://en.cppreference.com/w/cpp/language/rule_of_three#Rul...

Re: Implementing a class with void*

#78
post #76

Earlier quoted context omitted.

> After that, it would be UB to peek at the bytes of the object via the array because of aliasing issues 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).

I'm not so sure anymore, after looking at the standard again. It says that it's okay to access any object via a glvalue of type char, unsigned char, or std::byte; note that this does not include arrays of the same. If you have a field of array type, and you subscript it, the expression does involve a glvalue of that array type as one of the operands - but does this constitute "access"?

A little late, but yes, subscripting an array yields a gvalue of that type, but that is not an access unless you use the glvalue in some other computation (I assume). But even if you do access it (i.e. make a bitwise copy of the entire array) that should still not be UB.
Post reply on HN