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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Unified Pointer Library  (Прочитано 12834 раз)
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« : Июль 02, 2018, 10:44 »

Делаю такую штуку. Может ещё кому пригодится.

Unified Pointer Library (описание на русском) - библиотека унифицированных указателей (UPL), содержит концепты и реализации умных указателей, которые предназначены для управления временем жизни объектов. Предоставляет указатели upl::unique и upl::shared для уникального и совместного владения объектами, слабые ссылки для них upl::weak, и добавляет унифицированный тип владения upl::unified. Публичный интерфейс унифицированных указателей схож с интерфейсом умных указателей стандартной библиотеки C++. Библиотека header-only, лицензия MIT.

Ключевые особенности:
  • Возможность организации ассоциативных связей между объектами в соответствии с UML.
  • Определены концепты, которые в compile-time позволяют гибко определять тип указателей UPL в обобщённых алгоритмах.
  • Указатель upl::weak может ссылаться на объект, который находится под управлением upl::unique.
  • Указатель upl::unified позволяет передать уникальное владение объектом в цепочке, где может выполняться копирование.
  • С помощью upl::unified можно временно продлить время жизни объекта в заданной области видимости, что позволяет корректно завершить работу с ним, даже когда все остальные указатели на этот объект удалены.
  • Добавлены указатели с одинарной кратностью, которые не могут быть пустыми и всегда ссылаются на один объект.

Отвечу на возникающие вопросы. Аргументированная критика приветствуется Улыбающийся.
Записан

Пока сам не сделаешь...
vipet
Бывалый
*****
Offline Offline

Сообщений: 452


Просмотр профиля
« Ответ #1 : Июль 02, 2018, 13:53 »


Отвечу на возникающие вопросы.

в каких случаях и чем лучше stl?
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #2 : Июль 02, 2018, 14:27 »

в каких случаях и чем лучше stl?

Лучше в случаях работы в многопоточной среде. В частности, можно создать слабую ссылку/связь upl::weak на объект, находящийся под уникальным владением upl::unique. Далее, через upl::weak можно безопасно получить доступ к объекту. Тут возможны два варианта:
1. Объект удалился. upl::weak::expired() == true, объекта нет, обращаться не к чему.
2. На момент доступа через upl::weak объект существует, но внезапно может удалиться в другом потоке. upl::weak::lock() продлевает время жизни объекта до конца работы текущей области видимости.

Если коротко, то добавлена связка upl::unique <-> upl::weak, аналогичная std::shared_ptr <-> std::weak_ptr, только для уникального владения. Если подробнее, то в библиотеке набор указателей, каким я бы хотел его видеть. С нормальным уникальным владением, с унифицированным доступом, с различной кратностью, с поддержкой концептов в шаблонах и прочим.
Записан

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

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #3 : Июль 02, 2018, 14:34 »

А что с производительностью? Подмигивающий
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #4 : Июль 02, 2018, 14:44 »

А что с производительностью? Подмигивающий

Это от реализации зависит Улыбающийся. Кстати, можно написать свою, под свои нужды. Текущая реализация наивная, и ещё до конца не доделанная, ибо не хочется велосипед изобретать. Она нужна для проверки концепции. Сейчас делаю реализацию с более узким спектром возможностей, но на готовых std::shared_ptr/std::weak_ptr. В ней производительность будет такой же, как у std::shared_ptr.
Записан

Пока сам не сделаешь...
ssoft
Программист
*****
Offline Offline

Сообщений: 579


Просмотр профиля
« Ответ #5 : Июль 03, 2018, 10:38 »

Вопросов очень много). Пока по крупному

1. Предполагается альтернативная реализация указателям std или это принципиально новый набор умных указателей?

Какие преимущества? Почему для уникального владения нельзя взять и использовать std::shared_ptr, пусть даже контроль уникального использования ложится на программиста.
Можно же реализовать уникальное владение через using unique = shared_ptr; using weak = weak_ptr; using unified = shared_ptr;
Не возникнет ли трудностей у программиста в выборе "подходящих" указателей из большого числа вариантов?

