Godot: Status von C++ in Godot

Erstellt am 18. Juli 2017  ·  65Kommentare  ·  Quelle: godotengine/godot

In Godot verwenden wir noch C++ 98/03. Das Ziel dieser Ausgabe ist der Versuch zu entscheiden, ob wir trotzdem dabei bleiben müssen oder ob wir anfangen können, etwas modernes C++ (C++11/14/17 und sogar 20) Zeug zu verwenden.

Als Godot ursprünglich geschrieben wurde, war C++03 die aktuellste Version und Jahre später wurde dieser Stil (Version des Standards, Vermeidung der Standardbibliothek usw.) beibehalten, um Kompatibilitätsprobleme auf bestimmten Compilern (einige Konsolen, so weit wie ich weiss). Aber wir sollten diese Bedenken noch einmal prüfen.

Zu berücksichtigende Elemente (diese Liste soll weiter wachsen oder einige Einträge verwerfen; zunächst nur Brainstorming):

  • Intelligente Zeiger: Ist der benutzerdefinierte Zeiger-/Objektmechanismus von Godot damit kompatibel? Müsste Godot zu einem großen Teil neu geschrieben werden, um sie zu verwenden? Wäre der Nutzen größer als die Kosten?
  • Move-Semantik: Die hier zu stellenden Fragen sind mehr oder weniger die gleichen wie bei Smart-Pointern.
  • auto , constexpr , Lambdas usw. : Im Allgemeinen jede moderne C++-Funktion, die das Schreiben von Code erleichtert und/oder eine gewisse Chance zur Optimierung bietet, ohne den aktuellen Kern zu stören.
  • Standard-Multiprocessing-Primitive : C++ bietet jetzt Standard-Threading, Atomic, Speicherbarrieren usw. Müssen wir benutzerdefinierte Implementierungen beibehalten und für verschiedene Plattformen warten?
  • STL/Boost/Sonstiges : Würden wir etwas davon profitieren, wenn wir von benutzerdefinierten Containern zu einer guten bekannten Implementierung wechseln würden? Nachteile bzgl. Wartung, Performance, Kompatibilität etc.
  • register , Inlining von "Tricks" usw. : Dinge, die dem Code hinzugefügt wurden, um zu versuchen, den Compiler dazu zu bringen, leistungsfähigeren Code zu generieren. Einige von ihnen sind entweder veraltet oder haben keine tatsächlichen Auswirkungen oder können sogar die Leistung verschlechtern. Welche fallen lassen? Welche behalten?
  • etc,

Kleine Änderungen konnten frühzeitig vorgenommen werden. Aber wann sollten große Änderungen (in seinem Fall) vorgenommen werden? Godott 4.0? Oder sobald Godot 3.0 stabil wird?

Bitte lassen Sie mich ausdrücklich einige Leute einladen: @reduz , @punto-, @akien-mga, @karroffel , @bojidar-bg, @BastiaanOlij , @Faless.

discussion

Hilfreichster Kommentar

Ich möchte meine 2 (oder 20) Cent als jemand anbieten, der neu bei Godot und seiner Codebasis ist. Ich überwache und arbeite derzeit daran, _Battle for Wesnoth_ nach Godot zu portieren. Jetzt ist das Frontend (der Editor und die GDScript-API) großartig! Abgesehen von ein paar Ecken und Kanten hat es uns bisher erlaubt, mit einem guten Tempo voranzukommen. Aber wir haben uns auch vorgestellt, dass wir (das Team) irgendwann Patches für das Backend (die Engine) beisteuern. Zu diesem Zweck habe ich Anfang dieser Woche das Git-Repo geklont und angefangen, in C++ herumzustöbern, und ehrlich gesagt ... bin ich etwas bestürzt.

Ich habe Erfahrung mit der Verwaltung einer großen C++-Codebasis in Form von Wesnoths alter benutzerdefinierter Engine. Es begann ebenfalls als C++03, wurde aber modernisiert, um C++11 und spätere Funktionen zu verwenden, und ist jetzt C++14-kompatibel. Ich habe diese Modernisierungsbemühungen angeführt (oft etwas zu eifrig) und ich glaube, dass unsere Codebasis dadurch viel lesbarer und einfacher zu handhaben ist. Zugegeben, ich habe nie intensiv mit einer reinen C++03-Codebasis gearbeitet; Ich habe C++ unter Verwendung moderner C++-Features gelernt. Aber für mich ist die Idee, dass Dinge wie auto , range-for und Lambdas Ihren Code weniger lesbar machen, einfach ... in der Tat sehr seltsam.

Nehmen Sie auto , es ist sicherlich möglich, es zu missbrauchen und zu überbeanspruchen, aber es hat auch eine Menge legitimer Verwendungen. Einer der häufigsten Orte, an denen wir auto bereitgestellt haben, als wir die Wesnoth-Codebasis aktualisiert haben, waren for-Schleifen mit Iteratoren. Früher hätten wir so etwas:

for(std::vector<T>::iterator foo = container.begin(); foo != container.end(); ++foo) {}

Was chaotisch ist! In den Fällen, in denen wir tatsächlich einen Iterator benötigten, haben wir Folgendes getan:

for(auto foo = container.begin(); foo != container.end(); ++foo) {}

Ja, jetzt kennen Sie den expliziten Iteratortyp nicht, aber das müssen Sie fast nie wissen . Ich habe hier in einigen Posts einen Kommentar gelesen, in dem es heißt, dass es schwieriger wird, wenn der Container einige Dateien entfernt deklariert, aber mit modernen Code-Editoren und Intellisense ist das kein großes Problem.

In den meisten Fällen würden wir stattdessen einfach zu range-for wechseln:

for(const auto& foo : container) {}

Viel schneller zu tippen und auch prägnanter. Sie müssen sich keine Gedanken darüber machen, Iteratoren innerhalb der Schleife zu dereferenzieren oder Indizes im Auge zu behalten. Und es ist völlig klar, dass Sie den gesamten Container durchlaufen. Bei Iteratoren müssen Personen, die mit dem Code nicht vertraut sind, überprüfen, ob die Schleife tatsächlich von Anfang bis Ende verläuft.

Die Verwendung auto in Range-for-Schleifen hat hier auch einen zusätzlichen Vorteil. Es macht eine Sache weniger, die Sie aktualisieren müssen, wenn Sie jemals den Typ des Containers ändern! Ich kann Juans Argument wirklich nicht verstehen, dass diese Dinge Ihren Code weniger lesbar oder weniger leicht verständlich machen. Bei mir ist es genau umgekehrt.

Im State of Godot-Video erwähnte er auch Lambdas. Auch hier ist es sicherlich möglich, sie zu missbrauchen, aber sie sind auch ein unglaublich nützliches Werkzeug! Hier ist ein allgemeines Paradigma, das ich vor der Verwendung von C++11 in Wesnoths Codebasis gesehen habe:

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);
}

Langer, unordentlicher, aufgeblähter Code. Und hier ist, was wir mit C++11 verwendet haben:

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; });
}

Das ist nur der häufigste Fall! Und ja, ich weiß, dass Sie auch operator< für T implementieren können und dass std::sort dies standardmäßig verwendet, aber es veranschaulicht meinen Standpunkt. Vielleicht liegt es auch daran, dass Sie nur modernes C++ gelernt und fast ausschließlich damit gearbeitet haben, aber ich denke, Tools wie auto , range-for und Lambdas zu missachten, wo es angemessen ist, schießt sich selbst ins Knie.

Was mich zu meinem nächsten Punkt führt. Mir ist aufgefallen, dass Godot intern viele seiner eigenen benutzerdefinierten Typen anstelle der STL-Typen verwendet. Wenn es Ihnen um die Lesbarkeit von Code in Bezug auf Dinge wie auto geht, ist die Verwendung benutzerdefinierter Kerntypen gegenüber STL-Typen absolut ein Nachteil! Vor ein paar Tagen habe ich die Codebasis durchsucht und bin auf eine Menge Code gestoßen, der so aussah:

container.push_back(T(args));

Das ist jetzt ineffizient. push_back (zumindest in Bezug auf std::vector ) akzeptiert eine konstante Referenz; daher führt dieser Operator zu einer unnötigen Kopie. Ich wollte einen Patch erstellen, damit sie emplace_back verwenden ... aber dann wurde mir klar, dass die gesamte Codebasis benutzerdefinierte Containertypen verwendete 😐

Eines der großen Probleme mit Wesnoths Design und einer der Hauptgründe für die Entscheidung, sich für eine neue Engine (in diesem Fall Godot) zu entscheiden, war, dass Wesnoth in hohem Maße unter dem Not Invented Here-Syndrom litt. Während wir Bibliotheken wie Boost verwendet haben, war unsere Rendering-Pipeline benutzerdefiniert, unser UI-Toolkit war benutzerdefiniert, unsere Daten-/Skriptsprache war benutzerdefiniert ... Sie verstehen das Wesentliche. Bevor wir mit der Arbeit an der Godot-Portierung begonnen haben, habe ich versucht (und bin gescheitert), hardwarebeschleunigtes Rendering zu implementieren (bis zu diesem Zeitpunkt hatten wir CPU-basiertes Oberflächen-/Sprite-Rendering verwendet. 2019. Ja, ich weiß, X_X). Die Texture -API von SDL hatte keine Shader-Unterstützung, und Shader-Unterstützung war erforderlich. Am Ende entschied ich, dass die Implementierung unseres eigenen Renderers, obwohl möglich, dem Projekt später einen unnötigen Wartungsaufwand auferlegen würde. Wir hatten bereits wenige Core-Entwickler, und jemanden zu finden, der OpenGL schreiben kann (oder Vulkan, falls wir OGL jemals fallen lassen mussten), wäre nur ein unnötiger Schmerz gewesen, wenn eine Engine wie Godot einen perfekt guten, gut gewarteten Renderer hat, den wir verwenden könnten stattdessen.

