Re[5]: Мало, мало про это на собеседованиях спрашивают... ;)
От: Erop Россия  
Дата: 05.10.13 19:02
Оценка:
Здравствуйте, Molchalnik, Вы писали:

M>Но если в двух словах, я делаю свой фреймворк для проекта. Ну и вообще для жизни. Проект может ВНЕЗАПНО стать многопоточным. Одна из идей фреймворка — "Пользователь при создании объекта может определять для него new,delete, и кучу. Объект может удалятся (естессно) через указатель на базовый класс"


Ну, предположим, что пользователь захочет это всё проделать не с наследником твоего интерфейса, а со своим собственным классом, никак с твоим Фреймворком не связанным.
Как он это будет, по твоему делать, и, почему, в случае, если он выведется из твоего IObjecta, при условии, что у того будет виртуальный деструктор, этот его способ перестанет работать?..

Отдельный вопрос, зачем нужен ещё один "Фреймворк для жизни"? Их же и так уже наплодили три вагона?..
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: Мало, мало про это на собеседованиях спрашивают... ;)
От: Erop Россия  
Дата: 05.10.13 19:55
Оценка:
Здравствуйте, Molchalnik, Вы писали:

M>Это "как-то" очень интересно. Подскажешь пару вариантов?


Ну это уже очень зависит от целей и задач пользователя твоего Фреймворка.
Например, если ему плевать, на выравнивание большее, чем выравнивание указателя, то он может сделать так:
struct IHeap {
    virtual void* Alloc( size_t ) = 0;
    virtual void Free( void* ) = 0;

    // Нулевой аллокатор трактуем, как дефолтный.
    static void* AllocAt( size_t size, IHeap* at = 0 ) 
        { return at ? at->Alloc( size ) : ::operator new( size ); }
    static void FreeAt( void* block, IHeap* at = 0 )
    {
        if( at )
            at->Free( block );
        else
            ::operator delete( block );
    }
};

struct AllocateOnHeap {
    void* operator new( size_t size, IHeap* heap = 0 )
    {
        IHeap** res = (IHeap**) IHeap::AllocAt( size + sizeof( IHeap* ), heap );
        if( res )
            *res++ = heap;
        return res;
    }
    void operator delete( void* block, IHeap* heap ) 
    { 
        if( block )
            IHeap::FreeAt( -1 + (IHeap**)block, heap ); 
    }
    void operator delete( void* block )
    { 
        if( block )
            operator delete( block, -1[(IHeap**)block] ); 
    }

    void* operator new[]( size_t size, IHeap* heap = 0 ) 
        { return operator new( size, heap ); }
    void operator delete[]( void* block, IHeap* heap ) 
        { operator delete( block, heap ); }
    void operator delete[]( void* block ) 
        { operator delete( block ); }
};
и выводить свои классы с хитрым аллокатором из AllocateOnHeap...

В результате, при аллокации их по new, можно будет указать кучу, примерно так:
class MyClass : public AllocateOnHeap {
};

delete new( getMyHeap() ) MyClass;


Только это, IMHO, не твоя печаль...
Или ты чего-то не договариваешь
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Мало, мало про это на собеседованиях спрашивают... ;)
От: Molchalnik  
Дата: 08.10.13 05:41
Оценка:
Здравствуйте, Кодт, Вы писали:

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


M>>Я рассказал довольно много с помощью кода


К>Знаешь про отмазку TL;DR? Знаешь, что первые две буквы в ней означают?


честно — погуглил, прочитав у тебя

К>В принципе, shared_ptr — это и красиво, и сиплюсплюсно, и с приведением типов, — но с оверхедом.


Ну, я не в восторге от необходимости плодить лишние сущности — мне нужен, например, класс очереди без блокировок, но чтобы его использовать, я должен заводить ещё один непонятный объект, обращаться к искомому через сложные конструкции вроде Ptr.GetValue(), и всё это, без сомнения, сиплюсплюсно, но это не комплимент моему любимому с++ )))

а если без теории... то контекст/аллокатор должен работать и с контейнерами, которые а-приори интенсивно самостоятельно работают с памятью. Т.е. если некий умный указатель хранит ссылку на аллокатор, выделяет память для контейнера, то контейнеру придётся хранить ссылку на указатель либо на аллокатор, чтобы выделять память под свои элементы, а это уже какой-то ненужный оверхед... Впрочем, ты уже сказал об оверхеде)))

К>Можно подумать о какой-то аналогичной штуковине. Отрезать от неё всё ненужное — подсчёт ссылок, например. Останется только пара (указатель на объект, указатель на аллокатор).

