vak: (Default)
[personal profile] vak
Для изоляции реализации от интерфейса в Си++ обычно применяют идиому Pimple. Сергей Тарасов предлагает альтернативное решение, более простое: интерфейс в виде абстрактного класса. Надо будет попробовать переписать мой пример dependency-inversion-demo.

Date: 2022-11-14 08:33 (UTC)
ircicq: (Default)
From: [personal profile] ircicq
В его примере не видно основного преимущества Pimpl:
использовать '.' вместо ->

должно быть так:

book book1("Modern old-school C++", "Gang of thousands", 255);
cout << "Title: " << book1.title();

Date: 2022-11-14 08:44 (UTC)
vit_r: default (Default)
From: [personal profile] vit_r
Вопрос в том, в какой из версий проще и быстрее отловить ошибку.

Date: 2022-11-14 09:03 (UTC)
juan_gandhi: (Default)
From: [personal profile] juan_gandhi

Подумаю насчет pimpl, там есть ккой-то резон, конечно. Насчет же интерфейса в виде абстрактного класса - ха, в плюсах же нету интерфейсов, да? Ну, многое еще предстоит освоить. Cake pattern, например.

Попробую ужо этот ваш pimpl в Скале. Спасибо.

Date: 2022-11-14 12:49 (UTC)
From: [personal profile] chabapok
так уже не модно делать. Гляньте это, можно сразу с 9:00 https://www.youtube.com/watch?v=mkPTreWiglk

IMHO

Date: 2022-11-14 13:25 (UTC)
From: [personal profile] dedekha
Я старый пенек и 9/10 жизни проработал в обстановке где-было принято обрабатывать no memory ошибки - pimpl это всегда умножение этих (безполезных) усилий как минимум на 2, наверное поэтому имею к нему сильную алергию.

Правильный путь - это интерфейс выраженный через базовые классы (не обязательно абстрактные) и link-time initialization для производства конкретный классов.

Date: 2022-11-14 17:34 (UTC)
sab123: (Default)
From: [personal profile] sab123
Про pimpl я впервые слышу, но его смысл - для библиотек. Чтобы изменения в приватных частях (без изменения в публичных) не вызывали необходимости рекомпиляции кода, который испольует библиотеку. То есть, для интерфейсов, выкладываемых библиотекой наружу. Это особенно важно для разделяемых библиотек, чтобы сохранять совместимость интерфейса. Такие проблемы и решения существовали и до классов. Для классов внутри проекта и внутренних библиотек в нем смысла нет. В коде, который не озабочен бинарной совместимостью, вообще не должно быть слова private (ну, кроме как для удаления неопределенных вещей).

Абстрактный класс - это классический способ, и такой защиты не дает, потому что добавление приватного поля меняет размер объекта.
Edited Date: 2022-11-14 17:59 (UTC)

Date: 2022-11-14 22:30 (UTC)
sab123: (Default)
From: [personal profile] sab123
С pimpl и встроенным объектом - не видно. А так - видно, елси объекты создавать в виде прямо переменных, а не через new.

Date: 2022-11-15 05:54 (UTC)
sab123: (Default)
From: [personal profile] sab123
Pimpl дает. Ну, при условии, что публичный интерфейс фиксирован.

Date: 2022-11-15 21:36 (UTC)
sab123: (Default)
From: [personal profile] sab123
Ну да, а объект всегда конструируется внутри библиотеки, поэтому все знания о его нутрях и изменениях в них сконцентрированы внутри библиотеки.

Date: 2022-11-15 16:45 (UTC)
From: [personal profile] dedekha
сильно не согласен насчет private если не ограничивать публичныый доступ лет через 10 достаточно большую сисьтем будет просто невозможно изменять (см. https://www.hyrumslaw.com)

Date: 2022-11-15 21:35 (UTC)
sab123: (Default)
From: [personal profile] sab123
А если ограничивать - то ее сразу будет невозможно изменять.

Date: 2022-11-14 19:42 (UTC)
From: [personal profile] caztd
Зависит от задачи. Нужно знать обе техники и выбирать подходящую:
https://stackoverflow.com/questions/825018/pimpl-idiom-vs-pure-virtual-class-interface/2330745#2330745

Date: 2022-11-14 20:01 (UTC)
From: [personal profile] caztd
Все есть по ссылке.

"Abstract classes (pure virtuals) is something of which your clients must be aware: if you try to use them to reduce coupling and circular references, you need to add some way of allowing them to create your objects (e.g. through factory methods or classes, dependency injection or other mechanisms).

Also remember that by using inheritance you introduce a dependency on vtable layout. To maintain ABI you cannot change the virtual functions anymore (adding at the end is sort of safe, if the there are no child classes that add virtual methods of their own)"

В основном пимпл нужен для обслуживания внешних DLL интерфейсов.
Edited Date: 2022-11-14 20:05 (UTC)

Date: 2022-11-14 21:10 (UTC)
From: [personal profile] caztd
Вот это хорошая задача для собеседования:
в каком случае применение pure virtuals для интерфейсов нецелесообразно ;)

Тут много вариантов, даже у меня в практике было несколько разных случаев.
Самый простой указан в цитате: если клиент не под вашим контролем.
Вы выпускаете новую версию библиотеки (с расширенным, но совместимым интерфейсом)
и клиент не может перекомпилировать свой код,
а хочет просто заменить DLL.
Это накладывает жесткие ограничения на изменения интерфейса.
В реальности -- учитывая например, возможные разные хитрые оптимизации на стороне клиента,
интерфейс менять нельзя. Мы как то такой баг пару недель искали.
Тоже думали, что если новые функции в конце vtable добавлять, то ABI будет стабильна. Mistake.
То что вы считаете совместимым и то что компилятор считает совместимым --
это могут быть две большие разницы, понятные только экспертам, которых у вас в команде нет.

Другой вариант -- когда невозможно предоставить фэктори for interface instances.

Ну и наконец "for value types to minimize compile time dependencies."
Попробуйте например написать свой non-templated string class как DLL который
не весь определен в .h файле и к тому же независим от компилятора.
Как много там открытий чудных..

Date: 2022-11-14 20:34 (UTC)
From: [personal profile] dijifi
В основном пимпл нужен для обслуживания внешних DLL интерфейсов

У меня тут один ловкий коллега зафигачил интерфейс к DLL через inheritance

Date: 2022-11-14 20:46 (UTC)
yba: (Default)
From: [personal profile] yba
А что кому-то было раньше непонятно что можно сделать интерфейс + factory на C++?
просто если есть интерфейс то чисто теоретически мы предполагаем что подразумевается более чем одна имплементация
С другой стороны pimpl хорош для использования когда никаких разных имплементаций мы не подразумеваем, но по каким-то причинам хотим имплементацию спрятать, например по причине конфликтуюших хедеров или что-нибудь в таком роде

nz

Date: 2022-11-15 11:18 (UTC)
From: [personal profile] nz

Для изоляции реализации от интерфейса в Си++ обычно применяют идиому Pimple

Разделение интерфейса и реализации - не цель, а средство.

В зависимости от настоящей цели, инструменты могут применяться принципиально разные.

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

PImpl же применяется прежде всего там, где реализация одна, но требуется обеспечить к ней стабильный бинарный интерфейс (ABI). Например, для передачи типов между модулями, собранными разными версиями разных компиляторов. Интерфейсы здесь не особо подходят, потому что к каждому абстрактному классу присобачена таблица виртуальных методов. Добавили метод - ой, надо делать новый интерфейс.

Поэтому одно - не drop-in replacement другого, нет.