Live data from Hacker News

The PImpl idiom and the C++26 std:indirect type

mariusbancila.ro

31–40 of 122 posts

Re: The PImpl idiom and the C++26 std:indirect type

#31
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 StatisticsTracker , and then class TrackedWidget which uses that as a mixin, i.e. inheriting both Widget and StatisticsTracker. 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 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

Re: The PImpl idiom and the C++26 std:indirect type

#32
post #20

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.

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…

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.

Re: The PImpl idiom and the C++26 std:indirect type

#33

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.

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.

Re: The PImpl idiom and the C++26 std:indirect type

#34

Earlier quoted context omitted.

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…

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 year…

> But many people don't have that much time to learn the new features

Because they spend so much of their time struggling with the pain points of the older code.

> but can't intuitively understand what it's doing

For (most?) new vocabulary types, it is rather intuitive to understand what they do. optional, variant, indirect - you may not remember the details by heart immediately, but you get the general idea and expect that they would behave in some reasonable way. And mostly, they do. That's not to say they're perfect: I feel like vomiting looking at std::variant's and how you have to work with them, as opposed to a proper case classes / algebraic union types in the language itself. And yet - when someone puts one in their class, instead of a bunch of code in a bunch of methods, you know what's going on. It does "read like a different language" somewhat, and that's good. The nicer language has been struggling to get out, as the saying goes.

Re: The PImpl idiom and the C++26 std:indirect type

#35
post #5

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.

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.

We put

    #define WIN32_LEAN_AND_MEAN 1
    #include 
In precompiled headers to solve that particular problem.

Re: The PImpl idiom and the C++26 std:indirect type

#36

> 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.

The C++ core guideline support library has it.

https://github.com/microsoft/GSL/blob/main/docs/headers.md#u...

What do you mean by move-out unique_ptr? That the not_null ptr type would ne null after it's been moved?

In that case that's just a plain usage error, same as how you could memset it to null.

Re: The PImpl idiom and the C++26 std:indirect type

#37

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 kee…

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

This only works if it's always the same value. This doesn't work if the label is for example, set to `std::to_string(clickCount())`

Re: The PImpl idiom and the C++26 std:indirect type

#38
post #35

Earlier quoted context omitted.

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.

We put #define WIN32_LEAN_AND_MEAN 1 #include In precompiled headers to solve that particular problem.

That's kind of a hack, still best only used in implementation files, not headers.

Re: The PImpl idiom and the C++26 std:indirect type

#39

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 kee…

This is just a simple example, therefore nitpicking on the semantics of the Widget methods is a bit silly.

> 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:

The blog post explicitly explains why this doesn't work. You have to define these methods in the source file because they need to see the definition of the Impl struct.

Re: The PImpl idiom and the C++26 std:indirect type

#40
post #18
post #16

Earlier quoted context omitted.

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.

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.

Well that's a lie. I've long been back to raw pointers and it's by far the easiest way to do it. All of Pimpl, unique_ptr, and whatever other clever mechanism (I'm not even looking at std::indirect anymore) just aren't really ergonomic.

Nobody needs "deep copying", ever. It's not even well defined what it should mean (i.e. how deep etc.). It's purely a theoretical problem with no good practical (one-fits-all) solution. The only practical way is to copy what you need copied, when you need it. Done.

Post reply on HN