Ich spreche das an, weil ich denke, dass es ein gutes Beispiel dafür ist, warum es eine schlechte Idee sein kann, alles intern zu implementieren. Ja, Sie könnten Ihre Binärgröße ein wenig reduzieren, indem Sie die Standardbibliothek nicht verwenden, aber Sie tragen auch einen massiven Wartungsaufwand. Das Konvertieren dieser push_back -Aufrufe in emplace_back ist eine sehr tief hängende Frucht, die den Code sauberer und leistungsfähiger machen kann. Aber da Sie einen benutzerdefinierten vector -Typ haben, muss jemand ihn manuell implementieren, wenn Sie eine In-Place-Konstruktion wünschen. Und in allen anderen benutzerdefinierten Typen auch!

Ein noch größeres Problem ist, dass es die Eintrittsbarriere erhöht. Ich habe mir die Godot-Codebasis angesehen und C++-STL-Typen erwartet. Wenn ich diese nicht finde, muss ich oder jemand anderes jetzt genau lernen, welche Typen die Engine bereitstellt und welche API mit jedem einhergeht. Ich möchte std::map ? Nein, ich muss dictionary verwenden. Es macht den Betreuern das Leben nur schwerer und verkompliziert die Dinge für neue Mitwirkende. Und wirklich, ist Code, der wie eine Sache aussieht, aber eigentlich eine andere ist, nicht... unlesbar?

Vor ungefähr einem Jahr, als wir uns der Veröffentlichung von Wesnoth 1.14 näherten und auf Steam starteten, führte ich ein Refactoring-Projekt durch, um einen benutzerdefinierten Containertyp zu eliminieren, der im Wesentlichen std::list war, außer dass die Verwendung erase() nicht ungültig wurde Iteratoren. Hier ist der prinzipielle Commit, um den es geht . Danach war weitere Arbeit erforderlich, um es richtig zu machen, aber das Ergebnis war Code, der viel einfacher, leichter verständlich und weniger undurchsichtig war.

Abschließend denke ich, dass das Verbot bestimmter sehr nützlicher C++11-Funktionen und das Festhalten an benutzerdefinierten Typen gegenüber STL-Typen auf lange Sicht ein Hindernis für Godot sein wird. Ich weiß, dass das Umgestalten von Dingen lange dauert (glauben Sie mir, ich weiß), aber so wie die Dinge jetzt sind, scheint es sehr wahrscheinlich, dass Sie mit einem Catch-22 enden werden. Indem Sie versuchen, die Nachteile der Verwendung der STL (größere Binärgrößen usw.) zu vermeiden, wird es am Ende immer schwieriger, auf leistungsfähigeren und saubereren Code in neueren C++-Standards umzusteigen. Ich bin mir sicher, dass sich niemand besonders darauf freut, die Konstruktion vor Ort in allen benutzerdefinierten Typen zu implementieren. 😬

Ich weiß, dass meine Meinung hier nicht viel bedeutet, aber ich dachte, ich würde meine Gedanken aus der Perspektive von jemandem äußern, der mit einer großen C++-Codebasis gearbeitet hat, die von C++03 auf modernes C++ umgestiegen ist. Es ist viel Arbeit, aber auf lange Sicht denke ich, dass es sich lohnt.

Entschuldigen Sie die massive Textwand!

Alle 65 Kommentare

Das wurde oft diskutiert, die üblichen Antworten sind:

1) Kein Wunsch, die Codebasis auf etwas über 03 zu verschieben. Features sind es nicht wert. Für GDNative C++ ist es jedoch durchaus möglich, es zu verwenden.
2) Kein Wunsch, STL/Boost/Etc. zu verwenden. Die Begründung war immer die gleiche: a) Godot-Templates machen kleine Dinge, die sie normalerweise nicht tun (dh atomare Refcounts und Copy on Write) b) STL erzeugt riesige Debug-Symbole und Debug-Binärdateien aufgrund ihrer extrem langen verstümmelten Symbole

Wir bleiben so wie alles ist.

Ich habe einige Meinungen zu privaten Gesprächen eingeholt, aber die wollte ich machen
Diskussion öffentlicher.

Ich wusste nicht, dass es schon so viel diskutiert und geschlossen wurde.

Verwerfen Sie sogar auto, constexpr und so?

„Registrieren“ löschen?

El 18. Juli. 2017 13:54 Uhr, "Juan Linietsky" [email protected]
Beschreibung:

Das wurde oft diskutiert, die üblichen Antworten sind:

  1. Es ist nicht erwünscht, die Codebasis auf etwas über 03 zu verschieben. Features sind
    ist es nicht wert. Für GDNative C++ ist es jedoch durchaus möglich, es zu verwenden.
  2. Keine Lust auf STL/Boost/etc. Die Begründung war immer die
    gleich: a) Godot-Templates machen kleine Dinge, die sie normalerweise nicht tun (dh atomare
    Refcounts und Copy on Write) b) STL generiert riesige Debug-Symbole und debug
    Binärdateien aufgrund ihrer extrem langen, verstümmelten Symbole

Wir bleiben so wie alles ist.


Sie erhalten dies, weil Sie den Thread verfasst haben.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/godotengine/godot/issues/9694#issuecomment-316041201 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/ALQCtipKmepD_1Xw6iRXZ7aGoQlLfiwFks5sPJzqgaJpZM4ObOio
.

Ich habe dieses Problem angesprochen, es wäre sicherer, intelligente Zeiger zu verwenden, und dass dieser Code unter dem NIH-Syndrom leidet, aber höhere Stellen haben nicht die Absicht, ein Upgrade durchzuführen, also schlage ich vor, dass Sie auch aufgeben.

Es würde wahrscheinlich viele Arbeitsstunden dauern, den Code zu aktualisieren, die Entwickler lieber für die Implementierung neuer Funktionen aufwenden. Ich verstehe das irgendwie - Engine-Benutzer erhalten lieber neue Funktionen als "Refactoring" (ohne Dinge wie technologische Schulden zu verstehen).

@Marqin Tut mir leid, aber intelligente Zeiger sind eine schlechte Idee, weil:

1) Das einzig Nützliche ist das nachgezählte, der Rest ist mentale Masturbation für Merkmale, die bereits in der Sprache vorhanden sind.
2) Die Verwendung von intelligenten Zeigern überall ist eine schreckliche Idee, Sie riskieren, Referenzzyklen zu haben, die Speicher verlieren
3) Wir haben bereits etwas Ähnliches, Ref<> , mit dem zusätzlichen Vorteil, dass wir steuern, wie die referenzierende Zählung funktioniert, sodass wir Sonderfälle hinzufügen können, um Bindungen an Sprachen wie C# zu handhaben, die ihren eigenen gc haben

Also, bitte, bevor Sie argumentieren, dass wir Dinge aus einer Laune heraus entscheiden, fragen Sie einfach. Wir treffen normalerweise fundierte Entscheidungen und teilen den Prozess mit allen.

Was leicht getan werden könnte, wenn Sie die Qualität verbessern möchten, ist, mit dem Schreiben von Komponententests zu beginnen (fügen Sie dafür einen optionalen Job zu scons hinzu und lassen Sie einfach die Entwickler gmock/gtest selbst installieren [um ./thirdparty nicht zu verstopfen]).

der Rest ist mentale Masturbation für Merkmale, die bereits in der Sprache vorhanden sind

@reduz , wie ist unique_ptr mit seinem expliziten Besitz und der RAII-Speicherfreigabe bereits in der Sprache vorhanden?

Auch wird nicht nur die Speicherverwaltung, sondern auch das Threading einfacher (zB lock_guard ).

@RandomShaper Ich bin nicht gegen ein Upgrade zu einem späteren Zeitpunkt, aber obwohl es viele nützliche Funktionen gibt, neigen neuere C++-Versionen dazu, den Programmierer dazu zu bringen, weniger expliziten Code zu schreiben. Dies führt dazu, dass Code im Allgemeinen schwieriger von anderen gelesen werden kann (typisch für den Missbrauch von Auto-Schlüsselwörtern).

Der andere Vorteil, keine erweiterten Funktionen zu verwenden (z. B. verwenden wir keine Ausnahmen und rtti kann mit geringen Kosten deaktiviert werden), besteht darin, dass Compiler viel kleinere Binärdateien erzeugen.

Das ist gut. Ich respektiere dein Wort. Nur ich finde, darüber zu diskutieren, ist eine gesunde Sache.

Ich weiß, dass einige große Videospielfirmen (Ubisoft, IIRC) große Codebasen auf modernes C++ migriert haben, also muss es auch perfekt für Godot geeignet sein. Wie Sie bereits betont haben, erfordert dies natürlich Arbeit / Zeit.

Deshalb konnten wir:

  • Wählen Sie eine angemessene Teilmenge für eine möglichst schmerzfreie Migration des gesamten Codes aus.
  • oder wählen Sie zumindest eine angemessene Teilmenge als "genehmigt" für neuen Code aus.

In beiden Fällen müsste wahrscheinlich ein Codierungsstil definiert werden, um Funktionen nicht zu missbrauchen, wie Sie gesagt haben, dass dies bei auto der Fall ist.

Natürlich gibt es jetzt Dinge mit höherer Priorität, aber vielleicht, wenn 3.0 stabil wird oder zu einem anderen Zeitpunkt in der Zukunft, wäre es gut, sich schon entschieden zu haben.

@Marqin Wie bereits erwähnt, verwendet Godot ein eigenes Speicherzuweisungsschema für Knoten, was sinnvoller ist als alles, was von den neuen Standardbibliotheken bereitgestellt wird.

Auch Lock Guard besteht praktisch aus 3 Codezeilen und wir haben es bereits implementiert, ohne C++11+ zu verwenden

Nochmals, warum sollten wir davon ausgehen, dass etwas "Standard" 1) besser sein muss als das, was wir bereits haben, 2) besser ist, wenn wir es trotzdem verwenden? .Ich denke, es ist eine gemeinsame Phallus.

Was kommt als nächstes, unsere Druckfunktionen fallen zu lassen und ostream/istream zu verwenden? Ändern Sie unsere String-Klasse, die ziemlich großartig ist, und ersetzen Sie sie durch das viel verkrüppeltere std::string oder std::wstring?

Standardbibliotheken erfüllen nicht alle Zwecke und funktionieren nicht besser als alles andere, nur weil sie Standardbibliotheken sind. Sie sind nur für diejenigen da, die sie brauchen, und es ist in Ordnung, sie zu ignorieren und eigene Implementierungen zu schreiben, wenn Sie einen triftigen Grund dafür haben. Das tun wir und wir sind davon überzeugt.

@RandomShaper Das Problem ist Folgendes:

1) Es ist mehr Kosten als Nutzen.
2) Öffnet das Fenster zu vielen Argumenten darüber, wie die Standardmethode zum Schreiben von C++11+ ist. Es kann sich lohnen, sie irgendwann zu haben, und das Umschreiben eines großen Teils der Engine, um diese Funktionen zu nutzen, könnte eines Tages nützlich sein, aber ich denke, es gibt viel wichtigere Dinge, auf die man sich konzentrieren muss.
3) Wie bereits erwähnt, kann es möglich sein, dass die Portierung auf eine neue C++-Version so cool klingt, dass dies zu einer viel größeren Binärgröße führen kann. In einem solchen Fall kann es unerwünscht sein.

Es lohnt sich im Moment ernsthaft nicht, wir können es in ein paar Jahren noch einmal diskutieren

Was ich für sinnvoll halte, ist sicherzustellen, dass Godot mit Optionen wie --std=c++17 kompiliert wird, sodass Sie bei Bedarf problemlos eine in modernem C++ geschriebene Bibliothek einbringen können. Zum Beispiel würde ich für die Entfernung des Schlüsselworts register aus der Codebasis https://github.com/godotengine/godot/issues/9691 stimmen

Irgendwie verwandt erinnere ich mich, irgendwo gelesen zu haben, dass gcc-6.3 nicht unterstützt wird (google
sagt, es war in https://github.com/godotengine/godot/issues/7703 ). Es stört mich, da gcc-6.3 der Standard-Compiler in meiner Distribution (debian stable) ist. Kann das jemand bestätigen? Und warum ist das?

@efornara Einige Bibliotheken von Drittanbietern erfordern bereits neuere C ++ - Versionen, und das ist in Ordnung, da Scons dies mit geklonten Build-Umgebungen erledigt. Sehen Sie sich den Code des Drittanbieters etc2comp an, um zu sehen, wie er funktioniert.

@karroffel Danke, das wusste ich nicht.

Es wäre dann nur ein nettes Feature, aber es wird nicht zum Importieren einer Bibliothek benötigt (wenn Sie jedoch für den Glue-Code mehr Godot einfügen müssen, stolpern Sie möglicherweise über eine Header-Datei, die nicht kompiliert wird).

Übrigens, wenn jemand etwas Ähnliches tun muss und diesen Beitrag gefunden hat, ist die relevante Datei: https://github.com/godotengine/godot/blob/master/modules/etc/SCsub . Ich habe es mit grep gefunden und es sieht aus wie der einzige Ort, an dem dies im Moment benötigt wird.

Es ist nicht so sicher, c++11 mit Nicht-c++11-Code zu verknüpfen - http://gcc.gnu.org/wiki/Cxx11AbiCompatibility

@Marqin Wenn ich den Link nicht falsch verstehe, scheint dies tatsächlich den Fall zu unterstützen, dass Godot _not_ damit beginnt, Komponenten aus der Standardbibliothek zu verwenden, sondern stattdessen an benutzerdefinierten Komponenten festhält:

Die Sprache C++98 ist ABI-kompatibel mit der Sprache C++11, aber an mehreren Stellen in der Standardbibliothek wird die Kompatibilität unterbrochen.

Das Mischen von Sprachen sieht ziemlich sicher aus (nicht schön, gebe ich zu, und es könnte in Zukunft aufhören zu funktionieren), aber das Mischen von Dingen wie Paaren, Vektoren, Listen usw. verursacht bekanntermaßen Probleme.

@efornara Bei dem Link geht es darum, einige Bibliotheken von Drittanbietern, die C ++ 11 verwenden, mit Godot zu verknüpfen, die C ++ 03 verwenden.

@Marqin Ja, aber ich verstehe den Link so, dass Sie es tun können. Was Sie beispielsweise nicht tun können, ist, eine std::list<> von Godot an die Bibliothek des Drittanbieters zu übergeben.

@reduz wo finde ich die Dokumentation von Godots internem Ref<>? Wo finde ich Dokumentation darüber, wie sich Godot-interne Container von denen in STL unterscheiden?

@Marqin Für Container gibt es nicht viel:
http://docs.godotengine.org/en/stable/development/cpp/core_types.html#containers
Für Ref<> wurde mir klar, dass es keine gibt. Sollte wohl ergänzt werden.
Alle Vorschläge zur Verbesserung des Dokuments, um diese Dinge hinzuzufügen, sind sehr willkommen.

Ich persönlich finde den neuen C++-Standard ziemlich großartig, aber ich würde kein Godot-internes Refactoring vorschlagen, weil es zu viel Aufwand für zu wenig Gewinn erfordern würde.

Auch die Abwärtskompatibilität ist genau aus diesem Grund eines der Markenzeichen von C++. Trotzdem würde ich es gerne für neue Godot-Funktionen und in GDNative verwenden können.
Aus diesem Grund würde ich es vorziehen, Godot mit moderner C++-Unterstützung zu kompilieren.

@reduz Wie Sie sagten, können Sie mit C++ 11/14/17 weniger expliziten Code schreiben. Auch wenn dies ein Nachteil für C++-Neulinge ist, ist es eine gute Sache für C++-Power-User.
Was "auto" betrifft, denke ich persönlich, dass es auch für Power-User gut ist. Es kann Sie nicht nur vermeiden, Typen immer wieder einzugeben, wenn dies nicht unbedingt erforderlich ist, sondern es kann auch verhindern, dass einige Fehler eingegeben werden.

Zu Ihrer Information, ich habe einen aktuellen Master (godot3) kompiliert mit:

scons platform=x11 target=debug tools=yes builtin_openssl=true CCFLAGS=-std=c++17

auf einem Debian-Stretch (gcc-6.3). Ärgerlicherweise ist die Option auch beim Kompilieren von C-Dateien gesetzt, man wird also mit Warnungen überschwemmt, wenn man sie aktiviert, aber ansonsten lief alles glatt. Auch das Schlüsselwort register scheint keine Probleme zu bereiten.

Ich würde nicht so weit gehen, vorzuschlagen, dass die offiziellen Builds auf diese Weise kompiliert werden, aber es ist gut zu wissen, dass die Option vorhanden ist, wenn Sie sie in Ihrem Projekt benötigen. Was ich vorschlagen würde, ist, dass alle Änderungen, die dies brechen, als Regression behandelt werden.

BEARBEITEN: Einige Tippfehler korrigiert, wodurch die Aussage zu Warnungen klarer wurde.

Ärgerlicherweise wird die Option auch beim Kompilieren von C-Dateien gesetzt

Hast du es mit CPPFLAGS versucht?

@Hinsbart Nein, habe ich nicht. Vielleicht gibt es einen Weg, aber da ich mich nicht so gut mit Scons auskenne, habe ich einfach das gemacht, was möglich schien, ohne zu basteln:

$ scons platform=x11 builtin_openssl=true -h | grep FLAGS
CCFLAGS: Custom flags for the C and C++ compilers
CFLAGS: Custom flags for the C compiler
LINKFLAGS: Custom flags for the linker

BEARBEITEN: Übrigens, ich weiß nicht, wie es im Godot-Build-System verwendet wird, aber CPPFLAGS würde mich an die Vorgängeroptionen denken lassen. Ich persönlich habe immer CXXFLAGS für C++ verwendet.

Hast du es mit CPPFLAGS versucht?

Ich denke, CPPFLAGS wirkt sich auf jede Sprache aus, die den Präprozessor verwendet, also sind sowohl C als auch C++ enthalten.

@m4nu3lf Sie können mit GDNative jede beliebige Form von C++ verwenden.

Meiner Erfahrung nach ist jede Gelegenheit, Code zu löschen, eine gute Gelegenheit.

Vielleicht könnten wir eine Art Wiki-Seite einrichten, die dokumentiert, welche Dateien gelöscht und durch c++11-Varianten ersetzt werden können. Dies würde wahrscheinlich das Threading von Primitives und dergleichen beinhalten.

Veränderung um der Veränderung willen ist nicht gut (sprichwörtlich Koolaid), aber in diesem Fall haben sich das LLVM-Projekt und viele andere FOSS-Projekte zugunsten einiger klarerer Syntaxmuster, dh der neueren For-Iterator-Notation, aber auch weiterentwickelt die Trennung von Bedenken auf die jeweiligen Sprachlaufzeiten auszulagern, denn (seien wir ehrlich) es ist ideal für eine Spiele-Engine, so wenig plattformspezifischen Code wie möglich zu pflegen.

Der beste Code, den Sie jemals schreiben werden, ist der Code, den Sie rückgängig machen. :)

