Russian Qt Forum

Программирование => Общий => Тема начата: Igors от Июнь 21, 2019, 05:43



Название: Опять указатели (умные и не очень)
Отправлено: Igors от Июнь 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 не может быть вумным - слишком много кода завязано не него, последствия непредсказуемы. А вот добавлять в него вумные члены - пожалуйста

Спасибо


Название: Re: Опять указатели (умные и не очень)
Отправлено: ssoft от Июнь 21, 2019, 07:35
Напр в данном случае CPolygonList не может быть вумным - слишком много кода завязано не него, последствия непредсказуемы. А вот добавлять в него вумные члены - пожалуйста

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


Название: Re: Опять указатели (умные и не очень)
Отправлено: Igors от Июнь 21, 2019, 08:49
Можно добавить тогда в CPolygonList специальный объект, который в своем деструкторе почистит CPolygonList * из MyClass.
Тогда как в том анекдоте "наблюдение за наблюдающим" - ведь MyClass тоже могут грохнуть.

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


Название: Re: Опять указатели (умные и не очень)
Отправлено: ssoft от Июнь 21, 2019, 14:10
Тогда как в том анекдоте "наблюдение за наблюдающим" - ведь MyClass тоже могут грохнуть.
Если не проектируется новая архитектура, а сопровождается уже достаточно большой паровоз кода, то делаются всяческие заплатки из разряда предложенных. Каждая заплатка может потребовать еще заплаток и еще, и еще ...

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

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


Название: Re: Опять указатели (умные и не очень)
Отправлено: Igors от Июнь 21, 2019, 15:46
Если не проектируется новая архитектура, а сопровождается уже достаточно большой паровоз кода, то делаются всяческие заплатки из разряда предложенных. Каждая заплатка может потребовать еще заплаток и еще, и еще ...
Ну не все так печально как в моем любимом антипаттерне lava flow (https://pro-prof.com/forums/topic/antipatterns) :) Почему "сопровождаем"? Разрабатываем, добавляем новые фичи, приведенный псевдокод - одна из них. Давление старого кода конечно есть, но это нормально, "с нуля" хорошо плагины писать (иногда).

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

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

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

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


Название: Re: Опять указатели (умные и не очень)
Отправлено: Igors от Июнь 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;
}
...
};
В принципе устраивает, но "зачистка" остается больным местом. Здесь я полагаю что эл-тов хеша будет мало и чищу всегда, это конечно неаккуратно.  

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


Название: Re: Опять указатели (умные и не очень)
Отправлено: ViTech от Июнь 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 считаю нормальным решением, почему бы и нет. Другие решения могут быть сложнее.


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

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

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

Другие решения могут быть сложнее.
Так где они, эти другие? Почему я не вижу ни одного из них?  :)


Название: Re: Опять указатели (умные и не очень)
Отправлено: ViTech от Июнь 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 (http://www.prog.org.ru/topic_32395_0.html).


Название: Re: Опять указатели (умные и не очень)
Отправлено: Igors от Июнь 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 для синхронизации доступа к объекту. Типы этих сущностей зависят от того, что с ними будет делать метод класса, какие связи устанавливать между объектами и т.п.
Это втягивает обсуждение в долгие и обширные пояснения, которые никогда ничем не кончаются :) Рассказать-то не жалко, но толку от этого никакого. Наоборот, нужно стремиться дать только самую необходимую инфу отсекая все ненужное


Название: Re: Опять указатели (умные и не очень)
Отправлено: ViTech от Июнь 24, 2019, 12:13
Вижу настойчивое стремление втулить вумный указатель, хотя здесь он совершенно ни к чему и только захламляет код.

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

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

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


Название: Re: Опять указатели (умные и не очень)
Отправлено: Igors от Июнь 25, 2019, 06:16
(например, Smart observers to use with unique_ptr (https://www.codeproject.com/articles/1011134/smart-observers-to-use-with-unique-ptr)).
Мда... Вроде бы и идея понятна, сам только что решал похожую задачу, но.. "легкого чтения" почему-то не получается.  Давно заметил этот эффект: да, можно сделать свой темплейт класс, и он будет решать поставленные задачи (может даже прекрасно). НО при этом неизбежно возникает какая-то своя система правил/соглашений которую надо знать, и, по меньшей мере, принять. А это не так уж легко. Поэтому обычно фан темплейтов не полезет в то что наворотил другой фан :) Каждый будет свое воротить.

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

В общем, "нет в жизни счастья"  :)


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

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