Boost logo

Boost :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2024-05-10 15:04:51


Niall Douglas wrote:
> Of my two papers remaining, one has a pretty good chance of making it
> (path_view). The other has been repeatedly and publicly declared by multiple
> people as one of the finest library designs in C++ they've ever seen (std::error)
> but WG21 can't get over there being virtual functions in there. After three
> years of broken promises that WG21 will "do something" about ABI
> guarantees in libraries so the virtual functions can be replaced with something
> else, they just keep circling the drain about "virtual functions be bad".
>
> So maybe that will die too, we have three meetings left to find out. I'm of the
> opinion that carefully designed virtual functions are nothing like as bad as vast
> quantities of templates solidifying ABI. We continue to standardise vast tracts
> of new templates repeating the std::regex mistake. Yet a virtual function -
> they're somehow _worse_ - and I'm sorry, I just don't get it. Especially as there
> really is currently no better alternative to virtual functions for what they do,
> whereas most templates can be designed out entirely and WG21 could impose
> a "minimum possible templates" rule going forth if it chose.

Normal use of virtual functions, as in std::error_code/std::error_category case,
makes it absolutely impossible to develop the API within stable ABI constraints.
That's why all my papers about these were rejected. You can't add a virtual
function, because there's no way to reliably know whether the virtual function
is there. (As the object file where the vtable happened to be emitted could have
been compiled against an older C++ standard.)

There is a way to specify things (from the start) which makes API evolution
possible, and it's to add a version number to the interface:

struct error_category
{
    virtual long version() const noexcept { return 201103L; }
    // more virtuals
};

Then in error_code you can do

{
    if( cat_->version() >= 202300L )
    {
        // use virtual functions added in C++23
    }
    else
    {
        // don't
    }
}

Of course we can no longer do this for std::error_category.

Also of course, this has the overhead of twice the number of virtual calls.

I have, in my "things to propose" folder, a very unfinished proposal for
virtual data members, which can eliminate the overhead. But in any case,
you have to put the version in the spec when the interface is first
standardized, and so far LEWG/LWG haven't really displayed interest in
the "how we can specify our interfaces in a manner that would make it
possible to evolve them under ABI stability constraints" question.

(Or if they have, I don't know about it.)

There's actually a way in which we can evolve std::error_category, as it
stands today, and it's to derive error_category2 from it, and then use

{
    if( auto p = dynamic_cast<error_category2 const*>(cat_) )
    {
        // use virtual functions from error_category2
    }
    else
    {
        // don't
    }
}

There's nothing wrong with that _in principle_, but in practice
dynamic_cast is abysmally slow (without a good reason, arguably).


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk