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

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

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

Сообщений: 11445


Просмотр профиля
« : Июнь 21, 2019, 05:43 »

Добрый день

Задача только что возникла, тщательно не обдумывал, так что может и все просто. Псевдокод
Код
C++ (Qt)
struct MyClass {
...
const CDrawList & GetDrawList( const CPolygonList * poly )
{
  CDrawList & list = m_hash[poly];
  if (list.IsEmpty())
    poly->BuildDrawList(list, ...);
  return list;
}
...
};
 
Т.е. обычный кеш. Проблема в том что ключ (указатель на CPolygonList) могут застрелить из очень разных мест. Как автоматом вычищать его из MyClass ?

Да, и давно хотел сказать: всякий раз когда речь идет о вумных указателях почему-то никто не упоминает что в достаточно большом написанном коде "заменить голый на вумный" может быть очень и очень непросто. Напр в данном случае CPolygonList не может быть вумным - слишком много кода завязано не него, последствия непредсказуемы. А вот добавлять в него вумные члены - пожалуйста

Спасибо
« Последнее редактирование: Июнь 21, 2019, 05:45 от Igors » Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 574


Просмотр профиля
« Ответ #1 : Июнь 21, 2019, 07:35 »

Напр в данном случае CPolygonList не может быть вумным - слишком много кода завязано не него, последствия непредсказуемы. А вот добавлять в него вумные члены - пожалуйста

Можно добавить тогда в CPolygonList специальный объект, который в своем деструкторе почистит CPolygonList * из MyClass.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Июнь 21, 2019, 08:49 »

Можно добавить тогда в CPolygonList специальный объект, который в своем деструкторе почистит CPolygonList * из MyClass.
Тогда как в том анекдоте "наблюдение за наблюдающим" - ведь MyClass тоже могут грохнуть.

Ну и решение "без ограничений" тоже интересно. Хорошо, вот CPolygonList мог быть шаред - и что с того? Ключом его все равно не сделать.
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 574


Просмотр профиля
« Ответ #3 : Июнь 21, 2019, 14:10 »

Тогда как в том анекдоте "наблюдение за наблюдающим" - ведь MyClass тоже могут грохнуть.
Если не проектируется новая архитектура, а сопровождается уже достаточно большой паровоз кода, то делаются всяческие заплатки из разряда предложенных. Каждая заплатка может потребовать еще заплаток и еще, и еще ...

Ну и решение "без ограничений" тоже интересно. Хорошо, вот CPolygonList мог быть шаред - и что с того? Ключом его все равно не сделать.

Чтобы предложить решение, нужно проанализировать зависимости между CPolygonList, MyClass и может еще и другими сущностями. Кто кого агрегирует, кто на кого ссылается? Может шаред и не нужен вовсе. Какой эффект ожидается от шаред? Совместное владение, продление времени жизни, ленивые вычисления?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Июнь 21, 2019, 15:46 »

Если не проектируется новая архитектура, а сопровождается уже достаточно большой паровоз кода, то делаются всяческие заплатки из разряда предложенных. Каждая заплатка может потребовать еще заплаток и еще, и еще ...
Ну не все так печально как в моем любимом антипаттерне lava flow Улыбающийся Почему "сопровождаем"? Разрабатываем, добавляем новые фичи, приведенный псевдокод - одна из них. Давление старого кода конечно есть, но это нормально, "с нуля" хорошо плагины писать (иногда).

Чтобы предложить решение, нужно проанализировать зависимости между CPolygonList, MyClass и может еще и другими сущностями. Кто кого агрегирует, кто на кого ссылается? Может шаред и не нужен вовсе. Какой эффект ожидается от шаред? Совместное владение, продление времени жизни, ленивые вычисления?
Ну вот, опять "глубокий системный анализ" Улыбающийся А ведь Вы прекрасно понимаете о чем речь, да и имена вполне прозрачны.

CDrawList - это структура для подачи индексов в glDrawElements/glMultiDrawElements.
CPolygonList - ну ясно контейнер фейсов.

Но сам CPolygonList обычно не может сформировать CDrawList - фейсы могут быть сгруппированы "по материалам" - которыми и управляет MyClass, возможно имя типа CMaterialSet было уместнее. Понятно изменились/удалились фейсы (CPolygonList) - кешированный CDrawList нужно сбросить.   

