【游戏编程扯淡精粹】EASTL源码阅读

侯捷先生在《漫谈程序员与编程》 中讲到 STL 运用的三个档次:“会用 STL,是一种档次。对 STL 原理有所了解,又是一个档次。追踪过 STL 源码,又是一个档次。第三种档次的人用起 STL 来,虎虎生风之势绝非第一档次的人能够望其项背。”

本文编辑进度

WIP

  • doc
  • source

EASTL是什么

EASTL就是把STL重新实现一遍,其中:

  1. 大部分接口保持一致
  2. allocator做了重度改造,没法一致
  3. 加了一些游戏需要的容器和功能
  4. 内部做了一些优化,重写来保证跨平台,可调试,性能优化

本文是什么

过一遍EASTL源码仓库,以及《STL源码剖析》,提炼一些重点摘要

前置阅读

【游戏编程扯淡精粹】TinySTL源码阅读_游戏编程扯淡精粹-CSDN博客

前置知识

无,反正我不会模板元编程,就是来STL现学的

阅读方法

  • EASTL体量就大太多了,相比于TinySTL
  • 分几个pass来阅读
    1. 快速地过一遍,结合代码用例进行分析,不要让栈太深
    2. 接入ZeloEngine使用后再说
  • 配合教材 STL源码剖析 (豆瓣)
  • 按教材章节顺序,一边看书,一边看EASTL
  • 本文参考教材顺序,按自己实际阅读源码的顺序编排章节,读者按顺序阅读即可

生词本

  • 跨平台性=可移植性,portable,cross-platform
  • 模板特化,T实例化为具体类型MyType,提供MyType的替代实现,比如特殊优化,或特殊处理
  • type traits,类型特征,比如T::is_pointer,T::is_pod,用于模板编程中根据类型特征进行ifelse判断
  • typename,关键字,它向编译器标识表达式T是类型而不是值
  • RB-tree,红黑树

侯捷

  • 配置=>内存分配
  • 型别=>类型T

构建运行

  • CLion x64,-DEASTL_BUILD_TESTS:BOOL=ON

    • x86有问题,缺windows库链接
  • 选择EASTLTest运行

构建脚本

set build_folder=out
mkdir %build_folder%
pushd %build_folder%
call cmake .. -DEASTL_BUILD_TESTS:BOOL=ON -DEASTL_BUILD_BENCHMARK:BOOL=OFF
call cmake --build . --config Release
call cmake --build . --config Debug
call cmake --build . --config RelWithDebInfo
call cmake --build . --config MinSizeRel
pushd test
call ctest -C Release
call ctest -C Debug
call ctest -C RelWithDebInfo
call ctest -C MinSizeRel
popd
popd

接入引擎

  1. 重载全局new
  2. 替换掉部分:
    • vector
    • map
    • unique_ptr
    • shared_ptr

替换基本无感,运行性能也没有太大差别(CPU,内存)

问题

  1. 第三方库使用std作为接口参数,比如spdlog,sol2
  2. std::string,很难替换掉

doc/EASTL-n2271.pdf

Abstract

游戏平台和游戏设计对游戏软件的要求不同于其他平台的要求。

其中最重要的是,游戏软件需要大量的内存,但实际可用的内存是有限的

其次,游戏软件也面临着其他限制,例如较弱的处理器缓存,较弱的CPU和非默认内存对准要求

结果是,游戏软件需要小心使用内存和CPU。 C++标准库的容器,迭代器和算法可能对各种游戏编程需求有用。然而,标准图书馆的缺点和疏忽导致它并不是高性能游戏软件的理想选择。

这些弱点中最重要的是分配器模型。 C++标准图书馆被扩展和部分重新设计为EASTL,以便以便携式和一致的方式解决这些弱点。本文介绍了游戏软件开发问题,目前C++标准库的弱点,以及EASTL的解决方案。

Motivation for EASTL

