Godot: GodotでのC ++のステータス

作成日 2017年07月18日  ·  65コメント  ·  ソース: godotengine/godot

Godotでは、まだC ++ 98/03を使用しています。 この問題の目的は、それを維持する必要があるのか​​、それとも最新のC ++(C ++ 11/14/17、さらには20)のものを使い始めることができるのかを判断することです。

Godotが最初に作成されたとき、C ++ 03が最新バージョンであり、数年後、特定のコンパイラ(一部のコンソール、これまでのところ)での互換性の問題を回避するために、そのスタイル(標準のバージョン、標準ライブラリの回避など)が維持されました。私の知る限り)。 しかし、これらの懸念を再確認する必要があります。

考慮すべき項目(このリストは、成長を続けるか、一部のエントリを破棄することを目的としています。最初はブレインストーミングを行うだけです)。

  • スマートポインタ: Godotカスタムポインタ/オブジェクトメカニズムはそれと互換性がありますか? Godotを使用するには、大部分を書き直す必要がありますか? メリットはコストよりも大きいでしょうか?
  • 移動セマンティクス:ここで尋ねる質問は、スマートポインターの場合とほぼ同じです。
  • autoconstexpr 、ラムダなど:一般に、コードの記述を容易にしたり、現在のコアを中断することなく最適化の機会を提供したりする最新のC ++機能。
  • 標準のマルチプロセッシングプリミティブ:C ++は、標準のスレッド、アトミック、メモリバリアなどを提供するようになりました。カスタム実装を維持し、さまざまなプラットフォーム用に維持する必要がありますか?
  • STL / Boost / other :カスタムコンテナからよく知られた実装に切り替えることで、いくらかのメリットが得られますか? メンテナンス、パフォーマンス、互換性などに関するデメリット/デメリット。
  • register 、「トリック」のインライン化など:コンパイラにパフォーマンスの高いコードを生成させるためにコードに追加されたもの。 それらのいくつかは、非推奨であるか、実際の影響がないか、パフォーマンスを悪化させる可能性さえあります。 どちらをドロップしますか? どちらを維持しますか?
  • 等、

小さな変更は早期に行うことができます。 しかし、(その場合は)大きな変更はいつ行う必要がありますか? Godot 4.0? または、Godot 3.0が安定するとすぐに?

@ reduz、@ punto-、@ akien- mga 、@ karroffel 、@ bojidar -bg、@ BastiaanOlij、@ Falessなどの人を明示的に招待させてください。

discussion

最も参考になるコメント

Godotとそのコードベースを初めて使用する人として、2セント(または20セント)を提供したいと思います。 私は現在、_Battle forWesnoth_をGodotに移植する取り組みを監督および取り組んでいます。 これで、フロントエンド(エディターとGDScript API)は素晴らしいです! いくつかのラフなエッジに加えて、これまでのところ、良いペースで進むことができました。 しかし、私たち(チーム)は、ある時点でバックエンド(エンジン)のパッチを提供することも想像していました。 そのために、今週初めにgitリポジトリのクローンを作成し、C ++をいじり始めましたが、正直なところ...少しがっかりしています。

私は、Wesnothの古いカスタムエンジンの形式で大規模なC ++コードベースを管理した経験があります。 それもC ++ 03として始まりましたが、C ++ 11以降の機能を使用するように最新化され、現在はC ++ 14に準拠しています。 私はそのモダナイゼーションの取り組みを主導し(多くの場合、少し熱心になりすぎました)、コードベースがはるかに読みやすく、操作しやすくなったと感じています。 確かに、私は純粋なC ++ 03コードベースを広範囲に使用したことはありません。 最新のC ++機能を使用してC ++を学びました。 しかし、私にとっては、 auto 、range-for、ラムダなどによってコードが読みにくくなるという考えは、実に奇妙なことです。

autoを取ることは確かにそれを悪用して乱用することは可能ですが、それはまたたくさんの合法的な使用法を持っています。 Wesnothコードベースを更新したときにautoをデプロイした最も一般的な場所の1つは、イテレーターを使用したforループでした。 以前は、次のようなものがありました。

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

これは厄介です! 実際にイテレータが必要な場合は、次のようにします。

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

はい、明示的なイテレータタイプはわかりませんが、それを知る必要はほとんどありません。 ここでいくつかの投稿のコメントを読んで、コンテナがいくつかのファイルを離れて宣言されていると難しくなると言っていますが、実際には、最新のコードエディタとインテリセンスではそれほど問題にはなりません。

ほとんどの場合、代わりに範囲に切り替えるだけです。

for(const auto& foo : container) {}

入力がはるかに速く、簡潔にもなります。 ループ内のイテレータの逆参照や、インデックスの追跡について心配する必要はありません。 そして、コンテナ全体をループしていることは十分に明らかです。 イテレータを使用する場合、コードに慣れていない人は、ループが実際に最初から最後まで進んでいることを再確認する必要があります。

ここで範囲forループでautoを使用すると、追加の利点もあります。 コンテナのタイプを変更した場合に更新する必要があることが1つ少なくなります。 これらのことがコードを読みにくくしたり、理解しにくくしたりするというJuanの主張を私は本当に理解できません。 私にとって、それは正反対です。

State of Godotのビデオでは、ラムダについても言及しています。 繰り返しますが、それらを悪用することは確かに可能ですが、それらは信じられないほど便利なツールでもあります! C ++ 11を使用する前にWesnothのコードベースで見た一般的なパラダイムは次のとおりです。

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

長くて厄介なコードの膨張。 そして、これが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; });
}

これが最も一般的なケースです。 はい、Tにoperator<を実装することもでき、 std::sortはデフォルトでそれを使用することを知っていますが、これは私のポイントを示しています。 繰り返しになりますが、最新のC ++を学習しただけで、ほぼ独占的に機能しているだけかもしれませんが、 auto 、range-for、ラムダなどのツールを無視することは、自分の足を撃ち抜くことだと思います。

それが私の次のポイントにつながります。 GodotがSTLタイプではなく、独自のカスタムタイプの多くを内部的に使用していることに気づきました。 autoのようなものに関してコードの可読性が懸念される場合は、STLタイプよりもカスタムコアタイプを使用すると、絶対に不利益になります。 数日前、私はコードベースを閲覧していて、次のようなコードに出くわしました。

container.push_back(T(args));

さて、これは非効率的です。 push_back (少なくともstd::vectorに関して)はconst参照を取ります。 そのため、この演算子は不要なコピーになります。 emplace_backを使用するようにパッチを作成したかったのですが、コードベース全体がカスタムコンテナタイプを使用していることに気付きました😐

Wesnothの設計に関する大きな問題の1つであり、新しいエンジン(この場合はGodot)を採用することを決定する主な要因の1つは、WesnothがNot InventedHere症候群にかなり苦しんでいたことです。 Boostのようなライブラリを使用しましたが、レンダリングパイプラインはカスタムであり、UIツールキットはカスタムであり、データ/スクリプト言語はカスタムでした。 Godotポートでの作業を開始する前に、ハードウェアアクセラレーションによるレンダリングを実装しようとしました(失敗しました)(この時点まで、CPUベースのサーフェス/スプライトレンダリングを使用していました。2019年。はい、X_Xを知っています)。 SDLのTexture APIにはシェーダーサポートがなく、シェーダーサポートが必要でした。 結局、私は、独自のレンダラーを実装することは、可能ではあるが、プロジェクトに不必要なメンテナンスの負担を課すことになると判断しました。 コア開発者はすでにほとんどいませんでした。Godotのようなエンジンに、使用できる完全に優れた、手入れの行き届いたレンダラーがある場合、OpenGL(またはOGLを削除する必要がある場合はVulkan)を作成できる人を見つけるのは不必要な苦痛でした。代わりは。

