-
-
Notifications
You must be signed in to change notification settings - Fork 21k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Status of C++ in Godot #9694
Comments
This was discussed many times, the usual answers are:
We keep the same way as everything is. |
I got some opinions on private conversations, but I wanted to make the
discussion more public.
I didn't know it had been so much discussed and closed already.
Do you even discard auto, constexpr and such?
Drop 'register'?
El 18 jul. 2017 1:54 p. m., "Juan Linietsky" <notifications@github.com>
escribió:
This was discussed many times, the usual answers are:
1. No desired to move the codebase to anything above 03. Features are
not worth it. For GDNative C++, it's perfectly possible to use it though.
2. No desire to use STL/Boost/Etc. The rationale has always been the
same: a) Godot templates do small things they usualy don't (ie, atomic
refcounts and copy on write) b) STL generates huge debug symbols and debug
binaries due to their extremely long mangled symbols
We keep the same way as everything is.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#9694 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ALQCtipKmepD_1Xw6iRXZ7aGoQlLfiwFks5sPJzqgaJpZM4ObOio>
.
|
I raised this issue it would be safer to use smart pointers and that this code suffers from NIH syndrome, but higher-ups have no intention to upgrade, so I suggest you to also give up. |
It would probably take many manhours to upgrade the code, which devs prefer to spend on implementing new features. I somehow understand this - engine users prefer to get new features than "refactoring" (without understanding stuff like technological debt). |
@Marqin Sorry but smart pointers are a bad idea because:
So, pleaae before arguing that we decide stuff on a whim, just ask. We usually take informed decisions and share the process with everyone. |
What could be easly done if you want to improve quality, is to start writing unit tests (add some optional job to scons for that, and just make dev install gmock/gtest themselves[to not clog ./thirdparty]) |
@reduz, how is unique_ptr with it's explicit ownership and RAII memory freeing already present in the language? Also not only memory management, but also threading gets easier (eg. |
@RandomShaper I'm not against upgrading at some point in the future but, even though there are many useful features, newer C++ versions have a tendency to make the programmer write less explicit code. This results in code generally being more difficult to read by others (typical with auto keyword abuse). The other advantage of not using more advanced features (ig, we don't use exceptions and rtti can be disabled with little cost) is that compilers produce much smaller binaries. |
That's fine. I respect your word. Only I find discussing about this is a healthy thing. I know some big videogame companies (Ubisoft, IIRC) have migrated big code bases to modern C++ so it must be perfectly suited for Godot as well. Of course, as you pointed out, that requires work/time. That's why we could:
In both cases a coding style would probably need to be defined so not to abuse features, as you said it happens with Of course, now there are things with a higher priority, but maybe when 3.0 becomes stable or at some other point in the future it would be good to have decided already. |
@Marqin As mentioned, Godot uses it's own memory allocation scheme for nodes, which makes more sense than anything provided by the new standard libraries. Also lock guard is virtually 3 lines of code and we already have it implemented without using C++11+ Again, why should we assume that anything "standard" 1) needs to be better than what we already have 2) is better if we use it anyway? .I think it's a common phallacy. What's next, dropping our print functions and use ostream/istream? Change our String class, which is pretty awesome, and replace it with the much more crippled std::string or std::wstring? Standard libraries don't serve all purposes and don't work better than everything else just because they are standard libraries. They are just there for those who need them, and it's fine to ignore them and write your own implementations if you have a valid reason to do it. We do and we are confident about it. |
@RandomShaper The problem is that:
It's seriously not worth it at the moment, we can discuss it again a in a few years |
What I think would make sense is to make sure that Godot compiles with options like --std=c++17, so you can easily bring in a library written in modern C++ if you need to. For example, I would vote for the removal of register keyword from the codebase #9691 Somehow related, I remember having read somewhere that gcc-6.3 is not supported (google |
@efornara some third party libraries already require newer C++ versions, and that's fine because Scons takes care of that with cloned build environments. Check the etc2comp thridparty code to see how it works. |
@karroffel Thanks, I didn't know that. It would just be a nice feature then, but it isn't needed for importing a library (though, if for the glue code you need to include more of godot you might stumble across a header file that doesn't compile). By the way, if anyone needs to do something similar and has found this post, the relevant file is: https://github.com/godotengine/godot/blob/master/modules/etc/SCsub . I have found it using grep and it looks like the only place where this is needed at the moment. |
It's not that safe to link c++11 with not-c++11 code - http://gcc.gnu.org/wiki/Cxx11AbiCompatibility |
@Marqin Unless I misunderstand the link, this actually seems to support the case of godot not starting to use components from the Standard Library, but sticking to custom components instead:
Mixing languages looks fairly safe (not nice, I admit, and it might stop to work in the future), but mixing stuff like pairs, vectors, lists, etc... is known to cause problems. |
@efornara The link is about linking some third-party libs that use C++11 with Godot that use C++03. |
@Marqin Yes, but the way I understand the link is that you can do it. What you cannot do is, for example, to pass a std::list<> from godot to the third-party library. |
@reduz where I can find documentation of Godot's internal Ref<>? Where I can find documentation on how Godot internal containers differ from those in STL? |
@Marqin For containers there isn't much: |
Personally I find the new C++ standard quite awesome, but I wouldn't propose any Godot internal refactoring because it would require too much efforts for too little gain. Also one of the backward compatibility is one of the hallmarks of C++ for this exact reason. But still I would like to be able to use it for new Godot features and in GDNative. @reduz As you said C++11/14/17 lets you write less explicit code. Even tough this is a drawback for C++ newbies it is a good thing for C++ power users. |
FYI, I did compile a recent master (godot3) with:
on a debian stretch (gcc-6.3). Annoyingly, the option is also set when compiling C files, so you are flooded with warnings if you enable them, but, apart from that, it all went smoothly. Even the register keyword doesn't seem to cause trouble. I wouldn't go as far as to suggest that the official builds to be compiled this way, but it's good to know that the option is there if you need it in your project. What I would suggest is that any changes that break this are treated as a regression. EDIT: Corrected some typos, making the statement about warnings clearer. |
Did you try with |
@Hinsbart No, I didn't. Maybe there's a way, but since I don't know scons that well, I simply went with what seemed possible without tinkering:
EDIT: By the way, I don't know how it's used in the godot build system, but |
I think |
@m4nu3lf you can use whatever form of C++ you want with GDNative. |
In my experience, any opportunity to delete code is a good opportunity. Perhaps we could setup some sort of wiki page documenting which files could be deleted and replaced with c++11 variants. This would probably include threading primitives and such. Change for the sake of change, is no good (proverbial koolaid), but in this case the LLVM project and many other FOSS projects have moved on in favor of some of the clearer syntax patterns, i.e. the newer for-iterator notation, but also offloading separation of concerns to the respective language runtimes, because (let's be honest) maintaining as little platform specific code as possible is ideal for a game engine. The best code you'll ever write is the code you un-write. :) |
C++11 features that would improve maintainability of the codebase:
C++11 Features that would improve quality of life:
|
|
I don't think we should adopt a modern version for the sake of using new things or because there are 1 or 2 features that can be used. I wouldn't propose using something beyond C++11 because the benefit is not worth it. |
It absolutely shouldn't? Code that is not executed should have no performance impact unless you have a compiler bug or so, and in release it shouldn't be in the final binaries at all? In many cases just having proper move semantics alone increases performance, in some areas significantly, and though thats an older example the updated directives can have also have performance boosts among other features. |
@OvermindDL1 Yes, that's how it should be in theory. |
@lupoDharkael That twitter thread (once it finally loaded, holy wow twitter is a horrible interface... I keep forgetting that...) only talks about compiletime speed because math.h in libstdc++ is larger in C++17 mode (where libc++ does not have that issue) because of a greater amount of overloads and features for better runtime speed so any compile that brings in math.h has an increased compile time of about 300ms if using that specific older stdlib instead of the newer stdlib. It states about nothing about runtime performance (of which I've only seen be faster in higher C++ modes, depending on the features used, identical speed in the worst cases). So as for |
I'll investigate more about it. |
If you have ccache installed, a lot of time in the incremental build process will be spent on linking. You could shave a second or so by using |
Oh definitely, I cannot say enough good for both ccache and ninja as the backing builder (if using cmake or so for example), they both save so much time! Plus unity builds can be surprisingly amazing, that's where you make a new |
To add one to the pile: For example the union Wanted to slap a The other thing is using unique_ptr to ensure proper cleanup on destruction without having to write too much manual boilerplate, and without using unnecessary ref-counting. |
I'd like to offer my 2 (or 20) cents as someone new to Godot and its codebase. I'm currently overseeing and working on the effort to port Battle for Wesnoth to Godot. Now, the front end (the editor and GDScript API) is great! Besides a few rough edges, it's so far allowed us to progress at a good pace. But we also imagined we (the team) would contribute patches for the back end (the engine) at some point. To that end, earlier this week I cloned the git repo and started poking around in the C++, and honestly... I'm a bit dismayed. I do have experience managing a large C++ codebase in the form of Wesnoth's old custom engine. It too started off as C++03, but was modernized to use C++11 and later features and now is C++14 compliant. I spearheaded that modernization effort (often a bit too zealously) and I feel it made our codebase a lot more readable and easier to work with. Granted, I never worked extensively with a purely C++03 codebase; I learned C++ using modern C++ features. But to me, the idea that things like Take for(std::vector<T>::iterator foo = container.begin(); foo != container.end(); ++foo) {} Which is messy! In the cases we actually needed an iterator, we did this: for(auto foo = container.begin(); foo != container.end(); ++foo) {} Yes, now you don't know the explicit iterator type, but you almost never need to know that. I read a comment here some posts up saying it makes it harder if the container is declared some files away, but really, with modern code editors and intellisense that's not much of a problem. In most cases we'd instead just switch to range-for: for(const auto& foo : container) {} Much quicker to type and more concise too. You don't need to worry about dereferencing iterators inside the loop or about keeping track of indices. And it's abundantly clear that you're looping over the entire container. With iterators, people unfamiliar with the code need to double check that the loop is indeed going from beginning to end. Using In the State of Godot video he also mentioned lambdas. Again, it is certainly possible to abuse them, but they're also an incredibly useful tool! Here's a common paradigm I saw in Wesnoth's codebase prior to using C++11: struct sort_helper {
operator()(const T& a, const T& B) {
return a < b;
}
}
void init() const {
std::vector<T> foo;
foo.push_back(T(1));
foo.push_back(T(2));
foo.push_back(T(3));
std::sort(foo.begin(), foo.end(), sort_helper);
} Long, messy, code bloat. And here's what we used with C++11: void init() const {
std::vector<T> foo {
T(1),
T(2),
T(3),
};
std::sort(foo.begin(), foo.end(), [](const T& a, const T& b) { return a < b; });
} That's just the most common case! And yes, I know you can also implement Which leads me to my next point. I've noticed Godot internally uses many of its own custom types instead of the STL ones. If your concern is code readability with regards to things like container.push_back(T(args)); Now, this is inefficient. One of the big problems with Wesnoth's design and one of the major contributing factors to deciding to go with a new engine (in this case, Godot) was that Wesnoth suffered from Not Invented Here syndrome to a large degree. While we did use libraries like Boost, our rendering pipeline was custom, our UI toolkit was custom, our data/scripting language was custom.... you get the gist. Before we started work on the Godot port I tried (and failed) to implement hardware-accelerated rendering (up until this point we had been using CPU-based surface/sprite rendering. In 2019. Yes, I know X_X). SDL's I bring that up since I think it's a good example why implementing everything in-house can be a bad idea. Yes, you might reduce your binary size a bit by not using the standard library, but you also incur a massive maintenance burden. Converting those An even bigger issue is it raises the barrier to entry. I looked at the Godot codebase expecting C++ STL types. Not finding those means I or anyone else now needs to learn exactly which types the engine provides and what API goes along with each. I want About a year ago, as we closed in on Wesnoth's 1.14 release and launch on Steam, I undertook a refactoring project to eliminate a custom container type that was essentially In conclusion, I think outlawing certain very useful C++11 features and sticking with custom types over STL ones will be a hindrance for Godot in the long term. I know refactoring things takes a long time (trust me, I know), but the way things are now it seems very likely you'll end up with a catch-22. By trying to avoid the downsides of using the STL (lager binary sizes, etc), you'll end up making it harder and harder to switch to better-performing and cleaner code in newer C++ standards. I'm sure no one is particularly looking forward to implementing in-place construction in all the custom types. 😬 I know my opinion doesn't mean much here, but I figured I'd give my thoughts from the perspective of someone who has worked with a large C++ codebase that moved to modern C++ from C++03. It is a lot of work, but in the long term I feel it's worth it. Excuse the massive textwall! |
@Vultraz Fully agree. While I don't have (almost any) experience with C++, after working some time with GDNative + C++ I share your view. Just few days ago I posted on reddit in "Is contributing to Godot a good way to learn C++?" thread:
|
@Vultraz a well articulated write up, great read, thanks for that. It is good to hear a perspective like that. I'm an old style C++ programmer and as such I haven't had much problems with understanding the Godot source code, I've found it well structured and much to my liking. I think having been able to add VR support to the core of this engine relatively quickly while having very little pre-existing experience with the code base is a testament to how readable and understandable this engine is. It may be old school in some ways but I'm continuously surprised by how well certain parts are build. Yes there are parts that need modernization in order to be less memory hungry and more performant but as a whole, I've been impressed by it. When I hear people talking about newer syntax in C++, often I just feel old and really wonder what the fuss is all about. But I kinda get that when you learn more modern C++ and you then decide to contribute to Godot, it's weird that it seems to re-invent the wheel. But to people like me at least, we're so used to seeing classes like these implemented, we're just impressed by how well most are implemented here :) Now that all said, there are so many different ways to look at this problem its not funny. From the origins of Godot predating newer C++ syntax to what the core developers feel the most at home with to limitations emposed by the cross platform nature of Godot and the devices (some not public) it can(/could) run on. I don't feel there is a right or wrong in this, it's what you're used to and where you're coming from, and the question about whether the extra learning curve learning how Godot works outweighs the benefits of refactoring a large code base. I don't know if you've watched it but Juan held a talk at GDC that was put online: https://www.youtube.com/watch?v=C0szslgA8VY As for the reactions above on GDNative, common guys, GDNative is a recent addition that fills a particular need, its not indicative of how the core product is structured and build. |
@Vultraz @mnn I can actually see the point about STL containers, but purely because some implementations (MSVC, mostly) have horrendously slow performance in debug mode. But one could simply use EA's STL and be good (theirs is fast, and portable). Also: I personally found the lack of RAII to be most distressing. The amount of manual cleanup that is needlessly performed all over the code is weird, since RAII isn't new to C++11. |
That's of course true, but shouldn't be GDNative more user friendly than engine itself, since GDNative is used to create "scripts", so targeted at an audience which is expected to have even lower C++ skills that those willing to sink into internals of the engine?
I am a C++ noob (around two months working 2 days a week in C++), so I should have been the target demographic profiting from this decision. I find Godot containers lacking in basic functionality (comparing to stl which isn't too rich itself), I don't think containers are GDNative related, but I might be mistaken. I am doing my best avoiding Godot containers, because they are always pain to work with. I thought inconsistent and unexpected behaviour of Ref was engine's responsibility, but I guess I am wrong. I realize my programming background is probably rather unusual - professionally working years in JS/TS, year in Scala, smaller hobby projects in Haskell (few thousands lines, which isn't actually that small considering how concise Haskell is - many times I write code in C++ which in Haskell would be at least 5 times shorter and more readable). I wonder if I am the only one being discouraged by usage of archaic overly verbose tech. |
@mnn, GDNative was created to enable the creation of C based modules as dynamic libraries so you didn't have to recompile the entire engine. On top of that several language bindings were created so you could write modules in C++, python, rust, etc. that again did not require compiling the entire engine. The other goal of this was for developers to be able to create modules where you just deliver the module itself and just use it with a stable build of the engine. Many modules have become defunct because they aren't being maintained yet are tied into a specific version of the engine. So yeah, it's made it much easier and simpler to create modules from a compilation and deployment perspective. But due to its approach it has its limitations. When you stay within those limitations then writing C++ code in GDNative can remain simple, for as far as the subject matter of what you're building a module for is simple. Try and break out of those limitations and you're in for headaches. My current headache is around trying to implement OpenGL logic while that is all encapsulated inside of the visual server architecture and not really available inside of GDNative in a way that I need it to be. But that is more a factor of me wanting to do something with GDNative it was never designed for. |
Note also that GDNative was meant as a way to rewrite GDScript code that needs to be more performant. As a result you can't access anything inside of the engine that GDScript doesn't have access to either (like OpenGL) |
What about the latest coroutine support, ie co_await()? From a procgen perspective, this is HUGE. |
Coroutine support is part of the C++20 standard, which hasn't been finalized yet, for the record. |
Allow me to chime in, I'm currently writing advanced (think source hammer editor) brush/csg tools for the 3D Spatial Editor and this talk about not allowing auto is really confusing to me. Sometimes even use of auto can be explicit. Consider the following: I'm inside the SpatialEditorViewportContainer and I want to get the SpatialEditor, which is three items up the parent hierarchy (before anyone points out that there are better ways to access the SpatialEditor from the viewport container, please consider that I started looking at this codebase yesterday)
As you can see, the dynamic downcast already explicitly states the type of SP. Without auto, I would have to write redundant junk like:
Please, for the love of god, allow the use of auto! |
On the topic of which C++ standard to use: The proposed reflection and meta class features of C++20 and beyond would be really usefull to reduce macro clutter like
|
Also, I would like to echo that these restrictions make me really second guess my decision to contribute at all. Arround 2012, I self taught myself C++11 and not being able to use this standard seems like a slap to the face. C++11 and C++03 are completly different languages, and most of the reputation that C++ is hard to learn, hard to read and hard to write are the fault of C++03 (or 98 rather). Not using at least C++11 or 14 is detrimental to maintainabily and readability. I grew up with C++11 and the project lead evidently did not (when he began working on godot in 2007, I was 12 years old), so I guess this is more a case of preference and baby duck syndrome. I feel like not using C++11 is just to comfort people who are used to old school (aka, terrible) C++, at the expense of people like me who took the time to learn modern C++. As time goes on, more and more junior programmers such as myself will be raised on modern C++11 and beyond and will find the fact that the project is forever stuck in a lanuage that doesn't even have lambdas to be rather discouraging. In short: C++11 or bust! |
To be clear, I do not advocate the use of STL. Rolling your own containers is fine, but rejecting features like lambdas and auto seems asinine. |
I'll close the echo chamber for now as it's pointless to discuss further here. We already discussed months ago what kind of features of C++11 and/or some later versions we plan to use. We're just waiting on @hpvb to have time to finalize the guidelines he's writing based on our consensus, and we can then discuss some more about those guidelines once they are published. Until then, this is not constructive. |
In Godot we are still using C++ 98/03. The aim of this issue is trying to decide whether we must still stick to it or either we can start using some modern C++ (C++11/14/17 and even 20) stuff.
When Godot was initially written, C++03 was the most current version and years later that style (version of the standard, avoidance of the standard library, etc.) was kept to avoid compatibility problems on certain compilers (some consoles, as far as I know). But we should recheck these concerns.
Items to consider (this list is intended to keep growing or having some entries discarded; just brainstorming at first):
auto
,constexpr
, lambdas, etc.: In general, any modern C++ feature that makes writing code easier and/or provides some chance of optimization without disrupting the current core.register
, inlining "tricks", etc.: Stuff added to the code to try to make the compiler generate more performant code. Some of them either are deprecated or have no actual impact, or even can worsen performance. Which to drop? Which to keep?Small changes could be done early. But when should big changes (in its case) done? Godot 4.0? Or as soon as Godot 3.0 becomes stable?
Please let me invite some people explicitly: @reduz, @punto-, @akien-mga, @karroffel, @bojidar-bg, @BastiaanOlij, @Faless.
The text was updated successfully, but these errors were encountered: