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 、 lambdas 等:一般来说,任何现代 C++ 功能都可以使编写代码更容易和/或提供一些优化机会而不破坏当前核心。
  • 标准多处理原语:C++ 现在提供标准线程、原子、内存屏障等。我们是否必须保留自定义实现并为不同平台维护它们?
  • STL/Boost/other :从自定义容器切换到一个众所周知的实现,我们会得到一些好处吗? 关于维护、性能、兼容性等的缺点/优点。
  • register ,内联“技巧”等:添加到代码中以尝试使编译器生成更高性能的代码。 其中一些要么已被弃用,要么没有实际影响,甚至可能会降低性能。 丢哪个? 保留哪个?
  • 等等,

可以尽早进行小的更改。 但是什么时候应该做大的改变(在这种情况下)? 戈多4.0? 或者一旦 Godot 3.0 稳定了?

请让我明确邀请一些人:@ reduz 、@punto-、@akien-mga、@ karroffel 、@bojidar-bg、@BastiaanOlij、@ Faless

discussion

最有用的评论

作为 Godot 及其代码库的新手,我想提供 2(或 20)美分。 我目前正在监督并致力于将_韦诺之战_移植到 Godot。 现在,前端(编辑器和 GDScript API)很棒! 除了一些粗糙的边缘之外,到目前为止,它使我们能够以良好的速度进步。 但我们也想象我们(团队)会在某个时候为后端(引擎)贡献补丁。 为此,本周早些时候我克隆了 git repo 并开始在 C++ 中四处寻找,老实说......我有点沮丧。

我确实有以 Wesnoth 的旧自定义引擎的形式管理大型 C++ 代码库的经验。 它也是从 C++03 开始​​的,但经过现代化改造以使用 C++11 及更高版本的功能,现在与 C++14 兼容。 我带头进行了现代化工作(通常有点过于热心),我觉得它使我们的代码库更具可读性和更容易使用。 诚然,我从未广泛使用过纯 C++03 代码库。 我使用现代 C++ 特性学习了 C++。 但对我来说,像auto 、 range-for 和 lambdas 这样的东西会让你的代码不那么可读的想法只是......确实很奇怪。

auto为例,肯定有可能滥用和过度使用它,但它也有大量的合法用途。 当我们更新 Wesnoth 代码库时,我们部署auto的最常见的地方之一是使用迭代器的 for 循环。 以前,我们会有这样的东西:

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

哪个是乱七八糟的! 在我们实际上需要一个迭代器的情况下,我们这样做了:

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

是的,现在您不知道显式迭代器类型,但您几乎不需要知道这一点。 我在这里阅读了一些帖子的评论,说如果将容器声明为一些文件,这会变得更加困难,但实际上,对于现代代码编辑器和智能感知来说,这并不是什么大问题。

在大多数情况下,我们只是切换到 range-for:

for(const auto& foo : container) {}

打字速度更快,也更简洁。 您无需担心在循环内取消引用迭代器或跟踪索引。 很明显,您正在循环整个容器。 对于迭代器,不熟悉代码的人需要仔细检查循环是否确实从头到尾进行。

在 range-for 循环中使用auto还有一个额外的好处。 如果您更改容器的类型,它会减少您需要记住更新的一件事! 我真的无法理解 Juan 的论点,即这些东西会使您的代码不那么可读或不那么容易理解。 对我来说,恰恰相反。

在戈多的状态视频中,他还提到了 lambdas。 同样,当然可以滥用它们,但它们也是一个非常有用的工具! 这是我在使用 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 和 lambdas 之类的工具是在自取其辱。

这让我想到了下一点。 我注意到 Godot 在内部使用了许多它自己的自定义类型而不是 STL 类型。 如果您担心auto之类的代码可读性,那么使用自定义核心类型而不是 STL 绝对是不利的! 几天前,我在浏览代码库时发现了很多看起来像这样的代码:

container.push_back(T(args));

现在,这是低效的。 push_back (至少在std::vector方面)采用 const 引用; 因此,此运算符会导致不必要的复制。 我想打一个补丁让他们使用emplace_back ...但后来我意识到整个代码库都在使用自定义容器类型😐

韦诺设计的一大问题,也是决定使用新引擎(在本例中为戈多)的主要促成因素之一,是韦诺在很大程度上患有此处未发明综合症。 虽然我们确实使用了像 Boost 这样的库,但我们的渲染管道是自定义的,我们的 UI 工具包是自定义的,我们的数据/脚本语言是自定义的......你明白了要点。 在我们开始在 Godot 端口上工作之前,我尝试(但失败了)实现硬件加速渲染(到目前为止,我们一直在使用基于 CPU 的表面/精灵渲染。在 2019 年。是的,我知道 X_X)。 SDL 的Texture API 没有着色器支持,需要着色器支持。 最后,我决定实现我们自己的渲染器,虽然可能,但会给项目带来不必要的维护负担。 我们的核心开发人员已经很少了,当像 Godot 这样的引擎拥有一个非常好的、维护良好的渲染器供我们使用时,找到能够编写 OpenGL(或 Vulkan,如果我们需要放弃 OGL)的人只会是不必要的痛苦反而。