下面是一个清单,描述为什么STL不适合游戏开发:

  • 一些 STL 实现(尤其是 Microsoft STL)具有较差的性能特征,使其不适合游戏开发。EASTL 比所有现有的 STL 实现都快。
  • STL 有时很难调试,因为大多数 STL 实现使用神秘的变量名和不寻常的数据结构。
  • STL 分配器有时很难使用,因为它们有很多要求,并且一旦绑定到容器就不能修改。
  • STL 包含过多的功能,可能导致代码量超出预期。告诉程序员他们不应该使用该功能并不容易。
  • STL 是通过非常深入的函数调用实现的。这导致在非优化构建中的性能不可接受,有时在优化构建中也是如此。
  • STL 不支持包含对象的对齐。
  • STL 容器不允许您在不提供要从中复制的条目的情况下将条目插入容器。这可能是低效的。
  • 在现有的 STL 实现(例如 STLPort)中发现的有用的 STL 扩展(例如 slist、hash_map、shared_ptr)是不可移植的,因为它们在其他版本的 STL 中不存在或者在 STL 版本之间不一致。
  • STL 缺少游戏程序员认为有用的扩展(例如 intrusive_list),但在可移植的 STL 环境中可以得到最佳优化。
  • STL 的规范限制了我们有效使用它的能力。例如,STL 向量不能保证使用连续内存,因此不能安全地用作数组。
  • STL 在性能之前强调正确性,而有时您可以通过降低学术纯粹性来获得显着的性能提升。
  • STL 容器具有私有实现,不允许您以可移植的方式处理它们的数据,但有时这是一件很重要的事情(例如节点池)。
  • 所有现有版本的 STL 至少在其某些容器的空版本中分配内存。这并不理想,并且会阻止诸如容器内存重置之类的优化,这些优化可以在某些情况下大大提高性能。
  • STL 的编译速度很慢,因为大多数现代 STL 实现都非常大。
  • 有一些法律问题使我们很难自由使用 STLPort 等可移植的 STL 实现。
  • 我们对 STL 的设计和实现没有发言权,因此无法对其进行更改以满足我们的需求。

评价

总的来讲,STL注重的是标准,EASTL注重的是在游戏开发中的实践和性能

重点是allocator,以及容器的内存优化,内存对齐;然后是一些扩展

EASTL就是把STL重新实现一遍,大部分接口保持一致;allocator做了重度改造,没法一致;加了一些游戏需要的容器和功能;内部做了一些优化,重写来保证跨平台,可调试,性能优化

EASTL Design

Prime Directives

EASTL 的实施首先由以下按重要性顺序列出的指令指导。

  1. 效率(速度和内存使用)
  2. 正确性
  3. 可移植性
  4. 可读性

请注意,与必须将正确性放在首位的商业 STL 实现不同,我们更重视效率。因此,某些功能可能具有其他类似系统中不存在的使用限制,但允许更有效的操作,尤其是在对我们重要的平台上。

可移植性很重要,但并不重要。是的,EASTL 必须在我们将为其发布游戏的所有平台上编译和运行。但我们并不认为这意味着所有可以想象用于此类平台的编译器。例如,Microsoft VC6 可以用来编译 Windows 程序,但是 VC6 的 C++ 对 EASTL 的支持太弱,所以在 VC6 下根本无法使用 EASTL。

EASTL 比许多其他模板库(尤其是 Microsoft STL 和 STLPort)实现了更好的可读性。我们尽一切努力使 EASTL 代码简洁明了。有时我们需要提供优化(特别是与 type_traits 和迭代器类型相关)会导致代码不那么简单,但效率恰好是我们的主要指令,因此它覆盖了所有其他考虑因素。

评价

首先确定EA游戏发布的平台

然后在这些重点平台(包含编译器)上确保跨平台兼容性,以及性能最优化

其次,可读性是为了可维护性,可调试性,重写过程顺手完善的事情

标准化,和数学上的正确性,基本不考虑

线程安全

简单地说 EASTL 是线程安全的或线程不安全的还不够简单。但是,我们可以说,在线程安全方面,EASTL 做了正确的事情。

单个 EASTL 容器不是线程安全的。也就是说,如果这些访问中的任何一个正在修改操作,那么同时从多个线程访问容器实例是不安全的。可以同时从多个线程以及任何其他独立数据结构中读取给定容器。如果用户希望能够从多个线程对容器实例进行修改访问,则由用户来确保发生正确的线程同步。这通常意味着使用互斥锁。

容器以外的 EASTL 类在线程安全方面与容器相同。EASTL 函数(例如算法)本质上是线程安全的,因为它们没有实例数据并且完全在堆栈上操作。在撰写本文时,没有 EASTL 函数分配内存,因此不会通过这种方式带来线程安全问题。

用户很可能需要关注内存分配方面的线程安全。如果用户从多个线程修改容器,那么分配器将被多个线程访问。如果分配器在多个容器实例(相同类型的容器或不同类型的容器)之间共享,那么用户用来保护对单个实例的访问的互斥锁(如上所述)将不足以为跨多个实例使用的分配器提供线程安全。这里的常规解决方案是在分配器中使用互斥锁,如果它被执行以供多个线程使用。