2. В реализации используется куча счетчиков (куда уж тет без них))). В параллельном доступе std::unique_ptr будет эффективнее, чем указатели со счетчиками, а raw pointer еще чуть эффективнее (зависит от компилятора).

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

3. В примере используется тэг in_place. Однако экземпляр объекта string формируется в куче.
Не предполагает ли in_place формирование экземпляра непосредственно на стеке? И как быть тогда со всеми слабыми указателями и продлением жизни?

4. Почему для реализации ассоциативных связей выбраны умные указатели, а не умные ссылки, например)?

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

Другими словами можно написать так

Код
C++ (Qt)
void foo ( const Value & value );
 

или так

Код
C++ (Qt)
void foo ( Value const * const value );
 

Оба варианта будут работать, но первый выглядит на мой взгляд лучше.
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #6 : Июль 03, 2018, 12:35 »

Вопросов очень много). Пока по крупному

1. Предполагается альтернативная реализация указателям std или это принципиально новый набор умных указателей?

В идеале хотелось бы получить интеграцию с указателями std, чтобы, во-первых, изобретать велосипеды по минимуму, во-вторых была возможность взаимодействовать с кодом, ориентированным на std. Но получается так, что даже если сделать реализацию UPL на основе std::shared_ptr/weak_ptr, то совместимы будут только std::shared_ptr и upl::shared (возможно их взаимное преобразование друг в друга). std::unique_ptr и std::weak_ptr можно только в одну сторону перевести - в upl::unique, upl::weak, обратно не получится (хотя в случае upl::unique -> std::unique_ptr есть варианты, но довольно зыбкие). Так что, в зависимости от реализации UPL, это может быть частичная интеграция с указателями std, а может быть их альтернатива.

Какие преимущества? Почему для уникального владения нельзя взять и использовать std::shared_ptr, пусть даже контроль уникального использования ложится на программиста.
Можно же реализовать уникальное владение через using unique = shared_ptr; using weak = weak_ptr; using unified = shared_ptr;

Для уникального владения можно взять и использовать std::shared_ptr (я так понял, что многие так и делают). Но моё личное мнение такое, что это то же самое, как предложить не пользоваться ключевыми словами const, explicit, protected, private, delete и т п. Писать программы станет проще, не будет надоедливых ошибок компилятора, можно меньше ломать голову const-или-не-const. Станет ли программа от этого лучше? Сомневаюсь. Ещё такой момент: типов ассоциативных связей всего три. Три, Карл!!! Улыбающийся И тут говорят: выкидывайте из модели все композиции, потому что на великом и могучем С++ нормальное(потокобезопасное) уникальное владение сделать нельзя/трудно/не охота/а зачем, и мы это на std::shared_ptr будем делать. Для меня это нездоровая ситуация.

Не возникнет ли трудностей у программиста в выборе "подходящих" указателей из большого числа вариантов?

Программисты разные бывают, кто не хочет соблюдать const correctness, тот и о типе владения не будет особо задумываться, влепит std::shared_ptr  и всё. Выбор подходящих указателей нужен тем, кто хочет иметь полный набор возможностей, кто пишет классы по модели UML, и хочет защитить их от неправильного использования. Например, как с помощью explicit защищают от случайного преобразования типов.

2. В реализации используется куча счетчиков (куда уж тет без них))). В параллельном доступе std::unique_ptr будет эффективнее, чем указатели со счетчиками, а raw pointer еще чуть эффективнее (зависит от компилятора).

Так ли уж нужно жертвовать производительностью?

