Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Август 23, 2016, 09:46



Название: Более умный shared_ptr
Отправлено: Igors от Август 23, 2016, 09:46
Добрый день

Вообще говоря, shared_ptr (QSharedPointer) совсем неплох, но не раз у меня возникала нужда не только определить нулевой или нет, но и узнать какие объекты (указатели) сейчас им владеют. Можно ли как-то реализовать "на базе" существующих умных(?) указателей - или все печально, свой велосипед с нуля?

Спасибо


Название: Re: Более умный shared_ptr
Отправлено: ssoft от Август 23, 2016, 10:11
Вообще можно узнать только сколько владельцев у объекта, но чтобы все обратные связи владения?! Накладно это очень, да и на практике такой необходимости я ни разу не встречал. Скорее всего как-то по-другому это делают, какими-нибудь перекрестными ссылками.

Можно пример для анализа?


Название: Re: Более умный shared_ptr
Отправлено: Igors от Август 23, 2016, 10:52
Вообще можно узнать только сколько владельцев у объекта,
Заметим что в реализации QSharedPointer даже этого нет

...но чтобы все обратные связи владения?! Накладно это очень, да и на практике такой необходимости я ни разу не встречал. Скорее всего как-то по-другому это делают, какими-нибудь перекрестными ссылками.

Можно пример для анализа?
Предлагаю отталкиваться от того же менеджера который мы только что обсуждали. Пример: получено указание/команда "уходим". Удалили все контроллеры, удаляем мапу скелетонов. Оба-на, шаред-то живой (ссылок > 1). Насвистели в одном из контроллеров - в каком? Их там десятки. Заряжать печать в конструктор/деструктор объекта (скелетона) здесь малоэффективно, это может быть не связано с владением. Отследить все передачи шаредов - ну, мягко говоря, "сложно". Что еще, как бум искать?   


Название: Re: Более умный shared_ptr
Отправлено: Old от Август 23, 2016, 10:59
это может быть не связано с владением.
А с чем еще это может быть связано?

Отследить все передачи шаредов - ну, мягко говоря, "сложно". Что еще, как бум искать?  
Что значит "отследить все передачи шаредов"? Нет никакой нужды отслеживать куда передаются шареды, внутри метода/функции он сохранится не могут. Нужно просто посмотреть все места, где этот шаред может храниться, а это только какие-то коллекции в долгоживущих объектах, которые могут пережить менеджер.
Но это уже проблема той самой архитектуры. Таких мест изначально (при проектировании) допускать нельзя.


Название: Re: Более умный shared_ptr
Отправлено: ssoft от Август 23, 2016, 13:44
Конечно, можно для отладки реализовать свой shared поверх любого другого, который хранит отображения void*->typeid, void*->counter (в конструкторе принимает this, сохраняет typeid и инкрементирует счетчик; в деструкторе декрементирует счетчик и при его обнулении чистит отображения). Но мест где может "зависнуть" shared не должно допускаться изначально.

Для получения информации из QSharedPointer к сожалению требуется хак), например, такой:

Код
C++ (Qt)
       struct HackValue
       {
           struct Counter
           {
               QBasicAtomicInt weakref;
               QBasicAtomicInt strongref;
           };
 
           void * m_value;
           Counter * m_counter;
       };
 
       //QSharedPointer< Value > value;
       HackValue * hack = reinterpret_cast< HackValue * >( &value );
 
       // access
       hack->m_counter->weakref;
       hack->m_counter->strongref;
 


Название: Re: Более умный shared_ptr
Отправлено: Racheengel от Август 23, 2016, 14:14
Вот именно для решения подобной проблемы коллбэки и исполуются :)


Название: Re: Более умный shared_ptr
Отправлено: Igors от Август 24, 2016, 09:55
Вот именно для решения подобной проблемы коллбэки и исполуются :)
Хмм... ну допустим (или вообразим) shared_ptr имел бы какие-то калбэки - я все равно не вижу никакого разумного сценария  :).