Вот и ВСЕ. Более того, здесь не нужны даже эти (зачаточные) познания. Ясно просматривается "кеширование". Ясно что MyClass использует указатель(и) на др класс, это естественно, не один же класс все-все делает. Встает законный вопрос как обеспечить надежность/достоверность используемых указателей. Чего ото "темплейтить" клопа с "совместным владением", "временем жизни" и.т.п.? Я ни о чем таком не спрашивал Улыбающийся Впрочем если думать лень - повод всегда найдется
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Июнь 22, 2019, 06:54 »

В общем ничего выдающегося не придумал и задействовал стандартную связку shared + weak
Код
C++ (Qt)
struct CMaterialSet {
...
const CDrawList & GetDrawList( const CPolygonList * poly )
{
// выбрасываем трупики
  auto it = m_hash.begin();
  while (it != m_hash.end())
    if (it.value().m_LivePtr.isNull())   // бобик сдох ?
      it = m_hash.erase(it);
    else
      ++it;
 
// кешируем данные
  CDrawPair & p = m_hash[poly];
  p.m_LivePtr = poly->GetLivePtr();   // QWeakPointer = QSharedPointer
 
  if (p.m_list.IsEmpty())
    poly->BuildDrawList(p.m_list, this->m_indices);
 
  return p.m_list;
}
...
};
В принципе устраивает, но "зачистка" остается больным местом. Здесь я полагаю что эл-тов хеша будет мало и чищу всегда, это конечно неаккуратно.  

Интересно какие еще есть решения?
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #6 : Июнь 22, 2019, 12:45 »

Чтобы предложить решение, нужно проанализировать зависимости между CPolygonList, MyClass и может еще и другими сущностями. Кто кого агрегирует, кто на кого ссылается? Может шаред и не нужен вовсе. Какой эффект ожидается от шаред? Совместное владение, продление времени жизни, ленивые вычисления?
Ну вот, опять "глубокий системный анализ" Улыбающийся А ведь Вы прекрасно понимаете о чем речь, да и имена вполне прозрачны.

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

Если Вы говорите, что "что ключ (указатель на CPolygonList) могут застрелить из очень разных мест", то что может произойти здесь, если его таки застрелят?
Код
C++ (Qt)
const CDrawList & GetDrawList( const CPolygonList * poly )
{
...
  CDrawPair & p = m_hash[poly];
-->   p.m_LivePtr = poly->GetLivePtr();   // QWeakPointer = QSharedPointer
 
  if (p.m_list.IsEmpty())
-->     poly->BuildDrawList(p.m_list, this->m_indices);
...
}

В аргументе poly может быть nullptr? Или это тоже слишком глубокий анализ? Улыбающийся

В общем ничего выдающегося не придумал и задействовал стандартную связку shared + weak
...
В принципе устраивает, но "зачистка" остается больным местом. Здесь я полагаю что эл-тов хеша будет мало и чищу всегда, это конечно неаккуратно. 

Интересно какие еще есть решения?

В определённых ситуациях (эл-тов хеша будет мало и т.п.) хранить идентификатор и weak_ptr считаю нормальным решением, почему бы и нет. Другие решения могут быть сложнее.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Июнь 23, 2019, 11:37 »

Чтобы предложить решение, нужно проанализировать зависимости между CPolygonList, MyClass и может еще и другими сущностями. Кто кого агрегирует, кто на кого ссылается? Может шаред и не нужен вовсе. Какой эффект ожидается от шаред? Совместное владение, продление времени жизни, ленивые вычисления?
Я тоже считаю, что эти вопросы являются определяющими, и помогают в выборе типов (указателей умных/голых, ссылок, обёрток и т.п.) для работы с объектами в заданных ситуациях.
По существу эти вопросы = "изложите архитектуру проекта", для обсуждения на форуме это дело гиблое, вникать в чужую архитектуру/специфику никто не хочет (дай бог со своей разобраться). Да и логика порочная - выходит для решения совершенно конкретного (и довольно узкого) вопроса необходимо знать все-все о проекте. Это неправильно