Ist es aus Neugier möglich, Code zu schreiben, der sowohl mit älterem C++ als auch mit neuerem C++ kompiliert wird? Gibt es signifikante Breaking Changes zwischen den C++-Versionen?

Ist es aus Neugier möglich, Code zu schreiben, der sowohl mit älterem C++ als auch mit neuerem C++ kompiliert wird? Gibt es signifikante Breaking Changes zwischen den C++-Versionen?

Neueres C++ ist in 99,99...% der Fälle abwärtskompatibel mit älterem C++ (nur Dinge, die nicht hätten verwendet werden sollen oder falsch definiert waren, wurden als nicht mehr unterstützt definiert, die aber im Allgemeinen nur Kompilierungswarnungen verursachen, aber heute noch funktionieren) .

Neueres C++ hat jedoch bedeutende Funktionen, und diese funktionieren offensichtlich nicht in älteren C++-Versionen, und diese Funktionen sind nicht nur Funktionen zur Benutzerfreundlichkeit wie variadische Makros und auto und Lambdas, sondern auch Effizienzfunktionen wie move ing- und rvalue-Referenzen und natürlich variadische Makros sowie solche aus Sicherheitsgründen wie die neueren Besitzzeiger (insbesondere bei den C++ Core-Bibliotheken) und viele andere.

Wenn man bedenkt, dass jetzt sogar Visual Studio modernes C++17 unterstützt, gibt es wirklich keinen Grund, neuere Versionen nicht zu verwenden.

Ich wollte kommentieren. Ich konnte Godot mit dem C++17-Flag kompilieren, wobei das einzige Problem eines der Drittanbieter-Zeug war, das auto_ptr verwendete (das aus C++17 entfernt wurde, weil es so schlecht ist).

Das Problem ist nicht, dass es nicht in c ++ 17 kompiliert wird, das Problem sind Leute, die c ++ 17-Funktionen verwenden oder, noch schlimmer, PRs mit diesen starten möchten ... nur um festzustellen, dass sich das Projekt auf c befindet ++03.

Der Mangel an Lambdas allein ist ein RIESIGES Problem, das ich hier nicht sehe.
Godot muss mehrmals eine zeitliche Liste von Daten speichern, um diese dann zu iterieren.
In jedem dieser Fälle kann dies als „for_each“ oder eine ähnliche Struktur ausgeführt werden, die von selbst iteriert, den Code erheblich vereinfacht, die Speichernutzung senkt und die Leistung verbessert, da das Lambda inline/optimiert wird. (und das ist C++11, überall verwendet). Das Gleiche gilt für alle allgemeinen Schleifen in der verknüpften Liste.

Move-Operatoren würden auch bessere Datenstrukturen ermöglichen, die die aktuellen absolut schrecklichen durch einige besser optimierte ersetzen könnten.

String-Datentypen (wie "mystring"_hs) oder ähnliches könnten uns eine gute Möglichkeit bieten, gehashte Strings überall im Code zu verteilen, wodurch die String-Prüfung (sehr häufig in Skript- und Ereigniscode) viel schneller wird.

Selbst wenn std::thread nicht verwendet wird (ich denke, es ist selbst eine ziemlich schreckliche Abstraktion), ist die std-Atomics-Bibliothek erstaunlich und äußerst nützlich. std::atomarund dergleichen.

Und reden wir nicht einmal darüber, wie sehr es dem Projekt selbst schadet, Leute zu C++03 zu zwingen, wenn Leute moderne Bibliotheken nicht einfach selbst integrieren können, weil Godot das einzige nicht aufgegebene Open-Source-C++-Projekt auf einer so alten Version von C++ ist ( Soweit ich weiss)

Persönlich stimme ich zu, konservativ zu sein und nicht zum absolut neuesten C++-Standard zu gehen, aber ich denke, etwas wie C++11 mit einigen geprüften Funktionen von C++14 würde am besten funktionieren und Godot erheblich verbessern. C++11-14 ist gut genug für Unreal Engine, die auf PS4, Xbox, Switch, PC, Low-End-PC, Android, IOS und HTML5-Webassembly portiert wird. Ich verstehe nicht, warum Godot sich einschränken sollte, wenn es eine viel geringere Anzahl von Plattformen unterstützt.

String-Datentypen (wie "mystring"_hs) oder ähnliches könnten uns eine gute Möglichkeit bieten, gehashte Strings überall im Code zu verteilen, wodurch die String-Prüfung (sehr häufig in Skript- und Ereigniscode) viel schneller wird.

Außerdem können Sie sie in Dingen wie Schaltern verwenden. Wie ich vor Jahren einen Atom-Typ gemacht habe, um eine Fliegengewichts-String-Implementierung zu ersetzen:
https://github.com/OvermindDL1/OverECS/blob/master/StringAtom.hpp

Es gibt sowohl eine 32-Bit-Version (maximal 5 Zeichen oder 6, wenn Sie die Tabelle ein wenig umcodieren) als auch eine 64-Bit-Version (maximal 10 oder 12 Zeichen, je nachdem, ob Sie eine enge Codierung wählen oder nicht). Es ist vollständig reversibel, geschieht zur Kompilierzeit oder dynamisch zur Laufzeit in beide Richtungen, vollständig verwendbar in Schaltern usw. usw. Beispiel für die Verwendung dieser Datei:

switch(blah) {
  case "UPDATE"_atom64: ... pass
  case "PHYSUPDATE"_atom64: ... pass
  ...
}

Bei der Interaktion mit LUA-Code habe ich nur Zeichenfolgen verwendet und die Konvertierung an den Grenzen durchgeführt. Diese Datei ist nicht die "neueste" Version davon. Ich habe Funktionen hinzugefügt, um die Konvertierungen ohne Speicherzuweisung durchzuführen, wenn von Integer zu String zurückgekehrt wird (es ist unabhängig von String->Integer alloc-less, da es zur Kompilierzeit ausgeführt wird). . Es ist trivial, einen Visual Studio- oder GDB-Filter zu erstellen, der die Codierung umkehrt, wenn auch der Typ Atom64/Atom32/was auch immer angezeigt wird (der nur eine ganze Zahl darunter ist), sodass Sie den Zeichenfolgentyp anstelle eines seltsamen Hash-Werts sehen können.

Aber solche Dinge sind extrem nützlich, besonders in leistungskritischem Code und um Code leicht lesbar zu machen, und dafür war nur C++11 erforderlich, nicht einmal etwas neueres.

Zumindest würde ich sagen, dass C++14 der Godot-Standard sein sollte. C++17 wäre schön (einige sehr nützliche Leistungsverbesserungen in einigem neuen Code), aber C++14 ist jetzt eher ein universelles Minimum. Aber natürlich, da gcc/clang/VisualStudio und was sonst noch C++17 gut unterstützt (und sogar große Teile von C++20), scheint C++17 auch gut zu sein. Ich würde mich wahrscheinlich immer noch für C ++ 14 für "nur für den Fall" entscheiden.

@OvermindDL1 Das Atom-Ding ist großartig, ich liebe es. Es würde definitiv sehr gut zu Godot passen, wo es genau das ziemlich oft macht.

Das Atom-Ding ist großartig, ich liebe es. Es würde definitiv sehr gut zu Godot passen, wo es genau das ziemlich oft macht.

@ vblanco20-1 Nun, wenn Godot es will, steht es ihnen frei, den Code zu absorbieren. Es (und sein Vorgänger-String im Fliegengewicht) haben eine lange Verwendung in meiner alten C++-Engine gesehen. Es war so nützlich für kleine Ereignis-Tags, nur kurze Zeichenfolgen, die als „Schlüssel“ in Dinge verwendet werden konnten, und so trivial einfach, sich über Lua/LuaJit hin und her zu bewegen, und die Debugger-Filter waren eine große Hilfe.

Der Mangel an Lambdas allein ist ein RIESIGES Problem, das ich hier nicht sehe.

Bin ich der einzige, der denkt, dass Lambdas den Code in den meisten Fällen schwer lesbar machen?

@Faless : Nein, du bist nicht der einzige, ich denke, dass Lambdas schwer zu lesen sind, ist einer der Gründe, warum Akien die C++-Version nicht verbessert hat.

Lambdas sind nur kleine Inline-Funktionen. Ich weiß nicht, was Sie "schwer zu lesen" dazu sagen. Aber sie erlauben Dinge wie den Sortieralgorithmus, an den Sie die Vergleichsfunktion senden, oder den Rest der Standardalgorithmenbibliothek. Sie ermöglichen es Ihnen auch, Speicher zu sparen und die Leistung erheblich zu verbessern, da die Notwendigkeit zeitlicher Arrays entfällt (was im Quellcode mit Octree und anderen Systemen mehrfach vorkommt).

Und mit der std-Algorithmenbibliothek ist dies der einfachste Weg, ein Programm zu multithreaden, das Sie bekommen können, einfach durch paralleles For, paralleles Sortieren und paralleles Akkumulieren.

Es ist eine absolute Schande, dass die Leute sie als "seltsam" abtun, wenn sie die Codequalität, Leistung und Wiederverwendbarkeit so sehr verbessern können.

Aktuelles Beispiel für etwas, das ich bereits implementiert habe:

//old linked list iteration
while (scenario->instances.first()) {
            instance_set_scenario(scenario->instances.first()->self()->self, RID());
        }
//new (just abstracts above)
scenario->instances.for_each([]( RID& item  ){
    instance_set_scenario(item, RID());
});

Es gibt auch diesen anderen Fall, der viel zu oft wiederholt wird

//old. Note the 1024 array, wich is hard size, and is wasting memory

int culled = 0;
Instance *cull[1024];
culled = scenario->octree.cull_aabb(p_aabb, cull, 1024);
for (int i = 0; i < culled; i++) {

    Instance *instance = cull[i];
    ERR_CONTINUE(!instance);
    if (instance->object_ID == 0)
        continue;

    instances.push_back(instance->object_ID);
}