EASTL 既不使用静态变量也不使用全局变量,因此不存在会使用户难以实现线程安全的实例间依赖关系。

评价

  1. EASTL容器以及类本身都不是线程安全的,需要用户使用mutex来保证
  2. EASTL函数(algorithm)是线程安全的,因为没有数据,没有内存分配
  3. EASTL的allocator同样不是线程安全的,一般的做法是在allocator内部用mutex
  4. EASTL不使用全局变量

EASTL Benchmarks

虽然 EASTL 通常优于标准 STL,但这里有一些基准测试表明 EASTL 比标准 STL 慢。对此有三种主要解释:

  1. EASTL 正在做出某种速度、内存或设计折衷,从而导致给定的速度差异。在可能的这种情况下,EASTL 在一个基准上运行得更慢,以便在另一个被认为更重要的基准上运行得更快。这种解释约占案例的 60%。
  2. 编译器优化和生成的代码巧合地偏爱一种实现而不是另一种实现,通常当它们在视觉上几乎相同时。这种外植约占病例的30%。
  3. EASTL 还没有达到应有的优化水平。这种解释约占案例的 10%(在撰写本文时,整个 EASTL 中大约有三个这样的功能)。

EASTL 最佳实践

  1. 考虑侵入式容器。
  2. 考虑固定大小的容器。
  3. 考虑自定义分配器。
  4. 考虑哈希表而不是映射。
  5. 考虑一个用于不变数据的vector_map(又名排序向量)。
  6. 考虑 slist 而不是 list。
  7. 避免循环中多余的 end() 和 size()。
  8. 迭代容器而不是使用 operator[]。
  9. 学习正确使用字符串类。
  10. 如果您希望 size() 为 O(1),则缓存列表大小。
  11. 尽可能使用 empty() 而不是 size()。
  12. 了解您的容器效率。
  13. 使用vector::reserve。
  14. 使用 vector::set_capacity 来减少内存使用。
  15. 使用 swap() 而不是手动实现的版本。
  16. 考虑存储指针而不是对象。
  17. 考虑智能指针而不是原始指针。
  18. 使用迭代器前增量而不是后增量。
  19. 进行临时引用,以便可以跟踪/调试代码。
  20. 考虑使用 bitvector 或 bitset 而不是 vector。
  21. 向量可以被视为连续内存。
  22. 通过 find_as() 而不是 find() 搜索 hash_map。
  23. 利用 type_traits(例如EASTL_DECLARE_TRIVIAL_RELOCATE)。
  24. 命名容器以跟踪内存使用情况。
  25. 学习算法。
  26. 通过引用而不是值传递和返回容器。
  27. 考虑使用 reset_lose_memory() 进行快速容器拆解。
  28. 考虑使用 fixed_substring 而不是复制字符串。
  29. 考虑使用vector::push_back(void)。

EASTL Maintenance

  • 不使用 RTTI。
  • 不使用异常规范(例如,将“throw”声明符附加到函数中)。
  • 除非实现明确要求(例如vector::at),否则不使用异常处理本身。
  • 异常使用需要了解 EASTL_EXCEPTIONS_ENABLED。
  • 不使用宏(在 config.h 之外)。宏使用户的事情变得更加困难。
  • 不使用静态或全局变量。
  • 不使用全局 new、delete、malloc 或 free。所有内存都必须是用户可通过分配器参数指定的(默认指定或显式指定)。
  • 容器使用受保护的成员数据和函数,而不是私有的。这是因为这样做允许子类在不创建中间函数的情况下扩展容器。回想一下我们上面的主要指令,性能和简单性压倒一切。
  • 不使用多线程原语。
  • 没有使用 export 关键字。
  • 我们没有关于 C 风格转换与 C++ static_cast<> 等的规则。我们将始终使用 static_cast,除非调试器无法评估它们,因此在实践中它们可能会妨碍调试和跟踪。但是,如果转换是用户不需要在调试器中查看的转换,则首选 C++ 转换。
  • 没有任何外部库依赖项,包括标准 STL。EASTL 仅依赖于 EABase 和 C++ 编译器。
  • 所有代码都必须是 const 正确的。这不仅仅是为了可读性——除非在任何地方都正确使用了 const-ness,否则编译可能会失败。
  • 算法不涉及容器;它们仅指迭代器。
  • 算法通常不分配内存。如果出现这种情况,应该有一个允许用户提供分配器的算法版本。
  • 没有劣质的实现。除非具有专业质量,否则不应将任何设施添加到 EASTL。
  • 无论维护者的个人喜好如何,维护者都应该效仿 EASTL 风格的代码布局。做到入乡随俗。EASTL 使用 4 个空格进行缩进,这是 EA 中大部分代码的编写方式。
  • 在没有咨询同行小组的情况下,不应进行任何重大更改。

