Maintain both versions of a library - latest released standard, and the one under development (e.g. now it will be C++23, C++26)

To followup on the meeting - I would like to start a discussion on having two different visions of same library. I would like to advocate for keeping two versions of the same library (only in case the two versions are different due to changes applied with papers, of course)
Let’s take as an example: std::optional (was accepted in C++17)

First usecase:

  1. Assuming we accept a new paper for c++26, std::optional t will have extended API (allowing T&) for C++26 (due to: “P2988: std::optional<T&>”)
  2. At the point of acceptance - and before C++26 standard is closed (which may take up to 3 years according to latest C++ “train” model)

Question becomes: Do we want to provide both the latest approved version - C++23 (== C++17), AND the C++26 version.

A slightly different usecase:

  1. Let’s say we have a stable feature - containers (on 23)
  2. Now there’s a new C++26 paper: “P3372R2: constexpr containers and adapters” which doesn’t modify API but (potentially) modify the behaviour.

Third usecase:

  1. Say a paper P1234 proposng a “fix” for std::optional C++23 was accepted during C++26 cycle, which was not yet implemented on any official C++ standard lib distro (as, among other things, purpose of the project is to implement things before they make it to the standard)
  2. This will create a C++23 std::optioal, which is not the same one in the released standard lib versions.

In all cases, I think we’ll gain from maintaining two parallel versions of the source lib (std::optional, in this case) - if any diversion as in the above happens. I think it should be up to two versions - the latest standard released, and the one currently under development (e.g. now it will be C++23, C++26). So - taking above as an example - it will be std::optional 23 with the fix in P1234, and std::optional 26 with <T&> and the constexpr changes under development.

Main reasons as I see them:

  1. We have a reference for comparison for banchmarks and API
  2. We have a “stable” version (the one from previous cycle), which still have latest fixes not yet implemented in major standard lib releases.

(meeting attendees: @neatudarius @dsankel @Jeff-Garland @dietmarkuehl @river @RaduNichita)

Happy for input from all, of course!
WDYT?

Agree, but I’d propose that this be controlled by feature test macros and c++ version flags instead of forking the entire library to a new repo. This is easier for me as the user. Let’s focus on the optional scenarios as I think the other one is handled here as well. So laying it out we have:

C++ Version Feature optional version id
17 Pxyz Add optional beman_lib_optional = 0x0
23 Pxyz Monadic optional beman_lib_optional = 0x1
26 Pxyz optional ref beman_lib_optional = 0x2
26 Pxyz optional range beman_lib_optional = 0x4
29 Pxyz optional ref ref beman_lib_optional =0x8

I can download a single optional library and opt in to the features as I choose. By default if I select beman_cxx_version = 26 I’ll get 0x4. If I select 17 I’ll get 0x0. If we do the versions right we can probably allow a user to select an individual feature (hence the xor ready version names). (As another aside, it would be nice to do this with consteval variables instead of macros, but that maybe not work - tbd).

Anyway, that’s the basic idea.

As discussed previously, making these decisions on a case-by-case basis with some guidance will allow us to balance user experience, allow for proposal comparison when necessary, and other factors.

I support Jeff’s suggestion, but I worry about the complexity of having so many different feature combinations. I think 99% of our use cases would be addressed by having two build configurations: stable and unstable. “stable” provides all features that have been standardized, but no more. “unstable” provides all features that have been standardized as well as proposals that have not been standardized yet.

My concern about this is that I think it confuses the other states – like Production Ready. What does ‘unstable’, ‘production ready’ mean? Where saying c++29 implicitly says – not standard yet.

I was just wondering about how the Beman Project process would work with the “work in progress” code.

My comments I am basing on the David Sankles Beman Project presentation, timestamp 23:22.
There the project starts work only after a document has been submitted.

Feature test macros, C++ version flags and separate repos - have been mentioned.
These are a good idea, especially as “something” is required so that a programmer can verify with which version they are working (especially if they are working with multiple versions).

In the case of not exploding the repo “space”, guidelines for branches and release versions could be considered.

I believe a basic assumption should be questioned: should the projects have a main branch? I believe not.

In my opinion, official branches in a repo should be named (something like):

  • Release-Cpp20
  • Release-Cpp23
  • Release-Cpp26
  • Release-Cpp29

These branches would be closely following the working copy/published version of the C++ Standard.

The reason I think this, is I don’t see in the standard, that libraries are backward compatible.

A new library starts out by targeting the current C++ version the committee is working on. If it misses the train, then it is branched into the next version.

This is based on the history of the Fundamentals TS, where after every C++ version the document had to become updated, due to the the changed requirements of C++.

Other official branches names should be reserved from working papers numbers or issues numbers.

  • P0052
  • P0052v0
  • P0052v1

These versions would hold (if there are any) alternatives to the release version.

Doing a diff between the Release branch and the paper version branch can easily show the differences for review.

Considering conditional compilation depending on C++ version - I believe this will not be benefitional when reviewing by the C++ committee.
This puts unnecessary cognitive load on the persons reviewing, which can be removed completely by maintaining the different versions for different C++ standards.

Interesting idea, although I think that even if adopted that doesn’t preclude a main branch which would reflect the latest proposal. Also, keep in mind that not all libraries will have this dynamic – they will be one standard and done – so for them it would be a burden to maintain a bunch of branches. Tags on the other hand might be a trivial way to manage this – and for those libraries that need the branches, the tags would go on those version specific branches.

Tags can do the same of course.

Code review, what you say? As one of the instigators of this project I assure you that most of the actual code review needs to happen prior to a library arriving at the committee. In part this is why this project exists – the committee, in my opinion, is terrible at actual code review. Not because the group is incapable – quite the contrary. It’s a matter of time and how the committee works. Frankly at the committee level I’d be thrilled to achieve the level of being able to review what the design really means for users – which is mostly being able to trivially get access to the reference implementation to experiment with.

All the above isn’t really a counterpoint to your suggestion, but more of an FYI of the realities on the ground.