Но те, кто пользуется только типами голых указателей, обычно такими вопросами не задаются, им и выбирать-то не из чего Улыбающийся.
Т.е. вот если бы я имел под рукой с десяток всяких-разных вумных указателей - вот тогда... Улыбающийся А что собсно "тогда"? "Наплодить сущностей" можно много, а нужного ф-ционала как не было так и нет

Если Вы говорите, что "что ключ (указатель на CPolygonList) могут застрелить из очень разных мест", то что может произойти здесь, если его таки застрелят?
Проверять на null здесь нет смысла, т.к.тогда нечего возвращать. Поэтому да, пусть вылетает, вызывающий отвечает за валидность аргумента. Делать ли аргумент ссылкой - хз, спорно. Кстати ситуация не редкая.

Другие решения могут быть сложнее.
Так где они, эти другие? Почему я не вижу ни одного из них?  Улыбающийся
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #8 : Июнь 23, 2019, 16:57 »

По существу эти вопросы = "изложите архитектуру проекта", для обсуждения на форуме это дело гиблое, вникать в чужую архитектуру/специфику никто не хочет (дай бог со своей разобраться). Да и логика порочная - выходит для решения совершенно конкретного (и довольно узкого) вопроса необходимо знать все-все о проекте. Это неправильно

Про архитектуру всего проекта никто и не говорил, достаточно знаний про MyClass, CPolygonList  и связанными с ними сущностей в контексте текущей задачи.

Например, такой вариант:
Код
C++ (Qt)
const CDrawList & GetDrawList( const std::weak_ptr<const CPolygonList>& poly_carrier )
{
   std::shared_ptr<const CPolygonList> poly_lifetime_locker = poly_carrier.lock();
   if (!poly_lifetime_locker)
       return ???;
 
   const CPolygonList& poly_object = *poly_lifetime_locker;
   CDrawList & list = m_hash[poly_object.id()];
   if (list.IsEmpty())
       poly_object.BuildDrawList(list, ...);
   return list;
}

Как можно заметить, сущностей, связанных с poly, несколько больше, чем может показаться на первый взгляд (из первого сообщения). Их может быть ещё больше, например, poly_access_locker для синхронизации доступа к объекту. Типы этих сущностей зависят от того, что с ними будет делать метод класса, какие связи устанавливать между объектами и т.п. Можно всё это множество сущностей попробовать описывать только типом голого указателя, но мне что-то не хочется.

Т.е. вот если бы я имел под рукой с десяток всяких-разных вумных указателей - вот тогда... Улыбающийся А что собсно "тогда"? "Наплодить сущностей" можно много, а нужного ф-ционала как не было так и нет

Вы бы тогда может и нет, а другие вполне да Улыбающийся.

Проверять на null здесь нет смысла, т.к.тогда нечего возвращать. Поэтому да, пусть вылетает, вызывающий отвечает за валидность аргумента. Делать ли аргумент ссылкой - хз, спорно. Кстати ситуация не редкая.

Я считаю, что тогда лучше передавать по ссылке. Тогда по сигнатуре функции будет понятно, что ей нужен ровно один объект без всякой шелухи, nullptr она не принимает, и за существование объекта на время работы функции отвечает вызывающий.

Так где они, эти другие? Почему я не вижу ни одного из них?  Улыбающийся

Я не знаю, почему Вы не видите Улыбающийся. Одно уже было:
Можно добавить тогда в CPolygonList специальный объект, который в своем деструкторе почистит CPolygonList * из MyClass.
С отсылкой на вариант с обратной связью из SharedHash/Set.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Июнь 24, 2019, 06:44 »

