Здравствуйте, Мирный герцог, Вы писали:
МГ>Здравствуйте, C0x, Вы писали:
C0x>>Привет,
МГ>привет, подобная машинерия лишь усложняет код на ровном месте, и то что автору кажется "удобным" становится головной болью для мэйнтейнеров кода. Как ad-hoc решение используется тайпдеф а std::unique_ptr с кастомным делетером, о чём уже упомянуто выше, или нужно не полениться и написать таки враппер для конкретного типа хендла.
То есть тебе не кажется странным использовать объект с именем unique_ptr для совершенно другой цели — выполнить логику удаления? Это я и называю костылями — использования не по назначению типов. Что касается врапперов над хэндлами все ок,пока логика — просто удалить хэндл, а если чуть сложнее то уже придется городить ещё кучу объектов непонятного назначения.
Здравствуйте, C0x, Вы писали:
PB>>Сложно --- наследование и шаблоны на ровном месте, ненадежно --- можно забыть освободить ресурс или освободить не в том порядке. Пусть каждый ресурс будет отдельным объектом с RAII в составе A.
C0x>Мне вот это почему-то тоже кажется сложным. У меня есть типы типа HANDLE, оборачивать их в спец объект ради одной простой цели — надежного уничтожения везде и всегда кажется черезчурным.
Три даже не класса, а typedef'а для unique_ptr, каждый из которых делает одну простую вещь, сложнее, чем один класс, который в конструкторе и деструкторе жонглирует тремя хэндлами? Бонусом смартпоинтеры запретят копирование A, но разрешает его перемещение.
C0x>Да и хочется не с врапперами в коде работать а с исходными типами (видить их в полях класса и возращаемых зачениях функций).
А почему? Например, по HANDLE нельзя сказать, мы владеем этим объектом или нам его дали откуда-то и уничтожат вовне, а по Resource vs const Resource& или Resource vs HANDLE это видно, если не использовать голые хэндлы и указатели как владеющие (что сейчас рекомендуемая практика, кроме Qt). Насчет draw(HANDLE brush, HANDLE dc) против draw(const Brush& brush, const DC& dc) можно поспорить, что скрывается сущность ресурсов, но на мой взгляд, читаемость выше, а за точными типами пусть компилятор следит.
C0x>Смартпоинтеры на мой личный взгляд это в целом костыль, который превращает код в лапшу.
"Лапша" в данном случае в том, что вместо ApiFunc(resource1) придется писать ApiFunc(resource1.get()). Но это как раз и хорошо, что нельзя отдать куда-то хэндл и не подумать о владении им. Смартпоинтеры не просто автоматически освобождают ресурс, они еще и делают логику использующего их кода проще (не надо описывать освобождение, копирование, перемещение) и безопаснее (заставяют думать о владении).
Здравствуйте, C0x, Вы писали:
C0x>Также я стараюсь писать логику программы зная один очень полезный факт из мира Си, объекты на стеке удаляюься сразу же по выходу из блока кода, а это значит автоматический вызов деструктора.
Но тут тоже есть свои минусы, указатель то всяко быстрее переместить в памяти, чем всю структуру, плюс по указателю можно многократно ссылаться на одну и ту же структуру. Где-то имеет смысл стек данных, а где-то выгоднее использовать указатель.
Я даже читал как-то такую отмазу, что это не Java такая тормознутая, это повёрнутые на абстракциях программисты сделали из неё тормознутую. А если вроде как писать в другом стиле, то всё будет не так уж и плохо.
C++ традиционно считается быстрым, но так ли это? А дело то не в том, что он быстрый сам по себе, а в том, что программисты всё же стараются выбирать наиболее быстрые решения.
Когда программисты говорят, а вот у нас есть в языке такая то особая функция, а вот у вас её нет. Так без проблем, эти функции можно создать самим, если уж так надо.
Если мне нравится некая абстракция, а на самом деле это будет просто уродское решение, да ещё и тормознутое, могу ли я сказать, что идите вы все, я так вижу, или попытаться понять чуждую философию.
Некоторые считают, что C++ это язык с низким уровнем абстракции, потому что он позволяет вытворять низкоуровневые штучки. Но лично я бы сказал, что это язык с избыточным уровнем абстракций и пользуясь всеми возможностями можно зайти очень далеко.
Вот тот же механизм исключений в C++, если вдуматься это высокоуровневая абстракция. Её можно использовать, если нужны исключения, вопрос в том, можно ли обойтись без исключений. Это всё не так однозначно, а к примеру, в .NET я бы по аналогии напихал их везде и даже не стал бы думать над такими вопросами.
Здравствуйте, C0x, Вы писали:
C0x>Надоели всякие Begin, End, Open, Close методы в программах. Хочу придерживаться принципа RAII.
А почему он вас так привлекает? Или только потому что миллионы мух не ошибаются?
Гораздо логичнее что бы за все ресурсы, которые использует объект, отвечал не он, а тот кто заставил его работать.
Так в любой момент можно его пристрелить остановить и не торопясь освобождать то что он использовал, не заботясь о том в каком состоянии он умер.
Более того сразу можно смотреть что он использовал, сколько и ограничивать ресурсы по ходу выполнения если необходимо.
Здравствуйте, C0x, Вы писали:
C0x>То есть тебе не кажется странным использовать объект с именем unique_ptr для совершенно другой цели — выполнить логику удаления? Это я и называю костылями — использования не по назначению типов. Что касается врапперов над хэндлами все ок,пока логика — просто удалить хэндл, а если чуть сложнее то уже придется городить ещё кучу объектов непонятного назначения.
А зачем тогда у unique_ptr, shared_ptr есть такая возможность если не для этого
Здравствуйте, C0x, Вы писали:
C0x>Попробую объяснить свою думку. Мне использование спец. объектов (те же смартпоинтеры) для уничтожения кажется большим костылем. И такие костыли вызывают у программистов на Си#/Java насмешки типа "Да у вас там в Си++ всё на костылях и костылями погоняете".
Хочу, говорит, RAII использовать. А потом заявляет, что это костыль. Занятно.
А вообще, все эти высказывания про костыли, они ведь от непонимания. Или от поверхностного понимания, что еще хуже.
Здравствуйте, a7d3, Вы писали:
A>Здравствуйте, C0x, Вы писали:
C0x>>Здравствуйте, Basil2, Вы писали:
B>>>Но обычно в таком случае делается класс-обертка для конкретного типа ресурсов. Тогда весь код выглядит так:
C0x>>В простых случаях да, это поможет. Но если нужна сложная логика освобождения? Я видел какие-то велосипеды с shared_ptr куда пихается лямбда функция с какой-то логикой, но это как-то помоему велосипедно слишком выглядит и не похоже на какой-то общий паттерн, который можно по всей либе юзать.
A>А какая разница вызывается эта сложная логика после того как освобождаемый ресурс стал не нужен или же в случае раскрутки стека из-за исключения?
Меня интересует только второй случай, потому-что ресурс в любом случае станет не нужен как-только объект содержащий его будет уничтожен. У меня ресурсы не шарятся между объектами.
A>Просто не забывать про SOLID и делать классы максимально простыми — первая же буква S.
В моем случае SOLID то же вроде как не нарушается. У меня есть 1 базовый класс, делающий только одно — Release данных которые собственно сам же и представляет.
Здравствуйте, PlushBeaver, Вы писали:
PB>Здравствуйте, C0x, Вы писали:
PB>>>Сложно --- наследование и шаблоны на ровном месте, ненадежно --- можно забыть освободить ресурс или освободить не в том порядке. Пусть каждый ресурс будет отдельным объектом с RAII в составе A.
C0x>>Мне вот это почему-то тоже кажется сложным. У меня есть типы типа HANDLE, оборачивать их в спец объект ради одной простой цели — надежного уничтожения везде и всегда кажется черезчурным.
PB>Три даже не класса, а typedef'а для unique_ptr, каждый из которых делает одну простую вещь, сложнее, чем один класс, который в конструкторе и деструкторе жонглирует тремя хэндлами? Бонусом смартпоинтеры запретят копирование A, но разрешает его перемещение.
Конструктор и деструктор изначально для этого и нужны в Си++ у каждого объекта, чтобы его данные можно было подчищать. То что программисты ленивые и придумывают костыли, чтобы не забыть что-то удалить, это нормально, но это костыли порожденные пороком
C0x>>Да и хочется не с врапперами в коде работать а с исходными типами (видить их в полях класса и возращаемых зачениях функций).
PB>А почему? Например, по HANDLE нельзя сказать, мы владеем этим объектом или нам его дали откуда-то и уничтожат вовне, а по Resource vs const Resource& или Resource vs HANDLE это видно, если не использовать голые хэндлы и указатели как владеющие (что сейчас рекомендуемая практика, кроме Qt). Насчет draw(HANDLE brush, HANDLE dc) против draw(const Brush& brush, const DC& dc) можно поспорить, что скрывается сущность ресурсов, но на мой взгляд, читаемость выше, а за точными типами пусть компилятор следит.
Ну например хотя бы потому, что код работающий с Хэндлами легко найти на Github, а если его завернуть во Wrapper то на Гитхаб будет помойка из разнородного кода, который вроде бы делает одно и то же но при этом выглядит совершенно по разному.
C0x>>Смартпоинтеры на мой личный взгляд это в целом костыль, который превращает код в лапшу.
PB>"Лапша" в данном случае в том, что вместо ApiFunc(resource1) придется писать ApiFunc(resource1.get()). Но это как раз и хорошо, что нельзя отдать куда-то хэндл и не подумать о владении им.
Нет не хорошо, потому-что нужно придерживаться принципа KISS и YAGNI, иначе мы будем думать о многом, а в итоге нам нужно просто вызвать API функции и передать туда тупа Хэндл.
Здравствуйте, velkin, Вы писали:
V>Здравствуйте, C0x, Вы писали:
C0x>>Также я стараюсь писать логику программы зная один очень полезный факт из мира Си, объекты на стеке удаляюься сразу же по выходу из блока кода, а это значит автоматический вызов деструктора.
V>Но тут тоже есть свои минусы, указатель то всяко быстрее переместить в памяти, чем всю структуру, плюс по указателю можно многократно ссылаться на одну и ту же структуру. Где-то имеет смысл стек данных, а где-то выгоднее использовать указатель.
Нет, я видимо не правильно объяснил. Я стараюсь в логике алгоритмов не использовать указателей, а только стэковые объекты и ссылки на них. Передача объектов по ссылке по скорости то же самое или даже быстрее чем по указателю. New и Delete я конечно же буду использовать там где мне нужно выделять болшие массивы данных, например передавать картинки или звуковые потоки. Но эти данные будут завернуты внутри простых объектов у которых будет просто ссылка на эти данные и естественно деструктор который их почистит (либо vector<BYTE>).
V>Если мне нравится некая абстракция, а на самом деле это будет просто уродское решение, да ещё и тормознутое, могу ли я сказать, что идите вы все, я так вижу, или попытаться понять чуждую философию.
На то мы и программисты, чтобы делать то что нам нравится. Другое дело, что это не всегда можно пустить в Продакшен. Вот мне лично нравится жонглировать Шаблонами, я люблю в Си++ по максимуму использовать Шаблоны, кому-то это покажется сложным или тормозным и т.д. и т.п., но я лично считаю что Шаблоны например одно из самых крутых фич языка Си++.
V>Некоторые считают, что C++ это язык с низким уровнем абстракции, потому что он позволяет вытворять низкоуровневые штучки. Но лично я бы сказал, что это язык с избыточным уровнем абстракций и пользуясь всеми возможностями можно зайти очень далеко.
V>Вот тот же механизм исключений в C++, если вдуматься это высокоуровневая абстракция. Её можно использовать, если нужны исключения, вопрос в том, можно ли обойтись без исключений. Это всё не так однозначно, а к примеру, в .NET я бы по аналогии напихал их везде и даже не стал бы думать над такими вопросами.
Я начал писать свою библиотеку и старался опираться везде на коды ошибок. Потом столкнулся с проблемой что эти коды в 99% нужно переводить в строковое описание. В итоге пришел к тому, что проще кидать исключения со строками чем городить кучу не нужного кода.
Здравствуйте, a7d3, Вы писали:
A>Здравствуйте, C0x, Вы писали:
C0x>>Здравствуйте, T4r4sB, Вы писали:
TB>>>Каждая пара LoadHeavyResource1()-ReleaseHeavyResource1 должна быть в отдельном объекте. TB>>>Один объект — один ресурс.
C0x>>Попробую объяснить свою думку. Мне использование спец. объектов (те же смартпоинтеры) для уничтожения кажется большим костылем. И такие костыли вызывают у программистов на Си#/Java насмешки типа "Да у вас там в Си++ всё на костылях и костылями погоняете". А вот моё решении, на мой опять же взгляд, более похоже на четкий паттерн для решения задачи освобождения ресурсов заданного класса на базе языковой конструкции и порядка вызова деструкторов. И вот это мне кажется более аккуратным способом, чем тот же "костыль" IDisposable в C# для управления освобождением ресурсов.
A>Базовые классы и наследование от них — это инструмент взаимозаменяемости.
Поклонники ООП считают что это их инструмент, а я не поклонник и смотрю на это просто как на инструмент Языка.
A>по канонам ООП должно делаться через агрегированием с делегированием внешних вызовов.
Си++ не был бы Си++ если бы не был мультипарадигменным языком, поэтому Каноны ООП это лишь каноны ООП.
Здравствуйте, wander, Вы писали:
W>Здравствуйте, C0x, Вы писали:
C0x>>Попробую объяснить свою думку. Мне использование спец. объектов (те же смартпоинтеры) для уничтожения кажется большим костылем. И такие костыли вызывают у программистов на Си#/Java насмешки типа "Да у вас там в Си++ всё на костылях и костылями погоняете".
W>Хочу, говорит, RAII использовать. А потом заявляет, что это костыль. Занятно.
Смартпоинтер и RAII это одно и тоже?
W>А вообще, все эти высказывания про костыли, они ведь от непонимания. Или от поверхностного понимания, что еще хуже.
Естественно, за любым костылем лежит целая история причинно-следственных связей. Ну вот к примеру взять Смартпоинтер. Любой программист на языке с GC скажет что это очевидно костыль, просто потому-что у вас ребята нет GC, но вы пытаетесь обойти этот недостаток пихая во все щели свои смартпоинтеры. Программист на Си++99 смотря на смартпоинтер скажет что это костыль, потому-что вы лентяи и не можете аккуратно писать код и следить за указателями и хэндлами.
Здравствуйте, C0x, Вы писали:
C0x>Здравствуйте, a7d3, Вы писали:
A>>по канонам ООП должно делаться через агрегированием с делегированием внешних вызовов.
C0x>Си++ не был бы Си++ если бы не был мультипарадигменным языком, поэтому Каноны ООП это лишь каноны ООП.
И всего их две штуки — процедурная и ОО. Потому и выбор между — либо крестик снять, либо трусы надеть.
Это идиом с подходами в С++ много, но они все в рамках или процедурного (модульного) программирования или же ООП.
Так что или соблюдаешь ООП или идёшь дальше клянчить или выдумывать свои паттерны.
Здравствуйте, a7d3, Вы писали:
A>Здравствуйте, C0x, Вы писали:
C0x>>Здравствуйте, a7d3, Вы писали:
A>>>по канонам ООП должно делаться через агрегированием с делегированием внешних вызовов.
C0x>>Си++ не был бы Си++ если бы не был мультипарадигменным языком, поэтому Каноны ООП это лишь каноны ООП.
A>И всего их две штуки — процедурная и ОО. Потому и выбор между — либо крестик снять, либо трусы надеть. A>Это идиом с подходами в С++ много, но они все в рамках или процедурного (модульного) программирования или же ООП. A>Так что или соблюдаешь ООП или идёшь дальше клянчить или выдумывать свои паттерны.
А вот тут ты не прав. Есть ещё шаблоны и стат.программирование, ни с процедурами ни с ООП они ничего общего не имеют и дают совершенно интересные решения по сравнению с классическим ООП.
Здравствуйте, C0x, Вы писали:
... C0x>Смартпоинтеры на мой личный взгляд это в целом костыль, который превращает код в лапшу.
Нужно только разобраться со смартпоинтерами — и Ваше мнение о них изменится! Они станут Вашими друзьями.
На самом деле — это очень толковая штука, решающая многие проблемы, и прежде всего — высвобождения ресурсов.
Здравствуйте, C0x, Вы писали:
C0x>Здравствуйте, a7d3, Вы писали:
A>>А какая разница вызывается эта сложная логика после того как освобождаемый ресурс стал не нужен или же в случае раскрутки стека из-за исключения?
C0x>Меня интересует только второй случай, потому-что ресурс в любом случае станет не нужен как-только объект содержащий его будет уничтожен. У меня ресурсы не шарятся между объектами.
A>>Просто не забывать про SOLID и делать классы максимально простыми — первая же буква S.
C0x>В моем случае SOLID то же вроде как не нарушается. У меня есть 1 базовый класс, делающий только одно — Release данных которые собственно сам же и представляет.
Вопрос был в принципе, для понимания и анализа — проведения параллелей и осмысления. Сегодня интересует одно, а завтра другое — это не повод смотреть кусками, местами и урывками.
Сначала надо было уйти от классов управляющих множеством ресурсов напрямую. Если есть минимально возможные обёртки над ресурсами, каждым типом в отдельности, то это следование SRP.
А вот пихать эти классы-обёртки в качестве базовых, строя иерархию сугубо ради повторного использования кода — это нарушение LSP.
Здравствуйте, C0x, Вы писали:
... C0x>Нет не хорошо, потому-что нужно придерживаться принципа KISS и YAGNI, иначе мы будем думать о многом, а в итоге нам нужно просто вызвать API функции и передать туда тупа Хэндл.
ИМХО повседневная практика разработки софта — это как раз компромисс между минимальщиной (KISS & YAGNI) и реальной оценкой требований Заказчика.
И если есть надёжный и полезный для практики C++ инструмент, такой как смарт-поинтеры, то как раз отказ от его применения и есть ошибка
Здравствуйте, C0x, Вы писали:
C0x>Я стараюсь в логике алгоритмов не использовать указателей, а только стэковые объекты и ссылки на них. Передача объектов по ссылке по скорости то же самое или даже быстрее чем по указателю.
Можно пример кода, в котором передача указателя на стековый объект куда-то была бы медленнее, чем передача ссылки на такой же стековый объект?
Здравствуйте, C0x, Вы писали:
C0x>Здравствуйте, a7d3, Вы писали:
A>>Здравствуйте, C0x, Вы писали:
A>>И всего их две штуки — процедурная и ОО. Потому и выбор между — либо крестик снять, либо трусы надеть. A>>Это идиом с подходами в С++ много, но они все в рамках или процедурного (модульного) программирования или же ООП. A>>Так что или соблюдаешь ООП или идёшь дальше клянчить или выдумывать свои паттерны.
C0x>А вот тут ты не прав. Есть ещё шаблоны и стат.программирование, ни с процедурами ни с ООП они ничего общего не имеют и дают совершенно интересные решения по сравнению с классическим ООП.
Это какая-то своя терминология от любителя изобретать свои паттерны? Чем не устраивает общепринятая?
По крайней мере нужна для того, чтобы получалось комуницировать с собратьями по разуму.
Моё раздражение вызвано тем, что в С++ вечно лезут кулибины, которые ни языка, ни средств его, ни идиом с парадигмами не знают, но очень хотят себе пространство для самовыражения найти. Свою гениальность миру показать, ничего не изучая и не осваивая.
Для примера, в данном случае, в этом треде достаточно знать про делегирование конструкторов и вопрос решается — освоением средств языка.
А можно и лучше через SRP SOLID — каждому типу сделать свой класс отвечающий за стратегию захвата и освобождения. Тогда класс использующий эти ресурсы, будет отвечать лишь за порядок захвата (выделения) и освобождения.