替换默认的 STL 分配器
Posted
技术标签:
【中文标题】替换默认的 STL 分配器【英文标题】:Replace default STL allocator 【发布时间】:2011-11-23 18:53:27 【问题描述】:我有一个大量使用 STL 容器和字符串的大型(>250 个文件)库的源代码。我需要在有限堆的嵌入式环境中运行它,所以我想确保这个库本身的堆使用受到限制。
显而易见的解决方案是创建一个分配器,但是修改整个代码库以包含分配器模板参数是最后的一项大工作,如果我想要获取新版本的源代码,这也是不可取的。全局替换 new 和 delete 是不可行的,因为这会影响整个图像,而不仅仅是这个库。
我的下一个想法是一个愚蠢的 C 宏技巧,但这似乎是不可能的,尽管我承认自己不是一个聪明的宏作者。
所以我想“是否有编译器或编译指示开关在编译时指定分配器 类”?但我愿意接受任何事情。
如果有人能提出解决方案,我要问的下一个问题是,如何在包含此库的文件集中对 new/delete 执行相同的操作。
我正在使用 gcc 3.4.4 工具链在 Cygwin 下运行它,目标是 VxWorks,如果这能激发任何想法的话。
【问题讨论】:
gcc 3.4.4 这已经够老了,可以贴上“完全损坏”的标签。你有什么理由不切换到新版本? 我认为在 GCC 中这只是内部某个地方的一个简单宏,您应该可以切换它。默认是“新分配器”,但 GCC 提供了几个替代方案,例如“malloc 分配器”和池分配器等等。 假设您可以创建一个有限堆,然后仅为该库重载operator new
,因此库中的所有operator new
调用都转到您的有限堆。当有限的堆已满并调用重载的operator new
时,您想要什么行为?你真的希望operator new
在这种情况下失败吗?我不相信你会,除非当operator new
失败时该库仍然可以运行。也许如果库是数据库缓存或类似的东西。图书馆是做什么的?
我们这样做的方式是创建一个模板类来包装我们使用的每个容器。看看我发布的一个问题:***.com/q/8248076/1037208。它为您节省了一些麻烦,并使您更难犯错误。我对我们的解决方案并不完全满意,所以如果有人用更好的解决方案回答您的问题,我可能会考虑修改我们的解决方案。
@sehe,3.4.4 的原因是我们的目标系统是运行 VxWorks 的卫星硬件,而该工具链仍在 3.4.4 上。我不知道“为什么”,只是要求。
【参考方案1】:
我求助于预处理器来获得一个可能的解决方案,尽管它目前依赖于 GCC 3.4.4 实现来工作。
GCC <memory>
实现包括文件<bits/allocator.h>
,该文件又包括另一个文件<bits/c++allocator.h>
,该文件定义了一个宏,该宏定义了实现默认分配器基类的类。
由于在依赖于平台的路径中找到(/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/i686-pc-cygwin/bits
),我不觉得(非常)用我自己的“依赖于平台”的实现来代替它。
所以我只需在源包含路径的根目录中创建一个文件夹bits/
,然后在该文件夹中创建文件c++allocator.h
。我将所需的宏定义为我的分配器类的名称,它就像一个魅力,因为 gcc 在搜索系统包含之前搜索我的包含路径。
感谢您的所有回复。我想我可以使用这个“解决方案”,只要我可能使用 3.4.4,它就会起作用。
【讨论】:
对我如何在 Visual Studio 中执行此操作有任何想法吗?我试图找到默认分配器头文件,但迷路了 :) 谢谢!【参考方案2】:您可以从使用 EASTL(Enterprise Arts STL(部分)实施)中受益
EASTL -- Electronic Arts Standard Template Library
这适用于嵌入式/游戏开发,在 global heap is really scarce, non-existent or problematic in general 的环境中。
EASTL 的分配器模型的灵感来自(或类似?)著名的 Towards a Better Allocator Model 出版物 (PDF) 中的想法。
EASTL 非常适合自定义分配器。 事实上,它不附带分配器,因此需要提供(最小的)分配器,甚至可以让您的应用程序链接。
这里是 EASTL 的 github 仓库:https://github.com/electronicarts/EASTL
【讨论】:
这是一个可能的解决方案,尽管由于 std 与 eastl 命名空间,我必须编辑 EASTL 或我的库。【参考方案3】:所以我想“是否有编译器或编译指示开关来指定 allocator class at compile time”?但我愿意接受任何事情。
不,没有。
看看here。
分配器是每个 stl 容器中的模板参数。您将需要更改它们。我过去在嵌入式工作时也做过同样的事情。如果你愿意,我可以给你一些建议:
基本模板分配器:
namespace PFM_MEM
template <class T>
class CTestInstAllocator
public:
// type definitions
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
// rebind CTestInstAllocator to type U
template <class U>
struct rebind
typedef CTestInstAllocator<U> other;
;
// return address of values
pointer address (reference value) const
return &value;
const_pointer address (const_reference value) const
return &value;
/* constructors and destructor
* - nothing to do because the CTestInstAllocator has no state
*/
CTestInstAllocator()
CTestInstAllocator(const CTestInstAllocator&)
template <class U>
CTestInstAllocator (const CTestInstAllocator<U>&)
~CTestInstAllocator()
// return maximum number of elements that can be allocated
size_type max_size () const
return std::numeric_limits<size_t>::max() / sizeof(T);
// pvAllocate but don't initialize num elements of type T by using our own memory manager
pointer allocate (size_type num)
/**
* pvAllocate memory custom memory allocation scheme
*/
return(pointer)(CPfmTestInstMemManager::pvAllocate(num*sizeof(T)));
// initialize elements of allocated storage p with value value
void construct (pointer p, const T& value)
// initialize memory with placement new
new((void*)p)T(value);
// destroy elements of initialized storage p
void destroy (pointer p)
// destroy objects by calling their destructor
p->~T();
// vDeallocate storage p of deleted elements
void deallocate (pointer p, size_type num)
/**
*Deallocate memory with custom memory deallocation scheme
*/
CPfmTestInstMemManager::vDeallocate((void*)p);
;
// return that all specializations of this CTestInstAllocator are interchangeable
template <class T1, class T2>
bool operator== (const CTestInstAllocator<T1>&,
const CTestInstAllocator<T2>&)
return true;
template <class T1, class T2>
bool operator!= (const CTestInstAllocator<T1>&,
const CTestInstAllocator<T2>&)
return false;
请特别注意以下几行:
/**
* pvAllocate memory custom memory allocation scheme
*/
return(pointer)(CPfmTestInstMemManager::pvAllocate(num*sizeof(T)));
// vDeallocate storage p of deleted elements
void deallocate (pointer p, size_type num)
/**
*Deallocate memory with custom memory deallocation scheme
*/
CPfmTestInstMemManager::vDeallocate((void*)p);
这里是你调用 你的 new 并删除在你的堆上工作的地方。
我可以为您提供一个示例,说明如何构建一些基本的内存管理器以进一步帮助您。
【讨论】:
所以你说没有办法告诉 STL 全局使用自定义分配器;您必须确保在使用 stl 类型时,使用自定义分配器定义它。对吗? @OrenS。好吧,对于特定的编译器,可能有一个选项,但我是根据我的经验单独谈论的。我和 OP 在同一个地方,不得不这样做。编辑更正的答案。 @FailedDev,我在发帖之前看到了那个页面,并充满希望。如果我能弄清楚如何使我的任意分配器成为“扩展”分配器,我会很高兴。我只是不知道该怎么做。如果你知道的话,我会给我的下一只狗命名为“FailedDev”...... 这个话题和c++ 17有什么变化吗?现在这里是替换全局分配器的常用方法吗?以上是关于替换默认的 STL 分配器的主要内容,如果未能解决你的问题,请参考以下文章