//NEW. not implemented yet. 0 memory usage, can be inlined, and doesnt have a maximum size. 
//Its also shorter and will be faster in absolutely every case compared to old version.

scenario->octree.for_each_inside_aabb(p_aabb, [](Instance* instance){       
    ERR_CONTINUE(!instance);
    if (instance->object_ID == 0)
        continue;
    instances.push_back(instance->object_ID);
});

Aktuelles Beispiel für etwas, das ich bereits implementiert habe:

Ich bin mir nicht sicher, was hier der Gewinn ist ...

Es ist eine absolute Schande, dass die Leute sie als "seltsam" abtun, wenn sie die Codequalität, Leistung und Wiederverwendbarkeit so sehr verbessern können.

@ vblanco20-1 Nun, ich werde versuchen, mich zu erklären.
Die Leute schreiben normalerweise Dinge wie:

my_arr.push(
    [par1, par2, par3]{
      somefunc(par1, par2, par3);
    }
);

und dann, an einigen anderen Stellen:

func = my_arr.front();
func();

Was Ihnen beim Lesen weder einen Hinweis darauf gibt, welche Funktion ausgeführt wird, noch worauf Sie achten müssen. Es könnte in Ordnung sein, wenn es sich in derselben Datei befindet, aber was passiert, wenn Sie dieses Array über mehrere Dateien hinweg übergeben, ist, dass der gesamte Code unlesbar wird.

@Faless Ihr Beispiel ist ein Beispiel für das Schlimmste, was Sie mit Lambdas anstellen können (und diese Art der Verwendung sollte definitiv verboten werden).

Die Verwendung von Lambdas "über die Zeit" ist keine gute Verwendung, da sie zugewiesen werden müssen, und sie werden auch zu einer großen Gefahr, da die erfassten Variablen ihre Gültigkeit verlieren können, wenn Sie das Lambda ausführen (z -Zeiger und dann das Objekt löschen, bevor das Lambda aufgerufen wird, wird der erfasste Zeiger baumeln). Ich verteidige Lambdas nur wirklich für ihre Verwendung neben Datenstrukturen und Algorithmen, wo Sie sie "sofort" verwenden.