すべてを社内で実装することが悪い考えである理由の良い例だと思うので、それを取り上げます。 はい、標準ライブラリを使用しないことでバイナリサイズを少し減らすことができますが、メンテナンスの負担も大きくなります。 これらのpush_back呼び出しをemplace_backに変換することは、コードをよりクリーンでパフォーマンスの高いものにすることができる非常に簡単な成果です。 ただし、カスタムのvectorタイプがあるため、インプレース構築が必要な場合は、誰かが手動で実装する必要があります。 そして、他のすべてのカスタムタイプでも!

さらに大きな問題は、参入障壁が高くなることです。 私はC ++ STLタイプを期待しているGodotコードベースを見ました。 それらが見つからないということは、私や他の誰かが、エンジンが提供するタイプと、それぞれに対応するAPIを正確に知る必要があることを意味します。 std::mapが欲しいですか? いいえ、 dictionaryを使用する必要があります。 それは、メンテナの生活を困難にし、新しい貢献者の生活を複雑にします。 そして実際には、あるもののように見えるが実際には別のものであるコードではありません...判読できませんか?

約1年前、Wesnothの1.14リリースとSteamでのリリースに近づいたとき、 erase()を使用しても無効にならないことを除いて、本質的にstd::listであったカスタムコンテナタイプを排除するためのリファクタリングプロジェクトに着手しましたイテレータ。 これが問題の主要なコミットです。 その後、正しく処理するためにさらに作業が必要でしたが、その結果、コードははるかに単純で、理解しやすく、不透明度が低くなりました。

結論として、特定の非常に便利なC ++ 11機能を非合法化し、STL機能よりもカスタムタイプを使用することは、長期的にはGodotの障害になると思います。 リファクタリングには長い時間がかかることは知っていますが(私を信じてください、私は知っています)、現在の状況では、キャッチ22になってしまう可能性が非常に高いようです。 STLを使用することの欠点(ラガーバイナリサイズなど)を回避しようとすると、新しいC ++標準でよりパフォーマンスが高くクリーンなコードに切り替えることがますます難しくなります。 すべてのカスタムタイプでインプレース構築を実装することを特に楽しみにしている人はいないと思います。 😬

私の意見はここではあまり意味がないことは知っていますが、C ++ 03から最新のC ++に移行した大規模なC ++コードベースを使用したことがある人の視点から考えてみようと思いました。 大変な作業ですが、長期的にはやりがいを感じます。

巨大なテキストウォールすみません!

全てのコメント65件

これは何度も議論されましたが、通常の答えは次のとおりです。

1)コードベースを03より上に移動する必要はありません。機能はそれだけの価値はありません。 GDNative C ++の場合、それを使用することは完全に可能です。
2)STL / Boost / Etcを使用したくない。 理論的根拠は常に同じです:a)Godotテンプレートは通常は行わない小さなことを行います(つまり、アトミックrefcountsとコピーオンライト)b)STLは、非常に長いマングルシンボルのために巨大なデバッグシンボルとデバッグバイナリを生成します

私たちはすべてがそうであるのと同じ方法を保ちます。

プライベートな会話について意見をいただきましたが、
より公開された議論。

すでにそんなに議論されて閉鎖されているとは知りませんでした。

auto、constexprなども破棄しますか?

'登録'を削除しますか?

エル18年7月。 2017 1:54 pm、「JuanLinietsky」 [email protected]
escribió:

これは何度も議論されましたが、通常の答えは次のとおりです。

  1. コードベースを03より上に移動する必要はありません。機能は次のとおりです。
    それだけの価値はありません。 GDNative C ++の場合、それを使用することは完全に可能です。
  2. STL / Boost / Etcを使用する必要はありません。 理論的根拠は常に
    同じ:a)Godotテンプレートは、通常は実行しない小さなことを実行します(つまり、アトミック
    refcountsおよびコピーオンライト)b)STLは巨大なデバッグシンボルを生成し、デバッグします
    非常に長いマングルシンボルによるバイナリ

私たちはすべてがそうであるのと同じ方法を保ちます。


スレッドを作成したため、これを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/godotengine/godot/issues/9694#issuecomment-316041201
またはスレッドをミュートします
https://github.com/notifications/unsubscribe-auth/ALQCtipKmepD_1Xw6iRXZ7aGoQlLfiwFks5sPJzqgaJpZM4ObOio

私はこの問題を提起しましたが、スマートポインターを使用する方が安全であり、このコードはNIH症候群に苦しんでいますが、高位の人はアップグレードするつもりがないので、あきらめることをお勧めします。

コードのアップグレードにはおそらく多くの工数がかかり、開発者は新機能の実装に費やすことを好みます。 私はどういうわけかこれを理解しています-エンジンユーザーは「リファクタリング」よりも新しい機能を取得することを好みます(技術的負債のようなものを理解することなく)。

@Marqin申し訳ありませんが、スマートポインタは悪い考えです。理由は次のとおりです。

1)唯一の有用なものはrefcountedのものであり、残りは言語にすでに存在する機能のための精神的なマスターベーションです。
2)どこでもスマートポインタを使用するのはひどい考えです。メモリリークを引き起こす参照サイクルが発生するリスクがあります。
3)Ref <>に似たものがすでにあり、参照カウントの動作を制御できるという利点があります。そのため、独自のgcを持つC#などの言語へのバインディングを処理するための特別なケースを追加できます。

だから、気まぐれで物事を決めると主張する前に、ただ聞いてください。 私たちは通常、情報に基づいた決定を下し、プロセスを全員と共有します。

品質を向上させたい場合に簡単にできることは、単体テストの作成を開始することです(そのために、オプションのジョブをsconsに追加し、devにgmock / gtestをインストールさせます[詰まらないように./thirdparty])。

残りは、言語にすでに存在する機能のための精神的なマスターベーションです

@reduz 、明示的な所有権とRAIIメモリ解放を備えたunique_ptrは、言語にすでに存在しているのでしょうか。

また、メモリ管理だけでなく、スレッド化も簡単になります(例: lock_guard )。

@RandomShaper将来のある時点でアップグレードすることに反対はしていませんが、多くの便利な機能がありますが、新しいC ++バージョンでは、プログラマーがより明確でないコードを記述できる傾向があります。 その結果、コードは一般的に他の人が読みにくくなります(通常は自動キーワードの乱用があります)。

より高度な機能を使用しないことのもう1つの利点(たとえば、例外を使用せず、rttiをほとんどコストをかけずに無効にできる)は、コンパイラーがはるかに小さいバイナリーを生成することです。

それはいいです。 私はあなたの言葉を尊重します。 これについて話し合うのは健康的なことだと私だけが思います。

一部の大手ビデオゲーム会社(Ubisoft、IIRC)がビッグコードベースを最新のC ++に移行したことを知っているので、Godotにも完全に適合している必要があります。 もちろん、ご指摘のとおり、これには作業と時間が必要です。

それが私たちができる理由です:

  • 可能な限り痛みのないすべてのコードの移行のための合理的なサブセットを選択します。
  • または、少なくとも新しいコードに対して「承認済み」として妥当なサブセットを選択します。

