10 ms·
Is there anyone else that gets unreasonably angry at stuff like PIMPL? It is truly the most braindead, bereft of sense activity in this world. There was a comme
by stefan_ 3y ago
Is there anyone else that gets unreasonably angry at stuff like PIMPL? It is truly the most braindead, bereft of sense activity in this world. There was a comment in one of the many Rust threads that called C++ a respectable language now but then things like PIMPL snap you right back into the wasteland it is.
- wakawaka28 3y agoPIMPL is an elegant solution to multiple problems. Idk what you could possibly have against it besides the extra work involved. I don't think any language has solved the fundamental problem of hiding details better than PIMPL does.
- bananaboy 3y agoI really like C#'s `partial` keyword as a solution to the problem of hiding implementation details. It lets you declare a class over several files, so you can have one file which is only the public interface, and another which has private implementation.
- wakawaka28 3y agoThat is essentially the same idea as PIMPL. You put the private parts of the class (e.g., the data layout) in some file that is held privately. I guess you could argue that there is extra syntax involved with PIMPL because C++ is more low-level than C#, but it's not so bad. The actual implementation of a class can be spread over as many files as you want in C++.
- comex 3y agoAnd yet C, an even lower-level language, achieves the same effect without the duplication of PIMPL. You just forward-declare a struct, and declare functions that accept pointers to it: the header doesn't need to contain the struct fields, and you don't need to define any wrapper functions. Technically you can do the same in C++. But in C++ to make an idiomatic API you need methods instead of free functions, and you can't declare methods on forward-declared classes. Why not? Well, I can imagine some reasons… but they have more to do with C++'s idiosyncrasies than any fundamental limitation of a low-level language. The C++ committee could address this, but instead they seem to want to pretend separate compilation doesn't exist. (Why are there no official headers to forward-declare STL types, except for whatever happens to be in <iosfwd>?) Then they complain about how annoying it is to preserve ABI stability for the standard library, blaming the very concept of a stable ABI [1] [2], all while there are simple language tweaks that could make it infinitely more tractable! But now I'm ranting. [1] https://cor3ntin.github.io/posts/abi/ https://cor3ntin.github.io/posts/abi/ [2] https://thephd.dev/binary-banshees-digital-demons-abi-c-c++-help-me-god-please https://thephd.dev/binary-banshees-digital-demons-abi-c-c++-...
- wakawaka28 3y agoFirst of all, comparing C to C++ in this way is silly, because C++ is a very different language. But there are some similarities. > You just forward-declare a struct, and declare functions that accept pointers to it: the header doesn't need to contain the struct fields, and you don't need to define any wrapper functions. Those functions would be more verbose because they must contain an explicit `this` equivalent pointer. This would have to be repeated at every single call site. So it's not really helping. You don't need wrapper functions for PIMPL. You can have them if you think it's worthwhile, of course. >Technically you can do the same in C++. But in C++ to make an idiomatic API you need methods instead of free functions, and you can't declare methods on forward-declared classes. Why not? There are good technical reasons why you can't tack member functions into the interface of a forward-declared function. There would be nowhere for that information to go, if nothing else. I think I heard a talk about adding new metaprogramming features to C++ that might address this in like C++26, but anyway it's not a significant problem to simply avoid the problem I think you can probably make some template-based thing that would automate implementing the wrappers for you. But it would be a convoluted solution to what I consider a non-problem. >The C++ committee could address this, but instead they seem to want to pretend separate compilation doesn't exist. (Why are there no official headers to forward-declare STL types, except for whatever happens to be in <iosfwd>?) Most of the STL types that people need are based on templates. It does not make sense to forward-declare those. I just don't see a use case for forward-declaring much besides io stuff and maybe strings. >Then they complain about how annoying it is to preserve ABI stability for the standard library, blaming the very concept of a stable ABI [1] [2], all while there are simple language tweaks that could make it infinitely more tractable! But now I'm ranting. There seems to be a faction of the C++ committee that does not share the traditional commitment to backward compatibility. They have gone so far as to lobby for a rolling release language, which is guaranteed to be a disaster if implemented. I think wanting to break ABI might be a sign of that. Let's hope they use good judgement and not turn the language into an ever-shifting code rot generator. Keep in mind, there may be ABI breakage coming from your library provider anyway, on top of what the committee wants. So it's not necessarily such a cataclysmic surprise as something you're supposed to plan around anyway. ABI stability between language standards is mostly a concern for people who link code built with different C++ standards (probably, a lot of code). It wouldn't be the end of the world if you had to recompile old code to a newer standard, but it might generate significant work.
- bananaboy 3y agoYes but pimpl is really just a hack and workaround for the fact that you can't separate the public interface from the implementation details in C++ due to needing to put everything in the class definition. imho `partial` is superior, as you don't need an additional allocation and indirection.
- Maxatar 3y agoYou don't need any indirection with partial because in C# all classes already go through a layer of indirection. However, consider that in C# if you add a field to a struct, which is a value type and hence no indirection, then you do need to recompile all assemblies that make use of that struct. It's no different than C++ in this regard.
- nrclark 3y agoYou actually don't need to do that at all. It's common style in C++, but the language does not require it. With the right techniques, you can absolutely forward-declare basically all of a class's functionality. Then you can put it into its own translation unit. Members and function signatures have to be declared in the header, but details about member values/initialization and function implementations can absolutely be placed in a single translation unit.
- wakawaka28 2y agoThe data layout of a class must be part of its definition. So either you expose the layout (data members), add a layer of indirection with PIMPL, or resort to ugly hacks to otherwise hide the data layout such as having buffers as members. Another possibility is to not use exposed classes for member functions. Then you can just pass pointers around only and never use C++ features. Out of all of these, PIMPL solves the problem the best.
- nrclark 2y agoYes, that's true. But if the concern is build-times, exposing the data layout is harmless. We don't necessarily need full PIMPL just to get improved build times. By keeping the data layout in the .hpp, you can guarantee that your class can still stack-allocate.
- kevin_thibedeau 3y agoAda had all of C++'s problems figured out in 1983. PIMPL as a means of boosting compiler performance is fundamentally braindead. We shouldn't be bending over backwards with broken tools to make them sort of work.
- wakawaka28 3y agoPIMPL doesn't only boost compiler performance. It provides code-hiding and ABI stability for everyone using it effectively. It's like killing 3 birds with one stone. PIMPL for sure isn't gonna be the thing to convince me that C++ is broken. Ada has piqued my curiosity before but I think if it was as good as you make it sound, it might have at least 1% market share after 40 years. It doesn't. I can't justify the time investment to learn it unless I get a job that demands it.
- iainmerrick 2y agoIt's not PIMPL per se that's the problem, it's that C++ needs it but makes it very awkward to write. It feels like the language is fighting against you rather than setting you up for success. At least that's been my angle in this discussion.
- pjmlp 3y agoUnfortunely "bending over backwards with broken tools" is exactly to what made C++ a success in first place, an idea also adopted by Objective-C and TypeScript. Trying to make the best out of an improved langauge, while not touching the broken tools of the existing kingdom they were trying to build upon. Naturally such decision goes both ways, helps gain adoption, and becomes a huge weight to carry around when backwards compatibility matters for staying relevant.
- Maxatar 3y agoNo, maybe the acronym is ugly, sounds like pimple? But other than that the use of the technique is invaluable to writing stable ABIs which in turn makes distribution of C++ libraries a lot easier.
- bun_terminator 3y agoI have never used or seen pimpl in my life, and c++ is all I do
- MathMonkeyMan 3y agoDoes somebody using my "class Foo" really need to know everything about its "std::unordered_map<std::string, std::unique_ptr<NetBeanFactory<RageMixin, DefaultBowelEvictionPolicy>>>" data member? No, they need know only the size and alignment of "class Foo". Unfortunately, in C++, either a client has to see every type definition recursively down to the primitives, or you give them an opaque pointer and hide all of the internals in the implementation (all they know about "sizeof(Foo)" is "it contains a pointer to something on the heap, probably"). edit: Ok, there's also the copy constructors and other possibly auto-generated value semantic operations, but pretend you've defined those explicitly, too.
- gpderetta 3y agoYou can also provide pointers to base classes, but, unless you do some heroics, you are forced to use virtual functions.
- bun_terminator 3y agoI know what pimpl is - I relearn it before every job change for interviews. But I've never seen it in use, and don't see a use for it. Compile times are rarely an issue in my experience. At least not big enough to warrant something that extreme. I like to see things simple: I need something, so I include something. Compile times are really orthogonal to that, and mostly a job for compiler devs, hardware people, modules or whatever. Changing my code because of compile times seems pretty harsh
- Liquid_Fire 2y agopimpl is not for improving compile times (although it can help with that). It's for maintaining ABI compatibility by keeping your implementation details out of public headers.
- iainmerrick 3y agoYes! Why should I need to do extra work, messing up runtime performance by adding a pointer indirection, just to improve compilation time a bit? In C, idiomatic C, you can forward-declare a struct and the functions that operate on it, and you don't need any indirection at runtime. C++ has plenty of nice features, and in general I'd reach for it rather than C, but for some reason it can't do that!
- Maxatar 3y agoThis doesn't make much sense. Sure you can forward declare a struct and functions that operate on it, but you can't call the function or instantiate the struct without the definition. That's no different than in C++. The purpose of PIMPL is that you can actually call the function with a complete instantiation of the struct in such a way that changes to the struct do not require a rebuild of anything that uses the struct. It's not about just declaring things, it's about actually being able to use them.
- iainmerrick 3y agoI'm thinking of this kind of declaration in C: typedef struct foo Foo; extern Foo* foo_create(); extern const char* foo_getStringOrSomething(Foo* foo); That's a fully opaque type, and it's reasonably efficient. The one thing you can't do store a Foo on the stack, because you don't know its internal size and layout. So it's always a heap pointer, but there's only one level of indirection. In idiomatic C++ I think you'd have something like: class Foo { struct impl; std::unique_ptr<impl> _impl; std::string getStringOrSomething(); } If I have a pointer to a Foo, that's two pointer indirections to get to the opaque _impl. So, okay, I can store my Foo on the stack and then I'm back to one pointer. But if I want shared access from multiple places, I use shared_ptr<Foo>, and then I'm back to two indirections again. The idiomatic C++ way to avoid those indirections is to declare the implementation inline, but it make it private so it's still opaque. But then you get the exploding compile times that people are complaining about on this thread. The C approach is a nice balance between convenience, compilation speed and runtime performance. But you can't do this in idiomatic C++! It's an OO approach, but C++'s classes and templates don't help you implement it. C++ gives you RAII, which is very nice and a big advantage over C, but in other respects it just gets in the way. Edit to add: now I look at this, Foo::getStringOrSomething() will always be called with a pointer (Foo&) so it will always need a double-dereference to access the impl. Unless, again, you inline the definition so the compiler has enough information to flatten that indirection. I don't see how that pImpl approach can ever be as performant as the basic C approach. Am I missing something?