Если в программе получается организовать связи между объектами и доступ к ним с помощью std::unique_ptr и raw pointers, и исключены появления dangling pointers, то так и следует делать. Но в более-менее сложной программе не факт, что получится соблюдать целостность связей проще и эффективнее, чем подсчётом ссылок. upl::unique не является заменой std::unique_ptr, у них одинаковая семантика владения, но немного разное поведение при уничтожении объекта. UPL ориентирована на многопоточное использование, где объекты могут внезапно умирать.

Или предполагается различная реализация - со счетчиками/без счетчиков, атомарных/неатомарных и др., со всяческими сочетаниями этих свойств.
В этом случае комбинаторное сочетание свойств огромно и более того нецелесообразно (есть альтернативный подход).

Ситуация с реализациями такая: задумка UPL в том, чтобы сделать работу с указателями более упорядоченной и систематизированной. Можно провести аналогию со стандартными контейнерами. Есть концепты контейнеров, есть стандартные алгоритмы для работы с ними, и есть конкретные реализации контейнеров. В UPL я попытался определить типы владений и составить набор концептов указателей. Эти концепты можно использовать в обобщённых алгоритмах, чтобы не было привязок к каким-то конкретным типам указателей (std::shared_ptr, upl::shared). Соответственно, реализаций указателей может быть несколько, равно как можно написать свою версию std::vector, например. Будет ли кто писать свои реализации? Маловероятно ). Но такая возможность есть. В самой же UPL будет какая-то реализация, которая подойдёт для подавляющего большинства случаев. Скорей всего это будет реализация на основе std::shared_ptr/weak_ptr.

3. В примере используется тэг in_place. Однако экземпляр объекта string формируется в куче.
Не предполагает ли in_place формирование экземпляра непосредственно на стеке? И как быть тогда со всеми слабыми указателями и продлением жизни?

Тег std::in_place используется для "пробы пера": насколько удобно пользоваться такой формой создания/инициализации объекта, в отличие от new SomeObject(), make_unique<SomeObject>(),  make_shared<SomeObject>(). Обозначает: создание объекта в этом месте (программы, а не стек/куча) средствами самого класса. Для объекта на стеке свои правила времени жизни, там ссылок достаточно, вряд ли надо делать указатели настолько универсальными.

4. Почему для реализации ассоциативных связей выбраны умные указатели, а не умные ссылки, например)?

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

Другими словами можно написать так

Код
C++ (Qt)
void foo ( const Value & value );
 

или так

Код
C++ (Qt)
void foo ( Value const * const value );
 

Оба варианта будут работать, но первый выглядит на мой взгляд лучше.

Вообще, UPL - это побочный продукт более крупного проекта CppUml ). Просто получилось так, что небольшую часть можно выделить для стороннего использования, без зависимостей и заумных терминов UML. Для тех, кому привычнее семантика указателей и не нужно ничего лишнего. Реализацию указателей UPL можно отдельно протестировать и обкатать. И эту реализацию можно использовать в токенах UML, которые больше похожи на value-семантику. И в CppUml можно больше вводить терминов и сущностей, для тех кому это действительно надо. Но это уже другая история Улыбающийся.
Записан

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

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Июль 03, 2018, 13:09 »

Какие преимущества? Почему для уникального владения нельзя взять и использовать std::shared_ptr, пусть даже контроль уникального использования ложится на программиста.
Можно, но хреновато выходит - совершенно теряется смысл shared, то ли он действительно шарится, то ли чтобы на него навесить weak, то ли просто "сам удалится". 

А как насчет интрузивного использования? Вот у меня масса древнего кода с "голыми" указателями. Бездумно делать их вумными (теми или иными) - ну во-первых очень непросто при больших объемах кода, а главное - я совершенно не уверен что это правильно и решит все проблемы, вероятно наоборот, создаст новые. Однако проверить валидность указателя очень хотелось бы. Интересует простейшая ситуация: указатель, напр член класса, не отвечает ни за создание ни за удаление, он просто "использует" объект на который указатель указывает - и все


Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #8 : Июль 03, 2018, 13:55 »

А как насчет интрузивного использования? Вот у меня масса древнего кода с "голыми" указателями. Бездумно делать их вумными (теми или иными) - ну во-первых очень непросто при больших объемах кода, а главное - я совершенно не уверен что это правильно и решит все проблемы, вероятно наоборот, создаст новые. Однако проверить валидность указателя очень хотелось бы.

Здесь та самая проблема: Улыбающийся
Не возникнет ли трудностей у программиста в выборе "подходящих" указателей из большого числа вариантов?

Когда были только "голые" указатели, то выбора не было и думать особо не надо было. О владении вспоминали только когда память текла, и решали, кто таки должен удалять объект. И как это обозначить. При этом, под тот код можно было построить UML модель, хоть разработчик классов об этом и не подозревал Улыбающийся. С появлением умных указателей в С++11 уже появился выбор. Хотя, похоже, он сводился к shared_ptr или не shared_ptr?

С древним кодом сложно. Сначала надо выявлять, кто владелец, а кто просто посмотреть зашёл. Затем, по-хорошему, тип владения (а по-простому - std::shared_ptr). Если есть вероятность появления "висячих" указателей, и необходимо иметь возможность проверить валидность указателя, то без накладных расходов тут вряд ли обойдётся. В этом отношении std::unique_ptr вряд ли что-то адекватное может предложить. При использовании умных указателей изменится способ доступа к объектам. В частности для std::weak_ptr надо lock() делать. Так что работы много, и не факт, что всё хорошо закончится.

Интересует простейшая ситуация: указатель, напр член класса, не отвечает ни за создание ни за удаление, он просто "использует" объект на который указатель указывает - и все.

В этом случае стандарт С++ может предложить std::observer_ptr. Но года через 2, в С++20 Улыбающийся. Хотя, похоже, это простейшая обёртка над raw pointer и проверить "живучесть" объекта через него вряд ли получится. Можно написать свой.

Ещё ведутся работы над Profiles с Bounds safety и Lifetime safety, наверное что-то похожее на то, что в Rust есть. Но когда это будет нормально сделано (и будет ли сделано вообще) - не известно.

Так что спасение утопающих - дело рук самих утопающих Улыбающийся. И использовать стороннюю библиотеку, или обходиться стандартными инструментами - это, как обычно, на усмотрение разработчиков проекта.
Записан

Пока сам не сделаешь...
ssoft
Программист
*****
Offline Offline

Сообщений: 579


Просмотр профиля
« Ответ #9 : Июль 04, 2018, 08:50 »

И все-таки)

Какова предполагаемая аудитория UPL? Что меня, как разработчика, должно подвигнуть к использованию этих указателей взамен стандартных или вместе с ними?

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

Для понимания всех тонкостей требуется достаточно высокий порог вхождения, как следствие - низкий интерес к данной тематике.
Мы обсуждали с ViTech подобные вопросы с начала 2016 года на внутренней площадке, но никто так и не присоединился к какому-либо серьезному обсуждению.

ViTech, там у нас много интересных материалов накопилось, их можно публиковать при необходимости, если нет возражений. Их обсуждение позволило мне, по крайне мере, восполнить многие пробелы в понимании, что есть объектно ориентированное моделирование). Спасибо.

У меня пока что сложилось мнение, что одним разработчикам вообще до фени, что в рамках объектно ориентированного программирования прежде всего существует объектная модель. При этом модель имеет все эти понятия, как - зависимости, атрибуты, ассоциациативные связи и многое др. И реализуемая модель должна быть непротиворечивой - не иметь всяких циклических зависимостей и т.п.

Я не говорю, что это "плохие" разработчики. Нет! Они пишут рабочий код, бывает даже отличного качества.) Но для них понятия из разряда none/shared/composite + "продление жизни" чужды и сложны для восприятия.