どちらの場合も、 autoで発生すると言ったように、機能を乱用しないようにコーディングスタイルを定義する必要があります。

もちろん、今は優先度の高いものもありますが、3.0が安定したときなど、将来的にはすでに決めておくとよいでしょう。

@Marqin前述のように、Godotはノードに独自のメモリ割り当てスキームを使用します。これは、新しい標準ライブラリによって提供されるものよりも理にかなっています。

また、ロックガードは事実上3行のコードであり、C ++ 11+を使用せずにすでに実装されています

繰り返しになりますが、なぜ「標準」のものは1)すでに持っているものよりも優れている必要があり、2)とにかくそれを使用する場合は、より優れていると想定する必要がありますか? 。私はそれが一般的なファラシーだと思います。

次は、印刷機能を削除してostream / istreamを使用しますか? Stringクラスを変更します。これは非常に優れており、より不自由なstd :: stringまたはstd :: wstringに置き換えますか?

標準ライブラリはすべての目的を果たすわけではなく、標準ライブラリであるという理由だけで他のすべてよりもうまく機能するわけではありません。 それらはそれらを必要とする人々のためだけにあり、あなたがそれをする正当な理由があるならば、それらを無視してあなた自身の実装を書くことは問題ありません。 私たちはそうし、それについて自信を持っています。

@RandomShaper問題は次のとおりです。

1)メリットよりもコストがかかります。
2)C ++ 11+の標準的な書き方についての多くの議論への窓を開きます。 いつか持っておく価値があるかもしれませんし、これらの機能を利用するためにエンジンの大部分を書き直すことはいつか役立つかもしれませんが、もっと重要なことに焦点を当てることができると思います。
3)また、前述のように、新しいC ++バージョンへの移植と同じくらいクールなサウンドであるため、バイナリサイズがはるかに大きくなる可能性があります。 そのような場合、それは望ましくないかもしれません。

現時点では真剣に価値がないので、数年後にもう一度話し合うことができます

私が理にかなっていると思うのは、Godotが--std = c ++ 17のようなオプションでコンパイルされることを確認することです。そうすれば、必要に応じて最新のC ++で記述されたライブラリを簡単に取り込むことができます。 たとえば、コードベースからのregisterキーワードの削除に投票しますhttps://github.com/godotengine/godot/issues/9691

どういうわけか、gcc-6.3がサポートされていないことをどこかで読んだことを覚えています(google
https://github.com/godotengine/godot/issues/7703にあったと言います)。 gcc-6.3が私のディストリビューション(debian安定版)のデフォルトコンパイラであるため、気になります。 誰かがそれを確認できますか? なんで?

@efornara一部のサードパーティライブラリはすでに新しいC ++バージョンを必要としていますが、クローンビルド環境ではSconsがそれを処理するため、これで問題ありません。 etc2comp thridpartyコードをチェックして、どのように機能するかを確認してください。

@karroffelありがとう、私はそれを知りませんでした。

それは素晴らしい機能ですが、ライブラリのインポートには必要ありません(ただし、グルーコードの場合、godotをさらにインクルードする必要がある場合は、コンパイルされないヘッダーファイルに遭遇する可能性があります)。

ちなみに、誰かが同様のことをする必要があり、この投稿を見つけた場合、関連するファイルはhttps://github.com/godotengine/godot/blob/master/modules/etc/SCsubです。 grepを使用して見つけましたが、現時点でこれが必要なのはこの場所だけのようです。

c ++ 11をnot-c ++ 11コードとリンクするのはそれほど安全ではありません-http: //gcc.gnu.org/wiki/Cxx11AbiCompatibility

@Marqinリンクを誤解しない限り、これは実際には、godotが標準ライブラリのコンポーネントを使い始めていない場合をサポートしているようですが、代わりにカスタムコンポーネントに固執しています。

C ++ 98言語はC ++ 11言語とABI互換ですが、標準ライブラリのいくつかの場所で互換性が失われています。

言語の混合はかなり安全に見えますが(私は認めますが、将来的には機能しなくなる可能性があります)、ペア、ベクトル、リストなどを混合すると問題が発生することが知られています。

@efornaraリンクは、C ++ 11を使用するサードパーティのライブラリとC ++ 03を使用するGodotをリンクすることに関するものです。

@Marqinはい、しかし私がリンクを理解する方法はあなたがそれをすることができるということです。 たとえば、std :: list <>をgodotからサードパーティライブラリに渡すことはできません。

@reduzで、Godotの内部Ref <>のドキュメントを見つけることができますか? Godotの内部コンテナがSTLのものとどのように異なるかについてのドキュメントはどこにありますか?

@Marqinコンテナの場合、それほど多くはありません。
http://docs.godotengine.org/en/stable/development/cpp/core_types.html#containers
Ref <>については、何もないことに気づきました。 おそらく追加する必要があります。
これらのものを追加するためにドキュメントを改善する方法についての提案は大歓迎です。

個人的には、新しいC ++標準は非常に優れていると思いますが、Godotの内部リファクタリングは、利益が少なすぎるために多大な労力を必要とするため、提案しません。

また、下位互換性の1つは、この正確な理由からC ++の特徴の1つです。 それでも、Godotの新機能やGDNativeで使用できるようにしたいと思います。
この理由から、Godotを最新のC ++サポートでコンパイルすることをお勧めします。

@reduzあなたが言ったように、C ++ 11/14/17では、あまり明示的でないコードを書くことができます。 これはC ++初心者にとっては難しいことですが、C ++パワーユーザーにとっては良いことです。
「オート」については、個人的にはパワーユーザーにもいいと思います。 厳密に必要でない場合は、タイプを何度も入力することを回避できるだけでなく、いくつかのバグを入力することも回避できます。

参考までに、最近のマスター(godot3)を次のコマンドでコンパイルしました。

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

Debianストレッチ(gcc-6.3)。 厄介なことに、このオプションはCファイルのコンパイル時にも設定されるため、Cファイルを有効にすると警告が殺到しますが、それ以外はすべてスムーズに進みました。 registerキーワードでも問題ないようです。

公式ビルドをこのようにコンパイルすることを提案することはしませんが、プロジェクトで必要な場合はオプションがあることを知っておくとよいでしょう。 私が提案するのは、これを破る変更はすべてリグレッションとして扱われるということです。

編集:いくつかのタイプミスを修正し、警告に関する記述をより明確にしました。

迷惑なことに、このオプションはCファイルをコンパイルするときにも設定されます

CPPFLAGSで試しましたか?

@Hinsbartいいえ、しませんでした。 方法はあるかもしれませんが、私はスコンをよく知らないので、いじくり回すことなく、可能と思われることを単純に実行しました。

$ 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

編集:ちなみに、godotビルドシステムでどのように使用されているかはわかりませんが、 CPPFLAGSを使用すると、先行オプションについて考えることができます。 個人的には、C ++には常にCXXFLAGSを使用しています。

CPPFLAGSを試してみましたか?

CPPFLAGSはプリプロセッサを使用するすべての言語に影響を与えると思うので、CとC ++の両方が含まれています。

@ m4nu3lfGDNativeでは任意の形式のC ++を使用できます。

私の経験では、コードを削除する機会は良い機会です。

おそらく、どのファイルを削除してc ++ 11バリアントに置き換えることができるかを文書化したある種のwikiページを設定することができます。 これには、おそらくスレッドプリミティブなどが含まれます。

変更のための変更は良くありません(proverbial koolaid)が、この場合、LLVMプロジェクトと他の多くのFOSSプロジェクトは、より明確な構文パターンのいくつか、つまり新しいfor-iterator表記を支持して進んでいますが、 (正直に言うと)プラットフォーム固有のコードをできるだけ少なく維持することがゲームエンジンにとって理想的であるため、関心の分離をそれぞれの言語ランタイムにオフロードします。

これまでに作成する最高のコードは、作成しないコードです。 :)

