Название: Unified Pointer Library Отправлено: ViTech от Июль 02, 2018, 10:44 Делаю такую штуку. Может ещё кому пригодится.
Unified Pointer Library (https://gitlab.com/UnifiedPointers/Library/UPL) (описание на русском (https://gitlab.com/UnifiedPointers/Library/UPL/tree/master/doc/ru)) - библиотека унифицированных указателей (UPL), содержит концепты и реализации умных указателей, которые предназначены для управления временем жизни объектов. Предоставляет указатели upl::unique и upl::shared для уникального и совместного владения объектами, слабые ссылки для них upl::weak, и добавляет унифицированный тип владения upl::unified. Публичный интерфейс унифицированных указателей схож с интерфейсом умных указателей стандартной библиотеки C++. Библиотека header-only, лицензия MIT. Ключевые особенности:
Отвечу на возникающие вопросы. Аргументированная критика приветствуется :). Название: Re: Unified Pointer Library Отправлено: vipet от Июль 02, 2018, 13:53 Отвечу на возникающие вопросы. в каких случаях и чем лучше stl? Название: Re: Unified Pointer Library Отправлено: ViTech от Июль 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, только для уникального владения. Если подробнее, то в библиотеке набор указателей, каким я бы хотел его видеть. С нормальным уникальным владением, с унифицированным доступом, с различной кратностью, с поддержкой концептов в шаблонах и прочим. Название: Re: Unified Pointer Library Отправлено: Пантер от Июль 02, 2018, 14:34 А что с производительностью? ;)
Название: Re: Unified Pointer Library Отправлено: ViTech от Июль 02, 2018, 14:44 А что с производительностью? ;) Это от реализации зависит :). Кстати, можно написать свою, под свои нужды. Текущая реализация наивная, и ещё до конца не доделанная, ибо не хочется велосипед изобретать. Она нужна для проверки концепции. Сейчас делаю реализацию с более узким спектром возможностей, но на готовых std::shared_ptr/std::weak_ptr. В ней производительность будет такой же, как у std::shared_ptr. Название: Re: Unified Pointer Library Отправлено: ssoft от Июль 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. Почему для реализации ассоциативных связей выбраны умные указатели, а не умные ссылки, например)? Удобно ли использовать повсеместно указатели? Внешний вид указателя ничего не говорит о том, что это - атрибут или ассоциативная связь. В результате - одинаковая реализация, но разные свойства и смысловая нагрузка. Могу попробовать показать, что концепция ссылок более "дружелюбная" нежели указателей. Другими словами можно написать так Код
или так Код
Оба варианта будут работать, но первый выглядит на мой взгляд лучше. Название: Re: Unified Pointer Library Отправлено: ViTech от Июль 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 в том, чтобы сделать работу с указателями более упорядоченной и систематизированной. Можно провести аналогию со стандартными контейнерами. Есть концепты контейнеров (https://en.cppreference.com/w/cpp/named_req/Container), есть стандартные алгоритмы (https://en.cppreference.com/w/cpp/algorithm) для работы с ними, и есть конкретные реализации контейнеров (https://en.cppreference.com/w/cpp/container). В UPL я попытался определить типы владений (https://gitlab.com/UnifiedPointers/Library/UPL/blob/master/doc/ru/TheoreticalBasis.md#%D0%B2%D0%BB%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5) и составить набор концептов указателей (https://gitlab.com/UnifiedPointers/Library/UPL/blob/master/doc/ru/Reference.md#%D0%BA%D0%BE%D0%BD%D1%86%D0%B5%D0%BF%D1%82%D1%8B-%D1%83%D0%BA%D0%B0%D0%B7%D0%B0%D1%82%D0%B5%D0%BB%D0%B5%D0%B9). Эти концепты можно использовать в обобщённых алгоритмах, чтобы не было привязок к каким-то конкретным типам указателей (std::shared_ptr, upl::shared). Соответственно, реализаций указателей может быть несколько, равно как можно написать свою версию std::vector, например. Будет ли кто писать свои реализации? Маловероятно ). Но такая возможность есть. В самой же UPL будет какая-то реализация, которая подойдёт для подавляющего большинства случаев. Скорей всего это будет реализация на основе std::shared_ptr/weak_ptr. 3. В примере используется тэг in_place. Однако экземпляр объекта string формируется в куче. Не предполагает ли in_place формирование экземпляра непосредственно на стеке? И как быть тогда со всеми слабыми указателями и продлением жизни? Тег std::in_place (https://en.cppreference.com/w/cpp/utility/in_place) используется для "пробы пера": насколько удобно пользоваться такой формой создания/инициализации объекта, в отличие от new SomeObject(), make_unique<SomeObject>(), make_shared<SomeObject>(). Обозначает: создание объекта в этом месте (программы, а не стек/куча) средствами самого класса. Для объекта на стеке свои правила времени жизни, там ссылок достаточно, вряд ли надо делать указатели настолько универсальными. 4. Почему для реализации ассоциативных связей выбраны умные указатели, а не умные ссылки, например)? Удобно ли использовать повсеместно указатели? Внешний вид указателя ничего не говорит о том, что это - атрибут или ассоциативная связь. В результате - одинаковая реализация, но разные свойства и смысловая нагрузка. Могу попробовать показать, что концепция ссылок более "дружелюбная" нежели указателей. Другими словами можно написать так Код
или так Код
Оба варианта будут работать, но первый выглядит на мой взгляд лучше. Вообще, UPL - это побочный продукт более крупного проекта CppUml (https://gitlab.com/CppUml) ). Просто получилось так, что небольшую часть можно выделить для стороннего использования, без зависимостей и заумных терминов UML. Для тех, кому привычнее семантика указателей и не нужно ничего лишнего. Реализацию указателей UPL можно отдельно протестировать и обкатать. И эту реализацию можно использовать в токенах UML, которые больше похожи на value-семантику. И в CppUml можно больше вводить терминов и сущностей, для тех кому это действительно надо. Но это уже другая история :). Название: Re: Unified Pointer Library Отправлено: Igors от Июль 03, 2018, 13:09 Какие преимущества? Почему для уникального владения нельзя взять и использовать std::shared_ptr, пусть даже контроль уникального использования ложится на программиста. Можно, но хреновато выходит - совершенно теряется смысл shared, то ли он действительно шарится, то ли чтобы на него навесить weak, то ли просто "сам удалится". А как насчет интрузивного использования? Вот у меня масса древнего кода с "голыми" указателями. Бездумно делать их вумными (теми или иными) - ну во-первых очень непросто при больших объемах кода, а главное - я совершенно не уверен что это правильно и решит все проблемы, вероятно наоборот, создаст новые. Однако проверить валидность указателя очень хотелось бы. Интересует простейшая ситуация: указатель, напр член класса, не отвечает ни за создание ни за удаление, он просто "использует" объект на который указатель указывает - и все Название: Re: Unified Pointer Library Отправлено: ViTech от Июль 03, 2018, 13:55 А как насчет интрузивного использования? Вот у меня масса древнего кода с "голыми" указателями. Бездумно делать их вумными (теми или иными) - ну во-первых очень непросто при больших объемах кода, а главное - я совершенно не уверен что это правильно и решит все проблемы, вероятно наоборот, создаст новые. Однако проверить валидность указателя очень хотелось бы. Здесь та самая проблема: :) Не возникнет ли трудностей у программиста в выборе "подходящих" указателей из большого числа вариантов? Когда были только "голые" указатели, то выбора не было и думать особо не надо было. О владении вспоминали только когда память текла, и решали, кто таки должен удалять объект. И как это обозначить. При этом, под тот код можно было построить UML модель, хоть разработчик классов об этом и не подозревал :). С появлением умных указателей в С++11 уже появился выбор. Хотя, похоже, он сводился к shared_ptr или не shared_ptr? С древним кодом сложно. Сначала надо выявлять, кто владелец, а кто просто посмотреть зашёл. Затем, по-хорошему, тип владения (а по-простому - std::shared_ptr). Если есть вероятность появления "висячих" указателей, и необходимо иметь возможность проверить валидность указателя, то без накладных расходов тут вряд ли обойдётся. В этом отношении std::unique_ptr вряд ли что-то адекватное может предложить. При использовании умных указателей изменится способ доступа к объектам. В частности для std::weak_ptr надо lock() делать. Так что работы много, и не факт, что всё хорошо закончится. Интересует простейшая ситуация: указатель, напр член класса, не отвечает ни за создание ни за удаление, он просто "использует" объект на который указатель указывает - и все. В этом случае стандарт С++ может предложить std::observer_ptr (https://en.cppreference.com/w/cpp/experimental/observer_ptr). Но года через 2, в С++20 :). Хотя, похоже, это простейшая обёртка над raw pointer и проверить "живучесть" объекта через него вряд ли получится. Можно написать свой. Ещё ведутся работы над Profiles (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#pro-profiles) с Bounds safety и Lifetime safety, наверное что-то похожее на то, что в Rust есть. Но когда это будет нормально сделано (и будет ли сделано вообще) - не известно. Так что спасение утопающих - дело рук самих утопающих :). И использовать стороннюю библиотеку, или обходиться стандартными инструментами - это, как обычно, на усмотрение разработчиков проекта. Название: Re: Unified Pointer Library Отправлено: ssoft от Июль 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 при этом не меняется. Код
Так почему же указатели? ))) На самом деле мне удалось заинтересовать людей данной темой, правда немного в другом контексте. Опишу его в следующих постах), а также актуализирую и перенесу проект на gitlab. Название: Re: Unified Pointer Library Отправлено: ViTech от Июль 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 (https://gitlab.com/UnifiedPointers/Example/TransferUnique/blob/master/src/main.cpp#L158)). Приходится какой-то костыль лепить. Ещё пример. Есть Guidelines Support Library (https://github.com/Microsoft/GSL), в ней делают not_null (https://github.com/Microsoft/GSL/blob/master/include/gsl/pointers#L68) (Restricts a pointer or smart pointer to only hold non-null values). Но этот not_null не работает с unique_ptr (https://github.com/Microsoft/GSL/issues/89), уже почти 3 года прошло :). И всем пофиг (почти (https://github.com/Microsoft/GSL/pull/675)). Далее. 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 и концепты (пример (https://gitlab.com/UnifiedPointers/Example/ConceptPrinter/blob/master/src/main.cpp)) могут в этом помочь. Тогда и уникальное владение может будет проще использовать и оно получит распространение. Я много раз спрашивал - почему указатели? Это достаточно низкоуровневые типы - инструментальные кирпичики языка программирования, с помощью которых можно реализовать все что угодно. В UML нигде не вводится такого понятия, как Pointer. Слово "указатель" встречается в тексте всего 1 раз, в качестве примера технической реализации Link. Понятия объектной модели могут быть реализованы и совсем без указателей. При использовании указателей в примере ниже в зависимости от типа Type потребуется модификация кода, но смысловая нагрузка m_attribute при этом не меняется. Так почему же указатели? ))) В UML про указатели не говорят, зато много говорят о токенах объектов. Если коротко, то токены и указатели по сути одно и то же (умные указатели, а не голые с арифметикой). Я бы даже сказал так: токен - это урезанный указатель. По крайней мере с технической точки зрения (реализации ) у меня так получается. И все сущности более высокого уровня (свойства, параметры, переменные) строятся на основе токенов. И доступ к объекту через токен, скорей всего, будет выглядеть как *token. Тут мог бы помочь operator dot, но я что-то не вижу подвижек по нему, такое ощущение, что он заглох. Так что UPL про указатели для тех, кто привык к семантике умных указателей и не нужно ничего лишнего. На указателях тоже можно следовать моделям UML, просто это будет не так явно выглядеть. Получилось так, что эту часть можно выделить из более крупного проекта. Почему бы и нет. Тем же, кому нужны более высокоуровневые сущности: свойства, параметры, переменные, которые являются коллекциями по трактовке UML, предлагается другая библиотека, со всеми этими наворотами. В основе которой я вижу токены, коллекции, и Ranges (https://github.com/ericniebler/range-v3). Если появятся желающие это обсудить - я будут только рад :). Название: Re: Unified Pointer Library Отправлено: Igors от Июль 09, 2018, 06:30 Ну вот, и полемика (обещавшая быть такой интересной) почему-то быстро затухла :) Здесь, конечно, всегда есть проблема (или особенность) "общих" вещей - заниматься ими весьма приятно, а ответственности практически никакой, всегда можно сказать типа: "да мало ли что тебе это не нужно - а другому может очень нужно!".
Ладно, попробуем оживить. Вот напр типичное место старого кода которое давным-давно надо бы переделать Код В чем недостатки такой конструкции и как ее улучшить? Название: Re: Unified Pointer Library Отправлено: ssoft от Июль 09, 2018, 08:52 Ну вот, и полемика (обещавшая быть такой интересной) почему-то быстро затухла :) Мы с ViTech, как всегда, перешли в приват))). В чем недостатки такой конструкции и как ее улучшить? Это с какой стороны посмотреть)). Имхо, прописной истины как таковой нет, зато куча ЕСЛИ. С точки зрения программы на C++ здесь все нормально. С точки зрения решаемой проблемы тоже может быть все в порядке - быстро, эффективно, нужно только отметить в документации, что непотокобезопасно. Но об этом пусть другие думают, кто будет использовать))). С точки зрения поддержки, фиксов и т.п. Нужно применить некоторые усилия, чтобы понять что есть что. Указатель сам по себе ничего не говорит о том, чем является и какими свойствами должен обладать. Что это Light * ???? - значение, динамический массив, агрегация, композиция, какая предполагается мультипликативность и т.д. и т.п. Какие свойства присущи Light * masterLight ? Чтобы выяснить это, требуется проанализировать сам код, а при поиске ошибок еще и найти места некорректного использования. В этом могла бы помочь объектно-ориентированная модель, как абстракция более высокого уровня. Но для этого её нужно проектировать)). Или ... реализовать конструкции, которые явно отражают понятия модели в программе. Сразу качественный скачок от кодирования к моделированию сложен (как от 2D перейти к 3D, или от 3D к 4D), поэтому UPL предлагает ряд более простых инструментов в виде дополнительных умных указателей. Здесь я схитрю)), этот код не на UPL. Код
Из кода явно видно, что есть что. А наличие гаранта в методе позволяет обеспечить при необходимости "продление жизни" masterLight и/или однопоточный доступ к masterLight и/или ... Название: Re: Unified Pointer Library Отправлено: ViTech от Июль 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, с помощью которого можно создать указатель, параметры (тип владения, кратность) которого указываются в параметрах шаблона. Название: Re: Unified Pointer Library Отправлено: Igors от Июль 09, 2018, 14:11 ssoft всё правильно написал. Такая конструкция вполне имеет право на существование. Со множеством ЕСЛИ :). Например, .. Ну особой радости она не доставляет. Простой случай - удаление мастера. Надо пробежаться по всем объектам типа Light и обнулить удаляемого мастера. Но перед этим надо ж сохранить undo - значит еще пробежаться. Аналитик/эксперт спросит: a cколько может быть объектов типа Light? Ну сотни (тысяч пока не было). Значит и проблемы нет, проще = лучше. Не все так просто. Операция сама по себе приемлема по времени, но может попасть в другую с хорошей кратностью. Напр юзер уже не раз просил рисовать мастеров с др иконкой - и тут уже хз так ли безобидны такие пробежки. В общем, данные слишком примитивны, требуется много/больше кода чтобы эту простоту поддерживать. Обойтись стандартными вумными указателями я могу хотя бы так Код Думаю что лепить такую городушку не стоит - слишком мал "выйгрышь". Да, теперь можно "не вычеркивать", но на undo все равно прописывать придется. Лучше смотрится простая организация (напоминает парент-чайлд), т.е. - на объект можно ссылаться, он знает ссылающихся (напр хранит контейнер указателей) - и наоборот, объект хранит контейнер тех на которых он сам ссылается В общем виде это кусок пожирнее чем просто "валиден"(указатель) и "продлить жизнь" (что кстати спорно). Но как к нему подступиться? Название: Re: Unified Pointer Library Отправлено: ViTech от Июль 09, 2018, 18:53 Думаю что лепить такую городушку не стоит - слишком мал "выйгрышь". Да, теперь можно "не вычеркивать", но на undo все равно прописывать придется. Лучше смотрится простая организация (напоминает парент-чайлд), т.е. - на объект можно ссылаться, он знает ссылающихся (напр хранит контейнер указателей) - и наоборот, объект хранит контейнер тех на которых он сам ссылается В общем виде это кусок пожирнее чем просто "валиден"(указатель) и "продлить жизнь" (что кстати спорно). Работа программиста в том и заключается, чтобы определять, когда и какую городушку лепить :). Вариантов мильён, зависят от контекста задачи, где и как она должна выполняться, насколько оптимально. Если в одном потоке, то одно решение, если в нескольких, то уже совсем другое может быть. UPL - это не универсальное средство на все случаи жизни, лишь один из инструментов. Основная область применения: написание кода по модели UML, выполнение в многопоточной среде. Кстати, если писать программы в стиле как ssoft предлагает, то модель сразу с кодом появляется ). И в исходниках можно сразу модель увидеть, и не гадать, что и как друг с другом связано. Но это и с UPL можно делать, хоть и не так явно. Но как к нему подступиться? Начать с проектирования, модель составить. Она в том коде уже есть, надо её только на свет вынести :). Определить, кто чем владеет, а кто просто ссылается. Выполняться это будет в одном потоке или в нескольких. Потом выбрать оптимальный инструмент. Закодировать. Профит! :) Название: Re: Unified Pointer Library Отправлено: ssoft от Июль 10, 2018, 07:56 Начать с проектирования, модель составить. Согласен). Необходимо, прежде всего, определиться, что есть Light и какие отношения между Light и его Light::masterLight.
Ответы на эти вопросы позволят сформировать модель отношений и выбрать способ реализации. Название: Re: Unified Pointer Library Отправлено: Igors от Июль 10, 2018, 10:18 А я нет :) Начинаются бесконечные "выяснения подробностей", после которых ... наступает тишина. Не стоит даже начинать. Общая вещь - значит общая, как ее прикрутить к моему конкретному случаю - мои проблемы, разберусь, было бы что прикручивать. Вот что хотелось бы.
Основной минус любой умности указателей - маломощность. Ну да, можно проверить "не сдох ли" и продлить (жалкую) жизнь, но это по сути и все. Простейший приведенный пример показывает - этого мало, действительно умный указатель должен знать "всех кто на него ссылается", а не так себе "счетчик" (впрочем в Qt и счетчика не дают). Устанавливаем отношения между 2 объектами - значит устанавливаем, а то одному что-то установили (дав на руки weak), а другому вообще ничего. И приходится наращивать ф-ционал великом - а тогда чего умничали с указателем? Такое легко сделать "как-нибудь" (напр навязав базовый god класс(ы)) - но вот по-настоящему хорошей реализации я пока не видел. А делать это приходится часто, практически всегда. Вот Вам, разработчикам общих вещей, и карты в руки. Название: Re: Unified Pointer Library Отправлено: ssoft от Июль 10, 2018, 10:52 А я нет :) Начинаются бесконечные "выяснения подробностей", после которых ... наступает тишина. Не стоит даже начинать. Общая вещь - значит общая, как ее прикрутить к моему конкретному случаю - мои проблемы, разберусь, было бы что прикручивать. Вот что хотелось бы. ИМХО, без модели даже проект не начну. Это как без чертежей и технологии сборки пытаться создать сложную конструкцию или механизм. Вместо слаженного продукта получается набор костылей. Хотя бы набросок модели должен быть. Основной минус любой умности указателей - маломощность. Ну да, можно проверить "не сдох ли" и продлить (жалкую) жизнь, но это по сути и все. Существует большое количество умных указателей, каждый из которых обладает определенными особенностями - полезными свойствами и накладными расходами. Зная эти характеристики можно подобрать нужный тип указателя исходя их особенностей решаемой задачи. Используемый инструмент-библиотека, ИМХО, должен обеспечивать легкий способ изменения этих характеристик в случае, если особенности задачи меняются. К полезным свойствам, например, относятся "автоматическое удаление", "реализация отношений агрегации", "продление жизни", "потокобезопасность" и т.д. Разные виды указателей реализуют описанные свойства по-разному. Простейший приведенный пример показывает - этого мало, действительно умный указатель должен знать "всех кто на него ссылается", а не так себе "счетчик" (впрочем в Qt и счетчика не дают). Такой вариант может существовать, однако на практике очень мало задач, где накладные расходы не перевешивают сложность реализации и полезность свойства такого указателя. Согласованность счетчика можно обеспечить атомарными операциями, а для списка еще тот огород нужно городить. Там где действительно нужно знать "всех кто на него ссылается" используют паттерн Observer. Название: Re: Unified Pointer Library Отправлено: ViTech от Июль 11, 2018, 11:04 Основной минус любой умности указателей - маломощность. Ну да, можно проверить "не сдох ли" и продлить (жалкую) жизнь, но это по сути и все. Простейший приведенный пример показывает - этого мало, действительно умный указатель должен знать "всех кто на него ссылается", а не так себе "счетчик" (впрочем в Qt и счетчика не дают). Устанавливаем отношения между 2 объектами - значит устанавливаем, а то одному что-то установили (дав на руки weak), а другому вообще ничего. И приходится наращивать ф-ционал великом - а тогда чего умничали с указателем? Такой функционал не всегда нужен. Знать "всех кто на него ссылается" - это дополнительная функциональность, а не основная (указателя/токена/ассоциативной связи). Одна из целей UPL - классифицировать типы указателей и унифицировать работу с ними. Чтобы когда сделают нужный Вам super_shared_ptr, не приходилось опять пол проекта переписывать, чтобы заменить shared_ptr на его более умную версию. Потому что методам типа Light::printLight(SmartPtr<Light> light) вообще без разницы должно быть, в каком владении находится light и какая реализация у его умного указателя. Такое легко сделать "как-нибудь" (напр навязав базовый god класс(ы)) - но вот по-настоящему хорошей реализации я пока не видел. А делать это приходится часто, практически всегда. Вот Вам, разработчикам общих вещей, и карты в руки. Если Вам приходится делать это часто, практически всегда, вот и сделали бы одни раз хорошую общую реализацию. Сами бы пользовались, да и с другими могли бы поделиться ;). Название: Re: Unified Pointer Library Отправлено: Igors от Июль 11, 2018, 13:12 ИМХО, без модели даже проект не начну. Это как без чертежей и технологии сборки пытаться .. А я его и не начинал :) Это старый (но работающий) код который нужно улучшить. Попробуем прямолинейно. Все проблемы возникают из-за того что неизвестно кто ссылается на мастера, их всякий раз нужно искать "по всему списку объектов" - коряво. Так добавим нужные данные и методы, это выглядит примерно такКод Реализация очевидна. Ну и в деструкторе нужно подсуетиться. И в копировании (m_slaves копироваться не должен). По-моему общность здесь бросается в глаза. Тем более что с пяток таких мест я помню навскидку (masterMaterial и.т.п) Если Вам приходится делать это часто, практически всегда, вот и сделали бы одни раз хорошую общую реализацию. Сами бы пользовались, да и с другими могли бы поделиться ;). Ага, "ну вот и делай свой велик" :) Но скажите, почему я со стандартных средств ровным счетом ничего не имею? Разве велики - это так уж хорошо/правильно? Неужели у меня какая-то причудливая, редкая задача? Да нет же, самая рядовая/типовая. И какой тогда смысл в "умных" указателях если ума у них - шо у курчонка, а толку с гулькин "нос"? Название: Re: Unified Pointer Library Отправлено: ViTech от Июль 11, 2018, 13:55 ИМХО, без модели даже проект не начну. Это как без чертежей и технологии сборки пытаться .. А я его и не начинал :) Это старый (но работающий) код который нужно улучшить.Вот и скажите "спасибо" тем, кто начинал этот проект без модели :). При желании можно было наделать простейших обёрток типа std::observer_ptr, чтобы хотя бы отличать "владеющие" указатели от "ссылающихся". Но это всё лирика, если бы да кабы... Ага, "ну вот и делай свой велик" :) Но скажите, почему я со стандартных средств ровным счетом ничего не имею? Разве велики - это так уж хорошо/правильно? Неужели у меня какая-то причудливая, редкая задача? Да нет же, самая рядовая/типовая. И какой тогда смысл в "умных" указателях если ума у них - шо у курчонка, а толку с гулькин "нос"? Задача может типовая, но решений может быть множество под разные требования. Кому-то достаточно реализации на голых указателях в одном потоке, другим на умных указателях, чтоб про ручное удаление объектов забыть, третьим подавай потокобезопасность, четвёртым события, что добавилась/удалилась ссылка и т.д. Такие общие решения "паттернами" называют, и каждый реализацию сам делает, под свои потребности. Или готовые в сторонних библиотеках ищет. Велики - это плохо, но всё подряд в стандартную библиотеку тащить, может быть ещё хуже. Значит ещё не накопилась критическая масса желающих такого функционала и не выработано наиболее общее решение, которое устроит большинство, раз такой штуки нет в стандартной библиотеке. Название: Re: Unified Pointer Library Отправлено: ViTech от Июль 16, 2018, 12:04 Заменил реализацию указателей UPL на обёртки над указателями std. Теперь upl::unique/shared/unified - это обёртки над std::shared_ptr, а upl::weak - обёртка над std::weak_ptr. При этом несколько уменьшился диапазон возможностей указателей, но появилась более тесная интеграция с указателями std. upl::unique можно создавать из std::unique_ptr, upl::shared/unified из std::shared_ptr, upl::weak из std::weak_ptr. В обратную сторону работает только получение std::shared_ptr из upl::shared. Так что если уникальное владение делать через std::shared_ptr, то, при замене его на upl::unique, накладные расходы в run-time останутся такими же. Может несколько увеличиться время компиляции (не проверял ещё), но это может компенсироваться более понятными ошибками компиляции, которые я добавил для указателей upl. Также стоит обратить внимание на более удобный и унифицированный доступ к значению указателя через upl::access().
В качестве примера можно рассмотреть взаимодействие объектов трёх типов: Car, Engine и Monitor. Car уникально владеет одним Engine, несколько Monitor могут наблюдать за одним Engine, Car и Monitor независимы друг от друга (UML диаграмма классов в рисунке вложения). Эмулируется работа объектов в разных потоках. Составляется очередь команд, которая тоже выполняется в отдельном потоке. Основной код примера: Код
Проект примера с полным кодом и библиотекой UPL находится во вложении UplCar.zip. Более полные материалы располагаются в репозитории Car (https://gitlab.com/UnifiedPointers/Example/Car). Можете поэкспериментировать с примером, попробовать "поломать" модель (установить один двигатель в два разных авто или ещё что :)), написать те же классы, только с использованием стандартных указателей (с соблюдением требований (https://gitlab.com/UnifiedPointers/Example/Car/blob/master/doc/ru/Readme.md#%D1%82%D1%80%D0%B5%D0%B1%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F-%D0%BA-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B5) к программе) и сравнить, какой способ лучше. В качестве более "продвинутого" использования UPL (в частности концептов), можете посмотреть на тесты (https://gitlab.com/CppUml/Test/Detail/Owner). Например, в одном этом методе (https://gitlab.com/CppUml/Test/Detail/Owner/blob/master/include/Test/Owner/Construction/Move.h#L48) выполняется создание указателей с объектами разного типа, разным типом владения, разной кратностью и разной реализацией указателей. А здесь (https://gitlab.com/CppUml/Test/Detail/Owner/blob/master/include/Test/Owner/Construction/Move.h#L486) вызываются функции для проверки указателей по заданным правилам, согласно типу владения и реализации. |