аллокатор
От: Аноним  
Дата: 05.09.07 07:12
Оценка:
Здравствуйте.
Хочется реализовать возможность установки аллокатора во new/delete, и запретить использование "штатных" new/delete.

С объекатами классов получается примерно следующее:
class CAllocator
{
public:
    void* Alloc (size_t s) {return malloc (s);}
    void Free(void *p) {free (p);}
};

class CAllocBase
{
public:
    void* operator new (unsigned size, CAllocator &all)
    {
        return all.Alloc(size);
    }
    void operator delete (void *p, CAllocator &all)
    {
        all.Free(p);
    }
    void* operator new[] (unsigned size, CAllocator &all)
    {
        return all.Alloc(size);
    }
    void operator delete[] (void *p, CAllocator &all)
    {
        all.Free(p);
    }
private:
    
    void* operator new (unsigned size); // use: new (allocator) Class
    void operator delete (void *p); // use: delete (ptr, allocator)

};


class Foo : public CAllocBase
{
public:
    Foo ()
    {
    }
    Foo (int g)
    {
    }
};


И вот такой код обладает удовлетворительным поведением:

int main(int argc, char* argv[])
{
    СAllocator all;

    Foo *p = new (all) Foo (3); // OK
    p->operator delete (p, all); // OK
    //delete p; // ошибка при компоновке - не определён в CAllocBase::operator delete (void *p)

    Foo *p2 = new (all) Foo [7]; // OK
    p->operator delete [] (p, all); // OK

    Foo *p3 = new Foo (3); // error: cannot access private member... 
    delete p3; // error: cannot access private member... 
    return 0;
}


Можно ли тут добиться не удовлетворительного решения, а хорошего?
Чтобы не нужно было писать длинное выражение

p->operator delete []

и чтобы все ошибки возникали на этапе компиляции, а не компоновки.

Что касается POD-типов — понятно, как подсунуть туда аллокатор
void* operator new (unsigned size, CAllocator &allocator)
{
    return allocator.Malloc (size);
}

void operator delete_with_allocator (void *p, CAllocator &allocatoroc)
{
     return allocator.Free (p);
}

но не нравится использовение delete_with_allocator() и не понятно, как тут запретить вызов "delete p";
Re: аллокатор
От: remark Россия http://www.1024cores.net/
Дата: 05.09.07 08:45
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте.

А>Хочется реализовать возможность установки аллокатора во new/delete, и запретить использование "штатных" new/delete.

А>но не нравится использовение delete_with_allocator() и не понятно, как тут запретить вызов "delete p";


delete p тебе надо разрешить в обоих случаях (и для класса, и для POD).
Далее в своём new тебе надо класть указатель на аллокатор перед создаваемым объектом. Т.е. в первое слово блока памяти положить указатель, далее сдвинуть указатель на размер слова и вернуть его из new.
Далее в delete вынуть из блока памяти указатель на аллокатор. Т.е. сдвинуть указатель на блок на слово назад. И вернуть блок в аллокатор.
Причём свой delete тоже оставь.


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: добавлю
От: Erop Россия  
Дата: 05.09.07 09:07
Оценка:
Здравствуйте, remark, Вы писали:

R>Далее в своём new тебе надо класть указатель на аллокатор перед создаваемым объектом. Т.е. в первое слово блока памяти положить указатель, далее сдвинуть указатель на размер слова и вернуть его из new.


Добавлю:
А на выравнивание забей до появления реальных проблем с данными, которые надо выравнивать больше чем выравнивание указателя...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: аллокатор
От: Bell Россия  
Дата: 05.09.07 09:42
Оценка:
Здравствуйте, Аноним, Вы писали:

...

Стандартная ошибка — ты путаешь delete expression и dealocation function. Добавь в Foo деструктор с логом в консоль, и убедись, что он не вызывается.
delete expression вызывает сначала деструктор объекта (объектов в случае delete []), а потом dealocation function для освобождения памяти — operator delete (operator delete []). В своем примере ты явно вызываешь operator delete, который есть dealocation function, забывая о деструкторе.
Проблема в том, что delete expression (в отличие от new expression) нельзя передать дополнительные параметры, т.е. нельзя написать что-то вроде
Foo *p = new (all) Foo (3);
delete (all) p;

поэтому нужно позаботится о том, чтобы в operator delete как-то получить ссылку на нужный аллокатор. remark постом выше предложил один из вариантов.

ЗЫ
Как только заработает поиск — поищи тему о placement delete — там было неплохое обсуждение на эту тему.
Любите книгу — источник знаний (с) М.Горький
Re[3]: добавлю
От: remark Россия http://www.1024cores.net/
Дата: 05.09.07 10:16
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, remark, Вы писали:


R>>Далее в своём new тебе надо класть указатель на аллокатор перед создаваемым объектом. Т.е. в первое слово блока памяти положить указатель, далее сдвинуть указатель на размер слова и вернуть его из new.


E>Добавлю:

E>А на выравнивание забей до появления реальных проблем с данными, которые надо выравнивать больше чем выравнивание указателя...

Или используй _aligned_offset_malloc


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: аллокатор
От: Аноним  
Дата: 05.09.07 11:17
Оценка:
B>Стандартная ошибка — ты путаешь delete expression и dealocation function.