Im for_each-Beispiel ist for_each unempfindlich gegenüber Änderungen in der internen Datenstruktur (z. B. wenn Sie seine Variablen ein wenig neu anordnen), es erlaubt "undurchsichtigere" Datenstrukturen (was es einem Entwickler ermöglicht, "einfach-ish „Wechseln Sie von einer Datenstruktur zur anderen, um zu testen, welche besser funktioniert. Indem Sie sich darauf einlassen, können Sie viel kompliziertere Datenstrukturen implementieren, die undurchsichtig funktionieren, während die „Verwendungsebene“ einfach zu verwenden bleibt.

Es reduziert auch die Boilerplate und macht "klarer", was der Code tatsächlich tut (Iteration der verknüpften Liste). Nur das "first()->self()->self" loszuwerden, ist eine Verbesserung an sich.

Denken Sie daran, dass sich die Vorteile mit zunehmender Nutzung tatsächlich anhäufen, da Sie Dinge wie "unordered_for_each" haben können, die die Knoten in der Reihenfolge durchlaufen, in der sie sich im Speicher befinden (durch den Allocator oder wenn die verknüpfte Liste oben gespeichert wird ein Array) oder "reverse_for_each". Sogar Dinge wie „finden“ oder „sortieren“. (Ich empfehle die std::-Algorithmen nicht so sehr, zumindest bis C++20, wenn Ranges zusammengeführt werden. Die Implementierung Ihrer eigenen Algorithmen als Teil Ihrer Datenstruktur ist viel besser zu verwenden.)

Lambdas waren im Grunde das erste, was Epic Games von C++11 in die Unreal-Engine erlaubte, neben ihrer eigenen Bibliothek von Algorithmen wie Sort, Partition, Filter, RemoveAll, Find usw. Seitdem werden sie ziemlich oft im Quellcode verwendet, beides in Engine-Code und Gameplay-Code

Cool, zumindest sind wir uns einig, dass Lambdbas kein heiliger Gral sind
Programmierung, und wir haben bereits eine Regel definiert, wie man sie nicht verwendet.

Ich werde mehr über Aufführungen recherchieren.

Am Sa, 8. Dez. 2018, 17:06 vblanco20-1 < [email protected] schrieb:

@Faless https://github.com/Faless Ihr Beispiel ist ein Beispiel für die
Das Schlimmste, was Sie mit Lambdas machen können (und diese Art der Verwendung sollte es definitiv sein
verboten).

Die Verwendung von Lambdas "im Laufe der Zeit" ist keine gute Verwendung von ihnen, wie sie es haben werden
zuzuordnen, und sie werden auch zu einer großen Gefahr, da die erfassten Variablen
würde zu dem Zeitpunkt, an dem Sie das Lambda ausführen, nicht mehr gültig sein (z. B.
einen Zeiger erfassen und dann das Objekt löschen, bevor das Lambda aufgerufen wird,
der erfasste Zeiger baumelt). Ich verteidige eigentlich nur Lambdas
ihre Verwendung neben Datenstrukturen und Algorithmen, wo Sie sie verwenden
"sofort".

Im for_each-Beispiel ist for_each immun gegen Änderungen im Internal
Datenstruktur (zum Beispiel, wenn Sie seine Variablen ein wenig neu anordnen), erlaubt es
mehr "undurchsichtige" Datenstrukturen (was es einem Entwickler ermöglicht
Wechseln Sie "einfach" von einer Datenstruktur zur anderen, um zu testen, welche
könnte besser funktionieren. Wenn Sie das annehmen, können Sie viel mehr implementieren
komplizierte Datenstrukturen, die undurchsichtig wirken, unter Beibehaltung des „Nutzens“
Schicht einfach zu bedienen.

Es reduziert auch die Boilerplate und macht "klarer", was der Code ist
tatsächlich tun (Iterieren der verknüpften Liste). Einfach loswerden "
first()->self()->self " ist eine Verbesserung von sich selbst.

Denken Sie daran, dass sich die Vorteile mit zunehmender Nutzung tatsächlich anhäufen, da
Sie können Dinge wie "unordered_for_each" haben, die iterieren
die Knoten in der Reihenfolge, in der sie sich im Speicher befinden (durch den Zuordner oder wenn die
verkettete Liste wird über einem Array gespeichert) oder "reverse_for_each". Sogar Dinge
wie „finden“ oder „sortieren“. (Ich empfehle die std::algorithms nicht so sehr, at
zumindest bis C++20, wenn Ranges zusammengeführt werden. Implementierung eigener Algorithmen
als Teil Ihrer Datenstruktur ist viel besser zu verwenden)

Lambdas waren im Grunde das erste, was Epic Games von C++11 erlaubte
unreal Engine, neben ihrer eigenen Bibliothek von Algorithmen wie Sort,
Partition, Filter, RemoveAll, Find usw. Seitdem sind sie ziemlich oft
im Quellcode verwendet, sowohl im Engine-Code als auch im Gameplay-Code


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/godotengine/godot/issues/9694#issuecomment-445474001 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/ABnBbvBTxThAh0v8AfFCdGsSv2HFnEz6ks5u2_GKgaJpZM4ObOio
.

C++11-Funktionen, die die Wartbarkeit der Codebasis verbessern würden:

  • override und final
  • Bereichsbasierte for-Schleife
  • nullptr
  • Stark typisierte Aufzählungen
  • Schlüsselwort explicit

C++11 Features, die die Lebensqualität verbessern würden:

  • Rechtwinklige Klammer (nicht mehr Vector<Vector> > )

[[nodiscard]] (C++17 ?) siehe hier

Ich denke nicht, dass wir eine moderne Version übernehmen sollten, um neue Dinge zu verwenden oder weil es 1 oder 2 Funktionen gibt, die verwendet werden können. Ich würde nicht vorschlagen, etwas über C++11 hinaus zu verwenden, da sich der Nutzen nicht lohnt.
Mit der Weiterentwicklung von C++ wird die STL immer größer und während der Präprozessorphase werden dem Projekt Hunderte von Zeilen hinzugefügt. In den meisten Fällen hat es einen bemerkenswerten Einfluss auf die Leistung.

In den meisten Fällen hat es einen bemerkenswerten Einfluss auf die Leistung.

Das darf es auf keinen Fall? Code, der nicht ausgeführt wird, sollte keine Auswirkungen auf die Leistung haben, es sei denn, Sie haben einen Compiler-Fehler oder so, und in der Veröffentlichung sollte er überhaupt nicht in den endgültigen Binärdateien enthalten sein? In vielen Fällen erhöht allein die richtige Bewegungssemantik die Leistung, in einigen Bereichen erheblich, und obwohl dies ein älteres Beispiel ist, können die aktualisierten Direktiven neben anderen Funktionen auch Leistungssteigerungen haben.

@OvermindDL1 Ja, so sollte es theoretisch sein.
Sehen Sie sich das an: https://twitter.com/zeuxcg/status/1085781851568914432
Ich habe in letzter Zeit verschiedene Fälle wie diesen in den sozialen Medien gesehen. Es sind nicht so "kostenlose Abstraktionen", wie es sein sollte.

@lupoDharkael Dieser Twitter-Thread (sobald er endlich geladen ist, heiliges Wow, Twitter ist eine schreckliche Schnittstelle ... das vergesse ich immer wieder ...) spricht nur über die Geschwindigkeit der Kompilierzeit, weil math.h in libstdc++ im C++17-Modus größer ist (wobei libc++ hat dieses Problem nicht) aufgrund einer größeren Menge an Überladungen und Funktionen für eine bessere Laufzeitgeschwindigkeit, sodass jede Kompilierung, die math.h einbringt, eine längere Kompilierzeit von etwa 300 ms hat, wenn diese spezifische ältere stdlib anstelle der neueren stdlib verwendet wird. Es sagt nichts über die Laufzeitleistung aus (von der ich nur gesehen habe, dass sie in höheren C++-Modi schneller ist, abhängig von den verwendeten Funktionen, im schlimmsten Fall identische Geschwindigkeit). Was It's not as "zero cost abstractions" as it should. betrifft, bin ich mir immer noch nicht sicher, worauf Sie sich beziehen? Haben Sie Links zu aktuellen Berichten über Probleme mit der Laufzeitleistung, da der von Ihnen verlinkte Thread anscheinend überhaupt nichts damit zu tun hatte, da es sich nur um eine Erhöhung der Kompilierzeit um 300 ms beim Kompilieren mit math.h in der älteren stdlib handelte (ich bin Ich bin mir nicht sicher, ob ich das Problem einer pauschalen Verlängerung der Kompilierzeit eines kompilierten Objekts um 300 ms sehe)?

Ich werde mehr darüber recherchieren.
Ich verstehe, dass die Kosten einen Grund haben, aber längere Kompilierungszeiten sind für mich wirklich ein Anti-Feature. Ich habe nur einen Laptop und die Arbeit an Features für die Engine nimmt seine Zeit in Anspruch, da ich nach jeder Änderung auf den Kompilier- und Verknüpfungsprozess warten muss. Das noch weiter zu erhöhen ohne wirklich gerechtfertigten Nutzen, nur um eine neuere Version zu verwenden, ist keine gute Idee.

Ich habe nur einen Laptop und die Arbeit an Features für die Engine nimmt seine Zeit in Anspruch, da ich nach jeder Änderung auf den Kompilier- und Verknüpfungsprozess warten muss.

Wenn Sie ccache installiert haben, wird viel Zeit im inkrementellen Build-Prozess für das Verlinken aufgewendet. Sie könnten etwa eine Sekunde einsparen, indem Sie gold anstelle von ld zum Verlinken verwenden, und es ist wahrscheinlich möglich, es noch weiter zu optimieren, indem Sie zu lld wechseln .

Oh definitiv, ich kann nicht genug Gutes für Ccache und Ninja als Backing Builder sagen (wenn Sie zum Beispiel cmake oder so verwenden), beide sparen so viel Zeit!

Außerdem können Unity-Builds überraschend erstaunlich sein. Dort erstellen Sie eine neue unity.cpp -Datei, die nur alle cpp-Dateien im Projekt enthält, obwohl Sie normalerweise eine Unity-CPP-Datei pro "Modul" haben. eines Projekts, sodass Sie nur etwa ein Dutzend Speicher zur Verfügung haben, um den Speicher niedrig zu halten. Im Austausch für diesen zusätzlichen Speicher zur Kompilierungszeit wird sowohl das Kompilieren als auch das Verknüpfen so viel schneller. Diese sind jedoch weniger nützlich für den inkrementellen Wiederaufbau und nützlicher für Release-Builds.

Um eine zum Stapel hinzuzufügen:
static_assert

Zum Beispiel geht die Union SpatialMaterial::MaterialKey davon aus, dass die Struktur die gleiche Größe wie uint64_t hat, aber soweit ich das beurteilen kann, wird sie nirgendwo behauptet.

Wollte da static_assert(sizeof(MaterialKey) == sizeof(uint64_t)) reinhauen, konnte aber nicht.

Die andere Sache ist die Verwendung von unique_ptr, um eine ordnungsgemäße Bereinigung bei der Zerstörung sicherzustellen, ohne zu viele manuelle Boilerplates schreiben zu müssen und ohne unnötiges Ref-Counting zu verwenden.

Ich möchte meine 2 (oder 20) Cent als jemand anbieten, der neu bei Godot und seiner Codebasis ist. Ich überwache und arbeite derzeit daran, _Battle for Wesnoth_ nach Godot zu portieren. Jetzt ist das Frontend (der Editor und die GDScript-API) großartig! Abgesehen von ein paar Ecken und Kanten hat es uns bisher erlaubt, mit einem guten Tempo voranzukommen. Aber wir haben uns auch vorgestellt, dass wir (das Team) irgendwann Patches für das Backend (die Engine) beisteuern. Zu diesem Zweck habe ich Anfang dieser Woche das Git-Repo geklont und angefangen, in C++ herumzustöbern, und ehrlich gesagt ... bin ich etwas bestürzt.

Ich habe Erfahrung mit der Verwaltung einer großen C++-Codebasis in Form von Wesnoths alter benutzerdefinierter Engine. Es begann ebenfalls als C++03, wurde aber modernisiert, um C++11 und spätere Funktionen zu verwenden, und ist jetzt C++14-kompatibel. Ich habe diese Modernisierungsbemühungen angeführt (oft etwas zu eifrig) und ich glaube, dass unsere Codebasis dadurch viel lesbarer und einfacher zu handhaben ist. Zugegeben, ich habe nie intensiv mit einer reinen C++03-Codebasis gearbeitet; Ich habe C++ unter Verwendung moderner C++-Features gelernt. Aber für mich ist die Idee, dass Dinge wie auto , range-for und Lambdas Ihren Code weniger lesbar machen, einfach ... in der Tat sehr seltsam.

Nehmen Sie auto , es ist sicherlich möglich, es zu missbrauchen und zu überbeanspruchen, aber es hat auch eine Menge legitimer Verwendungen. Einer der häufigsten Orte, an denen wir auto bereitgestellt haben, als wir die Wesnoth-Codebasis aktualisiert haben, waren for-Schleifen mit Iteratoren. Früher hätten wir so etwas:

for(std::vector<T>::iterator foo = container.begin(); foo != container.end(); ++foo) {}

Was chaotisch ist! In den Fällen, in denen wir tatsächlich einen Iterator benötigten, haben wir Folgendes getan:

for(auto foo = container.begin(); foo != container.end(); ++foo) {}

Ja, jetzt kennen Sie den expliziten Iteratortyp nicht, aber das müssen Sie fast nie wissen . Ich habe hier in einigen Posts einen Kommentar gelesen, in dem es heißt, dass es schwieriger wird, wenn der Container einige Dateien entfernt deklariert, aber mit modernen Code-Editoren und Intellisense ist das kein großes Problem.

In den meisten Fällen würden wir stattdessen einfach zu range-for wechseln:

for(const auto& foo : container) {}

Viel schneller zu tippen und auch prägnanter. Sie müssen sich keine Gedanken darüber machen, Iteratoren innerhalb der Schleife zu dereferenzieren oder Indizes im Auge zu behalten. Und es ist völlig klar, dass Sie den gesamten Container durchlaufen. Bei Iteratoren müssen Personen, die mit dem Code nicht vertraut sind, überprüfen, ob die Schleife tatsächlich von Anfang bis Ende verläuft.

Die Verwendung auto in Range-for-Schleifen hat hier auch einen zusätzlichen Vorteil. Es macht eine Sache weniger, die Sie aktualisieren müssen, wenn Sie jemals den Typ des Containers ändern! Ich kann Juans Argument wirklich nicht verstehen, dass diese Dinge Ihren Code weniger lesbar oder weniger leicht verständlich machen. Bei mir ist es genau umgekehrt.

Im State of Godot-Video erwähnte er auch Lambdas. Auch hier ist es sicherlich möglich, sie zu missbrauchen, aber sie sind auch ein unglaublich nützliches Werkzeug! Hier ist ein allgemeines Paradigma, das ich vor der Verwendung von C++11 in Wesnoths Codebasis gesehen habe:

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);
}

Langer, unordentlicher, aufgeblähter Code. Und hier ist, was wir mit C++11 verwendet haben:

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; });
}

Das ist nur der häufigste Fall! Und ja, ich weiß, dass Sie auch operator< für T implementieren können und dass std::sort dies standardmäßig verwendet, aber es veranschaulicht meinen Standpunkt. Vielleicht liegt es auch daran, dass Sie nur modernes C++ gelernt und fast ausschließlich damit gearbeitet haben, aber ich denke, Tools wie auto , range-for und Lambdas zu missachten, wo es angemessen ist, schießt sich selbst ins Knie.

Was mich zu meinem nächsten Punkt führt. Mir ist aufgefallen, dass Godot intern viele seiner eigenen benutzerdefinierten Typen anstelle der STL-Typen verwendet. Wenn es Ihnen um die Lesbarkeit von Code in Bezug auf Dinge wie auto geht, ist die Verwendung benutzerdefinierter Kerntypen gegenüber STL-Typen absolut ein Nachteil! Vor ein paar Tagen habe ich die Codebasis durchsucht und bin auf eine Menge Code gestoßen, der so aussah:

container.push_back(T(args));

Das ist jetzt ineffizient. push_back (zumindest in Bezug auf std::vector ) akzeptiert eine konstante Referenz; daher führt dieser Operator zu einer unnötigen Kopie. Ich wollte einen Patch erstellen, damit sie emplace_back verwenden ... aber dann wurde mir klar, dass die gesamte Codebasis benutzerdefinierte Containertypen verwendete 😐

