Russian Qt Forum
Ноябрь 22, 2024, 16:17 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Более умный shared_ptr  (Прочитано 7570 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Август 23, 2016, 09:46 »

Добрый день

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

Спасибо
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #1 : Август 23, 2016, 10:11 »

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

Можно пример для анализа?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Август 23, 2016, 10:52 »

Вообще можно узнать только сколько владельцев у объекта,
Заметим что в реализации QSharedPointer даже этого нет

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

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

Сообщений: 4350



Просмотр профиля
« Ответ #3 : Август 23, 2016, 10:59 »

это может быть не связано с владением.
А с чем еще это может быть связано?

Отследить все передачи шаредов - ну, мягко говоря, "сложно". Что еще, как бум искать?  
Что значит "отследить все передачи шаредов"? Нет никакой нужды отслеживать куда передаются шареды, внутри метода/функции он сохранится не могут. Нужно просто посмотреть все места, где этот шаред может храниться, а это только какие-то коллекции в долгоживущих объектах, которые могут пережить менеджер.
Но это уже проблема той самой архитектуры. Таких мест изначально (при проектировании) допускать нельзя.
« Последнее редактирование: Август 23, 2016, 11:05 от Old » Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #4 : Август 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;
 
Записан
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #5 : Август 23, 2016, 14:14 »

Вот именно для решения подобной проблемы коллбэки и исполуются Улыбающийся
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Август 24, 2016, 09:55 »

Вот именно для решения подобной проблемы коллбэки и исполуются Улыбающийся
Хмм... ну допустим (или вообразим) shared_ptr имел бы какие-то калбэки - я все равно не вижу никакого разумного сценария  Улыбающийся.

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

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

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

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

Записан
Old
Джедай : наставник для всех
*******
Online Online

Сообщений: 4350



Просмотр профиля
« Ответ #7 : Август 24, 2016, 10:06 »

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

Сообщений: 584


Просмотр профиля
« Ответ #8 : Август 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;
   }
};
 

Сам я отношусь к такой необходимости скептически, но уж если действительно нужно, тогда как-то так).
« Последнее редактирование: Август 25, 2016, 09:50 от ssoft » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Август 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. Но вот что делать с конструктором копирования? С оператором = все хорошо (владелец есть), а тут как?
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #10 : Август 26, 2016, 08:25 »

Warning. Использование static не позволяет различить разные экземпляры одного и того же типа T. Тем более, если тип базовый, то вся информация о всех типах потомков будет сложена в одну переменную.

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

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Август 26, 2016, 09:35 »

Warning. Использование static не позволяет различить разные экземпляры одного и того же типа T. Тем более, если тип базовый, то вся информация о всех типах потомков будет сложена в одну переменную.
Не понял. То есть как это "не различить"? Указатели на шаред же разные

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

Сообщений: 584


Просмотр профиля
« Ответ #12 : Август 26, 2016, 10:07 »

Не понял. То есть как это "не различить"? Указатели на шаред же разные

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


Страница сгенерирована за 0.112 секунд. Запросов: 23.