А для тех, кто понимает о чем идет речь, понятно как это реализуется посредством стандартных инструментов. Они хорошо знают, что "unique + продление жизни" реализуется посредством shared механизма, и что COW (copy on write) с уникальным владением также реализуется посредством shared механизма. Они не видят смысла подтягивать достаточно сложный в понимании инструмент, для решения задач, которые решаются и стандартными средствами. Пусть с помощью стандартных средств получается немного сложнее, зато понятно всем.

Нужно предложить нечто большее.

Я много раз спрашивал - почему указатели? Это достаточно низкоуровневые типы - инструментальные кирпичики языка программирования, с помощью которых можно реализовать все что угодно.
В UML нигде не вводится такого понятия, как Pointer. Слово "указатель" встречается в тексте всего 1 раз, в качестве примера технической реализации Link. Понятия объектной модели могут быть реализованы и совсем без указателей.
При использовании указателей в примере ниже в зависимости от типа Type потребуется модификация кода, но смысловая нагрузка m_attribute при этом не меняется.

Код
C++ (Qt)
struct MyStruct
{
   //using Type = std::string;
   //using Type = std::unique_prt< std::string >;
   Type m_attribute;
};
 
void foo ( const std::string & value );
 
...
foo( my_struct.m_attribute ); //foo( *my_struct.m_attribute );
...
 

Так почему же указатели? )))

На самом деле мне удалось заинтересовать людей данной темой, правда немного в другом контексте. Опишу его в следующих постах), а также актуализирую и перенесу проект на gitlab.
« Последнее редактирование: Июль 04, 2018, 08:52 от ssoft » Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #10 : Июль 04, 2018, 13:14 »

А для тех, кто понимает о чем идет речь, понятно как это реализуется посредством стандартных инструментов. Они хорошо знают, что "unique + продление жизни" реализуется посредством shared механизма, и что COW (copy on write) с уникальным владением также реализуется посредством shared механизма. Они не видят смысла подтягивать достаточно сложный в понимании инструмент, для решения задач, которые решаются и стандартными средствами. Пусть с помощью стандартных средств получается немного сложнее, зато понятно всем.

В сухом остатке скорей всего происходит то, что выделено жирным Улыбающийся. Никто же не поругает за то, что композицию из модели UML реализовали через shared_ptr. Если вообще кто-то задумается о какой-то там модели. Кто-то может задумается, но успокоит себя мыслью: "на самом деле там же совместное владение, в методе/функции происходит же захват владения, зачем тогда уникальным прикидываться?". Кто-то попробует использовать unique_ptr, споткнётся пару раз, плюнет, и перейдёт на shared_ptr.

С уникальным владением сложнее обращаться, потому что оно накладывает ограничения. Например я столкнулся с таким: хочется создать очередь команд из std::queue<std::function<void()>>, но нельзя сделать std::function<void()> = std::bind(&SomeObject::someMethod, object, std::move(unique_arg));, потому что std::function - CopyConstructible (пример TransferUnique). Приходится какой-то костыль лепить.

Ещё пример. Есть Guidelines Support Library , в ней делают not_null (Restricts a pointer or smart pointer to only hold non-null values). Но этот not_null не работает с unique_ptr, уже почти 3 года прошло Улыбающийся. И всем пофиг (почти).

Далее. Igors затронул интересную тему. С голыми указателями писать программу проще, не надо задумываться о типах владения и прочем (потом может боком вылезти, но кто об этом задумывается). В параметрах методов/функций тоже всё проще: обычно какой-нибудь SomeObject *. Если же делать по уму и использовать разные типы владения, то в параметрах будет больше разнообразия: unique_ptr, shared_ptr, weak_ptr (и SomeObject * тоже могут присутствовать). С таким многообразием параметров очень скоро программу писать станет не интересно, и везде останутся одни лишь shared_ptr. Я подозреваю, что сейчас так и происходит.

Где-то может использоваться внутреннее управление временем жизни объектов, и другое уже не прилепишь. Тот же QObject.