Конечно, можно для отладки реализовать свой shared поверх любого другого, который хранит отображения void*->typeid, void*->counter (в конструкторе принимает this, сохраняет typeid и инкрементирует счетчик; в деструкторе декрементирует счетчик и при его обнулении чистит отображения).
Ваше "конечно" ободряет :) но у меня нет никаких соображений как это реализовать. Можно набросок (пусть очень черновой)? Спасибо

Ну и еще довольно очевидное соображение: нагородить свой конечно можно, но вряд ли его удастся использовать для чужого кода со стандартными шаред  (слишком много кромсать)

Но мест где может "зависнуть" shared не должно допускаться изначально.
Можно пойти дальше и заявить типа "все выделенные блоки памяти должны быть освобождены" - и это будет звучать столь же справедливо  :)

Возможно "мониторинг ресурсов" - более удачная формулировка чего хочется. Напр таблица (или просто консольная печать) где "объект такой-то сейчас используется этими <список>". Это может быть более или менее актуально, но смысл/основания имеет



Название: Re: Более умный shared_ptr
Отправлено: Old от Август 24, 2016, 10:06
Возможно "мониторинг ресурсов" - более удачная формулировка чего хочется. Напр таблица (или просто консольная печать) где "объект такой-то сейчас используется этими <список>". Это может быть более или менее актуально, но смысл/основания имеет
Можно при получении ресурса сообщать кто его получает (и собирается хранить). Менеджер будет хранить списки weak_ptr на всех клиентов для каждого ресурса. Если клиент разрушается, то weak_ptr это покажет.
Но здесь нельзя передавать указатель на ресурс другому объекту на хранение.


Название: Re: Более умный shared_ptr
Отправлено: ssoft от Август 24, 2016, 11:48
Вот такой набросок в notepad ). Думаю, что сам принцип понятен. Можно легко подружить и со стандартными через конструктор и оператор преобразования, только счетчики тогда не все указатели будут считать.

Код
C++ (Qt)
template < typename _Type >
class TShared;
 
template < typename _Type >
class TWeak;
 
struct TInfo
{
   std::map< void *, const char * > m_name_by_address;
   std::map< void *, int > m_shared_count_by_address;
   std::map< void *, int > m_weak_count_by_address;
 
   void insertShared ( void * address, const char * name )
   {
       m_name_by_address[ address ] = name;
       ++m_shared_count_by_address[ address ];
   }
 
   void removeShared ( void * address )
   {
       --m_shared_count_by_address[ address ];
       checkEmpty( address );
   }
 
   void insertWeak ( void * address, const char * name )
   {
       m_name_by_address[ address ] = name;
       ++m_weak_count_by_address[ address ];
   }
 
   void removeWeak ( void * address )
   {
       --m_weak_count_by_address[ address ];
       checkEmpty( address );
   }
 
   void checkEmpty ( void * address )
   {
       //TODO если weak и shared обнулились - удалить address из всех коллекций
   }
};
 
template < typename _Type >
class TShared
{
public:
   typedef TShared< _Type > ThisType; // using ThisType = TShared< _Type >;
 
private:
   const void * m_address;
   std::shared_ptr< _Type > m_pointer;
   std::shared_ptr< TInfo > m_info;
 
public:
   TShared ( void * address, _Type * value =std::null_ptr )
   : m_address( address )
   , m_pointer( value )
   , m_info( new TInfo ) // std::make_shared
   {
       m_info->insertShared( m_address, typeid( *value ).name() );
   }
 
   ~TShared ()
   {
       m_info->removeShared( m_address );
   }
 
   template < typename _OtherType >
   TShared ( const TShared< _OtherType > & other );
 
   template < typename _OtherType >
   TShared ( const TWeak< _OtherType > & other );
 
   template < typename _OtherType >
   ThisType & operator = ( const TShared< _OtherType > & other )
   {
       m_info->removeShared( m_address );
       m_pointer = other.m_pointer;
       m_info = other.m_info;
       m_info->insertShared( m_address );
       return *this;
   }
 