好奇心から、古いC ++と新しいC ++の両方でコンパイルするコードを書くことは可能ですか? C ++バージョン間に重大な重大な変更はありますか?

好奇心から、古いC ++と新しいC ++の両方でコンパイルするコードを書くことは可能ですか? C ++バージョン間に重大な重大な変更はありますか?

新しいC ++は、99.99で古いC ++と下位互換性があります...ケースの%(使用されるべきではなかったもの、または誤って定義されたもののみがサポートされなくなったと定義されましたが、一般的にはコンパイル警告のみが発生しますが、現在でも機能します) 。

ただし、新しいC ++には重要な機能があり、古いC ++バージョンでは明らかに機能しません。これらの機能は、可変個引数マクロやautoやラムダなどの使いやすさ機能だけでなく、 moveなどの効率機能でもあります。

VisualStudioでさえ最新のC ++ 17をサポートしていることを考えると、新しいバージョンを使用しない理由は実際にはありません。

コメントしたかった。 私はC ++ 17フラグでgodotをコンパイルすることができましたが、唯一の問題はauto_ptrを使用したサードパーティのものの1つです(それがどれほど悪いかのためにC ++ 17から削除されました)

問題は、それがc ++ 17でコンパイルされないということではありません。問題は、c ++ 17機能を使用したい人、さらに悪いことに、それらを使用してPRを開始することです...プロジェクトがc上にあることを発見するだけです。 ++ 03。

ラムダの欠如だけでも、ここでは見られない大きな問題です。
何度か、godotはデータの一時的なリストを保存して、それを繰り返す必要があります。
これらのケースのすべてにおいて、それは「for_each」またはそれ自体で反復する同様の構造として実行でき、コードを大幅に簡素化し、メモリ使用量を削減し、ラムダがインライン化/最適化されることでパフォーマンスを向上させます。 (これはC ++ 11であり、どこでも使用されています)。 リンクリストのすべての一般的なループについてもまったく同じです。

移動演算子はまた、より良いデータ構造を可能にし、現在の絶対にひどいものをより最適化されたものに置き換えることができます。

文字列データ型( "mystring" _hsなど)などを使用すると、ハッシュされた文字列をコード内のいたるところに拡散して、文字列チェック(スクリプトやイベントコードで非常に一般的)を大幅に高速化できます。

std :: threadが使用されていない場合でも(私自身はかなりひどい抽象化だと思います)、stdアトミックライブラリは素晴らしく、非常に便利です。 std :: atomicなど。

そして、人々が現代のライブラリを簡単に統合できない場合、C ++ 03を強制することでプロジェクト自体がどれほど傷つくかについても話さないでください。これは、godotがそのような古いバージョンのC ++で唯一の放棄されていないオープンソースC ++プロジェクトのようなものだからです(私の知る限りでは)

個人的には、保守的で最新のC ++標準に準拠しないことに同意しますが、C ++ 14のいくつかの精査された機能を備えたC ++ 11のようなものが最も効果的であり、Godotを大幅に改善すると思います。 C ++ 11-14は、ps4、xbox、switch、pc、low end pc、android、IOS、およびHTML5WebAssemblyに移植するUnrealEngineには十分です。 はるかに少ない数のプラットフォームをサポートしているのに、godotがそれ自体を制限する必要がある理由がわかりません。

文字列データ型( "mystring" _hsなど)などを使用すると、ハッシュされた文字列をコード内のいたるところに拡散して、文字列チェック(スクリプトやイベントコードで非常に一般的)を大幅に高速化できます。

さらに、スイッチなどで使用できます。 フライ級の文字列の実装を置き換えるために数年前にAtomタイプを作成したように:
https://github.com/OvermindDL1/OverECS/blob/master/StringAtom.hpp

32ビット(最大5文字、またはテーブルを少し再コーディングした場合は6文字)と64ビットバージョン(厳密なエンコードを選択するかどうかに応じて最大10文字または12文字)の両方があります。 これは完全にリバーシブルで、コンパイル時に、または実行時にいずれかの方向に動的に発生し、スイッチなどで完全に使用できます...そのファイルの使用例:

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

LUAコードを操作するときは、文字列を使用して、境界で変換を実行しました。 そのファイルはその「最新」バージョンではありません。整数から文字列に戻るときにメモリ割り当てなしで変換を行う関数を追加しました(コンパイル時に実行されるため、文字列->整数では割り当てられません) 。 Atom64 / Atom32 /その他の型(下にある整数)を表示するときにエンコードを逆にするVisual StudioまたはGDBフィルターを作成するのは簡単です。これにより、奇妙なハッシュ値の代わりに文字列型を確認できます。

しかし、このようなものは、特にパフォーマンスに敏感なコードやコードを読みやすくするために非常に役立ちます。これにはC ++ 11が必要であり、新しいものは必要ありません。

少なくとも、C ++ 14がGodotの標準であるべきだと思います。 C ++ 17は素晴らしいでしょう(いくつかの新しいコードでいくつかの非常に便利なパフォーマンスの強化)が、C ++ 14は今ではかなり普遍的な最小値です。 しかしもちろん、gcc / clang / VisualStudioやその他すべてがC ++ 17を正常にサポートしているので(そしてC ++ 20の大きなチャンクでさえ)、C ++ 17も同様に良いようです。 私はおそらくまだ「念のため」にC ++ 14を選ぶでしょう。

@ OvermindDL1そのアトムのことは素晴らしいです、それが大好きです。 それは間違いなくgodotと非常によく合います。そこでは、その正確なことを何度も実行します。

そのアトムのものは素晴らしいです、それを愛してください。 それは間違いなくgodotと非常によく合います。そこでは、その正確なことを何度も実行します。

@ vblanco20-1 Godotが望むなら、彼らは自由にコードを吸収することができます。 それ(そしてそれは前身のフライ級ストリングです)は私の古いC ++エンジンで長い間使用されてきました。 これは、小さなイベントタグ、物事の「キー」として使用する短い文字列に非常に役立ち、Lua / LuaJitを簡単に行き来できるほか、デバッガーフィルターが大いに役立ちました。

ラムダの欠如だけでも、ここでは見られない大きな問題です。

ほとんどの場合、ラムダによってコードが読みにくくなると考えるのは私だけですか?

@Faless :いいえ、あなただけではありません。ラムダが読みにくいことが、Akienがc ++バージョンをアップグレードしていない理由の1つだと思います。

ラムダは単なる小さなインライン関数です。 あなたがそれらについて「読みにくい」とコメントしているのかわかりません。 ただし、比較関数を送信するソートアルゴリズムや、残りのstdアルゴリズムライブラリなどを使用できます。 また、一時的な配列(octreeや他のシステムのソースコードで複数回発生する)の必要性を排除することで、メモリを節約し、パフォーマンスを大幅に向上させることができます。

また、stdアルゴリズムライブラリを使用すると、並列for、並列ソート、並列累積を介して、取得できるプログラムをマルチスレッド化する最も簡単な方法です。