Например, такой вариант:
Код
C++ (Qt)
const CDrawList & GetDrawList( const std::weak_ptr<const CPolygonList>& poly_carrier )
{
   std::shared_ptr<const CPolygonList> poly_lifetime_locker = poly_carrier.lock();
..
  CDrawList & list = m_hash[poly_object.id()];
 
Вижу настойчивое стремление втулить вумный указатель, хотя здесь он совершенно ни к чему и только захламляет код. Рисуются полигоны, если их нет - то и рисовать нечего. Поэтому аргумент CPolygonList засисять не нужно, он валиден "по построению", в защите нуждается кешированный CDrawList (точнее его ключ). Использовать уникальное id - есть такой вариант, но он не так уж дешев, особенно с учетом зачистки хеша (эту подробность Вы благополучно опустили).

Как можно заметить, сущностей, связанных с poly, несколько больше, чем может показаться на первый взгляд (из первого сообщения). Их может быть ещё больше, например, poly_access_locker для синхронизации доступа к объекту. Типы этих сущностей зависят от того, что с ними будет делать метод класса, какие связи устанавливать между объектами и т.п.
Это втягивает обсуждение в долгие и обширные пояснения, которые никогда ничем не кончаются Улыбающийся Рассказать-то не жалко, но толку от этого никакого. Наоборот, нужно стремиться дать только самую необходимую инфу отсекая все ненужное
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #10 : Июнь 24, 2019, 12:13 »

Вижу настойчивое стремление втулить вумный указатель, хотя здесь он совершенно ни к чему и только захламляет код.

Нет, это настойчивое стремление втулить совсем другое: анализ происходящего. В частности, что переменные одного типа (CPolygonList *) могут играть разные роли, а не одну, как это может показаться на первый взгляд. В m_hash нужен идентификатор объекта, а не указатель на него. Это так совпало (как это часто и бывает с типами голых указателей), что указатель (адрес в памяти) можно считать идентификатором. Вариант возможный, но не единственный и не самый надёжный. Далее, нужно определить, что объект с таким идентификатором существует. Сделать это можно с помощью weak_ptr и бонусом получить доступ к объекту и продление его жизни (если требуется), можно другими способами с другими бонусами/недостатками (например, Smart observers to use with unique_ptr). В итоге может получиться, что в GetDrawList() достаточно передать идентификатор объекта (poly), а не указатель/ссылку на него. И я не говорю, что нужно везде пихать умные указатели, а наоборот, делать это по необходимости. Чем меньше шелухи - тем лучше.

Поэтому аргумент CPolygonList засисять не нужно, он валиден "по построению", в защите нуждается кешированный CDrawList (точнее его ключ). Использовать уникальное id - есть такой вариант, но он не так уж дешев, особенно с учетом зачистки хеша (эту подробность Вы благополучно опустили).

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

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Июнь 25, 2019, 06:16 »

Мда... Вроде бы и идея понятна, сам только что решал похожую задачу, но.. "легкого чтения" почему-то не получается.  Давно заметил этот эффект: да, можно сделать свой темплейт класс, и он будет решать поставленные задачи (может даже прекрасно). НО при этом неизбежно возникает какая-то своя система правил/соглашений которую надо знать, и, по меньшей мере, принять. А это не так уж легко. Поэтому обычно фан темплейтов не полезет в то что наворотил другой фан Улыбающийся Каждый будет свое воротить.

Поэтому желание обойтись только стандартными средствами вполне понятно. К сожалению, они бедны. Само объявление шаред уже неприятно режет глаз - ведь никакого "общего ресурса" не планировалось, никто ничего не хотел "продлевать". А если углубиться в детали, то часто оказывается что поведение совсем не то что хотелось. Напр в моей реализации дыра - если CPolygonList копируется, то зачистка хеша будет работать неверно. Правда он у меня некопируемый, но все же. Если делать в общем виде - придется наследоваться от QSharedPointer порезав конструктор копирования (заодно дать более осмысленное имя). Но это уже "велик", "костыль" и.т.п,

В общем, "нет в жизни счастья"  Улыбающийся
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #12 : Июнь 25, 2019, 09:29 »

Поэтому желание обойтись только стандартными средствами вполне понятно. К сожалению, они бедны. Само объявление шаред уже неприятно режет глаз - ведь никакого "общего ресурса" не планировалось, никто ничего не хотел "продлевать". А если углубиться в детали, то часто оказывается что поведение совсем не то что хотелось. Напр в моей реализации дыра - если CPolygonList копируется, то зачистка хеша будет работать неверно. Правда он у меня некопируемый, но все же. Если делать в общем виде - придется наследоваться от QSharedPointer порезав конструктор копирования (заодно дать более осмысленное имя). Но это уже "велик", "костыль" и.т.п,

Ну я себе такой велик сделал и теперь очень доволен Улыбающийся. Чего и Вам желаю.
Записан

Пока сам не сделаешь...
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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