Eines der großen Probleme mit Wesnoths Design und einer der Hauptgründe für die Entscheidung, sich für eine neue Engine (in diesem Fall Godot) zu entscheiden, war, dass Wesnoth in hohem Maße unter dem Not Invented Here-Syndrom litt. Während wir Bibliotheken wie Boost verwendet haben, war unsere Rendering-Pipeline benutzerdefiniert, unser UI-Toolkit war benutzerdefiniert, unsere Daten-/Skriptsprache war benutzerdefiniert ... Sie verstehen das Wesentliche. Bevor wir mit der Arbeit an der Godot-Portierung begonnen haben, habe ich versucht (und bin gescheitert), hardwarebeschleunigtes Rendering zu implementieren (bis zu diesem Zeitpunkt hatten wir CPU-basiertes Oberflächen-/Sprite-Rendering verwendet. 2019. Ja, ich weiß, X_X). Die Texture -API von SDL hatte keine Shader-Unterstützung, und Shader-Unterstützung war erforderlich. Am Ende entschied ich, dass die Implementierung unseres eigenen Renderers, obwohl möglich, dem Projekt später einen unnötigen Wartungsaufwand auferlegen würde. Wir hatten bereits wenige Core-Entwickler, und jemanden zu finden, der OpenGL schreiben kann (oder Vulkan, falls wir OGL jemals fallen lassen mussten), wäre nur ein unnötiger Schmerz gewesen, wenn eine Engine wie Godot einen perfekt guten, gut gewarteten Renderer hat, den wir verwenden könnten stattdessen.

Ich spreche das an, weil ich denke, dass es ein gutes Beispiel dafür ist, warum es eine schlechte Idee sein kann, alles intern zu implementieren. Ja, Sie könnten Ihre Binärgröße ein wenig reduzieren, indem Sie die Standardbibliothek nicht verwenden, aber Sie tragen auch einen massiven Wartungsaufwand. Das Konvertieren dieser push_back -Aufrufe in emplace_back ist eine sehr tief hängende Frucht, die den Code sauberer und leistungsfähiger machen kann. Aber da Sie einen benutzerdefinierten vector -Typ haben, muss jemand ihn manuell implementieren, wenn Sie eine In-Place-Konstruktion wünschen. Und in allen anderen benutzerdefinierten Typen auch!

Ein noch größeres Problem ist, dass es die Eintrittsbarriere erhöht. Ich habe mir die Godot-Codebasis angesehen und C++-STL-Typen erwartet. Wenn ich diese nicht finde, muss ich oder jemand anderes jetzt genau lernen, welche Typen die Engine bereitstellt und welche API mit jedem einhergeht. Ich möchte std::map ? Nein, ich muss dictionary verwenden. Es macht den Betreuern das Leben nur schwerer und verkompliziert die Dinge für neue Mitwirkende. Und wirklich, ist Code, der wie eine Sache aussieht, aber eigentlich eine andere ist, nicht... unlesbar?

Vor ungefähr einem Jahr, als wir uns der Veröffentlichung von Wesnoth 1.14 näherten und auf Steam starteten, führte ich ein Refactoring-Projekt durch, um einen benutzerdefinierten Containertyp zu eliminieren, der im Wesentlichen std::list war, außer dass die Verwendung erase() nicht ungültig wurde Iteratoren. Hier ist der prinzipielle Commit, um den es geht . Danach war weitere Arbeit erforderlich, um es richtig zu machen, aber das Ergebnis war Code, der viel einfacher, leichter verständlich und weniger undurchsichtig war.

Abschließend denke ich, dass das Verbot bestimmter sehr nützlicher C++11-Funktionen und das Festhalten an benutzerdefinierten Typen gegenüber STL-Typen auf lange Sicht ein Hindernis für Godot sein wird. Ich weiß, dass das Umgestalten von Dingen lange dauert (glauben Sie mir, ich weiß), aber so wie die Dinge jetzt sind, scheint es sehr wahrscheinlich, dass Sie mit einem Catch-22 enden werden. Indem Sie versuchen, die Nachteile der Verwendung der STL (größere Binärgrößen usw.) zu vermeiden, wird es am Ende immer schwieriger, auf leistungsfähigeren und saubereren Code in neueren C++-Standards umzusteigen. Ich bin mir sicher, dass sich niemand besonders darauf freut, die Konstruktion vor Ort in allen benutzerdefinierten Typen zu implementieren. 😬

Ich weiß, dass meine Meinung hier nicht viel bedeutet, aber ich dachte, ich würde meine Gedanken aus der Perspektive von jemandem äußern, der mit einer großen C++-Codebasis gearbeitet hat, die von C++03 auf modernes C++ umgestiegen ist. Es ist viel Arbeit, aber auf lange Sicht denke ich, dass es sich lohnt.

Entschuldigen Sie die massive Textwand!

@Vultraz Stimme voll und ganz zu.

Ich habe zwar (fast) keine Erfahrung mit C++, aber nachdem ich einige Zeit mit GDNative + C++ gearbeitet habe, teile ich Ihre Ansicht.

Erst vor ein paar Tagen habe ich auf reddit im Thread „ Ist das Mitwirken an Godot eine gute Möglichkeit, C++ zu lernen? “ gepostet:

Ich würde es nicht empfehlen. Aus meiner Erfahrung bei der Arbeit mit GDNative und C++ erfinden sie das Rad mindestens mehrmals neu (benutzerdefinierte Sammlungen und Zeiger). Diese benutzerdefinierten Klassen sind undokumentiert (zumindest von GDNative C++ Bindings/Headers POV - abgesehen von einer Anleitung, die zu einem nicht kompilierten Demoprojekt führte, als ich es das letzte Mal versuchte, gibt es keine Dokumentation im Code [Header, Bindings] noch in offiziellen Dokumenten) und ein verwirrendes/unintuitives Verhalten aufweisen (z. B. das Verpacken einiger Engine-Klassen zu spät in Ref verursacht zufällige Abstürze, obwohl Ref intuitiv transparent sein sollte und daher das Verhalten einer Klasse, die umschlossen wird, nicht ändern sollte).

Ich bin auch kein Fan davon, meiner Meinung nach weniger lesbare Boilerplates zu bevorzugen, anstatt neue C++-Features zu verwenden. Ich mag Haskell (und seine Bibliotheken), seine Prägnanz, keine Anbiederung an Anfänger, um die Sprache für fortgeschrittene Benutzer nicht zu lähmen.

Aufgrund dieser Entscheidungen/Werte bezweifle ich, dass ich jemals zum Motor beitragen werde. (Es ist lustig, weil Engine-Entwickler angeben, dass der Grund für diese Entscheidungen darin besteht, Beiträge zu fördern. In meinem Fall hatte dies einen völlig gegenteiligen Effekt.)

@Vultraz ein gut artikulierter Artikel, großartig zu lesen, danke dafür. Es ist gut, eine solche Perspektive zu hören.

Ich bin ein C++-Programmierer alten Stils und als solcher hatte ich keine großen Probleme damit, den Godot-Quellcode zu verstehen, ich fand ihn gut strukturiert und sehr nach meinem Geschmack. Ich denke, dass ich dem Kern dieser Engine relativ schnell VR-Unterstützung hinzufügen konnte, während ich sehr wenig Erfahrung mit der Codebasis hatte, ist ein Beweis dafür, wie lesbar und verständlich diese Engine ist. Es mag in gewisser Weise altmodisch sein, aber ich bin immer wieder überrascht, wie gut bestimmte Teile gebaut sind. Ja, es gibt Teile, die modernisiert werden müssen, um weniger speicherhungrig und leistungsfähiger zu sein, aber insgesamt war ich davon beeindruckt.

Wenn ich Leute über neuere Syntax in C++ reden höre, fühle ich mich oft einfach alt und frage mich wirklich, was der ganze Wirbel soll. Aber ich verstehe irgendwie, dass, wenn Sie moderneres C++ lernen und sich dann entscheiden, zu Godot beizutragen, es seltsam ist, dass es das Rad neu zu erfinden scheint. Aber zumindest für Leute wie mich sind wir so daran gewöhnt, Klassen wie diese implementiert zu sehen, dass wir einfach beeindruckt sind, wie gut die meisten hier implementiert sind :)

Nun, da alles gesagt ist, es gibt so viele verschiedene Möglichkeiten, dieses Problem zu betrachten, es ist nicht lustig. Von den Ursprüngen von Godot vor der neueren C++-Syntax über das, womit sich die Core-Entwickler am meisten zu Hause fühlen, bis hin zu den Einschränkungen, die durch die plattformübergreifende Natur von Godot und die Geräte (einige nicht öffentlich) auferlegt werden, auf denen es ausgeführt werden kann (/könnte). Ich glaube nicht, dass es dabei ein Richtig oder Falsch gibt, es ist das, woran Sie gewöhnt sind und woher Sie kommen, und die Frage, ob die zusätzliche Lernkurve, um zu lernen, wie Godot funktioniert, die Vorteile der Umgestaltung eines großen Codes aufwiegt Base.

Ich weiß nicht, ob Sie es gesehen haben, aber Juan hielt einen Vortrag auf der GDC, der online gestellt wurde: https://www.youtube.com/watch?v=C0szslgA8VY
Während der Fragen und Antworten gegen Ende spricht er ein wenig über die Entscheidungsfindung in diesem Zusammenhang.
Es ist eine gute Uhr.