   template < typename _OtherType >
   ThisType & operator = ( const TWeak< _OtherType > & other )
   {
       m_info->removeShared( m_address );
       m_pointer = other.m_pointer;
       m_info = other.m_info;
       m_info->insertShared( m_address );
       return *this;
   }
};
 
template < typename _Type >
class TWeak
{
public:
   typedef TWeak< _Type > ThisType; // using ThisType = TWeak< _Type >;
 
private:
   const void * m_address;
   std::weak_ptr< _Type > m_pointer;
   std::shared_ptr< TInfo > m_info;
 
public:
   TWeak ( void * address )
   : m_address( address )
   , m_pointer( value )
   , m_info( new TInfo ) // std::make_shared
   {
       m_info->insertWeak( m_address, typeid( *value ).name() );
   }
 
   ~TWeak ()
   {
       m_info->removeWeak( m_address );
   }
 
   template < typename _OtherType >
   TWeak ( const TWeak< _OtherType > & other );
 
   template < typename _OtherType >
   TWeak ( const TShared< _OtherType > & other );
 
   template < typename _OtherType >
   ThisType & operator = ( const TWeak< _OtherType > & other )
   {
       m_info->removeWeak( m_address );
       m_pointer = other.m_pointer;
       m_info = other.m_info;
       m_info->insertWeak( m_address );
       return *this;
   }
 
   template < typename _OtherType >
   ThisType & operator = ( const TShared< _OtherType > & other )
   {
       m_info->removeWeak( m_address );
       m_pointer = other.m_pointer;
       m_info = other.m_info;
       m_info->insertWeak( m_address );
       return *this;
   }
};
 

Сам я отношусь к такой необходимости скептически, но уж если действительно нужно, тогда как-то так).


Название: Re: Более умный shared_ptr
Отправлено: Igors от Август 26, 2016, 06:10
Да. "владелец" (самого шаред) выглядит неизбежным. А потом так или иначе по ключу "владелец". Как-то не подумал об этом, спасибо.

А если так

Код
C++ (Qt)
template <class T>
struct TShared {
template <class X>
TShared( const X * owner );
...
private:
std::shared_ptr<T> mPtr;
void * mOwner;
const char * mTypeName;
 
static std::set<TShared *> mSet;
};
В принципе этого достаточно для создать/удалить/присвоить (ходовых операций), а для выдачи списка владельцев можно и пробежаться по mSet. Но вот что делать с конструктором копирования? С оператором = все хорошо (владелец есть), а тут как?


Название: Re: Более умный shared_ptr
Отправлено: ssoft от Август 26, 2016, 08:25
Warning. Использование static не позволяет различить разные экземпляры одного и того же типа T. Тем более, если тип базовый, то вся информация о всех типах потомков будет сложена в одну переменную.

Конструктор копирования - узкое место). Здесь либо введение "определенных правил работы", либо запрет копирования (что не всегда возможно).


Название: Re: Более умный shared_ptr
Отправлено: Igors от Август 26, 2016, 09:35
Warning. Использование static не позволяет различить разные экземпляры одного и того же типа T. Тем более, если тип базовый, то вся информация о всех типах потомков будет сложена в одну переменную.
Не понял. То есть как это "не различить"? Указатели на шаред же разные

Конструктор копирования - узкое место). Здесь либо введение "определенных правил работы", либо запрет копирования (что не всегда возможно).
Да, и что-то ничего удачного не видно  :'(


Название: Re: Более умный shared_ptr
Отправлено: ssoft от Август 26, 2016, 10:07
Не понял. То есть как это "не различить"? Указатели на шаред же разные

Да, не внимательно посмотрел на пример). Но все равно, существует разница между TShared< Base >::mSet и TShared< Derived >::mSet - по какому набору бегать (нужен общий базовый предок)?