Я тоже так думал, см. пост выше. Вопрос именно в том, какой компромисс с совестью лучше — похерить арифметику указателей, добавив в выделение памяти лишний скрытый указатель на аллокатор, обращаться к объекту через указатель, или удалять объект через виртуальную obj.DeleteMe(). или по указателю угадывать, к какой он куче относится.

К>Всё равно информация об аллокаторе у тебя вынесена вовне — определяется не классом, а клиентским кодом. Зачем-то...

Кодт, если у тебя есть предложения по более грамотной архитектуре, это круто, буду рад

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


К>(Никогда не играл в симбиан или pugixml? вот там народ развлекался с неповторимыми способами передачи контекста).

Не играл)))

К>Вообще, борьба за изоляцию аллокаторов — это последний рубеж оптимизации. Когда ты точно знаешь, что именно многопоточное пользование общей кучей является узким местом в программе, и/или когда слишком жёстко встала проблема совместной работы нескольких плагинов (DLL) со статически прилинкованными и поэтому разными менеджерами кучи.

К>Так что смотри — не устраиваешь ли ты сейчас преждевременную оптимизацию, о которой говорил Дейкстра?

Я не имею большого опыта многопоточности, а опыта конкурентного программирования вообще не имею, но мне казалось, что раздельные кучи для потоков — это здорово, потому что:
1) поток упал — ты его перезапустил, и юзер ничего не заметил
2) у тебя баг с памятью в потоке — мемори лик — и ты его легко нашёл, благодаря замене всего одного указателя на аллокатор/контекст
3) дополнительная возможность мониторить
4) ты думал, тебе не нужны раздельные кучи, а когда они всё же понадобились, пришлось неделю с матами искать все new и delete в коде и заменять их на


void * НеведомыйМетодСозданияОбъектаСПомощьюВнешнегоАллокатораИ100500Прибамбас(HeapСтранныйКласс & heap,...)
void НеведомыйМетодУдаленияОбъектаСПомощьюВнешнегоАллокатораИ100500Прибамбас(void * ptr, HeapСтранныйКласс & heap,...)


Ну и причина No. 5 — я вообще экспериментирую с архитектурным шаблоном, передавая при создании в любой класс юзердата, например, содержащий ссылку на аллокатор, но в принципе, это может быть любая информация. Экспериментирую в поисках исключительно программисткого просветления. Однажды я сделал статический полиморфизм, и через полгода из-за нововведений сверху он "не покатил", и я задумался, есть ли что-то неизменное в программистком мире, и начал исследования... Ой, а почему вы в белых халатах? а куда вы меня тащите? ААААА... отпустите ме...
Re[7]: Мало, мало про это на собеседованиях спрашивают... ;)
От: Кодт Россия  
Дата: 08.10.13 08:22
Оценка: 3 (1)
Здравствуйте, Molchalnik, Вы писали:

M>Ну, я не в восторге от необходимости плодить лишние сущности — мне нужен, например, класс очереди без блокировок, но чтобы его использовать, я должен заводить ещё один непонятный объект, обращаться к искомому через сложные конструкции вроде Ptr.GetValue(), и всё это, без сомнения, сиплюсплюсно, но это не комплимент моему любимому с++ )))


Во-первых, нахаляву ни lock-free, ни даже спрятанный за критическую секцию объект не получится.
В любом случае хоть синтаксически, да напишешь что-нибудь лишнее. Ну там
ATOMIC{ myQueue.push(x) }
atomic(myQueue).push(x);

Лучшее, что можно сделать — это написать фасадные функции, прячущие всю механику с глаз
myQueue.atomic_push(x);


Во-вторых, у shared_ptr синтаксис обычного указателя, и в большинстве случаев не надо писать ptr.get() — если только не заниматься хаками с передачей голых указателей там, где есть неголые.
Тотальное пересаживание на умные указатели и голые ссылки — решает.
А главное, что дальнейшая замена типа указателя (вместо shared использовать intrusive или любые другие, рукодельные) — пройдёт с минимальной кровью.

M>а если без теории... то контекст/аллокатор должен работать и с контейнерами, которые а-приори интенсивно самостоятельно работают с памятью. Т.е. если некий умный указатель хранит ссылку на аллокатор, выделяет память для контейнера, то контейнеру придётся хранить ссылку на указатель либо на аллокатор, чтобы выделять память под свои элементы, а это уже какой-то ненужный оверхед... Впрочем, ты уже сказал об оверхеде)))


Ты действительно пишешь интенсивно работающую программу? Если нет, то не парься загодя. Если да — подумай о том, нельзя ли вообще избавиться от динамического выделения памяти.
Паттерн flyweight, большие массивы заранее размещённых объектов и никакого "обычного" управления через new/delete, — только через свои функции.