Из таких предпосылок я делаю вывод, что просто никто не хочет связываться с уникальным владением, раз можно обойтись совместным и никто за это не осудит. Ну а "модель UML", "типы владения" и пр. - это просто слова, которые важны каким-нибудь перфекционистам. Мне например ). А так как есть shared_ptr и его в общем-то хватает, то зачем ещё какие-то библиотеки таскать, тем более от какого-то неизвестного ViTech ). Вот если б Herb Sutter такое предложил, то да )).

Нужно предложить нечто большее.

Нечто большее может как раз лежать в области унифицированного доступа к объектам. Грубо говоря, вместо зоопарка uniqe, shared, weak в параметрах, можно использовать один upl::unified. Потому что вся эта шелуха с указателями/ссылками возникает из-за ограниченности языков программирования/аппаратных платформ, а на деле нужны сами объекты. Чтобы очистить объект от этой шелухи и добраться до него приходится много неинтересного кода писать. Вот сущности типа upl::unified и концепты (пример) могут в этом помочь. Тогда и уникальное владение может будет проще использовать и оно получит распространение.

Я много раз спрашивал - почему указатели? Это достаточно низкоуровневые типы - инструментальные кирпичики языка программирования, с помощью которых можно реализовать все что угодно.
В UML нигде не вводится такого понятия, как Pointer. Слово "указатель" встречается в тексте всего 1 раз, в качестве примера технической реализации Link. Понятия объектной модели могут быть реализованы и совсем без указателей.
При использовании указателей в примере ниже в зависимости от типа Type потребуется модификация кода, но смысловая нагрузка m_attribute при этом не меняется.

Так почему же указатели? )))

В UML про указатели не говорят, зато много говорят о токенах объектов. Если коротко, то токены и указатели по сути одно и то же (умные указатели, а не голые с арифметикой). Я бы даже сказал так: токен - это урезанный указатель. По крайней мере с технической точки зрения (реализации ) у меня так получается. И все сущности более высокого уровня (свойства, параметры, переменные) строятся на основе токенов. И доступ к объекту через токен, скорей всего, будет выглядеть как *token. Тут мог бы помочь operator dot, но я что-то не вижу подвижек по нему, такое ощущение, что он заглох.

Так что UPL про указатели для тех, кто привык к семантике умных указателей и не нужно ничего лишнего. На указателях тоже можно следовать моделям UML, просто это будет не так явно выглядеть. Получилось так, что эту часть можно выделить из более крупного проекта. Почему бы и нет.

Тем же, кому нужны более высокоуровневые сущности: свойства, параметры, переменные, которые являются коллекциями по трактовке UML, предлагается другая библиотека, со всеми этими наворотами. В основе которой я вижу токены, коллекции, и Ranges. Если появятся желающие это обсудить - я будут только рад Улыбающийся.
Записан

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

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Июль 09, 2018, 06:30 »

Ну вот, и полемика (обещавшая быть такой интересной) почему-то быстро затухла  Улыбающийся  Здесь, конечно, всегда есть проблема (или особенность) "общих" вещей - заниматься ими весьма приятно, а ответственности практически никакой, всегда можно сказать типа: "да мало ли что тебе это не нужно - а другому может очень нужно!".

Ладно, попробуем оживить. Вот напр типичное место старого кода которое давным-давно надо бы переделать
Код
C++ (Qt)
struct Light {
 ...
 Light * masterLight;  // "голый" указатель на мастера
 
// пример использования мастера
 float GetIntesity( void ) const
 {
   if (masterLight && this->inheritIntensity())    // возвращаем значение мастера
     return masterLight->GetIntensity();
   else
     retur this->m_intensity;  // или собственное
 }
};
В чем недостатки такой конструкции и как ее улучшить?
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 579


Просмотр профиля
« Ответ #12 : Июль 09, 2018, 08:52 »