コードの品質、パフォーマンス、および再利用性を大幅に向上させることができる場合、人々はそれらを「奇妙な」ものとして書き留めることは絶対に残念です。

私がすでに実装したものの実際の例:

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

この他のケースもあり、何度も繰り返されます

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

私がすでに実装したものの実際の例:

ここでの利益はわかりません...

コードの品質、パフォーマンス、および再利用性を大幅に向上させることができる場合、人々はそれらを「奇妙な」ものとして書き留めることは絶対に残念です。

@ vblanco20-1さて、私は自分自身を説明しようとします。
人々は通常、次のようなことを書くことになります。

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

そして、他のいくつかの場所で:

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

これを読むと、実行されている関数や何を探すべきかについての手がかりが得られません。 同じファイルにある場合は問題ないかもしれませんが、その配列を複数のファイルに渡した場合は、コード全体が読み取れなくなります。

@Falessあなたの例は、ラムダで実行できる最悪の例です(そして、そのような使用は絶対に禁止されるべきです)。

ラムダを「時間の経過とともに」使用することは、それらを割り当てる必要があるため、それらの適切な使用法ではありません。また、ラムダを実行するまでにキャプチャされた変数が有効でなくなる可能性があるため、大きな危険になります(たとえば、ポインタを押してからラムダを呼び出す前にオブジェクトを削除すると、キャプチャされたポインタがぶら下がります)。 ラムダを「即座に」使用するデータ構造やアルゴリズムと一緒に使用するために、ラムダを実際に防御するだけです。

for_eachの例では、for_eachは内部データ構造の変更に影響されません(たとえば、変数を少し並べ替えると)、より「不透明な」データ構造が可能になります(これにより、開発者は「簡単に」できるようになります) 「あるデータ構造から別のデータ構造に変更して、どちらがより適切に機能するかをテストします。これを採用することで、「使用」レイヤーを使いやすくしながら、不透明に機能するはるかに複雑なデータ構造を実装できます。

また、定型文を減らし、コードが実際に何をしているのかをより「明確」にします(リンクリストを繰り返します)。 「first()-> self()-> self」を取り除くだけで、それ自体が改善されます。

これの利点は、実際には使用量が増えるにつれて蓄積されることに注意してください。「unordered_for_each」のように、ノードをメモリ内の順序で(アロケータを介して、またはリンクリストが一番上に格納されている場合)反復することができます。配列)、または「reverse_for_each」。 「検索」や「並べ替え」などでも。 (少なくとも、範囲がマージされるC ++ 20までは、std ::アルゴリズムをそれほどお勧めしません。データ構造の一部として独自のアルゴリズムを実装する方がはるかに優れています)

ラムダは基本的に、Epic GamesがC ++ 11から非現実的なエンジンに許可した最初のものであり、Sort、Partition、Filter、RemoveAll、Findなどの独自のアルゴリズムライブラリと並んでいます。それ以来、ラムダはソースコードで非常に頻繁に使用されています。エンジンコードとゲームプレイコード

かっこいい、少なくとも私たちは、lamdbasが聖杯ではないことに同意します
プログラミング、そしてそれらを使用しない方法についての1つのルールをすでに定義しました。

パフォーマンスについてもっと調べてみます。

2018年12月8日土曜日、17:06 vblanco20-1 < [email protected]は次のように書いています:

@Faless https://github.com/Falessあなたの例は、
ラムダでできる最悪の場合(そしてそのような使用法は間違いなく
禁止された)。

「時間を超えて」ラムダを使用することは、ラムダを適切に使用することではありません。
割り当てるために、そしてそれらはまた、捕獲された変数として、大きな危険になります
ラムダを実行するまでに有効でなくなります(たとえば、
ポインタをキャプチャし、ラムダを呼び出す前にオブジェクトを削除し、
キャプチャされたポインタはぶら下がります)。 私は本当にラムダを守るだけです
それらを使用するデータ構造およびアルゴリズムと一緒にそれらを使用する
「即座に」。

for_eachの例では、for_eachは内部の変更に影響されません
データ構造(たとえば、変数を少し並べ替えると)、
より「不透明な」データ構造(これにより、開発者は次のことができるようになります)
あるデータ構造から別のデータ構造に「簡単に」変更して、どちらをテストするか
うまくいくかもしれません。 それを受け入れることで、さらに多くのことを実装できます
「使用」を維持しながら、不透明に機能する複雑なデータ構造
使いやすいレイヤー。

また、定型文を減らし、コードが何であるかをより「明確」にします
実際にやっています(リンクリストを繰り返します)。 ただ「
first()-> self()-> self "はそれ自体の改善です。

これの利点は、実際には使用量が増えるにつれて蓄積されることを覚えておいてください。
あなたは繰り返す「unordered_for_each」のようなものを持つことができるでしょう
ノードがメモリ内にある順序で(アロケータを介して、または
リンクリストは配列の一番上に保存されます)、または「reverse_for_each」。 物事さえ
「検索」や「並べ替え」など。 (私はstd :: algorithmsをそれほどお勧めしません。
少なくとも範囲がマージされるC ++ 20まで。 独自のアルゴリズムの実装
データ構造の一部として使用する方がはるかに優れています)

ラムダは基本的に、EpicGamesがC ++ 11から
Unreal Engineと、Sortなどの独自のアルゴリズムライブラリ
パーティション、フィルター、RemoveAll、検索など。それ以来、それらはかなり頻繁に使用されます
エンジンコードとゲームプレイコードの両方でソースコードで使用されます


あなたが言及されたので、あなたはこれを受け取っています。
このメールに直接返信し、GitHubで表示してください
https://github.com/godotengine/godot/issues/9694#issuecomment-445474001
またはスレッドをミュートします
https://github.com/notifications/unsubscribe-auth/ABnBbvBTxThAh0v8AfFCdGsSv2HFnEz6ks5u2_GKgaJpZM4ObOio

コードベースの保守性を向上させるC ++ 11の機能:

  • overrideおよびfinal
  • 範囲ベースのforループ
  • nullptr
  • 強く型付けされた列挙型
  • explicitキーワード

生活の質を向上させるC ++ 11の機能:

  • 直角ブラケット(これ以上Vector<Vector> >ありません)

[[nodiscard]] (C ++ 17?)ここを参照

新しいものを使用するため、または使用できる機能が1つまたは2つあるため、最新バージョンを採用する必要はないと思います。 メリットは価値がないため、C ++ 11以外のものを使用することは提案しません。
C ++が進化するにつれて、stlはどんどん大きくなり、プリプロセッサの段階で数百行がプロジェクトに追加されます。 ほとんどの場合、パフォーマンスに顕著な影響があります。

ほとんどの場合、パフォーマンスに顕著な影響があります。

絶対にすべきではありませんか? 実行されないコードは、コンパイラのバグなどがない限り、パフォーマンスに影響を与えることはありません。リリースでは、最終的なバイナリに含まれるべきではありませんか? 多くの場合、適切な移動セマンティクスだけを使用すると、一部の領域でパフォーマンスが大幅に向上します。古い例では、更新されたディレクティブによって、他の機能の中でもパフォーマンスが向上する可能性があります。

@ OvermindDL1はい、理論的にはそうあるべきです。
これをチェックしてください: https ://twitter.com/zeuxcg/status/1085781851568914432
最近、ソーシャルメディアでこのようなケースをいろいろ見かけます。 本来あるべき「ゼロコストの抽象化」ではありません。