К>>Можно подумать о какой-то аналогичной штуковине. Отрезать от неё всё ненужное — подсчёт ссылок, например. Останется только пара (указатель на объект, указатель на аллокатор).

M>Я тоже так думал, см. пост выше. Вопрос именно в том, какой компромисс с совестью лучше — похерить арифметику указателей, добавив в выделение памяти лишний скрытый указатель на аллокатор, обращаться к объекту через указатель, или удалять объект через виртуальную obj.DeleteMe(). или по указателю угадывать, к какой он куче относится.

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

К>>Всё равно информация об аллокаторе у тебя вынесена вовне — определяется не классом, а клиентским кодом. Зачем-то...

M>Кодт, если у тебя есть предложения по более грамотной архитектуре, это круто, буду рад

Абстрактную архитектуру разрабатывать — дело неблагодарное.
Единственное общее предложение — пересесть на shared_ptr, если только действительно не высоконагруженное приложение.


M>Я не имею большого опыта многопоточности, а опыта конкурентного программирования вообще не имею, но мне казалось, что раздельные кучи для потоков — это здорово, потому что:

M>1) поток упал — ты его перезапустил, и юзер ничего не заметил

Лучше до греха не доводить. Поток может иметь зацепки за реальный мир, помимо общей кучи.
К тому же, пусть ты со своим фреймворком работаешь с отдельной кучей. Но пользователь может вовсю использовать общую — строки там всякие, маленькие временные объекты, векторы-шмекторы.

M>2) у тебя баг с памятью в потоке — мемори лик — и ты его легко нашёл, благодаря замене всего одного указателя на аллокатор/контекст


Это да. Но здесь можно расщедриться на дебажную сборку с толстыми указателями.

M>3) дополнительная возможность мониторить

M>4) ты думал, тебе не нужны раздельные кучи, а когда они всё же понадобились, пришлось неделю с матами искать все new и delete в коде и заменять их на

Когда понадобились, то не остановился на полумерах по замене всех new-delete, а воплотил Flyweight.
Опять же, много голых new-delete как бы намекает на Си поверх С++...

M>Ну и причина No. 5 — я вообще экспериментирую с архитектурным шаблоном, передавая при создании в любой класс юзердата, например, содержащий ссылку на аллокатор, но в принципе, это может быть любая информация. Экспериментирую в поисках исключительно программисткого просветления. Однажды я сделал статический полиморфизм, и через полгода из-за нововведений сверху он "не покатил", и я задумался, есть ли что-то неизменное в программистком мире, и начал исследования... Ой, а почему вы в белых халатах? а куда вы меня тащите? ААААА... отпустите ме...



Если достичь не просветления, а просто чистоты рук, и убивать объекты только там же, где они созданы, — то вполне можно ограничиться потокоспецифичными синглетонами (thread local storage).
Каждому потоку свой аллокатор.
Владение объектами между потоками не передавать. А если передавать, то там возникнет потребность в синхронизации доступа к чужой куче, и почему бы её не возложить на специальный удалятор, указанный в shared_ptr.
Перекуём баги на фичи!
Re[8]: Мало, мало про это на собеседованиях спрашивают... ;)
От: Molchalnik  
Дата: 08.10.13 09:42
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Лучшее, что можно сделать — это написать фасадные функции, прячущие всю механику с глаз

К>
К>myQueue.atomic_push(x);
К>

а ещё лучше — назвать их
myQueue.push_back(x)

, и никому не говорить, что они atomic, ибо это уже подробности реализации, упрятанные за интерфейс


К>Ты действительно пишешь интенсивно работающую программу?

50 на 50

К> Если нет, то не парься загодя. Если да — подумай о том, нельзя ли вообще избавиться от динамического выделения памяти.

К>Паттерн flyweight, большие массивы заранее размещённых объектов

буду много думать

К>и никакого "обычного" управления через new/delete, — только через свои функции.

ну это я завсегда, просто я думал эти функции хранить за интерфейсом чисто виртуального аллокатора/контекста

К>>>Можно подумать о какой-то аналогичной штуковине. Отрезать от неё всё ненужное — подсчёт ссылок, например. Останется только пара (указатель на объект, указатель на аллокатор).

M>>Я тоже так думал, см. пост выше. Вопрос именно в том, какой компромисс с совестью лучше — похерить арифметику указателей, добавив в выделение памяти лишний скрытый указатель на аллокатор, обращаться к объекту через указатель, или удалять объект через виртуальную obj.DeleteMe(). или по указателю угадывать, к какой он куче относится.

