700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 【游戏编程扯淡精粹】EASTL源码阅读

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

时间:2024-03-12 08:08:00

相关推荐

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

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

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

本文编辑进度

WIP

docsource

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=outmkdir %build_folder%pushd %build_folder%call cmake .. -DEASTL_BUILD_TESTS:BOOL=ON -DEASTL_BUILD_BENCHMARK:BOOL=OFFcall cmake --build . --config Releasecall cmake --build . --config Debugcall cmake --build . --config RelWithDebInfocall cmake --build . --config MinSizeRelpushd testcall ctest -C Releasecall ctest -C Debugcall ctest -C RelWithDebInfocall ctest -C MinSizeRelpopdpopd

接入引擎

重载全局new替换掉部分: vectormapunique_ptrshared_ptr

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

问题

第三方库使用std作为接口参数,比如spdlog,sol2std::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内部用mutexEASTL不使用全局变量

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.hfunctional_base.h

案例一

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

// 2 render queue, opaque and transparent, sorted by distance to camerausing 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; }};// /w/cpp/utility/functional/greater_voidtemplate <>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.halgorithm.h

描述

iterator和algorithm结合紧密,STL的一大思想是,将container和algorithm拆分,并以iterator作为桥梁对接 主要抽象是,algorithm只操作iterator,而不知道具体container,使得algorithm是通用的 iterator是一种类似智能指针的结构,指向Talgorithm非常简单,但是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 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 Functionfor_each(InputIterator first, InputIterator last, Function function){for(; first != last; ++first)function(*first);return function;}

allocator

header

allocator.hallocator_malloc.hfixed_allocator.h

描述

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

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

// EASTL allocatorclass 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,第一个是全局的默认分配器,第二个是每个类型特定的默认分配器

// GetStaticDefaultAllocatorEASTL_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会导致迭代器失效扩容步骤: 分配新内存allocateplacement new拷贝旧数据uninitialized_copy释放旧内存destructdeallocate插入新数据(不触发扩容)uninitialized_fill_n

list & slist

list是双向链表,支持双向迭代相比于vector,没有扩容的概念,每个节点都是独立内存,增删节点,不会使得迭代器失效节点就三个数据:next,prev和T dataslit是单链表,支持单项迭代,相比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_backpop_backback

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

queue

所需操作

push_backpop_frontfrontback

适配: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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。