@lupoDharkaelそのツイッタースレッド(最終的にロードされると、聖なるすごいツイッターは恐ろしいインターフェースです...私はそれを忘れ続けます...)libstdc ++のmath.hはC ++ 17モード(ここでlibc ++にはその問題はありません)、実行速度を向上させるためのオーバーロードと機能が多いため、math.hを取り込むコンパイルでは、新しいstdlibの代わりに特定の古いstdlibを使用すると、コンパイル時間が約300ミリ秒長くなります。 ランタイムパフォーマンスについては何も述べていません(使用する機能に応じて、より高いC ++モードでのみ高速になり、最悪の場合は同じ速度になります)。 それで、 It's not as "zero cost abstractions" as it should.に関しては、あなたが何を参照しているのかまだわかりませんか? 古いstdlibでmath.hを使用してコンパイルすると、コンパイル時間が約300ミリ秒増加しただけなので、リンクしたスレッドはそれとは何の関係もないように見えたため、ランタイムパフォーマンスの問題の実際のレポートへのリンクはありますか(私はとにかく、コンパイルされたオブジェクトのコンパイル時間が300ミリ秒増加するという問題が発生するかどうかはわかりません)?

それについてもっと調べます。
コストには理由があることは理解していますが、コンパイル時間が長くなることは、私にとって本当に反機能です。 私はラップトップしか持っておらず、変更のたびにコンパイルとリンクのプロセスを待たなければならないため、エンジンの機能の作業には時間がかかります。 新しいバージョンを使用するためだけに、本当に正当な利点なしにそれをさらに増やすことは良い考えではありません。

私はラップトップしか持っておらず、変更のたびにコンパイルとリンクのプロセスを待たなければならないため、エンジンの機能の作業には時間がかかります。

ccacheをインストールしている場合、インクリメンタルビルドプロセスの多くの時間がリンクに費やされます。 リンクにld goldを使用することで、1秒ほど短縮できます。またlldに切り替えることで、さらに最適化できる可能性があります。

確かに、バッキングビルダーとしてccacheとninjaの両方に十分な効果があるとは言えません(たとえば、cmakeなどを使用している場合)。どちらも時間を大幅に節約できます。

さらに、ユニティビルドは驚くほど驚くべきものになる可能性があります。ここで、プロジェクト内のすべてのcppファイルを含む新しいunity.cpp程度のファイルを作成しますが、実際には、通常、「モジュール」ごとにユニティcppファイルがあります。プロジェクトの場合、メモリを抑えるために1ダース程度しかありません。コンパイル時の余分なメモリと引き換えに、コンパイルとリンクの両方が非常に高速になります。 ただし、これらはインクリメンタル再構築にはあまり役立ちませんが、リリースビルドにはさらに役立ちます。

パイルに1つ追加するには:
static_assert

たとえば、ユニオンSpatialMaterial::MaterialKeyは、構造体のサイズがuint64_tと同じであると想定していますが、私が知る限り、どこにもアサートされていません。

そこでstatic_assert(sizeof(MaterialKey) == sizeof(uint64_t))を平手打ちしたかったのですが、できませんでした。

もう1つは、unique_ptrを使用して、手動の定型文を書きすぎず、不要な参照カウントを使用せずに、破棄時に適切なクリーンアップを保証することです。

Godotとそのコードベースを初めて使用する人として、2セント(または20セント)を提供したいと思います。 私は現在、_Battle forWesnoth_をGodotに移植する取り組みを監督および取り組んでいます。 これで、フロントエンド(エディターとGDScript API)は素晴らしいです! いくつかのラフなエッジに加えて、これまでのところ、良いペースで進むことができました。 しかし、私たち(チーム)は、ある時点でバックエンド(エンジン)のパッチを提供することも想像していました。 そのために、今週初めにgitリポジトリのクローンを作成し、C ++をいじり始めましたが、正直なところ...少しがっかりしています。

私は、Wesnothの古いカスタムエンジンの形式で大規模なC ++コードベースを管理した経験があります。 それもC ++ 03として始まりましたが、C ++ 11以降の機能を使用するように最新化され、現在はC ++ 14に準拠しています。 私はそのモダナイゼーションの取り組みを主導し(多くの場合、少し熱心になりすぎました)、コードベースがはるかに読みやすく、操作しやすくなったと感じています。 確かに、私は純粋なC ++ 03コードベースを広範囲に使用したことはありません。 最新のC ++機能を使用してC ++を学びました。 しかし、私にとっては、 auto 、range-for、ラムダなどによってコードが読みにくくなるという考えは、実に奇妙なことです。

autoを取ることは確かにそれを悪用して乱用することは可能ですが、それはまたたくさんの合法的な使用法を持っています。 Wesnothコードベースを更新したときにautoをデプロイした最も一般的な場所の1つは、イテレーターを使用したforループでした。 以前は、次のようなものがありました。

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

これは厄介です! 実際にイテレータが必要な場合は、次のようにします。

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

はい、明示的なイテレータタイプはわかりませんが、それを知る必要はほとんどありません。 ここでいくつかの投稿のコメントを読んで、コンテナがいくつかのファイルを離れて宣言されていると難しくなると言っていますが、実際には、最新のコードエディタとインテリセンスではそれほど問題にはなりません。

ほとんどの場合、代わりに範囲に切り替えるだけです。

for(const auto& foo : container) {}

入力がはるかに速く、簡潔にもなります。 ループ内のイテレータの逆参照や、インデックスの追跡について心配する必要はありません。 そして、コンテナ全体をループしていることは十分に明らかです。 イテレータを使用する場合、コードに慣れていない人は、ループが実際に最初から最後まで進んでいることを再確認する必要があります。

ここで範囲forループでautoを使用すると、追加の利点もあります。 コンテナのタイプを変更した場合に更新する必要があることが1つ少なくなります。 これらのことがコードを読みにくくしたり、理解しにくくしたりするというJuanの主張を私は本当に理解できません。 私にとって、それは正反対です。

State of Godotのビデオでは、ラムダについても言及しています。 繰り返しますが、それらを悪用することは確かに可能ですが、それらは信じられないほど便利なツールでもあります! C ++ 11を使用する前にWesnothのコードベースで見た一般的なパラダイムは次のとおりです。

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

長くて厄介なコードの膨張。 そして、これが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; });
}

これが最も一般的なケースです。 はい、Tにoperator<を実装することもでき、 std::sortはデフォルトでそれを使用することを知っていますが、これは私のポイントを示しています。 繰り返しになりますが、最新のC ++を学習しただけで、ほぼ独占的に機能しているだけかもしれませんが、 auto 、range-for、ラムダなどのツールを無視することは、自分の足を撃ち抜くことだと思います。

それが私の次のポイントにつながります。 GodotがSTLタイプではなく、独自のカスタムタイプの多くを内部的に使用していることに気づきました。 autoのようなものに関してコードの可読性が懸念される場合は、STLタイプよりもカスタムコアタイプを使用すると、絶対に不利益になります。 数日前、私はコードベースを閲覧していて、次のようなコードに出くわしました。

container.push_back(T(args));

さて、これは非効率的です。 push_back (少なくともstd::vectorに関して)はconst参照を取ります。 そのため、この演算子は不要なコピーになります。 emplace_backを使用するようにパッチを作成したかったのですが、コードベース全体がカスタムコンテナタイプを使用していることに気付きました😐

