9 ms·
I 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 h
by bananaboy 2y ago
I 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 2y 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 2y 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 2y 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 2y 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 2y 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 2y 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.