我提出这一点是因为我认为这是一个很好的例子,说明为什么在内部实施一切可能是一个坏主意。 是的,您可能会通过不使用标准库来稍微减少二进制文件的大小,但您也会承担大量的维护负担。 将那些push_back调用转换为emplace_back是一个非常容易实现的目标,它可以使代码更清晰、更高效。 但是因为你有一个自定义的vector类型,如果你想要就地构建,就需要有人手动实现它。 在所有其他自定义类型中也是如此!

更大的问题是它提高了进入门槛。 我查看了期望 C++ STL 类型的 Godot 代码库。 找不到这些意味着我或其他任何人现在需要准确了解引擎提供的类型以及每种类型的 API。 我想要std::map吗? 不,我需要使用dictionary 。 它只会让维护者的生活变得更加困难,并使新贡献者的事情变得复杂。 真的,不是看起来像一件事但实际上是另一件事的代码......不可读?

大约一年前,当我们接近 Wesnoth 的 1.14 版本并在 Steam 上发布时,我进行了一个重构项目,以消除一个自定义容器类型,该类型本质上是std::list ,除了使用erase()并没有失效迭代器。 这是有问题的原则提交。 在那之后需要进一步的工作才能正确,但结果是代码更简单、更容易理解且不透明。

总之,我认为从长远来看,取缔某些非常有用的 C++11 特性并坚持使用自定义类型而不是 STL 类型将成为 Godot 的障碍。 我知道重构事情需要很长时间(相信我,我知道),但现在的情况似乎很可能你最终会得到一个 catch-22。 通过尝试避免使用 STL(更大的二进制大小等)的缺点,您最终会越来越难切换到更新的 C++ 标准中性能更好、更简洁的代码。 我敢肯定,没有人特别期待在所有自定义类型中实现就地构造。 😬

我知道我的意见在这里没有多大意义,但我想我会从使用大型 C++ 代码库的人的角度发表我的想法,该代码库从 C++03 迁移到现代 C++。 这是很多工作,但从长远来看,我觉得这是值得的。

原谅巨大的文字墙!

所有65条评论

这个被讨论过很多次了,通常的答案是:

1) 不希望将代码库移动到 03 以上的任何内容。功能不值得。 对于 GDNative C++,完全可以使用它。
2) 不想使用 STL/Boost/Etc。 基本原理始终相同:a) Godot 模板做他们通常不做的小事情(即原子引用计数和写入时复制) b) STL 生成巨大的调试符号和调试二进制文件,因为它们的符号非常长

我们保持与一切相同的方式。

我对私人谈话有一些意见,但我想提出
讨论更公开。

我不知道它已经被讨论过这么多并且已经关闭了。

你甚至会丢弃 auto、constexpr 等吗?

放弃“注册”?

埃尔 7 月 18 日。 2017 下午 1:54,“Juan Linietsky” [email protected]
说明:

这个被讨论过很多次了,通常的答案是:

  1. 不希望将代码库移动到 03 以上的任何内容。功能是
    不值得。 对于 GDNative C++,完全可以使用它。
  2. 不想使用 STL/Boost/等。 理由一直是
    相同:a) Godot 模板做他们通常不做的小事情(即原子
    refcounts and copy on write) b) STL 生成巨大的调试符号和调试
    二进制文件,因为它们的符号非常长

我们保持与一切相同的方式。


您收到此消息是因为您编写了该主题。
直接回复此邮件,在 GitHub 上查看
https://github.com/godotengine/godot/issues/9694#issuecomment-316041201
或使线程静音
https://github.com/notifications/unsubscribe-auth/ALQCtipKmepD_1Xw6iRXZ7aGoQlLfiwFks5sPJzqgaJpZM4ObOio
.

我提出了这个问题,使用智能指针会更安全,并且这段代码患有NIH综合症,但高层无意升级,所以我建议你也放弃。

升级代码可能需要很多工时,而开发人员更愿意将这些时间花在实现新功能上。 我以某种方式理解这一点 - 引擎用户更喜欢获得新功能而不是“重构”(不了解技术债务之类的东西)。

@Marqin对不起,但智能指针是个坏主意,因为:

1)唯一有用的是引用的,其余的是对语言中已经存在的特征的精神自慰。
2)到处使用智能指针是一个糟糕的主意,您可能会有引用循环泄漏内存的风险
3) 我们已经有了类似的东西, Ref<> ,还有一个额外的优势是我们可以控制引用计数的工作方式,因此我们可以添加特殊情况来处理与 C# 等语言的绑定,并拥有自己的 gc

所以,在争论我们一时兴起决定事情之前,请先问问。 我们通常会做出明智的决定,并与每个人分享这个过程。