Ну вот, и полемика (обещавшая быть такой интересной) почему-то быстро затухла  Улыбающийся 

Мы с ViTech, как всегда, перешли в приват))).

В чем недостатки такой конструкции и как ее улучшить?

Это с какой стороны посмотреть)). Имхо, прописной истины как таковой нет, зато куча ЕСЛИ.

С точки зрения программы на C++ здесь все нормально.

С точки зрения решаемой проблемы тоже может быть все в порядке - быстро, эффективно, нужно только отметить в документации, что непотокобезопасно.
Но об этом пусть другие думают, кто будет использовать))).

С точки зрения поддержки, фиксов и т.п. Нужно применить некоторые усилия, чтобы понять что есть что. Указатель сам по себе ничего не говорит о том, чем является и какими свойствами должен обладать.
Что это Light * Непонимающий? - значение, динамический массив, агрегация, композиция, какая предполагается мультипликативность и т.д. и т.п. Какие свойства присущи Light * masterLight ?
Чтобы выяснить это, требуется проанализировать сам код, а при поиске ошибок еще и найти места некорректного использования.

В этом могла бы помочь объектно-ориентированная модель, как абстракция более высокого уровня. Но для этого её нужно проектировать)). Или ... реализовать конструкции, которые явно отражают понятия модели в программе.
Сразу качественный скачок от кодирования к моделированию сложен (как от 2D перейти к 3D, или от 3D к 4D), поэтому UPL предлагает ряд более простых инструментов в виде дополнительных умных указателей.

Здесь я схитрю)), этот код не на UPL.

Код
C++ (Qt)
struct Light {
 ...
   using WeakMaster = AssociationEnd< Light, AggregationKind::None >;
   WeakMaster masterLight;  // полюс вида None ассоциативной связи агрегации c мастером
 
// пример использования мастера
   float GetIntesity( void ) const
   {
       // гарант доступа к мастеру на протяжении всего вызова метода
       AccessGuard<WeakMaster > master_access_guard = masterLight;
       if ( master_access_guard && this->inheritIntensity())    // возвращаем значение мастера
           return master_access_guard->GetIntensity();
       else
           return this->m_intensity;  // или собственное
   }
};
 

Из кода явно видно, что есть что. А наличие гаранта в методе позволяет обеспечить при необходимости "продление жизни" masterLight и/или однопоточный доступ к masterLight и/или ...
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #13 : Июль 09, 2018, 11:33 »

Ладно, попробуем оживить. Вот напр типичное место старого кода которое давным-давно надо бы переделать
...
В чем недостатки такой конструкции и как ее улучшить?

ssoft всё правильно написал. Такая конструкция вполне имеет право на существование. Со множеством ЕСЛИ Улыбающийся. Например, такая конструкция может легко и в одном потоке сломаться. Тот, кто удаляет masterLight, сначала должен удалить все связи с ним из других Light. По идее, это должен делать тот же, кто эти связи устанавливал. Если есть такие гарантии отсутствия "висячих" указателей, то можно и без умных указателей обойтись. Вопрос в том, что проще: такое ручное управление связями, или использование других средств определения "живучести" объекта? Чтобы можно было "на месте" узнать, жив объект или уже помер. Например, такая функциональность есть у QPointer. Но он только для QObject и не является потокобезопасным. А в многопоточности всё намного веселее.

Решить проблему "висячих" указателей можно с помощью умных указателей std. Чтобы совсем не ломать голову, можно вместо Light * masterLight использовать std::shared_ptr<Light> masterLight. Тогда код Light::GetIntesity останется таким же, там, похоже, и менять ничего не придётся. Только masterLight теперь будет жить так долго, пока существует хоть один Light, связанный с ним. Правильно это или нет, должно определяться в модели, на этапе проектирования. Там определяется характер связи MasterLight <-> Light. В терминологии UML нужно определить тип агрегации, т.е.  является ли MasterLight частью Light (Light - это "целое", а MasterLight - его "часть")? В терминологии владения аналогичный вопрос: является ли Light владельцем MasterLight? Скорей всего нет. Поэтому в терминах UML тип агрегации AggregationKind::None, а в терминах владения - это слабая (weak) ссылка. Поэтому, немного поломав голову, поле изменится на std::weak_ptr<Light> masterLight.

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