Was die obigen Reaktionen auf GDNative angeht, ganz normale Leute, GDNative ist eine neue Ergänzung, die einen bestimmten Bedarf erfüllt, es gibt keinen Hinweis darauf, wie das Kernprodukt strukturiert und aufgebaut ist.
Versuchen Sie, ein Modul zu erstellen (https://docs.godotengine.org/en/3.1/development/cpp/custom_modules_in_cpp.html), ja, das erfordert, dass Sie Ihre Änderungen in das Kernprodukt kompilieren, was das Problem ist, das GDNative zu lösen versucht, aber das gibt es Sie ein viel besseres Verständnis dafür, wie der Motor wirklich aufgebaut ist. Viele der oben genannten Argumente sind Mängel von GDNative, nicht von Godot selbst.
Ich hätte gerne eine dynamische Modullösung, die es mir erlaubt, Module auf die gleiche Weise zu bauen, wie wir statische Module bauen, vielleicht wird das eines Tages möglich sein, aber bis dahin reicht GDNative.

@Vultraz @mnn Ich kann den Sinn von STL-Containern tatsächlich verstehen, aber nur, weil einige Implementierungen (hauptsächlich MSVC) im Debug-Modus eine schrecklich langsame Leistung haben. Aber man könnte einfach die STL von EA verwenden und gut sein (ihre ist schnell und portabel).

Außerdem: Ich persönlich fand das Fehlen von RAII am bedrückendsten. Die Menge an manueller Bereinigung, die unnötigerweise im gesamten Code durchgeführt wird, ist seltsam, da RAII in C++11 nicht neu ist.

Was die obigen Reaktionen auf GDNative angeht, ganz normale Leute, GDNative ist eine neue Ergänzung, die einen bestimmten Bedarf erfüllt, es gibt keinen Hinweis darauf, wie das Kernprodukt strukturiert und aufgebaut ist.

Das stimmt natürlich, aber GDNative sollte nicht benutzerfreundlicher sein als die Engine selbst, da GDNative verwendet wird, um "Skripte" zu erstellen, die so auf eine Zielgruppe ausgerichtet sind, von der erwartet wird, dass sie über noch geringere C++-Kenntnisse verfügt, als diejenigen, die bereit sind, in Interna zu versinken der Motor?

Aufgrund dieser Entscheidungen/Werte bezweifle ich, dass ich jemals zum Motor beitragen werde. (Es ist lustig, weil Engine-Entwickler angeben, dass der Grund für diese Entscheidungen darin besteht, Beiträge zu fördern. In meinem Fall hatte dies einen völlig gegenteiligen Effekt.)

Ich bin ein C++-Noob (ca. zwei Monate arbeite ich 2 Tage die Woche in C++), also hätte ich die Zielgruppe sein sollen, die von dieser Entscheidung profitiert. Ich finde, dass Godot-Containern grundlegende Funktionen fehlen (im Vergleich zu stl, die selbst nicht zu umfangreich sind). Ich glaube nicht, dass Container GDNative-bezogen sind, aber ich könnte mich irren. Ich tue mein Bestes, Godot-Container zu vermeiden, weil es immer mühsam ist, mit ihnen zu arbeiten. Ich dachte, dass das inkonsistente und unerwartete Verhalten von Ref in der Verantwortung des Motors liegt, aber ich glaube, ich liege falsch.

Mir ist klar, dass mein Programmierhintergrund wahrscheinlich eher ungewöhnlich ist - beruflich jahrelang in JS/TS, Jahr in Scala, kleinere Hobbyprojekte in Haskell (einige tausend Zeilen, was eigentlich nicht so klein ist, wenn man bedenkt, wie prägnant Haskell ist - oft schreibe ich Code in C++, was in Haskell mindestens fünfmal kürzer und besser lesbar wäre). Ich frage mich, ob ich der einzige bin, der durch die Verwendung archaischer, übermäßig ausführlicher Technologie entmutigt wird.

@mnn , GDNative wurde erstellt, um die Erstellung von C-basierten Modulen als dynamische Bibliotheken zu ermöglichen, sodass Sie nicht die gesamte Engine neu kompilieren mussten. Darüber hinaus wurden mehrere Sprachbindungen erstellt, sodass Sie Module in C++, Python, Rust usw. schreiben konnten, die wiederum nicht das Kompilieren der gesamten Engine erforderten.

Das andere Ziel war, dass Entwickler Module erstellen können, bei denen Sie nur das Modul selbst liefern und es einfach mit einem stabilen Build der Engine verwenden. Viele Module sind nicht mehr verfügbar, weil sie noch nicht gewartet werden und an eine bestimmte Version der Engine gebunden sind.

Also ja, es ist viel einfacher und einfacher geworden, Module aus Kompilierungs- und Bereitstellungsperspektive zu erstellen. Aber aufgrund seines Ansatzes hat es seine Grenzen. Wenn Sie diese Einschränkungen einhalten, kann das Schreiben von C++-Code in GDNative einfach bleiben, da der Gegenstand dessen, wofür Sie ein Modul erstellen, einfach ist.

Versuchen Sie, diese Einschränkungen zu durchbrechen, und Sie werden Kopfschmerzen bekommen. Meine derzeitigen Kopfschmerzen drehen sich um den Versuch, OpenGL-Logik zu implementieren, während das alles in der visuellen Serverarchitektur eingekapselt ist und in GDNative nicht wirklich so verfügbar ist, wie ich es brauche. Aber das liegt eher daran, dass ich etwas mit GDNative machen möchte, für das es nie entwickelt wurde.
Wenn Sie es für das verwenden, wofür es entwickelt wurde, können Sie einige wirklich coole Dinge damit machen.

Beachten Sie auch, dass GDNative als Möglichkeit gedacht war, GDScript-Code umzuschreiben, der leistungsfähiger sein muss. Infolgedessen können Sie auf nichts innerhalb der Engine zugreifen, auf das GDScript keinen Zugriff hat (wie OpenGL).

Was ist mit der neuesten Coroutine-Unterstützung, dh co_await()? Aus procgenischer Sicht ist das RIESIG.

Die Unterstützung von Coroutinen ist Teil des C++20-Standards, der noch nicht finalisiert wurde, um es festzuhalten.

Erlauben Sie mir, mich einzumischen, ich schreibe derzeit fortgeschrittene (denken Sie an den Source-Hammer-Editor) Pinsel-/CSG-Tools für den 3D Spatial Editor, und dieses Gerede darüber, dass Auto nicht zugelassen wird, ist für mich wirklich verwirrend. Manchmal kann _selbst die Verwendung von auto explizit_ sein. Folgendes berücksichtigen:

Ich befinde mich im SpatialEditorViewportContainer und möchte den SpatialEditor abrufen, der drei Elemente in der übergeordneten Hierarchie ist (bevor jemand darauf hinweist, dass es bessere Möglichkeiten gibt, vom Viewport-Container aus auf den SpatialEditor zuzugreifen, bedenken Sie bitte, dass ich angefangen habe, mir diese Codebasis anzusehen gestern)

auto sp = Object::cast_to<SpatialEditor>(get_parent()->get_parent()->get_parent());

Wie Sie sehen können, gibt der dynamische Downcast _bereits explizit den Typ des SP_ an. Ohne Auto müsste ich überflüssigen Müll schreiben wie:

SpatialEditor sp = Object::cast_to<SpatialEditor>(get_parent()->get_parent()->get_parent());

Bitte, um Himmels willen, erlauben Sie die Verwendung von Auto!

Zum Thema, welcher C++-Standard verwendet werden soll: Die vorgeschlagenen Reflexions- und Metaklassenfunktionen von C++20 und darüber hinaus wären wirklich nützlich, um Makro-Unordnung zu reduzieren

GDCLASS(SpatialEditorViewportContainer, Container);

Außerdem möchte ich wiederholen, dass diese Einschränkungen mich dazu bringen, meine Entscheidung, überhaupt einen Beitrag zu leisten, wirklich zu hinterfragen. Um 2012 herum habe ich mir C++11 selbst beigebracht und diesen Standard nicht verwenden zu können, scheint ein Schlag ins Gesicht zu sein. C++11 und C++03 sind völlig unterschiedliche Sprachen, und der Ruf, dass C++ schwer zu lernen, schwer zu lesen und schwer zu schreiben ist, ist größtenteils auf C++03 (oder besser gesagt 98) zurückzuführen. Nicht mindestens C++11 oder 14 zu verwenden ist _schädlich_ für die Wartbarkeit und Lesbarkeit. Ich bin mit C++11 aufgewachsen und der Projektleiter offensichtlich nicht (als er 2007 anfing, an Godot zu arbeiten, war ich 12 Jahre alt), also denke ich, dass dies eher ein Fall von Präferenz und Baby-Duck-Syndrom ist. Ich habe das Gefühl, C++11 nicht zu verwenden, dient nur dazu, Leute zu trösten, die an altes (auch bekannt als schreckliches) C++ gewöhnt sind, auf Kosten von Leuten wie mir, die sich die Zeit genommen haben, modernes C++ zu lernen.

Im Laufe der Zeit werden immer mehr Junior-Programmierer wie ich mit modernem C++11 und darüber hinaus aufwachsen und die Tatsache, dass das Projekt für immer in einer Sprache feststeckt, die nicht einmal Lambdas hat, als ziemlich entmutigend empfinden.

Kurz gesagt: C++11 oder Pleite!

Um es klar zu sagen, ich befürworte nicht die Verwendung von STL. Das Rollen Ihrer eigenen Container ist in Ordnung, aber das Ablehnen von Funktionen wie Lambdas und Auto erscheint unsinnig.

Ich werde die Echokammer vorerst schließen, da es sinnlos ist, hier weiter zu diskutieren. Wir haben bereits vor Monaten darüber gesprochen, welche Art von Funktionen von C++11 und/oder einigen späteren Versionen wir verwenden möchten.

Wir warten nur darauf, dass @hpvb Zeit hat, die Richtlinien fertigzustellen, die er basierend auf unserem Konsens schreibt, und wir können dann mehr über diese Richtlinien diskutieren, sobald sie veröffentlicht sind. Bis dahin ist dies nicht konstruktiv.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

RebelliousX picture RebelliousX  ·  3Kommentare

ducdetronquito picture ducdetronquito  ·  3Kommentare

SleepProgger picture SleepProgger  ·  3Kommentare

blurymind picture blurymind  ·  3Kommentare

gonzo191 picture gonzo191  ·  3Kommentare