Wesnothの設計に関する大きな問題の1つであり、新しいエンジン(この場合はGodot)を採用することを決定する主な要因の1つは、WesnothがNot InventedHere症候群にかなり苦しんでいたことです。 Boostのようなライブラリを使用しましたが、レンダリングパイプラインはカスタムであり、UIツールキットはカスタムであり、データ/スクリプト言語はカスタムでした。 Godotポートでの作業を開始する前に、ハードウェアアクセラレーションによるレンダリングを実装しようとしました(失敗しました)(この時点まで、CPUベースのサーフェス/スプライトレンダリングを使用していました。2019年。はい、X_Xを知っています)。 SDLのTexture APIにはシェーダーサポートがなく、シェーダーサポートが必要でした。 結局、私は、独自のレンダラーを実装することは、可能ではあるが、プロジェクトに不必要なメンテナンスの負担を課すことになると判断しました。 コア開発者はすでにほとんどいませんでした。Godotのようなエンジンに、使用できる完全に優れた、手入れの行き届いたレンダラーがある場合、OpenGL(またはOGLを削除する必要がある場合はVulkan)を作成できる人を見つけるのは不必要な苦痛でした。代わりは。

すべてを社内で実装することが悪い考えである理由の良い例だと思うので、それを取り上げます。 はい、標準ライブラリを使用しないことでバイナリサイズを少し減らすことができますが、メンテナンスの負担も大きくなります。 これらのpush_back呼び出しをemplace_backに変換することは、コードをよりクリーンでパフォーマンスの高いものにすることができる非常に簡単な成果です。 ただし、カスタムのvectorタイプがあるため、インプレース構築が必要な場合は、誰かが手動で実装する必要があります。 そして、他のすべてのカスタムタイプでも!

さらに大きな問題は、参入障壁が高くなることです。 私はC ++ STLタイプを期待しているGodotコードベースを見ました。 それらが見つからないということは、私や他の誰かが、エンジンが提供するタイプと、それぞれに対応するAPIを正確に知る必要があることを意味します。 std::mapが欲しいですか? いいえ、 dictionaryを使用する必要があります。 それは、メンテナの生活を困難にし、新しい貢献者の生活を複雑にします。 そして実際には、あるもののように見えるが実際には別のものであるコードではありません...判読できませんか?

約1年前、Wesnothの1.14リリースとSteamでのリリースに近づいたとき、 erase()を使用しても無効にならないことを除いて、本質的にstd::listであったカスタムコンテナタイプを排除するためのリファクタリングプロジェクトに着手しましたイテレータ。 これが問題の主要なコミットです。 その後、正しく処理するためにさらに作業が必要でしたが、その結果、コードははるかに単純で、理解しやすく、不透明度が低くなりました。

結論として、特定の非常に便利なC ++ 11機能を非合法化し、STL機能よりもカスタムタイプを使用することは、長期的にはGodotの障害になると思います。 リファクタリングには長い時間がかかることは知っていますが(私を信じてください、私は知っています)、現在の状況では、キャッチ22になってしまう可能性が非常に高いようです。 STLを使用することの欠点(ラガーバイナリサイズなど)を回避しようとすると、新しいC ++標準でよりパフォーマンスが高くクリーンなコードに切り替えることがますます難しくなります。 すべてのカスタムタイプでインプレース構築を実装することを特に楽しみにしている人はいないと思います。 😬

私の意見はここではあまり意味がないことは知っていますが、C ++ 03から最新のC ++に移行した大規模なC ++コードベースを使用したことがある人の視点から考えてみようと思いました。 大変な作業ですが、長期的にはやりがいを感じます。

巨大なテキストウォールすみません!

@Vultraz完全に同意します。

私は(ほとんど)C ++の経験はありませんが、GDNative + C ++でしばらく作業した後、あなたの意見を共有します。

ほんの数日前、私はredditに「 Godotに貢献することはC ++を学ぶ良い方法ですか? 」スレッドに投稿しました:

私はそれをお勧めしません。 GDNativeとC ++での作業の経験から、彼らは少なくとも数回ホイールを再発明しました(カスタムコレクションとポインター)。 これらのカスタムクラスは文書化されていません(少なくともGDNative C ++バインディング/ヘッダーPOVから-前回試したときにコンパイルされないデモプロジェクトにつながる1つのガイドを除いて、コード[ヘッダー、バインディング]にも公式ドキュメントにも文書はありません)紛らわしい/直感的でない動作があります(たとえば、Refが直感的に透過的である必要があるため、ラップされているクラスの動作を変更してはならない場合でも、Refの後半でエンジンクラスをラップするとランダムにクラッシュします)。

また、新しいC ++機能を使用する代わりに、IMOの読みにくい定型文を好むのも好きではありません。 私はHaskell(およびそのライブラリ)が好きです。その簡潔さは、上級ユーザーの言語を損なうことのないように、初心者に甘んじることはありません。

これらの決定/価値観のために、私がエンジンに貢献することはないと思います。 (エンジン開発者がこれらの決定の背後にある理由は貢献を奨励することであると述べているので、面白いです。私の場合、それは完全に反対の効果をもたらしました。)

@Vultrazは明確に表現された書き込み、すばらしい読み取り、ありがとうございます。 そのような視点を聞くのは良いことです。

私は古いスタイルのC ++プログラマーなので、Godotのソースコードを理解するのにそれほど問題はありませんでした。 コードベースの既存の経験がほとんどなくても、このエンジンのコアにVRサポートを比較的迅速に追加できたことは、このエンジンがどれほど読みやすく理解しやすいかを証明していると思います。 ある意味古い学校かもしれませんが、特定のパーツがどれだけうまく構築されているかに私は絶えず驚いています。 はい、メモリをあまり必要とせず、パフォーマンスを向上させるためにモダナイゼーションが必要な部分がありますが、全体として、私はそれに感銘を受けました。

人々がC ++の新しい構文について話しているのを聞くと、私はしばしば年を取り、大騒ぎが何であるかを本当に不思議に思っています。 しかし、より現代的なC ++を学び、Godotに貢献することを決心したとき、それが車輪の再発明のように見えるのは奇妙なことです。 しかし、少なくとも私のような人々にとっては、これらのようなクラスが実装されているのを見るのに慣れているので、ここでほとんどがうまく実装されていることに感銘を受けています:)

とにかく、この問題を面白くなく見るには、さまざまな方法があります。 新しいC ++構文よりも前のGodotの起源から、コア開発者がGodotのクロスプラットフォームの性質とそれを実行できる(/可能である)デバイス(一部は公開されていない)によって課せられる制限に最も慣れているものまで。 これには正しいか間違っているとは思いません。それはあなたが慣れ親しんでいるものであり、どこから来ているのか、そしてGodotがどのように機能するかを学ぶ余分な学習曲線が大きなコードをリファクタリングすることの利点を上回るかどうかについての質問ですベース。

あなたがそれを見たかどうかはわかりませんが、フアンはオンラインで公開されたGDCで講演を行いました: https ://www.youtube.com/watch?v = C0szslgA8VY
最後のQ&Aで、彼はこれに関する意思決定について少し話します。
いい時計です。

