Заменить распределитель STL по умолчанию
У меня есть источник для большой ( > 250 файлов) библиотеки, которая сильно использует контейнеры и строки STL. Мне нужно запустить его во встроенной среде с ограниченной кучей, поэтому я хочу убедиться, что сама эта библиотека ограничена в использовании кучи.
Очевидным решением является создание распределителя, но изменение всей базы кода для включения параметра шаблона распределителя является большой задачей в крайнем случае и нежелательным, если я когда-либо захочу использовать новую версию источника. Глобальная замена нового и удаления невозможна, поскольку это влияет на все изображение, а не только на эту библиотеку.
Моя следующая мысль была глупым трюком C макроса, но это не похоже, что это было бы возможно, хотя я признаю, что не являюсь умным автором макроса.
Итак, я подумал: "Есть ли компилятор или прагма-переключатель для указания класса allocator < > class во время компиляции"? Но я открыт для всего.
Следующий вопрос, который я спрошу, может ли кто-нибудь придумать решение, заключается в том, как сделать то же самое для new/delete в наборе файлов, содержащих эту библиотеку.
Я использую toolchain gcc 3.4.4 для запуска этого в Cygwin с целью VxWorks, если это искрометные идеи.
Ответы
Ответ 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/
в корне моего источника include path, а затем создаю файл c++allocator.h
в этой папке. Я определяю требуемый макрос как имя моего класса распределителя, и он работает как шарм, поскольку gcc ищет мои включенные пути перед поиском системы.
Спасибо за все ваши ответы. Я думаю, что я могу пойти с этим "решением", которое будет работать только до тех пор, пока я использую 3.4.4.
Ответ 2
Вы могли бы извлечь выгоду из использования EASTL (Enterprise Arts STL (частичная) реализация)
EASTL - Стандартная библиотека шаблонов Electronic Arts
Это было предназначено для встраивания/разработки игр, в средах, где глобальная куча действительно недостаточна, вообще отсутствует или проблематична.
Модель распределителя EASTL была вдохновлена (или напоминает?) Идеями в знаменитой публикации " На пути к лучшей модели распределителя" (PDF).
EASTL хорошо подходит для пользовательских распределителей. На самом деле, он не поставляется с распределителем, поэтому предоставление (минимальное) требуется даже для того, чтобы приложение связывалось.
Вот репозиторий GitHub для EASTL: https://github.com/electronicarts/EASTL
Ответ 3
Итак, я подумал: "Есть ли компилятор или прагма-переключатель, чтобы указать allocator < > class во время компиляции"? Но я открыт для чего-либо.
Нет, нет.
Посмотрите здесь.
Выделители являются аргументом шаблона в каждом контейнере 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);
Вот место, где вы вызываете свой новый и удаляете, которые работают в вашей куче.
Я мог бы предоставить вам пример того, как создать базовый менеджер памяти, чтобы помочь вам в дальнейшем.