替换默认的STL分配器

26

我有一个使用STL容器和字符串的大型(> 250个文件)库的源代码。我需要在具有限制堆大小的嵌入式环境中运行它,因此我希望确保该库本身在堆使用方面受到限制。

显而易见的解决方案是创建一个分配器,但修改整个代码库以包括分配器模板参数是最后一步的大工程,并且如果我想要获取源代码的新版本,这种修改是不可取的。全局替换new和delete并不可行,因为那会影响整个系统图像,而不仅仅是这个库。

我的下一个想法是愚蠢的C宏技巧,但这似乎不可能,虽然我承认我不是聪明的宏作者。

所以我想,“是否有编译器或#pragma开关可以在编译时指定allocator<>类”?但我对任何解决方案都持开放态度。

如果有人能提出一个解决方案,我接下来要问的问题是如何在组成该库的文件集内部执行相同的操作,针对new/delete。

我正在使用gcc 3.4.4工具链在Cygwin下运行它,目标是VxWorks,如果这能激发任何思路。


gcc 3.4.4太老了,已经被标记为“完全失效”。你没有换成较新版本的原因吗? - sehe
我想在GCC中,这只是一个简单的宏,在内部某个地方,您应该能够切换。默认值是“新分配器”,但是GCC带有几个替代方案,如“malloc分配器”和池分配器等等。 - Kerrek SB
我们的做法是创建一个模板类,用于包装我们使用的每个容器。看一下我发布的一个问题:http://stackoverflow.com/q/8248076/1037208 。这样可以避免一些麻烦,并且使出错更加困难。我对我们的解决方案并不完全满意,所以如果有人回答你的问题并提供了更好的解决方案,我可能会考虑修改我们的解决方案。 - OSH
1
@sehe,3.4.4的原因是我们的目标系统是运行VxWorks的卫星硬件,而该工具链仍然在3.4.4上。我不知道为什么,只知道这是要求。 - Reilly
@James Brock,我的库是一个故障分析包。您向该包描述系统模型,然后提供实时传感器输入。它确定系统是否处于“一致”状态,并在不一致时尝试找到故障组件。它创建了许多“故障候选项”来搜索故障空间。内存使用量不确定。这种能力是可取的,但并非关键。因此,如果它抛出“内存不足”的错误,我们不介意在时间t_n重新启动它。 - Reilly
显示剩余3条评论
3个回答

9
我使用预处理器得到了一个可能的解决方案,虽然它目前依赖于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中完成这个的想法?我试图找到默认的分配器头文件,但是迷失了:) 谢谢! - OSH

6
你可以受益于使用EASTL(Enterprise Arts STL(部分)实现)。 EASTL -- Electronic Arts Standard Template Library 这是为嵌入式/游戏开发设计的,在这些环境中,全局堆栈非常稀缺、不存在或通常存在问题
EASTL的分配器模型受到了著名的 Towards a Better Allocator Model出版物(PDF)中的思想的启发(或类似?)。
EASTL非常适合使用自定义分配器。事实上,它不带有任何分配器,因此提供(最小的)一个是必需的,甚至需要将您的应用程序链接起来。
这是EASTL的github存储库: https://github.com/electronicarts/EASTL

这是一个可能的解决方案,尽管我必须编辑EASTL或我的库,因为std与eastl命名空间冲突。 - Reilly

3

所以我想,“是否有编译器或特殊开关可以在编译时指定allocator<>类?”但是我对任何东西都持开放态度。

没有。

看一下这里

在每个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);

这里是你调用你的新建和删除函数的地方,它们作用于你的堆上。
我可以提供一个构造基本内存管理器的示例来帮助你进一步理解。

那么你的意思是说没有办法告诉STL全局使用自定义分配器;你必须确保在使用STL类型时,使用自定义分配器进行定义。对吗? - OSH
@OrenS。对于特定的编译器,可能会有一个选项,但我是根据自己的经验来说的。我曾经和OP处于同样的困境,不得不这样做。编辑已纠正的答案。 - FailedDev
@FailedDev,在我发帖之前,我看过那个页面并抱有希望。如果我能弄清楚如何将我的任意分配器变成“扩展”分配器,那就太好了。但我也不知道该怎么做。如果你知道的话,我会把我的下一只狗命名为“FailedDev”... - Reilly
1
这个主题和C++17有什么变化吗?现在是否有一种常见的方法来替换全局分配器? - Markus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接