Live data from Hacker News

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

mariusbancila.ro

51–60 of 122 posts

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

#51
Sadly, you can't easily do the full pimpl idiom in C++.

The pimpl idiom is a C idiom where a header declares an opaque structure and prototypes of functions that take pointers to that structure. In C the OP example would look something like

    // widget.h
    typedef struct Widget_t Widget; /* opaque! */

    Widget* Widget_Create(const string* pName);
    Widget* Widget_Clone(Widget*);
    void Widget_Destroy(Widget*);

    void Widget_click(Widget*);
    int Widget_clickCount(const Widget*);
    const string* Widget_label(const Widget*);

    // widget.c
    struct Widget_t {
        int clicks;
        string *name;
    };
    // ... implementations of the functions from the .h ...
In particular, in C `Widget` directly has `clicks` and `name` as fields.

But in c++ we like to use methods on objects, and in order to do this, you need the class declaration in scope, which means your current compilation unit needs to have seen all of Widget's data members. In practice this means if you try to use "pimpl" in C++, you do something like the OP where there is a pointer to an opaque type inside your class.

However, this is not the same thing. Methods are called with a `this` pointer, which means every access to the internal structure adds a second pointer dereference. This is why this isn't the true pimpl -- it wastes an extra deref on every access.

You can get true pimpl in current C++ but it's a lot of boilerplate and heavily relies on compiler inlining. An implementation of the example from the OP: https://godbolt.org/z/6EznxeG1n . In practice this is too much work, hard to read, and so nobody does it.

For the c++ standards committee: please add an "opaque class" feature where the class can only define non-virtual method prototypes. Then the full class declaration, in the associated cpp file, could include its parent classes, actual data layout, and function implementations.

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

#52
post #50
post #23

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.

But if the pimpl size grows too big, you're forced to break ABI? And before it grows too big, it wastes memory. For your use cases it may not matter, and the saved pointer indirection may be more important, but maybe the person who has a million item vector of objects doesn't appreciate a 300% "just in case" memory overhead. The overhead may also hurt cache hits. If you're doing this to save the pointer indirection,…

"Breaking ABI" isn't an issue unless you can't compile your code anymore. It's pathetic that C++ has been so hamstrung over ABI that we're willing to stop improving.

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

#53
post #6

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

The gotcha is that this is a 90s C pattern, and software that actually needed this has been written for 3 decades by now

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

#54

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…

[deleted]

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

#55
post #51

Sadly, you can't easily do the full pimpl idiom in C++. The pimpl idiom is a C idiom where a header declares an opaque structure and prototypes of functions that take pointers to that structure. In C the OP example would look something like // widget.h typedef struct Widget_t Widget; /* opaque! */ Widget* Widget_Create(const string* pName); Widget* Widget_Clone(Widget*); void Widget_Destroy(Widget*); void Widget_clic…

Completely agreed, but in fairness to the c++ standards committee, this is solved from a standards perspective by c++ modules

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

#56
post #28

Earlier quoted context omitted.

Yes. If anything, this is taking a complex yet common idiom and making it simpler.

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 decreases complexity only if you migrate all of the existing code, which nobody ever does.

In cases where I assume that enough test coverage exists, I simplify code that I am currently working on or which I need to read very often.

This way I have already replaced a lot of for-loops by range-based for-loops. It helps me to understand code faster.

But code parts that noone needs to touch or see do not need to be more readable.

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

#57
post #15
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.

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

Yes, it is a good fit when a customer is supposed to use some functionality one has implemented but shall not see the implementation.

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

#58
post #51

Sadly, you can't easily do the full pimpl idiom in C++. The pimpl idiom is a C idiom where a header declares an opaque structure and prototypes of functions that take pointers to that structure. In C the OP example would look something like // widget.h typedef struct Widget_t Widget; /* opaque! */ Widget* Widget_Create(const string* pName); Widget* Widget_Clone(Widget*); void Widget_Destroy(Widget*); void Widget_clic…

A similar opaque pointer pattern with member functions is possible to do with inheritance.

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

#59
post #4

Earlier quoted context omitted.

where "more and more complex" do u see in this article? This is a basic C++ idiom, which constantly used by developers

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…

Part of the problem is that features are not added fully

This here should not need to happen:

> document that moved-from objects cannot be used

It's moved from, so of course it cannot be used. Shouldn't have to add an assertion in every method to guard a fundamental invariant.

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

#60

Oh god, what monstrocity have we created?!? All this complexity follows unique_ptr and copy constructor madness. Anything with pointers with ownership should never be copied - period. Reference pointers - OK if scope/lifetime is known. Can we have c++11 lite?

So data structures like std::vector should never be copied - period?
Post reply on HN