Back to News
Advertisement
Advertisement

⚡ Community Insights

Discussion Sentiment

50% Positive

Analyzed from 1379 words in the discussion.

Trending Topics

#pimpl#std#class#more#used#widget#indirect#code#need#language

Discussion (37 Comments)Read Original on HackerNews

amluto•about 1 hour ago
I have a little class called EImpl that is kind of like std::indirect except that it embeds the impl instead of pointing to it. It takes three template parameters: an embedded struct, a size and an alignment. It static_asserts that the embedded struct fits in the size and alignment, and it embeds it with approximately zero overhead. It’s about as easy to use as any other pImpl technique.
Panzerschrek•about 1 hour ago
> Never null: it always holds a value, except in the moved-from state

I am wondering why C++ can't implement "non-null" unique_ptr version in the same way? As I know, that the main argument against implementing it is, that it's can't be done, since move-out unique_ptr still can be null.

3form•about 2 hours ago
This looks great indeed - I wonder if there are any particular gotchas, though, as things often are in C++next land.

With many of the features coming into the language over time, I kinda wish that a bit more restricted subset of it eventually becomes a thing, but I know in practice it might as well be a completely different language. That, and I expect that still many other things have not been resolved as well as they are elsewhere, such as build system and dependency management (although I haven't touched this stack for a while now, so I would love to be surprised).

dingaling911•about 1 hour ago
"Holds a value, except sometimes"
zabzonk•about 2 hours ago
Hmm. Do people use PIMPL that much (I have used it, but rarely) that we need std library support (and testing, documentation, understanding)? Just asking.
dvratil•about 2 hours ago
It's often used in libraries where you need to guarantee ABI compatibility. Fixing a bug or implementing a feature may require adding a new member into the class, which would change its size (thus break ABI compatibility). PIMPL is the typical solution here, since the inner/impl class is not part of the public ABI.

I also like to use it sometimes to "hide" private methods and their documentation into PIMPL, so the public header is kept clean.

zabzonk•about 2 hours ago
> PIMPL is the typical solution here, since the inner/impl class is not part of the public ABI.

Yep, that's what I've used it for. Didn't find it too difficult to implement it myself, but I guess every bit of convenience/bug avoidance helps.

flohofwoe•about 2 hours ago
This std::indirect thingie looks more like a general helper for any data 'dangling off' an object, not limited to pimpl.

Not sure how much pimpl is used in reality, but it's a pretty ok solution to speed up build times (apart from unity builds), because it avoids having to include headers that are only needed for the private state into the public interface header.

feverzsj•about 2 hours ago
Yes, if you actually care compile times.
RossBencina•about 1 hour ago
Indeed. I primarily used PIMPL when I want to avoid polluting public header files with implementation detail #includes in cases where forward declarations are impossible or unwieldy and inline methods are irrelevant.
otabdeveloper4•about 1 hour ago
Lucky for you, I don't.
einpoklum•about 1 hour ago
My approach to reducing the compile time of code which uses a class is moving the functionality out of the class and into standalone functions; or at least moving the method definitions into a non-header `.cpp` file.
green7ea•about 2 hours ago
I remember using it all the time for the Windows headers because they pollutes the compilation unit like you wouldn't believe — the rule was to only include them in c/cpp files.
maccard•32 minutes ago
We put

    #define WIN32_LEAN_AND_MEAN 1
    #include <windows.h>
In precompiled headers to solve that particular problem.
neonz80•about 2 hours ago
They didn't add PImpl support, they added std::indirect which can be used for PImpl among other things.
seanhunter•about 2 hours ago
Back when I used to write C++ it was used all over the place. Admittedly that was a log time ago.
jeffreygoesto•about 1 hour ago
How you doin' fellow Qt-kids? ;)
Yomguithereal•about 1 hour ago
Let's pop the PImpl!
einpoklum•about 1 hour ago
The example is problematic, in that:

1. click() should not be a member of the widget. A widget does not click; a user clicks a widget. A click can change a widget's state, but the state might change because of other effects, e.g. pressing a key when the widget is focused. But then, that's just one of the issues with treating UI widgets this way.

2. More to the point - clickCount. If this is a button, it shouldn't keep a record, or aggregate, of its clicks within it; and if it's a widget where this does really matter, like a range control where more clicks mean a value that goes farther along the range - you still would not keep the count of clicks, but the current position. Statistics about the interaction with an object should not be part of the object itself. At most it might be legitimate to have, say, a Widget class, a template like <class Stats> StatisticsTracker , and then class TrackedWidget which uses that as a mixin, i.e. inheriting both Widget and StatisticsTracker<ClickStats>. And that's already stretching it beyond what I would find reasonable.

3. Having something named is another aspect of objects which may be a good fit for a mixin class.

Anyway, an 'indirect' type for objects you don't know the definition of sounds nice.

A few more nitpickis about the example:

1. Instead of explicitly applying the rule-of-0 with `= default` for the copy&move ctor&assignment and the destructor - just _don't_ write anything:

    class Widget
    {
    public:
        void click();
        int  clickCount() const;
        std::string label() const;
    private:
        struct Impl;
        std::indirect<Impl> pimpl_;
    };
and that's the beauty of the rule of 0.

2. Why return an std::string for the label? The label() method should return an std::string_view

shevy-java•about 2 hours ago
C++ is getting more and more complex. It used to be said that people use only a small percentage of it when writing C++, but I am beginning to think that the cake is a lie here.
pjmlp•about 1 hour ago
Besides being a common idiom, for how many warts C++ might have, no one is rewriting LLVM, GCC, V8, JVM/ART and .NET runtimes, CUDA/Metal/DirectX, Unreal, Godot,.... into something else, RIR is not happening there.

People will contend themselves with "C++ the good parts", helped by clang-tidy, PVS, MSVC analyse, and move on.

feelamee•about 2 hours ago
where "more and more complex" do u see in this article? This is a basic C++ idiom, which constantly used by developers
seanhunter•about 2 hours ago
Yes. If anything, this is taking a complex yet common idiom and making it simpler.
usrnm•about 1 hour ago
Is it actually simpler, though? The unfortunate reality of this world is the fact that C++ is not the latest standard of the language or the newest shiny library, it's all of them at the same time. Adding a new way of doing the same thing can decreases complexity only if you migrate all of the existing code, which nobody ever does.
dvratil•about 2 hours ago
I think the parent's point is that we started with raw pointers to implement PIMPL, then we had std::unique_ptr, and now we have std::indirect. So there are now three different ways how PIMPL can be implemented, each has its gotcha's and subtle differences that one needs to keep in mind. In large codebases you will now have to deal with all three solutions being used, depending on how old the code is.
gblargg•about 1 hour ago
The point of each improvement is fewer easily-made errors. Having implicit deep copying handled avoids lots of errors with manually implementing it the oldest way.
konstmonst•about 2 hours ago
std::indirect looks for me like another pointless c++ thing that already works with forward pointer declaration. You can add it to another ton of pointless things C++ adds without fixing the old ones. The issue with c++ is that it is so big, that everyone uses some kind of dialect of it and the fancier it gets, the less readable it becomes and the more magic happens behind the curtains. A developer of a C++ codebase now has to learn a specific meta language of this codebase. Fuck that, I have enough languages and their idiosynchronies to remember for my work now. After using Go for a pair of years returning to C++ is like coming back to a big archaic mess. I'll just go learn Rust instead and forget all those new useless C++ templates like std::indirect
wwind123•about 1 hour ago
I think it's kind of awkward either way. The standard committee keeps adding new features to the language to address common pain points in the industry. But many people don't have that much time to learn the new features, and hates it when seeing something in the code but can't intuitively understand what it's doing. I once witnessed a 10+ year C++ coder (that had been immersed in some old C++ code base for many years) seeing a piece of C++14 code for the first time -- he said it reads like an entirely different language, not the C++ he's familiar with at all.
coffeeaddict1•about 2 hours ago
This is actually useful, but despite it is another extra thing you will have to remember when reading C++ code. I guess with LLMs things aren't so bad.
skrebbel•about 2 hours ago
Why? It’s still the good (bad) old pimpl pattern. It just got a bit shorter. When reading you dont even need to grok “std::indirect”, you see the word pimpl and you know what’s going on.
einpoklum•40 minutes ago
You need to remember _less_, rather than more, when you use this kind of vocabulary types. Think about std::optional. Before that (and if you didn't write something like it yourself), you had to, for each class, remember the bespoke semantics of when and how it represents the lack of some members, and you would have to have non-defaulted ctors, move assignments and dtors, and then whenever you used that class you would need to think about what those custom method do, which might be different than other classes which have optional members. Now you just tell yourself "oh, it just has an optional member, no biggie". Look at my comment above regarding how short the implementation of Widget becomes when you squeeze the juice from having the rule of 0.
MaPi_•about 1 hour ago
I don't really get why people keep repeating the "C++ is too big" complaint together with the implication that you need to remember the entirety of the standard library. In comparison Java has networking, GUI framework and even MIDI in its standard libraries. Is it because C++ is more closely related to C which library is so small that it barely contains anything useful? I much prefer code that uses a library feature rather than yet another poorly implemented and not documented hand rolled version of it.
dooglius•about 1 hour ago
Networking, GUI frameworks, and MIDI are presumably all self-contained and you would not need to be familiar with them except when working on networking, GUIs, or MIDI files, respectively. This is a general-purpose thing that could show up in any c++ code.