К>Арифметика указателей для одиночных объектов не нужна — смело похерь её.

К>А с массивами разговор отдельный.

Запретить delete[] и operator[] для своих классов?

К>Абстрактную архитектуру разрабатывать — дело неблагодарное.

К>Единственное общее предложение — пересесть на shared_ptr, если только действительно не высоконагруженное приложение.
у меня просто условно-абстрактный интерфейс поинтера для всего. если нужно — хоть shared_ptr, хоть weak_ptr, хоть smart_ptr прячь за него...


К>Опять же, много голых new-delete как бы намекает на Си поверх С++...


я не успел пока наплодить много, не более 5 на большой код...
Оставил их потому, что хотел заменить функциями из аллокатора/контекста
Re[9]: Мало, мало про это на собеседованиях спрашивают... ;)
От: Кодт Россия  
Дата: 08.10.13 19:06
Оценка:
Здравствуйте, Molchalnik, Вы писали:

К>>Лучшее, что можно сделать — это написать фасадные функции, прячущие всю механику с глаз

К>>
К>>myQueue.atomic_push(x);
К>>

M>а ещё лучше — назвать их
M>
M>myQueue.push_back(x)
M>

M>, и никому не говорить, что они atomic, ибо это уже подробности реализации, упрятанные за интерфейс

А это уже как сказать.
Если у тебя есть внешние гарантии отсутствия гонок в данном месте, то можно сэкономить время на неблокирующей версии функции.
Или, к примеру, у тебя групповая операция. Было бы эффективно сделать
myQueue.lock(); // todo использовать scoped lock
myQueue.push_back(x);
myQueue.push_back(y);
.....
myQueue.unlock();

// или так
auto lockedQueue = myQueue.locked();
lockedQueue.push_back(x);
lockedQueue.push_back(y);
.....

чем дёргать мьютекс на каждый чих.

К>>и никакого "обычного" управления через new/delete, — только через свои функции.

M>ну это я завсегда, просто я думал эти функции хранить за интерфейсом чисто виртуального аллокатора/контекста

Во фреймворке — проще всего сделать фабричные функции YourPointer<T> create<T>() и void destroy(YourPointer<T>), например.
Для первой версии — они будут реализованы по-простому, через new/delete, а потом хоть какую политику устроить.
И искать несложно, функции-то в одном месте определены, в отличие от прямых операций.

К>>Арифметика указателей для одиночных объектов не нужна — смело похерь её.

К>>А с массивами разговор отдельный.

M>Запретить delete[] и operator[] для своих классов?


Как вариант. Но для начала — просто воздерживаться от прямого конструирования массивов где попало.
Отдельный разговор состоит не в том, чтоб запретить, а в начать с того, как массивы используются.
Массивов же много разных. И разреженные, и полиморфные, и какие угодно. Голая адресная арифметика нечасто востребована, достаточно арифметики итераторов или нумерации.
Перекуём баги на фичи!
Re[6]: Удаление полиморфного класса через контекст
От: Molchalnik  
Дата: 25.12.15 18:58
Оценка:
Здравствуйте, enji, Вы писали:


E>Пихаешь аллокатор в boost::thread_specific_ptr или аналог


I. Подскажите, пожалуйста, коллеги, какие есть аналоги кроме следующих:

1. boost::thread_specific_ptr
2. для С++ 11: thread_local, _Thread_local
3. для gcc: __thread
4. для posix: pthread_key
5. для msvc: __declspec(thread)
6. для win:TlsAlloc, TlsSetValue, TlsGetValue, TlsFree
7. для QT: QThreadStorage<T>

II. Нигде не ошибся?
Re: Удаление полиморфного класса через контекст
От: Ops Россия  
Дата: 25.12.15 20:40
Оценка: +1
Здравствуйте, Molchalnik, Вы писали:

M>жду помощи


Кто выделяет память, тот знает, как освобождать. Умные указатели со своим делетером — самое простое.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re: Удаление полиморфного класса через контекст
От: UA Украина  
Дата: 25.12.15 20:40
Оценка: :)
Здравствуйте, Molchalnik, Вы писали:

Если ты отдаешь клиенту твоего фреймворка указатель на интерфейс, то что может быть в итоге кроме вызова Release?
Re: Удаление полиморфного класса через контекст
От: ELazin http://rsdn.ru/forum/prj/6225353.1
Автор: ELazin
Дата: 26.10.15
Дата: 26.12.15 08:48
Оценка: +2
Здравствуйте, Molchalnik, Вы писали:

