【游戏编程扯淡精粹】EASTL源码阅读
侯捷先生在《漫谈程序员与编程》 中讲到 STL 运用的三个档次:“会用 STL,是一种档次。对 STL 原理有所了解,又是一个档次。追踪过 STL 源码,又是一个档次。第三种档次的人用起 STL 来,虎虎生风之势绝非第一档次的人能够望其项背。”
本文编辑进度
WIP
- doc
- source
EASTL是什么
EASTL就是把STL重新实现一遍,其中:
- 大部分接口保持一致
- allocator做了重度改造,没法一致
- 加了一些游戏需要的容器和功能
- 内部做了一些优化,重写来保证跨平台,可调试,性能优化
本文是什么
过一遍EASTL源码仓库,以及《STL源码剖析》,提炼一些重点摘要
前置阅读
【游戏编程扯淡精粹】TinySTL源码阅读_游戏编程扯淡精粹-CSDN博客
前置知识
无,反正我不会模板元编程,就是来STL现学的
阅读方法
- EASTL体量就大太多了,相比于TinySTL
- 分几个pass来阅读
- 快速地过一遍,结合代码用例进行分析,不要让栈太深
- 接入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
接入引擎
- 重载全局new
- 替换掉部分:
- vector
- map
- unique_ptr
- shared_ptr
替换基本无感,运行性能也没有太大差别(CPU,内存)
问题
- 第三方库使用std作为接口参数,比如spdlog,sol2
- 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 的实施首先由以下按重要性顺序列出的指令指导。
- 效率(速度和内存使用)
- 正确性
- 可移植性
- 可读性
请注意,与必须将正确性放在首位的商业 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 既不使用静态变量也不使用全局变量,因此不存在会使用户难以实现线程安全的实例间依赖关系。
评价
- EASTL容器以及类本身都不是线程安全的,需要用户使用mutex来保证
- EASTL函数(algorithm)是线程安全的,因为没有数据,没有内存分配
- EASTL的allocator同样不是线程安全的,一般的做法是在allocator内部用mutex
- EASTL不使用全局变量
EASTL Benchmarks
虽然 EASTL 通常优于标准 STL,但这里有一些基准测试表明 EASTL 比标准 STL 慢。对此有三种主要解释:
- EASTL 正在做出某种速度、内存或设计折衷,从而导致给定的速度差异。在可能的这种情况下,EASTL 在一个基准上运行得更慢,以便在另一个被认为更重要的基准上运行得更快。这种解释约占案例的 60%。
- 编译器优化和生成的代码巧合地偏爱一种实现而不是另一种实现,通常当它们在视觉上几乎相同时。这种外植约占病例的30%。
- EASTL 还没有达到应有的优化水平。这种解释约占案例的 10%(在撰写本文时,整个 EASTL 中大约有三个这样的功能)。
EASTL 最佳实践
- 考虑侵入式容器。
- 考虑固定大小的容器。
- 考虑自定义分配器。
- 考虑哈希表而不是映射。
- 考虑一个用于不变数据的vector_map(又名排序向量)。
- 考虑 slist 而不是 list。
- 避免循环中多余的 end() 和 size()。
- 迭代容器而不是使用 operator[]。
- 学习正确使用字符串类。
- 如果您希望 size() 为 O(1),则缓存列表大小。
- 尽可能使用 empty() 而不是 size()。
- 了解您的容器效率。
- 使用vector::reserve。
- 使用 vector::set_capacity 来减少内存使用。
- 使用 swap() 而不是手动实现的版本。
- 考虑存储指针而不是对象。
- 考虑智能指针而不是原始指针。
- 使用迭代器前增量而不是后增量。
- 进行临时引用,以便可以跟踪/调试代码。
- 考虑使用 bitvector 或 bitset 而不是 vector。
- 向量可以被视为连续内存。
- 通过 find_as() 而不是 find() 搜索 hash_map。
- 利用 type_traits(例如EASTL_DECLARE_TRIVIAL_RELOCATE)。
- 命名容器以跟踪内存使用情况。
- 学习算法。
- 通过引用而不是值传递和返回容器。
- 考虑使用 reset_lose_memory() 进行快速容器拆解。
- 考虑使用 fixed_substring 而不是复制字符串。
- 考虑使用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种迭代器:
- 输入迭代器(Input Iterator):只能向前单步迭代元素,不允许修改由该迭代器所引用的元素;
- 输出迭代器(Output Iterator):只能向前单步迭代元素,对由该迭代器所引用的元素只有写权限;
- 向前迭代器(Forward Iterator):该迭代器可以在一个区间中进行读写操作,它拥有输入迭代器的所有特性和输出迭代器的部分特性,以及向前单步迭代元素的能力;
- 双向迭代器(Bidirectional Iterator):在向前迭代器的基础上增加了向后单步迭代元素的能力;
- 随机访问迭代器(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 value
lb->second = val;
return false;
} else {
// no key exists, insert new pair
m.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 VectorBase
template <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会导致迭代器失效
- 扩容步骤:
- 分配新内存
allocate
,placement new
- 拷贝旧数据
uninitialized_copy
- 释放旧内存
destruct
,deallocate
- 分配新内存
- 插入新数据(不触发扩容)
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