functor

就是Python的operator模块

header

  • functional.h
  • functional_base.h

案例一

RenderItem按视距排序,不透明物体从近到远,透明物体从远到近

// 2 render queue, opaque and transparent, sorted by distance to camera
using OpaqueDrawables = std::multimap<float, RenderItem, std::less<float>>;
using TransparentDrawables = std::multimap<float, RenderItem, std::greater<float>>;if (material.isBlendable()) {transparentDrawables.emplace(distantToCamera, renderItem);
} else {opaqueDrawables.emplace(distantToCamera, renderItem);
}

案例二

可以自动推导,省略T=int

std::greater<>()(6, 4)  // -> true

greater

第一个函数是核心实现,a>b

第二个函数是一个特化,做了输入和输出的类型自动推导(案例二)

template <typename T = void>
struct greater : public binary_function<T, T, bool>
{EA_CPP14_CONSTEXPR bool operator()(const T& a, const T& b) const{ return a > b; }
};// http://en.cppreference.com/w/cpp/utility/functional/greater_void
template <>
struct greater<void>
{template<typename A, typename B>EA_CPP14_CONSTEXPR auto operator()(A&& a, B&& b) const-> decltype(eastl::forward<A>(a) > eastl::forward<B>(b)){ return eastl::forward<A>(a) > eastl::forward<B>(b); }
};

iterator & algorithm