Я когда прочитал про "грамотный дизайн" — сразу неладное почувствовал, но потом увидел код, потом прочитал некоторые комментарии ...
Re[7]: Удаление полиморфного класса через контекст
От: BulatZiganshin  
Дата: 26.12.15 13:13
Оценка:
Здравствуйте, Molchalnik, Вы писали:

в boost есть даже библиотека flyweight — оно тебе не подойдёт?
Люди, я люблю вас! Будьте бдительны!!!
Re[7]: Удаление полиморфного класса через контекст
От: dolgop8791  
Дата: 28.12.15 10:16
Оценка:
Здравствуйте, Molchalnik, Вы писали:

M>I. Подскажите, пожалуйста, коллеги, какие есть аналоги кроме следующих:


Я думаю у каждой библиотеки, которая претендует на звание "взрослой" и "базовой" для с++ есть что-то подобное:

ACE — ACE_TSS
Poco — ThreadLocal
Re[2]: Удаление полиморфного класса через контекст
От: Molchalnik  
Дата: 01.01.16 20:00
Оценка:
Здравствуйте, ELazin, Вы писали:

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


EL>Я когда прочитал про "грамотный дизайн" — сразу неладное почувствовал, но потом увидел код, потом прочитал некоторые комментарии ...


Какие конкретно претензии? Оценки не интересны, интересна конструктивная критика.
Re[2]: Удаление полиморфного класса через контекст
От: Molchalnik  
Дата: 01.01.16 20:06
Оценка:
Здравствуйте, UA, Вы писали:

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


UA>Если ты отдаешь клиенту твоего фреймворка указатель на интерфейс, то что может быть в итоге кроме вызова Release?


ты это о чём?
Re[2]: Удаление полиморфного класса через контекст
От: Molchalnik  
Дата: 01.01.16 20:12
Оценка:
Здравствуйте, Ops, Вы писали:

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


M>>жду помощи


Ops>Кто выделяет память, тот знает, как освобождать. Умные указатели со своим делетером — самое простое.


Спасибо. Правда, Кодт предложил это в этой теме в 2013, но это не важно. Всё равно благодарю. Только тогда уж и свою фабрику, и запрет на создание, и запрет на удаление,в общем, долго мучатся с каждым классом.
Re[3]: Удаление полиморфного класса через контекст
От: Erop Россия  
Дата: 21.12.16 00:27
Оценка:
Здравствуйте, Molchalnik, Вы писали:

M>Спасибо. Правда, Кодт предложил это в этой теме в 2013, но это не важно. Всё равно благодарю. Только тогда уж и свою фабрику, и запрет на создание, и запрет на удаление,в общем, долго мучатся с каждым классом.


Можно обойтись запретами в базовом классе
Автор: Erop
Дата: 05.07.09

Правда деструктор получится запретить только RT.

(схема такая: в objectPlaceholder зводим operator delete в котором пишем assert)
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Мало, мало про это на собеседованиях спрашивают... ;)
От: push  
Дата: 26.12.16 12:24
Оценка:
Здравствуйте, Erop, Вы писали:


E>    void operator delete( void* block )
E>    { 
E>        if( block )
E>            operator delete( block, -1[(IHeap**)block] ); 
E>    }


Можно уточнить момент выше? Не понял, что происходит. Почему решили, что при таком вызове есть какой-то указатель перед блоком? Если он есть, то почему бы не использовать оператор выше, просто добавив значение по умолчанию для второго параметра?
Re[7]: Мало, мало про это на собеседованиях спрашивают... ;)
От: Erop Россия  
Дата: 26.12.16 14:01
Оценка:
Здравствуйте, push, Вы писали:

P>Можно уточнить момент выше? Не понял, что происходит. Почему решили, что при таком вызове есть какой-то указатель перед блоком?

Не помню уже о чём там речь, но, скорее всего указатель клался в парном к delete операторе new...

P>Если он есть, то почему бы не использовать оператор выше, просто добавив значение по умолчанию для второго параметра?


1) Зачем?
2) Где взять значение?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: Мало, мало про это на собеседованиях спрашивают... ;)
От: push  
Дата: 26.12.16 14:51
Оценка:
Здравствуйте, Erop, Вы писали:

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


P>>Можно уточнить момент выше? Не понял, что происходит. Почему решили, что при таком вызове есть какой-то указатель перед блоком?

E>Не помню уже о чём там речь, но, скорее всего указатель клался в парном к delete операторе new...

Всё, разобрался, там просто парный new не написан. Вместо него используется new с дефолтным вторым параметром.
В общем этот delete можно удалить, а в том, что повыше второму параметру присвоить нулевое дефолтное значение.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.