Core: Bump C++ Standard to C++20#100749
Conversation
|
Has the concerns with support for c++20 on niche compilers like those for various console ports been resolved? That has been the main thing holding this back other than just the workload, we really don't want various porting providers running afoul of this |
|
I'm not certain if that's been resolved, and I certainly wouldn't want this merged before that was the case either. |
|
Absolutely, generally absolutely in favor of adding various new QOL features from C++20, brought it up in the platforms channel and we'll see what information we can dig up |
494f436 to
078c644
Compare
078c644 to
257e648
Compare
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
257e648 to
a758194
Compare
This comment was marked as outdated.
This comment was marked as outdated.
a758194 to
fa4450f
Compare
|
I want to throw my support behind the concept of moving to c++20, in particular it will give us access to: https://en.cppreference.com/w/cpp/utility/source_location Which will allow us to instrument our builds better, allowing us to log where our error macros happen. So instead of getting a message that p_index is out of range in cowdata, we could potentially log where the actual call that went out of index happened instead. It'll take a bit of doing, but I think that that alone is worth the upgrade. |
|
Oh wow, that looks extremely useful! The header looks pretty lightweight, similar to |
Maybe we should aim low for this initially then. Do your first 2 commits as separate PRs, at that point we technically have a C++20 codebase, we can then start using I think there's things in this still that are more controversial than just the switch to a newer standard. |
|
After some local testing, it'd definitely be more involved than this PR should reasonably change. While using it as a drop-in replacement for |
That's what I'm already doing tbh. Everything else I've tried to dig into has felt like it goes beyond the scope of drop-in QOL bumps, so the changes remained conservative (albeit wide-reaching). I could maybe see myself reverting the three-way comparison commit if people are THAT skeptical (even if its inclusion is an inevitability), but none of the others strike me as intrusive or contentious. |
Oh yeah, it is. I did say "It'll take some doing", but because it is a type it can be put in template parameters, so we can schlep it around to the macro site. I wasn't suggesting we do it in this PR, I just think having C++20 is a prerequisite to starting doing it! |
|
As im currently working on refactoring Variant, i feel like concepts from C++20 would be extremely helpful allowing us to more easily register them and remove clutter. I'm imagining a system that would only require you to modify the class itself and everything else would handle itself. |
d6c9b7a to
382f1e4
Compare
|
Since we're apparently using this thread to add to the c++20 wish list:
Probably more that I forgot :) |
|
From the core meeting: We're requesting reviews from the people assigned. If we approve we will merge this for 4.5. Main questions are around the effects on the C# bindings and GDExtension compatibility. |
There was a problem hiding this comment.
Looks great! I can't find anything to be sceptical about - you've really boiled this PR down to its essentials (except the equality changes, I guess. But that's mostly code removal.)
One thing caught my eye - I'm seeing you adding a few equality comparisons that weren't there previously. Is this because automatic conversions are no longer applied when you're removing the operator!= versions?
core/variant/variant.h
Outdated
| bool operator!=(const Variant &p_variant) const; | ||
| bool operator<(const Variant &p_variant) const; | ||
|
|
||
| // FIXME: Starting with C++20, the above equality operator is no longer sufficient for comparison |
There was a problem hiding this comment.
Could you quickly explain what was happening earlier, and why it's no longer possible in c++20?
There was a problem hiding this comment.
The short version is implicit conversion operators have finally bitten us in the ass
The long version is that C++20's way of adding new overloads for comparison operators means that there's now a lot more room for ambiguous comparisons. In particular, the synthesized comparisons mean that multiple ways of handling an implicit conversion can occur. This is surprisingly difficult to articulate, but a somewhat straightforward example reveals itself when trying to compile without these operators:
platform\windows\display_server_windows.cpp(2970): error C2666: 'Variant::operator ==': overloaded functions have similar conversions
D:\Godot\godot\core/variant/variant.h(816): note: could be 'bool Variant::operator ==(const Variant &) const'
D:\Godot\godot\core/object/ref_counted.h(104): note: or 'bool Ref<Resource>::operator ==(const Ref<Resource> &) const' [synthesized expression 'y == x']
platform\windows\display_server_windows.cpp(2970): note: while trying to match the argument list '(const T, const Ref<Resource>)'
with
[
T=Variant
]
platform\windows\display_server_windows.cpp(2970): error C2666: 'Variant::operator ==': overloaded functions have similar conversions
D:\Godot\godot\core/variant/variant.h(816): note: could be 'bool Variant::operator ==(const Variant &) const'
D:\Godot\godot\core/math/vector2.h(160): note: or 'bool Vector2::operator ==(const Vector2i &) const' [synthesized expression 'y == x']
D:\Godot\godot\core/math/vector2.h(247): note: or 'bool Vector2::operator ==(const Vector2 &) const' [synthesized expression 'y == x']
platform\windows\display_server_windows.cpp(2970): note: while trying to match the argument list '(const T, const Vector2)'
with
[
T=Variant
]
The relevant code in question:
void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
_THREAD_SAFE_METHOD_
ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
if (p_cursor.is_valid()) {
RBMap<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
if (cursor_c) {
if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
...In all likelyhood, we've probably been dodging several ambiguous bullets on live and/or are already footgunning ourselves without even realizing it. This entire setup will need a more thorough review, likely accompanied with a re-evaluation of the implicit conversion of Variant, but this is very much a stopgap solution to maintain the same comparison functionality as llive.
| _FORCE_INLINE_ bool operator!=(const GCHandleIntPtr &p_other) { return value != p_other.value; } | ||
|
|
||
| // FIXME: C++20 doesn't allow aggregate initializers to bypass deleted constructors. | ||
| // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1008r1.pdf |
There was a problem hiding this comment.
This should be tracked with a follow-up issue. I assume not deleting it won't cause problems in the meantime?
There was a problem hiding this comment.
It will not cause problems, no. This was relying on a quirk of pre-c++20 construction that bypassed a deleted constructor with {} initialization. In practice, this class is only used internally, and making it truly closed off could still be done if desired via friend constructors or a static method, but that felt outside the scope of this PR.
It's swapping out a "secondary" comparison operator for a "primary" one. C++20 makes synthesized operators from the primary ones, so it achieves the same result as before but allows for equality operation as well. A lot of this is admittedly more bloated than it should be, as many of the changes in C++20 were made with utilizing defaulted comparison operators & three-way comparison |
|
Revisited the structure after the core meeting, and managed to significantly decrease the number of changed files/lines. To ensure C++17 compatibility, there's no more removed operators; only additional operators to account for the ambiguous cases in C++20.
I don't believe that there's any real way to circumvent the Variant operator bloat, as it otherwise requires a VERY invasive number of changes. As the comment notes, it's an area that can be revisited with a more general pass later. |
dsnopek
left a comment
There was a problem hiding this comment.
This looks good to me! There's nothing that seems particularly dangerous or problematic for the areas of the engine I usually work in
Ivorforce
left a comment
There was a problem hiding this comment.
I appreciate that you got it even more minimal!
raulsntos
left a comment
There was a problem hiding this comment.
I tried compiling this branch and I'm getting these warnings:
modules/mono/csharp_script.cpp:1110:59: warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'GCHandleIntPtr' and 'GCHandleIntPtr') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
if (!r_gchandle.is_released() && r_gchandle.get_intptr() == p_gchandle_to_free) { // Do not lock unnecessarily
~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~
modules/mono/mono_gd/../mono_gc_handle.h:48:22: note: ambiguity is between a regular call to this operator and a call with the argument order reversed
_FORCE_INLINE_ bool operator==(const GCHandleIntPtr &p_other) { return value == p_other.value; }
^
modules/mono/csharp_script.cpp:1112:60: warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'GCHandleIntPtr' and 'GCHandleIntPtr') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
if (!r_gchandle.is_released() && r_gchandle.get_intptr() == p_gchandle_to_free) {
~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~
modules/mono/mono_gd/../mono_gc_handle.h:48:22: note: ambiguity is between a regular call to this operator and a call with the argument order reversed
_FORCE_INLINE_ bool operator==(const GCHandleIntPtr &p_other) { return value == p_other.value; }
^
modules/mono/csharp_script.cpp:1120:55: warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'GCHandleIntPtr' and 'GCHandleIntPtr') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
if (!gchandle.is_released() && gchandle.get_intptr() == p_gchandle_to_free) { // Do not lock unnecessarily
~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~
modules/mono/mono_gd/../mono_gc_handle.h:48:22: note: ambiguity is between a regular call to this operator and a call with the argument order reversed
_FORCE_INLINE_ bool operator==(const GCHandleIntPtr &p_other) { return value == p_other.value; }
^
modules/mono/csharp_script.cpp:1122:56: warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'GCHandleIntPtr' and 'GCHandleIntPtr') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
if (!gchandle.is_released() && gchandle.get_intptr() == p_gchandle_to_free) {
~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~
modules/mono/mono_gd/../mono_gc_handle.h:48:22: note: ambiguity is between a regular call to this operator and a call with the argument order reversed
_FORCE_INLINE_ bool operator==(const GCHandleIntPtr &p_other) { return value == p_other.value; }
^
However, it finishes building and everything appears to work as expected.
Interesting that didn't cause warnings on our CI. Still, that's probably caused by the equality operators in that class not being |
raulsntos
left a comment
There was a problem hiding this comment.
Not getting any warnings now, and I see no problems with this for the .NET area.
Calinou
left a comment
There was a problem hiding this comment.
Tested locally on Windows 11 24H2 with MSVC 2022, it works as expected. I've also tested an Ubuntu 22.04 Docker container and Godot can still be built.
Note that this makes it no longer possible to compile Godot on Ubuntu 20.04 (unless you install a more recent GCC/Clang through third-party repositories), but 20.04 is EOL since April 2025 anyway.
Code looks good to me.
Some notes:
- Is MSVC 2019 tested (on its latest update) to make sure it works? I don't have it installed, but I don't remember if it has full C++20 support.
- Using Apple's Clang, is it still possible to compile on old macOS versions like 10.15 or 11? We still support 10.15 currently, although only with the Compatibility renderer. It's not a big deal if it can't be compiled on 10.15 anymore, as long as binaries compiled on newer macOS versions still work there.
|
I've been using this branch (its various incarnations) almost exclusively for around 6 months. Mostly targeting macOS and Android. Re: macOS, I haven't changed anything from the defaults, the resulting binaries are currently minimum macOS 11.0: macOS 11 was released just over 5 years ago and can be installed on devices that are over 10 years old (https://support.apple.com/en-au/103111). Obviously this isn't my call to make, and I don't know what the current minimum version is, but requiring macOS 11 seems reasonable for a runtime constraint. Not exactly sure about the minimum macOS version required to build. I believe |
Is it ARM64 or x86-64 binary? For ARM64 minimum version Godot targets is 11.0, since it's the first OS release on Apple Silicon, for x86-64 it's 10.13 (but in support is only partial, only Vulkan won't work on it or 10.14).
Godot uses macOS 15 features, so min. version of Xcode required to compile it is 16.0 which runs on macOS 14.5. |
|
That was an arm64 binary. I've just built for x86_64. I don't really have a good way to test it unfortunately. I've tried to dust off (literally) my old Intel Macbook 15, but apparently it won't boot 😅 However, all indications are the build is at least trying to target 10.13: Looks like my MoltenVK is built for 10.15. |
This comment was marked as off-topic.
This comment was marked as off-topic.
bruvzg
left a comment
There was a problem hiding this comment.
Tested various builds, and did not encounter any issues. Code looks good to me. Is there anything that still blocking this?
In contrast to the above PR, which attempted to simultaneously support C++17 alongside later standards, this PR aims to make our baseline C++20 outright. The immediate benefit is removing the need for several workarounds via macros &
#if __cpluspluswrappers, making for a much cleaner codebase overall. The much, much bigger benefit is the freedom to actually integrate C++20 features, allowing this PR to satisfy our contribution Best Practices & not exist as a solution looking for a problem.EDIT: After a core meeting, it was instead decided that integrations of features would be best handled in entirely separate PRs, so that we can pick-and-choose which features are best to integrate. While that dampens the effect of this PR in isolation, it's now doing so with the knowledge that it will not exist in isolation and would soon be followed with proper integrations of the new features that the repo would directly benefit from. Future edits of this OP will point to specific discussions/implementations of said features up until this is merged.