如果您想提高质量,可以轻松完成的是开始编写单元测试(为此向scons添加一些可选工作,然后让 dev 自己安装 gmock/gtest [不阻塞 ./thirdparty])

剩下的就是对语言中已经存在的特征的精神自慰

@reduz ,unique_ptr 的明确所有权和 RAII 内存释放如何已经存在于语言中?

不仅内存管理,而且线程也变得更容易(例如lock_guard )。

@RandomShaper我不反对在未来的某个时候升级,但是,即使有许多有用的功能,较新的 C++ 版本也倾向于让程序员编写不太明确的代码。 这导致代码通常更难被其他人阅读(典型的自动关键字滥用)。

不使用更高级功能的另一个优点(例如,我们不使用异常并且可以以很少的成本禁用 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(谷歌
说它在 https://github.com/godotengine/godot/issues/7703 )。 这让我很困扰,因为 gcc-6.3 是我的发行版(debian stable)中的默认编译器。 谁能证实这一点? 为什么是这样?

@eornara一些第三方库已经需要更新的 C++ 版本,这很好,因为 Scons 使用克隆的构建环境来处理这个问题。 检查 etc2comp 第三方代码以了解它是如何工作的。

@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 _not_ 开始使用标准库中的组件,而是坚持使用自定义组件的情况:

C++98 语言与 C++11 语言的 ABI 兼容,但标准库中的几个地方破坏了兼容性。

混合语言看起来相当安全(我承认这不是很好,而且它可能在未来停止工作),但是混合诸如对、向量、列表等的东西......众所周知会导致问题。

@eornara该链接是关于将一​​些使用 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 内部重构,因为它需要付出太多努力而收获太少。

出于这个确切原因,向后兼容性之一也是 C++ 的标志之一。 但我仍然希望能够将它用于新的 Godot 功能和 GDNative。
出于这个原因,我更喜欢用现代 C++ 支持编译 Godot。

@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 文件时也会设置该选项,因此如果启用它们,您会收到大量警告,但除此之外,一切都很顺利。 即使是 register 关键字似乎也不会造成麻烦。

我不会建议以这种方式编译官方构建,但如果您在项目中需要它,很高兴知道该选项存在。 我的建议是,任何破坏这一点的更改都被视为回归。

编辑:更正了一些错别字,使有关警告的声明更加清晰。

烦人的是,编译 C 文件时也设置了该选项

你试过CPPFLAGS吗?

@Hinsbart不,我没有。 也许有办法,但由于我不太了解 scons,所以我只是简单地采用了看似可行的方法,而无需修补:

$ 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会让我想到前驱选项。 就个人而言,我一直将CXXFLAGS用于 C++。

你试过 CPPFLAGS 吗?

我认为CPPFLAGS会影响使用预处理器的每种语言,因此包括 C 和 C++。

@m4nu3lf您可以在 GDNative 中使用您想要的任何形式的 C++。

以我的经验,任何删除代码的机会都是一个好机会。

也许我们可以设置某种 wiki 页面,记录可以删除哪些文件并用 c++11 变体替换。 这可能包括线程原语等。

为了改变而改变是不好的(众所周知的 koolaid),但在这种情况下,LLVM 项目和许多其他 FOSS 项目已经开始支持一些更清晰的语法模式,即更新的 for-iterator 表示法,但也将关注点分离卸载到各自的语言运行时,因为(说实话)尽可能少地维护特定于平台的代码对于游戏引擎来说是理想的。

你写过的最好的代码就是你不写的代码。 :)

出于好奇,是否可以编写同时使用旧 C++ 和新 C++ 编译的代码? C++ 版本之间是否存在重大的重大变化?

出于好奇,是否可以编写同时使用旧 C++ 和新 C++ 编译的代码? C++ 版本之间是否存在重大的重大变化?

较新的 C++ 在 99.99...% 的情况下向后兼容较旧的 C++(只有不应该使用或错误定义的内容被定义为不再支持,但通常只会导致编译警告但现在仍然有效) .

但是,较新的 C++ 具有重要的特性,而这些显然不适用于较旧的 C++ 版本,并且这些特性不仅是可变参数宏和auto和 lambda 等可用性特性,而且还有move等效率特性

考虑到现在即使是 Visual Studio 也支持现代 C++17,真的没有理由不使用更新的版本。

我想发表评论。 我能够用 C++17 标志编译 godot,唯一的问题是使用 auto_ptr 的第三方东西之一(由于它有多糟糕,它已从 C++17 中删除)

问题不在于它不能在 c++17 中编译,问题是那些想要使用 c++17 特性的人,或者更糟糕的是,使用这些特性启动 PR... 才发现项目在 c ++03。

单独缺少 lambda 是一个巨大的问题,我在这里看不到。
有几次,godot 必须存储一个时间数据列表,然后对其进行迭代。
在每一种情况下,它都可以作为“for_each”或类似的结构来完成,它可以自行迭代,大量简化代码,降低内存使用量,并通过 lambda 进行内联/优化来提高性能。 (这是 C++11,随处使用)。 对于链表上的所有一般循环,完全相同的事情。

移动运算符还将允许更好的数据结构,它可以通过一些更好的优化来取代当前绝对糟糕的数据结构。

字符串数据类型(如“mystring”_hs)或类似数据类型可以为我们提供一种在代码中到处散布散列字符串的好方法,从而使字符串检查(在脚本和事件代码中很常见)更快。

即使不使用 std::thread (我自己认为这是一个相当糟糕的抽象),std atomics 库也很棒,而且非常有用。 标准::原子和喜欢。

更不用说强迫人们使用 C++03 对项目本身造成了多大的伤害,因为人们无法轻松地自己集成现代库,因为 godot 就像在这样一个旧版本的 C++ 上唯一未被放弃的开源 C++ 项目(我所知道的)

就个人而言,我同意保守并且不采用绝对最新的 C++ 标准,但我认为像 C++11 这样的具有 C++14 一些经过审查的特性的东西是最有效的,并显着改进 Godot。 C++11-14 对 Unreal Engine 来说已经足够好了,它可以移植到 ps4、xbox、switch、pc、low end pc、android、IOS 和 HTML5 webassembly。 我不明白为什么当 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/whatever 类型(下面只是一个整数)时,制作一个反转编码的 Visual Studio 或 GDB 过滤器是微不足道的,因此您可以看到字符串类型而不是一些奇怪的散列值。

但是这样的东西非常有用,特别是在性能敏感代码和使代码易于阅读时,这只需要 C++11,甚至不需要任何更新的东西。

至少我会说 C++14 应该是 Godot 标准。 C++17 会很好(一些新代码中的一些非常有用的性能增强),但现在 C++14 是一个普遍的最低要求。 但当然,由于 gcc/clang/VisualStudio 和其他任何东西现在都支持 C++17(甚至是 C++20 的大块),C++17 似乎也不错。 我可能仍然会选择 C++14 作为“以防万一”。

@OvermindDL1 Atom 太棒了,喜欢它。 它肯定非常适合 godot,它在很多时候都在做同样的事情。

Atom 的东西太棒了,喜欢它。 它肯定非常适合 godot,它在很多时候都在做同样的事情。

@vblanco20-1 好吧,如果 Godot 想要它,他们可以自由地吸收代码。 它(以及它的前身享元字符串)在我的旧 C++ 引擎中得到了长期使用。 它对于小事件标签非常有用,只是用作“键”的短字符串,而且在 Lua/LuaJit 上来回移动非常容易,而且调试器过滤器也有很大帮助。

单独缺少 lambda 是一个巨大的问题,我在这里看不到。

我是唯一一个认为 lambda 在大多数情况下会使代码难以阅读的人吗?

@Faless :不,你不是唯一一个,我认为 lambdas 难以阅读是 Akien 没有升级 c++ 版本的原因之一。

Lambda 只是小的内联函数。 我不知道您对它们的“难以阅读”的评论是什么。 但它们允许发送比较函数的排序算法或 std 算法库的其余部分。 由于消除了对时间数组的需求(这在八叉树和其他系统的源代码中多次发生),它们还允许您节省内存并显着提高性能

使用 std 算法库,这是您可以获得的多线程程序的最简单方法,只需通过并行处理、并行排序和并行累积。

当他们可以大大提高代码质量、性能和可重用性时,人们将它们视为“怪异”,这绝对是一种耻辱。

我已经实现的实际示例:

//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你的例子是你可以用 lambdas 做的最坏的例子(这种用法绝对应该被禁止)。

“跨时间”使用 lambdas 并不是很好地使用它们,因为它们必须分配,而且它们也成为一个巨大的危险,因为捕获的变量可能在您执行 lambda 时停止有效(例如,捕获一个指针,然后在调用 lambda 之前删除对象,捕获的指针将悬空)。 我只是真正捍卫 lambdas 与数据结构和算法一起使用,你可以“立即”使用它们。

在 for_each 示例中,for_each 不受内部数据结构更改的影响(例如,如果您稍微重新排序其变量),它允许更多“不透明”的数据结构(这允许开发人员能够“轻松地” 从一种数据结构更改为另一种数据结构,以测试哪个可能更好。通过接受它,您可以实现更复杂的数据结构,这些数据结构工作不透明,同时保持“使用”层易于使用。

它还减少了样板文件并使代码更“清晰”地实际在做什么(迭代链表)。 摆脱“ first()->self()->self ”本身就是一种改进。

请记住,这样做的好处实际上会随着更多的使用而累积,因为您将能够拥有像“unordered_for_each”这样的东西,它按照节点在内存中的顺序迭代节点(通过分配器,或者如果链表存储在顶部一个数组)或“reverse_for_each”。 甚至诸如“查找”或“排序”之类的东西。 (我不推荐 std::algorithms 那么多,至少在 C++20 合并 Ranges 之前。将自己的算法作为数据结构的一部分实现更好用)

Lambda 基本上是 Epic Games 从 C++11 允许进入虚幻引擎的第一件事,以及他们自己的算法库,如 Sort、Partition、Filter、RemoveAll、Find 等。从那时起,它们就经常在源代码中使用,两者在引擎代码和游戏代码中

酷,至少我们同意 lamdbas 不是圣杯
编程,我们已经定义了一条关于如何不使用它们的规则。

我会做更多关于性能的研究。

2018 年 12 月 8 日星期六 17:06 vblanco20-1 < [email protected]写道:

@Faless https://github.com/Faless你的例子是
你可以用 lambdas 做的最糟糕的事情(这种用法绝对应该是
禁止)。

“跨时间”使用 lambdas 并不是很好地使用它们,因为它们会有
分配,它们也成为一个巨大的危险,因为捕获的变量
将在您执行 lambda 时停止有效(例如,
捕获指针,然后在调用 lambda 之前删除对象,
捕获的指针将悬空)。 我只是真正为 lambdas 辩护
它们与数据结构和算法一起使用,在哪里使用它们
“即刻”。

在 for_each 示例中,for_each 不受内部变化的影响
数据结构(例如,如果你重新排序它的变量),它允许
更多“不透明”的数据结构(允许开发人员能够
“轻松地”从一种数据结构更改为另一种数据结构以测试哪个数据结构
可能会更好。 通过接受它,您可以实现更多
不透明地工作的复杂数据结构,同时保持“使用”
层易于使用。

它还减少了样板文件并使代码更加“清晰”
实际上在做(迭代链表)。 只是摆脱“
first()->self()->self " 是对自身的改进。

请记住,这实际上会随着使用次数的增加而累积,因为
你将能够拥有像“unordered_for_each”这样迭代的东西
节点在内存中的顺序(通过分配器,或者如果
链表存储在数组顶部)或“reverse_for_each”。 连东西
比如“查找”或“排序”。 (我不太推荐 std::algorithms ,在
至少在 C++20 合并 Ranges 之前。 实现自己的算法
作为数据结构的一部分使用起来要好得多)

Lambda 基本上是 Epic Games 从 C++11 允许的第一件事
虚幻引擎,以及他们自己的算法库,如排序,
Partition、Filter、RemoveAll、Find 等。从那时起,它们经常
在源代码中使用,包括引擎代码和游戏代码


你收到这个是因为你被提到了。
直接回复此邮件,在 GitHub 上查看
https://github.com/godotengine/godot/issues/9694#issuecomment-445474001
或使线程静音
https://github.com/notifications/unsubscribe-auth/ABnBbvBTxThAh0v8AfFCdGsSv2HFnEz6ks5u2_GKgaJpZM4ObOio
.

可提高代码库可维护性的 C++11 功能:

  • overridefinal
  • 基于范围的 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 的编译都会增加大约 300 毫秒的编译时间。 它没有说明运行时性能(我只看到在更高的 C++ 模式下更快,这取决于所使用的功能,在最坏的情况下速度相同)。 所以对于It's not as "zero cost abstractions" as it should.我仍然不确定你在引用什么? 您是否有任何指向运行时性能问题的实际报告的链接,因为您链接的线程似乎与此无关,因为在较旧的 stdlib 中使用 math.h 进行编译时,编译时间仅增加了大约 300 毫秒(我是不确定我是否看到编译对象的编译时间增加了 300 毫秒的问题)?

我会对此进行更多调查。
我知道成本是有原因的,但更大的编译时间对我来说确实是一个反特征。 我只有一台笔记本电脑,处理引擎的功能需要时间,因为每次更改后我都必须等待编译+链接过程。 在没有真正合理的好处的情况下增加更多,只是为了使用更新的版本不是一个好主意。

我只有一台笔记本电脑,处理引擎的功能需要时间,因为每次更改后我都必须等待编译+链接过程。

如果您安装了 ccache,增量构建过程中的大量时间将用于链接。 您可以通过使用gold而不是ld进行链接来节省一秒钟左右的时间,并且可以通过切换到lld来进一步优化它

哦,当然,对于 ccache 和 ninja 作为支持构建器(例如,如果使用 cmake 左右),我不能说足够好,它们都节省很多时间!

加上统一构建可以令人惊讶地惊人,这就是你制作一个新的unity.cpp左右的文件,其中只包含项目中的所有 cpp 文件,尽管实际上你通常每个“模块”都有一个统一 cpp 文件一个项目,所以你只有十几个来保持内存,以换取编译时额外的内存,它的编译和链接速度都快得多。 这些对于增量重建不太有用,但对发布版本更有用。

要将一个添加到堆中:
static_assert

例如,联合SpatialMaterial::MaterialKey假定结构与 uint64_t 具有相同的大小,但据我所知,它没有在任何地方断言。

想在里面打一个static_assert(sizeof(MaterialKey) == sizeof(uint64_t)) ,但不能。

另一件事是使用 unique_ptr 来确保在销毁时进行适当的清理,而无需编写太多手动样板文件,也无需使用不必要的引用计数。

作为 Godot 及其代码库的新手,我想提供 2(或 20)美分。 我目前正在监督并致力于将_韦诺之战_移植到 Godot。 现在,前端(编辑器和 GDScript API)很棒! 除了一些粗糙的边缘之外,到目前为止,它使我们能够以良好的速度进步。 但我们也想象我们(团队)会在某个时候为后端(引擎)贡献补丁。 为此,本周早些时候我克隆了 git repo 并开始在 C++ 中四处寻找,老实说......我有点沮丧。

我确实有以 Wesnoth 的旧自定义引擎的形式管理大型 C++ 代码库的经验。 它也是从 C++03 开始​​的,但经过现代化改造以使用 C++11 及更高版本的功能,现在与 C++14 兼容。 我带头进行了现代化工作(通常有点过于热心),我觉得它使我们的代码库更具可读性和更容易使用。 诚然,我从未广泛使用过纯 C++03 代码库。 我使用现代 C++ 特性学习了 C++。 但对我来说,像auto 、 range-for 和 lambdas 这样的东西会让你的代码不那么可读的想法只是......确实很奇怪。

auto为例,肯定有可能滥用和过度使用它,但它也有大量的合法用途。 当我们更新 Wesnoth 代码库时,我们部署auto的最常见的地方之一是使用迭代器的 for 循环。 以前,我们会有这样的东西:

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

哪个是乱七八糟的! 在我们实际上需要一个迭代器的情况下,我们这样做了:

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

是的,现在您不知道显式迭代器类型,但您几乎不需要知道这一点。 我在这里阅读了一些帖子的评论,说如果将容器声明为一些文件,这会变得更加困难,但实际上,对于现代代码编辑器和智能感知来说,这并不是什么大问题。

在大多数情况下,我们只是切换到 range-for:

for(const auto& foo : container) {}

打字速度更快,也更简洁。 您无需担心在循环内取消引用迭代器或跟踪索引。 很明显,您正在循环整个容器。 对于迭代器,不熟悉代码的人需要仔细检查循环是否确实从头到尾进行。

在 range-for 循环中使用auto还有一个额外的好处。 如果您更改容器的类型,它会减少您需要记住更新的一件事! 我真的无法理解 Juan 的论点,即这些东西会使您的代码不那么可读或不那么容易理解。 对我来说,恰恰相反。

在戈多的状态视频中,他还提到了 lambdas。 同样,当然可以滥用它们,但它们也是一个非常有用的工具! 这是我在使用 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 和 lambdas 之类的工具是在自取其辱。

这让我想到了下一点。 我注意到 Godot 在内部使用了许多它自己的自定义类型而不是 STL 类型。 如果您担心auto之类的代码可读性,那么使用自定义核心类型而不是 STL 绝对是不利的! 几天前,我在浏览代码库时发现了很多看起来像这样的代码:

container.push_back(T(args));

现在,这是低效的。 push_back (至少在std::vector方面)采用 const 引用; 因此,此运算符会导致不必要的复制。 我想打一个补丁让他们使用emplace_back ...但后来我意识到整个代码库都在使用自定义容器类型😐

韦诺设计的一大问题,也是决定使用新引擎(在本例中为戈多)的主要促成因素之一,是韦诺在很大程度上患有此处未发明综合症。 虽然我们确实使用了像 Boost 这样的库,但我们的渲染管道是自定义的,我们的 UI 工具包是自定义的,我们的数据/脚本语言是自定义的......你明白了要点。 在我们开始在 Godot 端口上工作之前,我尝试(但失败了)实现硬件加速渲染(到目前为止,我们一直在使用基于 CPU 的表面/精灵渲染。在 2019 年。是的,我知道 X_X)。 SDL 的Texture API 没有着色器支持,需要着色器支持。 最后,我决定实现我们自己的渲染器,虽然可能,但会给项目带来不必要的维护负担。 我们的核心开发人员已经很少了,当像 Godot 这样的引擎拥有一个非常好的、维护良好的渲染器供我们使用时,找到能够编写 OpenGL(或 Vulkan,如果我们需要放弃 OGL)的人只会是不必要的痛苦反而。

我提出这一点是因为我认为这是一个很好的例子,说明为什么在内部实施一切可能是一个坏主意。 是的,您可能会通过不使用标准库来稍微减少二进制文件的大小,但您也会承担大量的维护负担。 将那些push_back调用转换为emplace_back是一个非常容易实现的目标,它可以使代码更清晰、更高效。 但是因为你有一个自定义的vector类型,如果你想要就地构建,就需要有人手动实现它。 在所有其他自定义类型中也是如此!

更大的问题是它提高了进入门槛。 我查看了期望 C++ STL 类型的 Godot 代码库。 找不到这些意味着我或其他任何人现在需要准确了解引擎提供的类型以及每种类型的 API。 我想要std::map吗? 不,我需要使用dictionary 。 它只会让维护者的生活变得更加困难,并使新贡献者的事情变得复杂。 真的,不是看起来像一件事但实际上是另一件事的代码......不可读?

大约一年前,当我们接近 Wesnoth 的 1.14 版本并在 Steam 上发布时,我进行了一个重构项目,以消除一个自定义容器类型,该类型本质上是std::list ,除了使用erase()并没有失效迭代器。 这是有问题的原则提交。 在那之后需要进一步的工作才能正确,但结果是代码更简单、更容易理解且不透明。

总之,我认为从长远来看,取缔某些非常有用的 C++11 特性并坚持使用自定义类型而不是 STL 类型将成为 Godot 的障碍。 我知道重构事情需要很长时间(相信我,我知道),但现在的情况似乎很可能你最终会得到一个 catch-22。 通过尝试避免使用 STL(更大的二进制大小等)的缺点,您最终会越来越难切换到更新的 C++ 标准中性能更好、更简洁的代码。 我敢肯定,没有人特别期待在所有自定义类型中实现就地构造。 😬

我知道我的意见在这里没有多大意义,但我想我会从使用大型 C++ 代码库的人的角度发表我的想法,该代码库从 C++03 迁移到现代 C++。 这是很多工作,但从长远来看,我觉得这是值得的。

原谅巨大的文字墙!

@Vultraz完全同意。

虽然我(几乎没有)使用 C++ 的经验,但在使用 GDNative + C++ 工作了一段时间后,我同意你的观点。

就在几天前,我在 reddit 上发布了“为 Godot 做贡献是学习 C++ 的好方法吗? ”主题:

我不会推荐它。 根据我使用 GDNative 和 C++ 的经验,他们至少多次重新发明轮子(自定义集合和指针)。 这些自定义类没有记录(至少来自 GDNative C++ bindings/headers POV - 除非我上次尝试的一个指南导致非编译演示项目,代码 [headers, bindings] 和官方文档中都没有文档)和具有令人困惑/不直观的行为(例如,在 Ref 后期包装一些引擎类会导致随机崩溃,即使 Ref 直观地应该是透明的,因此不应该改变被包装的类的行为)。

我也不喜欢使用 IMO 可读性较差的样板而不是使用新的 C++ 功能。 我喜欢 Haskell(及其库),它简洁,不迎合初学者,以免削弱高级用户的语言。

由于这些决定/价值观,我怀疑我是否会为引擎做出贡献。 (这很有趣,因为引擎开发人员表示这些决定背后的原因是鼓励贡献。在我的情况下,它产生了完全相反的效果。)

@Vultraz写得很清楚,很好读,谢谢。 很高兴听到这样的观点。

我是一个老式的 C++ 程序员,因此我在理解 Godot 源代码方面没有太多问题,我发现它结构良好并且非常符合我的喜好。 我认为能够相对较快地将 VR 支持添加到该引擎的核心,同时对代码库几乎没有预先存在的经验,这证明了该引擎的可读性和可理解性。 在某些方面它可能是老派,但我一直对某些部分的构建程度感到惊讶。 是的,有些部分需要现代化以减少内存消耗并提高性能,但总的来说,我对它印象深刻。

当我听到人们谈论 C++ 中的新语法时,我常常觉得自己老了,真的想知道大惊小怪是怎么回事。 但是我有点明白,当你学习更多现代 C++ 并决定为 Godot 做出贡献时,它似乎重新发明了轮子,这很奇怪。 但至少对于像我这样的人来说,我们已经习惯于看到这样的类实现了,我们只是对这里的大多数类的实现方式印象深刻:)

综上所述,有很多不同的方法来看待这个问题,这并不好笑。 从 Godot 的起源早于较新的 C++ 语法,到核心开发人员最熟悉的东西,再到 Godot 的跨平台性质和它可以(/可以)运行的设备(有些不是公开的)所施加的限制。 我觉得这没有对错之分,这是你习惯的和你来自哪里的问题,以及关于学习 Godot 如何工作的额外学习曲线是否超过重构大型代码的好处的问题根据。

我不知道你是否看过它,但胡安在 GDC 上发表了演讲,并已上线: https ://www.youtube.com/watch?v=C0szslgA8VY
在接近尾声的问答环节中,他谈到了围绕这个问题的决策。
这是一块好手表。

至于上面对 GDNative 的反应,普通人,GDNative 是最近添加的,可以满足特定需求,它并不表示核心产品的结构和构建方式。
尝试构建一个模块(https://docs.godotengine.org/en/3.1/development/cpp/custom_modules_in_cpp.html),是的,这需要将您的更改编译到核心产品中,这是 GDNative 试图解决的问题,但这给出了您可以更好地了解引擎的实际结构。 上面放弃的许多论点是 GDNative 的缺点,而不是 Godot 本身的缺点。
我希望有一个动态模块解决方案,它允许我以与构建静态模块相同的方式构建模块,也许有一天这将是可能的,但在那之前 GDNative 就足够了。

@Vultraz @mnn我实际上可以看到关于 STL 容器的要点,但这纯粹是因为某些实现(主要是MSVC )在调试模式下的性能非常慢。 但是人们可以简单地使用 EA 的 STL 并且做得很好(他们的 STL 速度快且便携)。

另外:我个人发现缺乏RAII是最令人痛苦的。 由于 RAII 对 C++11 来说并不新鲜,因此在整个代码中不必要地执行的手动清理量很奇怪。

至于上面对 GDNative 的反应,普通人,GDNative 是最近添加的,可以满足特定需求,它并不表示核心产品的结构和构建方式。

这当然是对的,但 GDNative 不应该比引擎本身对用户更友好,因为 GDNative 用于创建“脚本”,因此针对的受众是预期具有更低 C++ 技能的受众,而那些愿意陷入内部的人引擎?

由于这些决定/价值观,我怀疑我是否会为引擎做出贡献。 (这很有趣,因为引擎开发人员表示这些决定背后的原因是鼓励贡献。在我的情况下,它产生了完全相反的效果。)

我是一个 C++ 新手(大约两个月,每周 2 天在 C++ 中工作),所以我应该是从这个决定中受益的目标人群。 我发现 Godot 容器缺乏基本功能(与本身不太丰富的 stl 相比),我不认为容器与 GDNative 相关,但我可能弄错了。 我正在尽最大努力避免使用 Godot 容器,因为使用它们总是很痛苦。 我认为 Ref 的不一致和意外行为是引擎的责任,但我想我错了。

我意识到我的编程背景可能相当不寻常——在 JS/TS 专业工作多年,在 Scala 工作了一年,在 Haskell 中进行了较小的爱好项目(几千行,考虑到 Haskell 的简洁程度,这实际上并不是那么小——很多时候我编写代码在 C++ 中,在 Haskell 中至少要短 5 倍且更具可读性)。 我想知道我是否是唯一一个因使用过时的过于冗长的技术而气馁的人。

@mnn ,创建 GDNative 是为了将基于 C 的模块创建为动态库,因此您不必重新编译整个引擎。 除此之外,还创建了几种语言绑定,因此您可以使用 C++、python、rust 等编写模块,而无需编译整个引擎。

这样做的另一个目标是让开发人员能够创建模块,您只需交付模块本身并将其与稳定的引擎构建一起使用。 许多模块已经失效,因为它们还没有被维护到引擎的特定版本中。

所以,是的,从编译和部署的角度创建模块变得更加容易和简单。 但由于其方法,它有其局限性。 当您保持在这些限制范围内时,在 GDNative 中编写 C++ 代码可以保持简单,因为您构建模块的主题很简单。

尝试突破这些限制,你会头疼的。 我目前最头疼的问题是尝试实现 OpenGL 逻辑,而这一切都封装在可视化服务器架构中,而在 GDNative 内部并没有以我需要的方式真正可用。 但这更多是我想用 GDNative 做一些事情的一个因素,它从来没有被设计过。
当你将它用于它的设计用途时,你可以用它做一些非常酷的事情。

另请注意,GDNative 是一种重写需要更高性能的 GDScript 代码的方法。 因此,您无法访问 GDScript 无法访问的引擎内部的任何内容(例如 OpenGL)

最新的协程支持,即 co_await() 呢? 从 procgen 的角度来看,这是巨大的。

协程支持是 C++20 标准的一部分,但尚未最终确定。

请允许我插话,我目前正在为 3D 空间编辑器编写高级(想想源锤子编辑器)画笔/csg 工具,而关于不允许自动的讨论真的让我感到困惑。 有时_甚至可以显式地使用 auto_。 考虑以下:

我在 SpatialEditorViewportContainer 内,我想获取 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 及更高版本上成长,并且会发现该项目永远停留在一种甚至没有 lambda 表达式的语言中,这是相当令人沮丧的。

简而言之:C++11 或破产!

需要明确的是,我不提倡使用 STL。 滚动你自己的容器很好,但拒绝像 lambdas 和 auto 这样的特性似乎很愚蠢。

我将暂时关闭回声室,因为在这里进一步讨论毫无意义。 几个月前,我们已经讨论过我们计划使用 C++11 和/或一些更高版本的哪些特性。

我们只是在等待@hpvb有时间根据我们的共识最终确定他正在编写的指南,然后我们可以在这些指南发布后讨论更多关于这些指南的内容。 在那之前,这不是建设性的。

此页面是否有帮助?
0 / 5 - 0 等级