上記のGDNativeに対する反応については、GDNativeは特定のニーズを満たす最近の追加であり、コア製品がどのように構造化および構築されているかを示すものではありません。
モジュール(https://docs.godotengine.org/en/3.1/development/cpp/custom_modules_in_cpp.html)を作成してみてください。そうすると、GDNativeが解決しようとする問題であるコア製品に変更をコンパイルする必要がありますが、エンジンが実際にどのように構成されているかをよりよく理解できます。 上記の議論の多くは、Godot自体ではなく、GDNativeの欠点です。
静的モジュールを構築するのと同じ方法でモジュールを構築できる動的モジュールソリューションが欲しいのですが、いつかは可能になるかもしれませんが、それまではGDNativeで十分です。

@Vultraz @mnn STLコンテナーについてのポイントを実際に見ることができますが、純粋に一部の実装(MSVC、ほとんど)のデバッグモードでのパフォーマンスがひどく遅いためです。 しかし、EAのSTLを使用するだけで、優れたものにすることができます(高速で、移植性があります)。

また、私は個人的に、 RAIIの欠如が最も苦痛であることに気づきました。 RAIIはC ++ 11の新機能ではないため、コード全体で不必要に実行される手動クリーンアップの量は奇妙です。

上記のGDNativeに対する反応については、GDNativeは特定のニーズを満たす最近の追加であり、コア製品がどのように構造化および構築されているかを示すものではありません。

それはもちろん真実ですが、GDNativeは「スクリプト」の作成に使用されるため、エンジン自体よりもユーザーフレンドリーであってはなりません。GDNativeは、C ++スキルがさらに低いと予想されるオーディエンスを対象としているため、そのエンジン?

これらの決定/価値観のために、私がエンジンに貢献することはないと思います。 (エンジン開発者がこれらの決定の背後にある理由は貢献を奨励することであると述べているので、面白いです。私の場合、それは完全に反対の効果をもたらしました。)

私はC ++の初心者です(C ++で週に2日働いている約2か月)ので、この決定から利益を得るターゲット人口統計である必要があります。 Godotコンテナーには基本的な機能が不足していることがわかります(それ自体があまり豊富ではないstlと比較して)、コンテナーはGDNativeに関連しているとは思いませんが、誤解されている可能性があります。 Godotコンテナは常に操作が面倒なので、回避するように最善を尽くしています。 Refの一貫性のない予期しない動作はエンジンの責任だと思いましたが、私は間違っていると思います。

私のプログラミングのバックグラウンドはおそらくかなり珍しいことだと思います-JS / TSでプロとして働いていた年、Scalaで1年、Haskellで小さな趣味のプロジェクト(数千行、Haskellがどれほど簡潔かを考えると実際にはそれほど小さくありません)-何度もコードを書いていますC ++では、Haskellでは少なくとも5倍短く、読みやすくなります)。 古風な過度に冗長な技術の使用に落胆しているのは私だけではないかと思います。

@ mnn 、GDNativeは、Cベースのモジュールをダイナミックライブラリとして作成できるようにするために作成されたため、エンジン全体を再コンパイルする必要はありませんでした。 その上、いくつかの言語バインディングが作成されたため、C ++、python、rustなどでモジュールを記述でき、エンジン全体をコンパイルする必要はありませんでした。

これのもう1つの目標は、開発者がモジュール自体を提供するだけでモジュールを作成し、それをエンジンの安定したビルドで使用できるようにすることでした。 多くのモジュールは、まだメンテナンスされていないため、特定のバージョンのエンジンに関連付けられているため、機能しなくなりました。

そうですね、コンパイルとデプロイメントの観点からモジュールを作成するのがはるかに簡単で簡単になりました。 しかし、そのアプローチのために、それには限界があります。 これらの制限の範囲内にとどまる場合、モジュールを構築する対象の主題が単純である限り、GDNativeでのC ++コードの記述は単純なままです。

これらの制限を打ち破ってみてください。そうすれば、頭痛の種になります。 私の現在の頭痛の種は、OpenGLロジックを実装しようとすることですが、それはすべてビジュアルサーバーアーキテクチャ内にカプセル化されており、GDNative内では必要な方法で実際には利用できません。 しかし、それは私がGDNativeで何かをしたいというより多くの要因です。
それが設計された目的のためにそれを使用するとき、あなたはそれでいくつかの本当にクールなことをすることができます。

また、GDNativeは、パフォーマンスを向上させる必要があるGDScriptコードを書き直す方法として意図されていたことにも注意してください。 その結果、GDScriptがどちらにもアクセスできないエンジン内の何かにアクセスすることはできません(OpenGLなど)

最新のコルーチンサポート、つまりco_await()はどうですか? procgenの観点から、これは巨大です。

コルーチンのサポートは、記録のためにまだ完成していないC ++ 20標準の一部です。

チャイムを鳴らしてください。私は現在、3D Spatial Editor用の高度な(ソースハンマーエディターを考えてください)ブラシ/ csgツールを書いています。自動を許可しないことについてのこの話は、私にとって本当に混乱しています。 場合によっては、_autoの使用でさえ明示的になることがあります_。 次のことを考慮してください。

私はSpatialEditorViewportContainer内にいて、親階層の3つの項目であるSpatialEditorを取得したいと思います(ビューポートコンテナからSpatialEditorにアクセスするためのより良い方法があると誰かが指摘する前に、このコードベースを見始めたことを考慮してください昨日)

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

ご覧のとおり、動的ダウンキャストは_すでにSPのタイプを明示的に示しています_。 自動がなければ、次のような冗長なジャンクを作成する必要があります。

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

神の愛のために、自動車の使用を許可してください!

使用するC ++標準のトピックについて:C ++ 20以降で提案されているリフレクションおよびメタクラス機能は、次のようなマクロの乱雑さを減らすのに非常に役立ちます。

GDCLASS(SpatialEditorViewportContainer, Container);

また、これらの制限により、私が貢献するという私の決定を本当に二番目に推測することになることを繰り返したいと思います。 2012年頃、私はC ++ 11を独学で学びましたが、この標準を使用できないことは、平手打ちのようです。 C ++ 11とC ++ 03は完全に異なる言語であり、C ++の習得、読み取り、書き込みが難しいという評判のほとんどは、C ++ 03(または98)のせいです。 少なくともC ++ 11または14を使用しないことは、読みやすさと読みやすさを維持するために_有害_です。 私はC ++ 11で育ちましたが、プロジェクトリーダーは明らかにそうではありませんでした(彼が2007年にgodotに取り組み始めたとき、私は12歳でした)。 C ++ 11を使用しないのは、現代のC ++を学ぶのに時間をかけた私のような人々を犠牲にして、古い学校(別名、ひどい)C ++に慣れている人々を慰めるためだけだと思います。

時間が経つにつれて、私のようなジュニアプログラマーは、現代のC ++ 11以降で育ち、プロジェクトがラムダさえも持たない言語に永遠にとどまっているという事実に気付くでしょう。

要するに:C ++ 11またはバスト!

明確にするために、私はSTLの使用を推奨していません。 独自のコンテナーをローリングすることは問題ありませんが、ラムダや自動などの機能を拒否するのは無罪のようです。

ここでこれ以上議論するのは無意味なので、今はエコーチェンバーを閉じます。 数か月前に、C ++ 11やそれ以降のバージョンで使用する予定の機能の種類についてすでに説明しました。

@hpvbが私たちのコンセンサスに基づいて作成しているガイドラインを完成させる時間を待っているところです。その後、ガイドラインが公開されたら、それらについてさらに話し合うことができます。 それまでは、これは建設的ではありません。

このページは役に立ちましたか?
0 / 5 - 0 評価