Но это всё лирика, теперь к практике ). ssoft написал пример, и он будет выглядеть так же, если Light * masterLight заменить на  std::weak_ptr<Light> masterLight или upl::weak<Light> masterLight. И если нет разницы, зачем все эти навороты, если хватает стандартных указателей? Проблема возникает с уникальным владением. Допустим, необходимо обеспечить, чтобы только один объект владел masterLight, тогда, по-хорошему, надо использовать std::unique_ptr<Light>, а поле Light::masterLight тогда какого типа будет? Голый указатель? От чего уходили, к тому и вернулись ). UPL как раз позволяет решить эту проблему связкой upl::unique <-> upl::weak.

Ещё один момент: тип параметров. Допустим, нужно "напечатать"  masterLight. Какая сигнатура будет у метода Light::printLight(), в случае, когда Light может храниться в Light *, std::unique/shared/weak_ptr<Light>? Да даже в случае std::shared/weak_ptr<Light>? Допустим: Light::printLight(std::shared<Light>). Тогда, если тип поля std::shared_ptr<Light> masterLight, то можно передать просто masterLight. В случае же std::weak_ptr<Light> masterLight нужно уже передавать или masterLight.lock() или std::shared_ptr<Light>{masterLight}. Т.е. в зависимости от типа поля код программы может изменяться. В UPL есть унифицированный указатель upl::unified, который может создаваться из любого другого типа, и метод Light::printLight(upl::unified<Light>) будет работать с любым типом поля (upl::unique/shared/weak).

Для любителей шаблонов есть концепты указателей, позволяющие гибко определять тип указателей и владения, и шаблонный upl::pointer, с помощью которого можно создать указатель, параметры (тип владения, кратность) которого указываются в параметрах шаблона.
Записан

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

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Июль 09, 2018, 14:11 »

ssoft всё правильно написал. Такая конструкция вполне имеет право на существование. Со множеством ЕСЛИ Улыбающийся. Например, ..
Ну особой радости она не доставляет. Простой случай - удаление мастера. Надо пробежаться по всем объектам типа Light и обнулить удаляемого мастера. Но перед этим надо ж сохранить undo - значит еще пробежаться. 

Аналитик/эксперт спросит: a cколько может быть объектов типа Light?  Ну сотни (тысяч пока не было). Значит и проблемы нет, проще = лучше. Не все так просто. Операция сама по себе приемлема по времени, но может попасть в другую с хорошей кратностью. Напр юзер уже не раз просил рисовать мастеров с др иконкой - и тут уже хз так ли безобидны такие пробежки. В общем, данные слишком примитивны, требуется много/больше кода чтобы эту простоту поддерживать.

Обойтись стандартными вумными указателями я могу хотя бы так
Код
C++ (Qt)
struct Light {
...
QSharedPointer<Light> mSharedPtr;  // приватный шаред на себя, создается в конструкторе с пустым Deleter
...
QWeakPointer<Light> GetWeakPtr( void );
};
Думаю что лепить такую городушку не стоит - слишком мал "выйгрышь". Да, теперь можно "не вычеркивать", но на undo все равно прописывать придется. Лучше смотрится простая организация (напоминает парент-чайлд), т.е.

- на объект можно ссылаться, он знает ссылающихся (напр хранит контейнер указателей)
- и наоборот,  объект хранит контейнер тех на которых он сам ссылается

В общем виде это кусок пожирнее чем просто "валиден"(указатель) и "продлить жизнь" (что кстати спорно). Но как к нему подступиться?
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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