Ах, ну да.. спасибо.

B>поэтому нужно позаботится о том, чтобы в operator delete как-то получить ссылку на нужный аллокатор. remark постом выше предложил один из вариантов.


а какие еще могут быть варианты?
Re[3]: аллокатор
От: Bell Россия  
Дата: 05.09.07 11:23
Оценка:
Здравствуйте, Аноним, Вы писали:


B>>Стандартная ошибка — ты путаешь delete expression и dealocation function.

А>Ах, ну да.. спасибо.
Пожалуйста м

B>>поэтому нужно позаботится о том, чтобы в operator delete как-то получить ссылку на нужный аллокатор. remark постом выше предложил один из вариантов.


А>а какие еще могут быть варианты?

Аллокатор — глобальный объект (синглтон) к примеру....
А вообще нужно смотреть по задаче — универсального решения, увы, нет (за исключением стандартных new/delete).
Любите книгу — источник знаний (с) М.Горький
Re[4]: добавлю
От: Erop Россия  
Дата: 05.09.07 12:22
Оценка:
Здравствуйте, remark, Вы писали:

R>Или используй _aligned_offset_malloc


Пробема в том, что в operator new ты не знаешь выравнивание создаваемого объекта. Мало того, ты предлагаешь положить в начало выровненного блока указатель на аллокатор. То есть если есть типы, выравнивание которых больше, чем у указателя, то они гарантированно попадут в невыровненную память...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: добавлю
От: remark Россия http://www.1024cores.net/
Дата: 05.09.07 12:51
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, remark, Вы писали:


R>>Или используй _aligned_offset_malloc


E>Пробема в том, что в operator new ты не знаешь выравнивание создаваемого объекта.


operator new знает выравнивание, которое ему надо обеспечить, — выранивание подходящее для любого объекта.

E>Мало того, ты предлагаешь положить в начало выровненного блока указатель на аллокатор. То есть если есть типы, выравнивание которых больше, чем у указателя, то они гарантированно попадут в невыровненную память...


Посмотри всё-таки _aligned_offset_malloc

Вот пример кода, который учитывает все выравнивания:

#include <malloc.h>

struct allocator
{
    static size_t const max_align = 16;

    void* alloc(size_t size)
    {
        void** p = (void**)_aligned_offset_malloc(size > sizeof(void*) ? size : sizeof(void*) + 1, max_align, sizeof(void*));
        *p = this;
        return p + 1;
    }

    void free(void* p)
    {
        void** pp = (void**)p;
        _aligned_free(pp - 1);
    }

    static void dispatch_free(void* p)
    {
        void** pp = (void**)p;
        allocator* self = (allocator*)*(pp - 1);
        self->free(p);
    }
};

void* operator new (size_t size, allocator& a)
{
    return a.alloc(size);
}

void operator delete (void* p, allocator& a)
{
    return a.free(p);
}

void operator delete (void* p)
{
    return allocator::dispatch_free(p);
}

int main()
{
    allocator a1;
    allocator a2;

    int* p1 = new(a1) int(1);
    int* p2 = new(a2) int(2);

    delete p1;
    delete p2;
}




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[6]: добавлю
От: Erop Россия  
Дата: 05.09.07 13:36
Оценка:
Здравствуйте, remark, Вы писали:

R>operator new знает выравнивание, которое ему надо обеспечить, — выранивание подходящее для любого объекта.


Ну жалко же выравнивать всё на 64 байта только потому, что бывают какие-то никому ненужные выравненные на 64 типы

Хотя на популярных платформах всё подижь то сведётся к 8 или 16 байтовому выравниванию. Вот такой вот размер указателя получится
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: добавлю
От: remark Россия http://www.1024cores.net/
Дата: 05.09.07 16:16
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, remark, Вы писали:


R>>operator new знает выравнивание, которое ему надо обеспечить, — выранивание подходящее для любого объекта.


E>Ну жалко же выравнивать всё на 64 байта только потому, что бывают какие-то никому ненужные выравненные на 64 типы

E>Хотя на популярных платформах всё подижь то сведётся к 8 или 16 байтовому выравниванию. Вот такой вот размер указателя получится

Если ты хочешь *и* расширенную функциональность, *и* вычислительную эффективность, *и* емкостную эффективность, то VirtualAlloc(), ALLOCATION_GRANULARITY, PAGE_SIZE тебе в руки и дальше со всеми остановками — freelist, block fragmentation, block coalescing и т.д.

Если ты готов отказаться хотя бы от одного требования из этих трёх, то всё очень просто и легко. Если нет, то уж извиняй — много хочешь...


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[8]: убавлю :)
От: Erop Россия  
Дата: 05.09.07 16:28
Оценка:
Здравствуйте, remark, Вы писали:

R>Если ты готов отказаться хотя бы от одного требования из этих трёх, то всё очень просто и легко. Если нет, то уж извиняй — много хочешь...


R>

А теперь читаем оригинальный пост
Автор: Erop
Дата: 05.09.07
Ы?
Просто выравнивание указателя -- это не так уж и мало, ИМХО...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.