下面不讲如何自定义iterator了,我目前从来没遇到过需求(C#,Python,Lua),再加上C++ iterator比较难写,简单了解,按需深入

header

  • iterator.h
  • algorithm.h

描述

  • iterator和algorithm结合紧密,STL的一大思想是,将container和algorithm拆分,并以iterator作为桥梁对接

    • 主要抽象是,algorithm只操作iterator,而不知道具体container,使得algorithm是通用的
  • iterator是一种类似智能指针的结构,指向T
  • algorithm非常简单,但是iterator并不容易,这里引入模板的type_traits和类型萃取机制,主要是处理容器的T在不同情况下的差异,比如T是指针
  • 一个container要支持iterator,就必须实现iterator要求的type_traits“接口”

iterator分类

迭代器可以分为不同的种类,这是因为他们使用不同的算法,有5种迭代器。

例如,find()算法需要一个可以递增的迭代器,而reverse()算法需要一个可以递减的迭代器等。

常见容器,vector、deque提供的是随机访问迭代器,list提供的是双向迭代器,set和map提供的是向前迭代器。

有5种迭代器:

  1. 输入迭代器(Input Iterator):只能向前单步迭代元素,不允许修改由该迭代器所引用的元素;
  2. 输出迭代器(Output Iterator):只能向前单步迭代元素,对由该迭代器所引用的元素只有写权限;
  3. 向前迭代器(Forward Iterator):该迭代器可以在一个区间中进行读写操作,它拥有输入迭代器的所有特性和输出迭代器的部分特性,以及向前单步迭代元素的能力;
  4. 双向迭代器(Bidirectional Iterator):在向前迭代器的基础上增加了向后单步迭代元素的能力;
  5. 随机访问迭代器(Random Access Iterator):不仅综合以后4种迭代器的所有功能,还可以像指针那样进行算术计算;

案例一

封装常见algorithm对container的全迭代

#include <algorithm>template<class C, class Func>
inline Func ForEach(C &c, Func f) {return std::for_each(c.begin(), c.end(), f);
}template<class C, class Func>
inline void EraseIf(C &c, Func f) {c.erase(std::remove_if(c.begin(), c.end(), f), c.end());
}template<class C, class T>
inline void Erase(C &c, const T &t) {c.erase(std::remove(c.begin(), c.end(), t), c.end());
}template<class C, class T>
inline auto Find(C &c, const T &value) {return std::find(c.begin(), c.end(), value);
}template<class C, class Pred>
inline auto FindIf(C &c, Pred p) {return std::find_if(c.begin(), c.end(), p);
}

案例二

自己按需实现新的algorithm

template<typename MAP, typename K, typename V>
inline bool AddOrUpdate(MAP &m, const K &key, const V &val) {typename MAP::iterator lb = m.lower_bound(key);if (lb != m.end() && !m.key_comp()(key, lb->first)) {// lb points to a pair with the given key, update pair's valuelb->second = val;return false;} else {// no key exists, insert new pairm.insert(lb, std::make_pair(key, val));return true;}
}

for_each

template <typename InputIterator, typename Function>
inline Function
for_each(InputIterator first, InputIterator last, Function function)
{for(; first != last; ++first)function(*first);return function;
}

allocator

header

  • allocator.h
  • allocator_malloc.h
  • fixed_allocator.h

描述

EASTL 所做的是使用一种更熟悉的内存分配模式,即只有一个分配器类接口,它被所有容器使用。此外,EASTL 容器允许您访问它们的分配器并查询它们、命名它们、更改它们等。

EASTL 选择在容器交换和分配操作期间使分配器不会在容器之间复制。这意味着如果容器 A 与容器 B 交换其内容,则两个容器都保留其原始分配器。类似地,将容器 A 分配给容器 B 会导致容器 B 保留其原始分配器。等效的容器应通过 operator==; 报告。如果分配器相等,EASTL 将进行智能交换,否则将进行暴力交换。

// EASTL allocator
class allocator
{public:allocator(const char* pName = NULL);void* allocate(size_t n, int flags = 0);void* allocate(size_t n, size_t alignment, size_t offset, int flags = 0);void  deallocate(void* p, size_t n);const char* get_name() const;void        set_name(const char* pName);
};
allocator* GetDefaultAllocator();

评价

就是malloc和free的类包装,对应std::allocator<char>,与std区别在于没用模板

dummy

啥也不干

class EASTL_API dummy_allocator
{public:EASTL_ALLOCATOR_EXPLICIT dummy_allocator(const char* = NULL) { }dummy_allocator(const dummy_allocator&) { }dummy_allocator(const dummy_allocator&, const char*) { }dummy_allocator& operator=(const dummy_allocator&) { return *this; }void* allocate(size_t, int = 0)                 { return NULL; }void* allocate(size_t, size_t, size_t, int = 0) { return NULL; }void  deallocate(void*, size_t)                 { }const char* get_name() const      { return ""; }void        set_name(const char*) { }
};inline bool operator==(const dummy_allocator&, const dummy_allocator&) { return true;  }
inline bool operator!=(const dummy_allocator&, const dummy_allocator&) { return false; }

get_default_allocator

默认allocator,第一个是全局的默认分配器,第二个是每个类型特定的默认分配器

// GetStaticDefaultAllocator
EASTL_API allocator* GetDefaultAllocator();
EASTL_API allocator* SetDefaultAllocator(allocator* pAllocator);// Example:
// MyAllocatorType* gpSystemAllocator;
// MyAllocatorType* get_default_allocator(const MyAllocatorType*) { return gpSystemAllocator; }
template <typename Allocator>
Allocator* get_default_allocator(const Allocator*);EASTLAllocatorType* get_default_allocator(const EASTLAllocatorType*);

allocator_malloc

用malloc实现allocator

基本用法:vector<int, allocator_malloc> intVector

void* allocate(size_t n, int /*flags*/ = 0){ return malloc(n); }void* allocate(size_t n, size_t alignment, size_t alignmentOffset, int /*flags*/ = 0)
{ if((alignment <= EASTL_SYSTEM_ALLOCATOR_MIN_ALIGNMENT) && ((alignmentOffset % alignment) == 0))return malloc(n);return NULL;
}void deallocate(void* p, size_t /*n*/){ free(p); }

vector

  • 2k行
  • vector作为container的代表,是最先分析的
  • vector的重点在于内存动态扩容,这是与array最大的区别
  • vector的基本操作不再赘述,一是TinySTL以及分析过了,二是确实简单

VectorBase

抽了一个基类,处理ctor中分配内存时抛出异常的情况

template <typename T, typename Allocator>
struct VectorBasetemplate <typename T, typename Allocator = EASTLAllocatorType>
class vector : public VectorBase<T, Allocator>

数据结构

  • std::vector结构简单清晰,三个iterator/T*,指向开头,数据结尾,容量结尾
  • eastl::vector使用compressed_pair,当作pair
namespace TinySTL{class vector{private:T *start_;T *finish_;T *endOfStorage_;
...
namespace eastl{struct VectorBase{protected:T*                                          mpBegin;T*                                          mpEnd;eastl::compressed_pair<T*, allocator_type>  mCapacityAllocator;
...

扩容

  • luaState.stack其实也是vector,和下图有一张类似的增长示意图
  • 扩容的realloc是重新分配一大块内存,而不是在原末尾接续一块内存
  • 扩容的realloc会导致迭代器失效
  • 扩容步骤:
    1. 分配新内存allocateplacement new
    2. 拷贝旧数据uninitialized_copy
    3. 释放旧内存destructdeallocate
  • 插入新数据(不触发扩容)uninitialized_fill_n

list & slist

  • list是双向链表,支持双向迭代
  • 相比于vector,没有扩容的概念,每个节点都是独立内存,增删节点,不会使得迭代器失效
  • 节点就三个数据:next,prev和T data
  • slit是单链表,支持单项迭代,相比list好处是节约一个prev指针的开销
struct ListNodeBase{ListNodeBase* mpNext;ListNodeBase* mpPrev;
}template <typename T, typename Allocator>
class ListBase{protected:eastl::compressed_pair<base_node_type, allocator_type>  mNodeAllocator;}
}template <typename T, typename Allocator = EASTLAllocatorType>
class list : public ListBase<T, Allocator> {}

评价

list就是一个基本的双向链表

链表这块实现方法比较多,之后对比一下侵入式

deque

  • 优先使用vector进行操作(比如sort),因为内存连续性能高
  • 使用deque的主要原因,是在头部插入删除快
  • deque是分段连续的,对vector的扩容问题,deque的解决是额外分配一块不连续的内存
  • 代价是,迭代器设计复杂,所以谨慎使用
  • deque源码比vector复杂得多

由于上述问题,deque源码就不看了

stack & queue

stack和queue是deque的子集,因此封装一下(或者不封装)deque就有了,也称为adapter

stack

所需操作

  • push_back
  • pop_back
  • back

适配:vector, deque, string, list, intrusive_list

queue

所需操作

  • push_back
  • pop_front
  • front
  • back

适配:deque, list, intrusive_list

template <typename T, typename Container = eastl::vector<T> >
class stack {}template <typename T, typename Container = eastl::deque<T, EASTLAllocatorType, DEQUE_DEFAULT_SUBARRAY_SIZE(T)> >
class queue {}

由于只是简单封装,源码不看

heap/priority queue

heap的结构是完全二叉树,实际用vector表示

由于主要是数据结构算法,源码不看

map & set

  • 底层实现核心有两种:rbtree和hashtable
  • 上层容器:(map,set) x (multi, not-multi)
  • 一共是八种容器

很没营养。。

EABase

什么是 EABase?

EABase 是一小组定义独立于平台的数据类型和宏的头文件。因此,它类似于许多具有 platform.h、system.h、define.h 等文件的项目。不同之处在于 EABase 非常全面,并且是指定的 Electronic Arts 全球新项目标准。

关于基本类型和定义,其中许多已经出现在最新的 C 语言标准中,尽管 C++ 标准尚未正式采用它们。EABase 弥补了差距并定义了这些尚未定义的值。关于编译器和平台定义,EABase 提供了一种标准可靠的方法来识别或指定编译器、平台、字节序、对齐属性等。

使用说明

您可能不想使用 float_t 和 double_t。它们的存在是为了与 C99 兼容,但您很少使用它们,因为它们的大小是可变的。

Prid8 等使用起来有些痛苦和丑陋,您可能会发现您不喜欢它们。它们也是为了 C99 兼容性。

intptr_t 不是指向 int 的指针;它是一个与指针大小相同的 int,因此您可以安全地在其中存储指针。

EA::result_type 很少使用并且存在是为了向后兼容。

EABase 具体定义了什么?

在这里,我们列出了 EABase 定义的内容,按类别分组。这些定义在此文件顶部列出的文件修改日期之前是最新的。

基本类型和定义

BOOL8_T,INT8_T,UINT8_T,INT16_T,UINT16_T,INT32_T,UINT32_T,INT64_T,UINT64_T,FLOAT_T,DOUPY_T,(EASTDC PACKINGS IMPLING INT128_T)
INTPTR_T,UINTPTR_T,INTMAX_T,UINTMAX_T,SSIZE_T
CHAR8_T ,CHAR16_T,CHAR32_T
INT8_C(),UINT8_C(),UINT8_C(),等
INT8_MIN、INT8_MAX、UINT8_MAX 等
PRId8、PRId16、PRId32 等 SCNd8、SCNd16、SCNd32 等

结果类型和定义

EA::result_type
EA::SUCCESS, EA::FAILURE
EA_SUCCEEDED(), EA_FAILED()

编译器定义

EA_COMPILER_GNUC
EA_COMPILER_SN
EA_COMPILER_MSVC
EA_COMPILER_METROWERKS
EA_COMPILER_INTEL
EA_COMPILER_BORLANDC

EA_COMPILER_VERSION = <整数>
EA_COMPILER_NAME = <字符串>
EA_COMPILER_STRING = <字符串>

EA_COMPILER_NO_STATIC_CONSTANTS
EA_COMPILER_NO_TEMPLATE_SPECIALIZATION
EA_COMPILER_NO_TEMPLATE_PARTIAL_SPECIALIZATION
EA_COMPILER_NO_MEMBER_TEMPLATES
EA_COMPILER_NO_MEMBER_TEMPLATE_SPECIALIZATION
EA_COMPILER_NO_TEMPLATE_TEMPLATES
EA_COMPILER_NO_MEMBER_TEMPLATE_FRIENDS
EA_COMPILER_NO_VOID_RETURNS
EA_COMPILER_NO_COVARIANT_RETURN_TYPE
EA_COMPILER_NO_DEDUCED_TYPENAME
EA_COMPILER_NO_ARGUMENT_DEPENDENT_LOOKUP
EA_COMPILER_NO_EXCEPTION_STD_NAMESPACE
EA_COMPILER_NO_EXPLICIT_FUNCTION_TEMPLATE_ARGUMENTS
EA_COMPILER_NO_EXCEPTIONS
EA_COMPILER_NO_UNWIND

EA_COMPILER_IS_ANSIC
EA_COMPILER_IS_C99
EA_COMPILER_HAS_C99_TYPES
EA_COMPILER_IS_CPLUSPLUS
EA_COMPILER_MANAGED_CPP

实用程序

EA_ALIGN_OF()
EA_PREFIX_ALIGN()
EA_POSTFIX_ALIGN()
EA_ALIGNED()
EA_PACKED()
EA_LIKELY()
EA_UNLIKELY()
EA_ASSUME()
EA_PURE
EA_WCHAR_T_NON_NATIVE
EA_WCHAR_SIZE
EA_RESTRICT
EA_DEPRECATED
EA_PREFIX_DEPRECATED
EA_POSTFIX_DEPRECATED
EA_FORCE_INLINE
EA_NO_INLINE
EA_PREFIX_NO_INLINE
EA_POSTFIX_NO_INLINE
EA_PASCAL
EA_PASCAL_FUNC()
EA_SSE = [0 | 1]
EA_IMPORT
EA_EXPORT
EA_OVERRIDE
EA_INIT_PRIORITY
EA_MAY_ALIAS

平台定义

EA_PLATFORM_MAC
EA_PLATFORM_OSX
EA_PLATFORM_IPHONE
EA_PLATFORM_ANDROID
EA_PLATFORM_LINUX
EA_PLATFORM_WINDOWS
EA_PLATFORM_WIN32
EA_PLATFORM_WIN64
EA_PLATFORM_HPUX
EA_PLATFORM_SUN

EA_PLATFORM_NAME
EA_PLATFORM_DESCRIPTION
EA_PROCESSOR_POWERPC,EA_PROCESSOR_X86,EA_PROCESSOR_ARM等
EA_SYSTEM_LITTLE_ENDIAN,EA_SYSTEM_BIG_ENDIAN
EA_ASM_STYLE_ATT,EA_ASM_STYLE_INTEL,EA_ASM_STYLE_MOTOROLA
EA_PLATFORM_PTR_SIZE
EA_PLATFORM_WORD_SIZE

【游戏编程扯淡精粹】EASTL源码阅读相关推荐

  1. 【游戏编程扯淡精粹】如何学习编程语言

    [游戏编程扯淡精粹]如何学习编程语言 文章目录 [游戏编程扯淡精粹]如何学习编程语言 如果你没有学过编程 如果你只是想提升工作效率 如果你想学习计算机科学与技术 如果你已经是熟悉了一门编程语言 如果你 ...

  2. 【游戏编程扯淡精粹】游戏编程设计模式

    [游戏编程扯淡精粹]游戏编程设计模式 本文最初写于2018/9/4 毛星云 RIP 如何练习设计模式 基本盘:长期维护一个大型工程,持续积累 维护一个设计模式表格,日常查找使用 多学几门编程语言和编程 ...

  3. 【游戏编程扯淡精粹】调试方法论

    [游戏编程扯淡精粹]调试方法论 文章目录 [游戏编程扯淡精粹]调试方法论 故障树分析法 Bug跟踪系统 & Bug交流分享 面对Bug的心态 如何在工作中正确高效地修bug? 找到原作者和模块 ...

  4. 【游戏编程扯淡精粹】UE5 蓝图

    [游戏编程扯淡精粹]UE5 蓝图 最近新学蓝图编程..还没看蓝图VM代码,有些点不保证准确 本文主要是从使用角度,分析你为什么需要学习蓝图,蓝图适合做什么,不适合做什么 最后 Bonus,跟一下 Bl ...

  5. 【游戏编程扯淡精粹】工作两年总结

    [游戏编程扯淡精粹]工作两年总结 大纲 正文: 做了什么,接下来做什么? 如何阅读<游戏引擎架构> Roadmap & Milestone 个人知识系统 个人技能成长的依赖倒置 最 ...

  6. 【游戏编程扯淡精粹】工作第三年总结

    工作第三年总结 文章目录 工作第三年总结 #1 做了什么 自研路线 Lua 脚本系统 ToolX #2 职业发展 如何做事 技术中台化 内卷的职业市场 个人成长 #3 心态建设 Owner vs 打工 ...

  7. 【通知】▁▂▃ Himi 著作《Android游戏编程之从零开始》★书籍源码+第4/6/7样章★博客系列源码整理打包-免费下载★ ▃▂▁

    2011年9月22日(Himi的22岁生日当天),Himi的著作:<Android游戏编程之从零开始>一书正式发售: (大家可以到新华书店.淘宝.拍拍.当当.亚马逊等进行购买): 感谢一直 ...

  8. [转] Himi 著作《Android游戏编程之从零开始》★书籍源码+第4/6/7样章—免费下载★...

    本文转自:http://blog.csdn.net/xiaominghimi/article/details/6802444 Himi 原创, 转载请注明出处,谢谢! 原文地址:http://blog ...

  9. [置顶]▁▂▃ Himi 著作《Android游戏编程之从零开始》★书籍源码免费下载★ ▃▂▁...

    Himi 原创, 转载请注明出处,谢谢! 原文地址:http://blog.csdn.net/xiaominghimi/article/details/6802444 2011年9月22日(Himi的 ...

最新文章

  1. 105、Replicated Mode VS Global Mode (Swarm12)
  2. Dubbo——面试问题集(1~3)
  3. spring和spring_Spring交易可见性
  4. Java LocalDate类| minusYears()方法与示例
  5. 代码中log一直报错
  6. mysql 5.7 my default_Windows64位mysql5.7以上版本包解压中没有data目录和my-default.ini及服务无法启动的快速解决办法(问题小结)...
  7. day02.1 爬取豆瓣网电影信息
  8. “猜猜红桃A在哪里”——android小游戏(入门学习必备)
  9. Geoda空间自相关分析—局部Moran‘I指数(运用GeoDa制作Lisa图)
  10. 实战四:Kaggle自行车租赁预测比赛
  11. PS小知识(五)——羽化(图片拼接后去缝、自然过渡渐变)
  12. java socket 聊天室_java利用Socket实现聊天室功能实例
  13. live2d 3行代码 为网站添加萌萌哒看板娘
  14. 山东大学为什么火了_比校花更诱人,山东大学因为它,火了!
  15. 中国移动规范学习——4A技术要求(综述)
  16. KiCad V6使用记录
  17. 李建忠讲23种设计模式笔记-上
  18. kubernetes 曲线救国式下载 kubeadm 1.21 相关镜像
  19. pm2 : 无法加载文件 C:\Users\zhanghuan\AppData\Roaming\npm\pm2.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 htt ps:/go.micr
  20. SONM月报_2018年8月

热门文章

  1. commom fileupload结构
  2. 公交专用道:三环将实现全环连通
  3. NOLO使用记录——遇到的问题(连接问题)
  4. [Pandas] 筛选DataFrame含有空值的数据行
  5. PBI培训(4):大文件拆分,csv乱码处理
  6. visual studio 2019工程运行时不要弹出cmd黑窗口
  7. 一木.溪桥学Python-04: Python基础数据类型int ,float ,bool ,complex,None,str, 转义字符
  8. 上传txt文章,自动转变为英文文章,插入图片地址,自动识别并插入html
  9. 人类最优秀Web软件排行
  10. 欢迎使用CSDN-markdown编辑器sadsad