Интерфейсы и реализации
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.03.24 20:39
Оценка:
Здравствуйте!

Что-то туплю.

Обычно, если реализация зависит от платформы, не парюсь и делаю абстрактный интерфейс с чисто виртуальными функциями, делаю реализацию под target-платформу, а фабрика создаёт объект в куче и возвращает указатель на интерфейс, голенький или shared.

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

Из минусов — использование динамической память, и все вызовы идут через vtbl, что само по себе не добавляет производительности, но и мешает оптимизации, и довольно часто приходится использовать dynamic_cast, чтобы из указателя на интерфейс получить указатель на конкретную реализацию, что тоже немного бъёт по перфомансу

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

Ну, допустим, есть класс окна и методы для работы с изображением курсора:
class Window
{
    Cursor createStockCursor(EStockCursor sc);
    Cursor serCursor(Cursor c);
};


Для винды, например, класс Cursor будет каким-то таким:
class Cursor // movable non-copiable
{
    HCURSOR hCursor = 0;

    ~Cursor()
    {
        DestroyCursor(hCursor);
    }
};


Тут проблема — HCURSOR системно зависимый, и тут либо в каждом хидере каждого класса обложиться ifdef'ами при определении раскладки класса, либо обложится ifdef'ами и инклюдить соответствующую реализацию.

В принципе, оба варианта рабочие, только в первом все кишки всех реализаций будут торчать наружу в хидере, во втором варианте они чутка скрыты, но тоже торчат наружу, если чуть копнуть. Ну, и ещё такой вариант подразумевает практически обязательное разделение на .h/.cpp.

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

Можно ещё попробовать через странные рекурсивные шаблоны, как в WTL, но это не очень интуитивно и слегка геморойно.

Пока пишу под компы, конкретно под винду, с прицелом на кроссплатформу, но точно знаю, что когда-нибудь захочется и в STMку запихать

В общем, как бы и на елку залезть, и жопу не ободрать?
Есть идеи, как сделать шоколадно?
Маньяк Робокряк колесит по городу
Re: Интерфейсы и реализации
От: reversecode google
Дата: 05.03.24 20:59
Оценка: +2
давай резюмирую твой длинный опус
за 20+ лет .кодинга
ты так и не научился делать декомпозицию
ведь это для слабаков
сразу пиши код!
Re[2]: Интерфейсы и реализации
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.03.24 21:03
Оценка:
Здравствуйте, reversecode, Вы писали:

R>давай резюмирую твой длинный опус


А можно без тебя как-нибудь?
Маньяк Робокряк колесит по городу
Re[3]: Интерфейсы и реализации
От: reversecode google
Дата: 05.03.24 21:17
Оценка:
хочется что бы то что сказал тебе я, сказали другие?
ну жди
Re: Интерфейсы и реализации
От: SkyDance Земля  
Дата: 05.03.24 21:28
Оценка:
M>Но, подумалось, а может попробовать сделать что-то более статическое?
M>В принципе, оба варианта рабочие, только в первом все кишки всех реализаций будут торчать наружу в хидере

Поздравляю! Вы наконец-то поняли, для чего предназначены шаблоны (templates).
Re[2]: Интерфейсы и реализации
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.03.24 21:34
Оценка:
Здравствуйте, SkyDance, Вы писали:

M>>Но, подумалось, а может попробовать сделать что-то более статическое?

M>>В принципе, оба варианта рабочие, только в первом все кишки всех реализаций будут торчать наружу в хидере

SD>Поздравляю! Вы наконец-то поняли, для чего предназначены шаблоны (templates).



Нет, не понял. Можно пояснить? Все используемые типы прошаблонить, сделать dummy для типа инстанциации, и запилить отдельные специализации? Ну, как-то идея тоже не очень нравится.
Маньяк Робокряк колесит по городу
Re: Интерфейсы и реализации
От: CreatorCray  
Дата: 05.03.24 23:37
Оценка:
Здравствуйте, Marty, Вы писали:

M>Обычно, если реализация зависит от платформы, не парюсь и делаю абстрактный интерфейс с чисто виртуальными функциями, делаю реализацию под target-платформу

А зачем? У тебя ж вызывающий код под другую платформу будет весь пересобран под другую платформу, нафига тут рантайм заморочки?

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

И как часто это у тебя в реальности используется?

M>Также плюсом является более простой интероп с другими языками

И как часто это у тебя в реальности используется?

M>Но, подумалось, а может попробовать сделать что-то более статическое?

Это положительная динамика.

M>подразумевает практически обязательное разделение на .h/.cpp.

И это правильная практика

M>Я же как-то уже привык писать шарповом стиле, всё в одном месте, в хидере.

Это не очень правильная практика.

M>но это не очень интуитивно и слегка геморойно.

Почему? Я пользую иногда, там где оно делает жизнь проще.

M>Есть идеи, как сделать шоколадно?

Если хочешь чтоб реально было гЫбко — придётся пошаблонить.
Главное соблюдать баланс и не кидаться в крайности и шаблонить только то, что по другому нормально не вытанцовывается.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Re[2]: Интерфейсы и реализации
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 05.03.24 23:50
Оценка:
Здравствуйте, CreatorCray, Вы писали:

M>>Обычно, если реализация зависит от платформы, не парюсь и делаю абстрактный интерфейс с чисто виртуальными функциями, делаю реализацию под target-платформу

CC>А зачем? У тебя ж вызывающий код под другую платформу будет весь пересобран под другую платформу, нафига тут рантайм заморочки?

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

CC>И как часто это у тебя в реальности используется?

Ну вот выбор канваса на GDI или на GDI+ я отдал на откуп пользователю в рантайме, причем часть операций можно делать через одно, а часть через другое, в зависимости от требуемой скорости и качества отрисовки


M>>Также плюсом является более простой интероп с другими языками

CC>И как часто это у тебя в реальности используется?

У меня есть проект с движком squirrel, туда shared_ptr<IInterface> прокидывается, не приходя в сознание. Ну и есть вероятность, что объекты с не слишком сложными интерфейсами (или с интерфейсами в виде усеченного множества плюсового интерфейса) могут быть динамическими плагинами.


M>>Но, подумалось, а может попробовать сделать что-то более статическое?

CC>Это положительная динамика.

M>>подразумевает практически обязательное разделение на .h/.cpp.

CC>И это правильная практика

Я отвык


M>>Я же как-то уже привык писать шарповом стиле, всё в одном месте, в хидере.

CC>Это не очень правильная практика.

Ну, может быть, но это в большинстве случаев удобно.


M>>но это не очень интуитивно и слегка геморойно.

CC>Почему? Я пользую иногда, там где оно делает жизнь проще.

M>>Есть идеи, как сделать шоколадно?

CC>Если хочешь чтоб реально было гЫбко — придётся пошаблонить.
CC>Главное соблюдать баланс и не кидаться в крайности и шаблонить только то, что по другому нормально не вытанцовывается.

ну вот и интересно, как бы красивше сделать
Маньяк Робокряк колесит по городу
Re: Интерфейсы и реализации
От: rg45 СССР  
Дата: 06.03.24 00:57
Оценка: 2 (1)
Здравствуйте, Marty, Вы писали:

M>Обычно, если реализация зависит от платформы, не парюсь и делаю абстрактный интерфейс с чисто виртуальными функциями, делаю реализацию под target-платформу, а фабрика создаёт объект в куче и возвращает указатель на интерфейс, голенький или shared.


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


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

M>В общем, как бы и на елку залезть, и жопу не ободрать?

M>Есть идеи, как сделать шоколадно?

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

template <typename T>
concept Cursor = std::default_initializable<T> && BooleanTestable<T>;

template <typename T>
concept Window =
requires (const T& w) {
   // Constant properties:
   {w.GetSize()} -> std::same_as<Vector<2, int>>;
   {w.GetPos()} -> std::same_as<Vector<2, int>>;
   {w.GetCursor()} -> Cursor;
} &&
requires (T& w, Vector<2, int> v, Cursor auto cursor) {
   // Modifying operations:
   w.SetSize(v);
   w.SetPos(v);
   {w.SetCursor(cursor)} -> Cursor;
   {w.createStockCursor(std::declval<EStockCursor>())} -> Cursor;
};


Потом набрасываешь классы реализаций и тут же можно проверять их на соответствие концептам:

class WinWindow
{
public:
   GetSize() . . .
   SetSize() . . .
   GetCursor() . . .
   SetCursor() . . .
};

// Concept check:
static_assert(Window<WinWindow>);


Ну и правильное разбиение сложных задач на подзадачи, конечно же, важно. Вряд ли ты будешь писать полностью отдельные реализации под каждую платформу. Скорее всего, логика верхнего уровня будет общей для всех платформ, различия будут в каких-то деталях. Вот нужно правильно это детали детали отделить от общей реализации.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 06.03.2024 1:24 rg45 . Предыдущая версия . Еще …
Отредактировано 06.03.2024 1:08 rg45 . Предыдущая версия .
Отредактировано 06.03.2024 1:03 rg45 . Предыдущая версия .
Re[2]: Интерфейсы и реализации
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 06.03.24 01:25
Оценка:
Здравствуйте, rg45, Вы писали:

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


R>Ну вот тут желательно сразу определиться с потребностями. Переносимость на различные платформы — это одно, а возможность выбора реализации в рантайме — совсем другое. Если тебе действительно нужно переключаться между реализациями в рантайме, то ты на правильном пути.


Изначально я закладывался на то, чтобы работало даже на самой древней винде. Я вообще ещё и 9X по привычке поддерживаю в некоторых проектах, но в данном проекте решил, что хотя бы WinXP SP3. GDI — опять же — совместимость (в тч по всяким RDP и тп), плюс как эталон минимального АПИ. GDI+ прикрутил позже для большей гладкости изображаемого, и оказалось, что оно вообще тормоз (а со шрифтами так вообще откровенно хуже), поэтому сделал возможность в динамике переключаться. В этот раз изначальный выбор в пользу динамики сработал в плюс. Но есть сомнения, надо ли и дальше использовать этот подход.

Под линупсы хз, наверное буду через Qt делать, и не думаю, что так уж надо будет в рантайме уметь использовать Qt4 и Qt5 одновременно, так что наверное можно отказаться от динамики, хотя, хз.


M>>В общем, как бы и на елку залезть, и жопу не ободрать?

M>>Есть идеи, как сделать шоколадно?

R>Если говорить вообще о переходе от динамического полиморфизма к статическому, безотносительно данной задачи, то тут принцип не очень сложный — то, что было абстрактным интерфейсом становится концептом. Но тут главное без фанатизма — не обязательно описывать концептами прямо все-все подряд. В каждой задаче есть набор каких-то относительно статичных сущностей — типов и констант, для которых кастомизация не очень актуальна — ну так пусть эти типы и константы такими и остаются. При необходимости их можно использовать при определении концептов.


Я пока сижу на MSVC2019, которая с++17, и которая вроде как последняя умеет собирать exe-шники в WinXP SP3 без каких-либо приседаний. Слезать с неё не особо планирую, поэтому всякие новомодные штуки типа концептов не пробовал, за неимением. Ну и если потом думать о порте под железяку, то использование самых последних плюсиков тут скорее минус. Ну, или даже если брать какие-то стабильные дистры линупса, или те же российские клоны, там искаропки далеко не свежак.

Так что тут вопрос о том, как лучше приготовить кактус, чтобы при поедании не слишком уколоться


R>
R>template <typename T>
R>concept Cursor = std::default_initializable<T> && BooleanTestable;
R>


Не, можно попроще как-то?
Маньяк Робокряк колесит по городу
Re[3]: Интерфейсы и реализации
От: rg45 СССР  
Дата: 06.03.24 01:31
Оценка:
Здравствуйте, Marty, Вы писали:

R>>
R>>template <typename T>
R>>concept Cursor = std::default_initializable<T> && BooleanTestable;
R>>


M>Не, можно попроще как-то?


Да можно и без концептов, в принципе, сделать то же самое, просто следить за соблюдением контрактов будет значительно труднее. И в написании перегруженных функций концепты здорово помогают. Можно конечно, пробовать лепить самописные концепты поверх SFINAE. Я пробовал — получается довольно убого.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 06.03.2024 1:32 rg45 . Предыдущая версия .
Re[4]: Интерфейсы и реализации
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 06.03.24 01:37
Оценка:
Здравствуйте, rg45, Вы писали:

R>>>
R>>>template <typename T>
R>>>concept Cursor = std::default_initializable<T> && BooleanTestable;
R>>>


M>>Не, можно попроще как-то?


R>Да можно и без концептов, в принципе, сделать то же самое, просто следить за соблюдением контрактов будет значительно труднее. И в написании перегруженных функций концепты здорово помогают. Можно конечно, пробовать лепить самописные концепты поверх SFINAE. Я пробовал — получается довольно убого.


А ещё проще как?
Маньяк Робокряк колесит по городу
Re: Интерфейсы и реализации
От: watchmaker  
Дата: 06.03.24 02:21
Оценка: 2 (1) +1
Здравствуйте, Marty, Вы писали:

M>Обычно, если реализация зависит от платформы, не парюсь и делаю абстрактный интерфейс с чисто виртуальными функциями, делаю реализацию под target-платформу, а фабрика создаёт объект в куче и возвращает указатель на интерфейс

Норм.


M> и все вызовы идут через vtbl

Не используешь LTO или thinLTO?
Компилятор может и не всегда девиртуализирует вызовы, но зато делает это сам, и автоматически, и не не требует менять код.
И оптимизировать потом можно только то, с чем не справился автоматически компилятор.

M>Но, подумалось, а может попробовать сделать что-то более статическое?

Pimpl же.
Виртуальность в нём опциональна. Когда используется не для фабрики, которая реально порождает много разных классов, а для сокрытия особенностей реализации (особенно платформозависимых) одного класса, то обычно виртуальность как раз и не используется.

M>Из минусов — использование динамической память

Как бы да, но и со статическими типами, прямо скажем, код не всегда пишется без динамической памяти :)

В том же классическом паттерне pimpl обычно сам интерфейс держит лишь указатель на реализацию класса, которая живёт в динамической памяти, ибо так писать гораздо проще.
Но это тоже не обязательно. Некоторые проекты обмазываются целиком FastPimpl и не тратят место на лишние указатели, получают возможность создавать классы в том числе и на стеке, и не показывают при этом в интерфейсе как они внутри устроены. Больше действий по поддержке, но в целом терпимо.

M> и довольно часто приходится использовать dynamic_cast, чтобы из указателя на интерфейс получить указатель на конкретную реализацию, что тоже немного бъёт по перфомансу

Это даже ближе к хакам, чем FastPimpl, но если ты знаешь всю иерархию классов (например полсотни классов, частично отнаследованных друг от друга), и при этом в иерархии ней нет виртуальных наследований, а все листовые классы известны, и не сильно используется cross-cast, то можно использовать и static_cast. Потому что если двигаться по иерархии нужно только в одном направлении (только вверх или только вниз), и нет неоднозначеностей, присущих множественному наследованию, то всё сильно упрощается. Достаточно хранить в самом базовом классе какую-нибудь битовую маску или набор тегов, перечисляющих интерфейсы, которые реализует конкретный экземпляр наследника (чтобы проверку dynamic_cast заменить на проверку битовой маски наследника). Этакий ручной и узкозаточенный RTTI для фиксированных иерархий.
У Страуструпа есть статья с похожей идеей, в которой большинство ограничений убрано (кроме главного, что всё же знание списка всех возможных наследников очень сильно желательно): https://www.stroustrup.com/fast_dynamic_casting.pdf
Ну и делать это придётся вручную без поддержки компилятора, что всегда неприятно.
Re[2]: Интерфейсы и реализации
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 06.03.24 03:22
Оценка:
Здравствуйте, watchmaker, Вы писали:

M>>Обычно, если реализация зависит от платформы, не парюсь и делаю абстрактный интерфейс с чисто виртуальными функциями, делаю реализацию под target-платформу, а фабрика создаёт объект в куче и возвращает указатель на интерфейс

W>Норм.

Принято


M>> и все вызовы идут через vtbl

W>Не используешь LTO или thinLTO?
W>Компилятор может и не всегда девиртуализирует вызовы, но зато делает это сам, и автоматически, и не не требует менять код.
W>И оптимизировать потом можно только то, с чем не справился автоматически компилятор.

Ну, специально я LTO не отключал, и оно вроде по умолчанию работает. Но, в целом, то, что компилятор целиком видит во время компиляции, оно компилятору для оптимизации всё же удобнее, как мне казалось


M>>Но, подумалось, а может попробовать сделать что-то более статическое?

W>Pimpl же.
W> Виртуальность в нём опциональна. Когда используется не для фабрики, которая реально порождает много разных классов, а для сокрытия особенностей реализации (особенно платформозависимых) одного класса, то обычно виртуальность как раз и не используется.

Ну, виртуальность это не самая большая проблема, но pimpl не далеко от этого ушел


M>>Из минусов — использование динамической память

W>Как бы да, но и со статическими типами, прямо скажем, код не всегда пишется без динамической памяти

Если без контейнеров и строк, то вообще нет проблем, несколько лет под STMки писал практически со всеми плюшками C++14, но при полном отсутствии динамической памяти. Ну, совсем без контейнеров не получилось, написал пару своих типа кольцевого списка и подобного


W>В том же классическом паттерне pimpl обычно сам интерфейс держит лишь указатель на реализацию класса, которая живёт в динамической памяти, ибо так писать гораздо проще.

W>Но это тоже не обязательно. Некоторые проекты обмазываются целиком FastPimpl и не тратят место на лишние указатели, получают возможность создавать классы в том числе и на стеке, и не показывают при этом в интерфейсе как они внутри устроены. Больше действий по поддержке, но в целом терпимо.

Ну, по большому счёту pimpl — это делегирующая обёртка над моим IInterface. Я конечно упрощаю, но примерно как-то так. Извращения типа FastPimpl'а не хочется использовать, пока всё-таки скорость разработки в приоритете над остальным.


M>> и довольно часто приходится использовать dynamic_cast, чтобы из указателя на интерфейс получить указатель на конкретную реализацию, что тоже немного бъёт по перфомансу

W>Это даже ближе к хакам, чем FastPimpl, но если ты знаешь всю иерархию классов (например полсотни классов, частично отнаследованных друг от друга), и при этом в иерархии ней нет виртуальных наследований, а все листовые классы известны, и не сильно используется cross-cast, то можно использовать и static_cast. Потому что если двигаться по иерархии нужно только в одном направлении (только вверх или только вниз), и нет неоднозначеностей, присущих множественному наследованию, то всё сильно упрощается. Достаточно хранить в самом базовом классе какую-нибудь битовую маску или набор тегов, перечисляющих интерфейсы, которые реализует конкретный экземпляр наследника (чтобы проверку dynamic_cast заменить на проверку битовой маски наследника). Этакий ручной и узкозаточенный RTTI для фиксированных иерархий.

Да не, таким гемором заниматься нет особой нужды, одного, максимум пары динамик кастов достаточно, чтобы проверить поддержку конкретных реализаций, и усложнять лишними оптимизациями пока тут не вижу смысла


W>У Страуструпа есть статья с похожей идеей, в которой большинство ограничений убрано (кроме главного, что всё же знание списка всех возможных наследников очень сильно желательно): https://www.stroustrup.com/fast_dynamic_casting.pdf

W>Ну и делать это придётся вручную без поддержки компилятора, что всегда неприятно.


Спасибо за коментарии, и за ссылки.


В целом, наверно и дальше буду делать через абстрактные интерфейсы с фабриками, благо это проще остального, и максимально совместимо даже с весьма древними компиляторами. А если приспичит в жоскую встройку это запихать, то там можно и new переопределить, и написать под него менеджер памяти, и это будет проще остального, и это будет одной из самых просто решаемых проблем в этом случае
Маньяк Робокряк колесит по городу
Re[3]: Интерфейсы и реализации
От: CreatorCray  
Дата: 06.03.24 03:30
Оценка:
Здравствуйте, Marty, Вы писали:

M>Ну вот выбор канваса на GDI или на GDI+

GDI+ лучше не использовать совсем, уж очень оно тормозное.
Лучше DirectWrite backend добавить если надо всякие украшательства.

M>У меня есть проект с движком squirrel, туда shared_ptr<IInterface> прокидывается

А внутри как используется?

M>>>Я же как-то уже привык писать шарповом стиле, всё в одном месте, в хидере.

CC>>Это не очень правильная практика.
M>Ну, может быть, но это в большинстве случаев удобно.
Зависит от размеров проекта. Маленькое шевелится и даже выходит что то вроде WPO/IPO, но на большом компилер таким может подавиться.

M>ну вот и интересно, как бы красивше сделать

Это ж тока на практике можно узнать, начав делать, решив всё что по пути понавылазило, и таки сделав.
Что по пути понавылазит зависит от того, что там за API, какие интерфейсы, требования, ограничения, итп.
В итоге решение одних может другим не подойти.
Ну и опять таки полное понимание что надо сделать есть только у тебя.
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Re[3]: Интерфейсы и реализации
От: CreatorCray  
Дата: 06.03.24 03:30
Оценка:
Здравствуйте, Marty, Вы писали:

M>Изначально я закладывался на то, чтобы работало даже на самой древней винде.

А о каком типе софта вообще речь то кстати?
Ну т.е. что оно делает?

M> Я вообще ещё и 9X по привычке поддерживаю в некоторых проектах, но в данном проекте решил, что хотя бы WinXP SP3.

А в этом есть реальная необходимость? Ну т.е. твоя клиентская база всё ещё на 32битных виндах сидит?

M> GDI — опять же — совместимость (в тч по всяким RDP и тп)

RDP много чего умеет, не только GDI

M>GDI+ прикрутил позже для большей гладкости изображаемого, и оказалось, что оно вообще тормоз (а со шрифтами так вообще откровенно хуже)

Угу, вот только нафига? Cleartype шрифты обычный GDI умеет и при этом ещё и быстро.

M> поэтому сделал возможность в динамике переключаться.

Лучше бы не добавлял вовсе.

M>Под линупсы хз, наверное буду через Qt делать, и не думаю, что так уж надо будет в рантайме уметь использовать Qt4 и Qt5 одновременно, так что наверное можно отказаться от динамики

Я бы выбросил и GDI+ и динамику
... << RSDN@Home 1.3.110 alpha 5 rev. 62>>
Re: Интерфейсы и реализации
От: qaz77  
Дата: 06.03.24 05:25
Оценка:
Здравствуйте, Marty, Вы писали:

M>Обычно, если реализация зависит от платформы, не парюсь и делаю абстрактный интерфейс с чисто виртуальными функциями, делаю реализацию под target-платформу, а фабрика создаёт объект в куче и возвращает указатель на интерфейс, голенький или shared.


Динамический полиморфизм — вещь хорошая, но не без недостатков.
Кроме дин. памяти (кстати, не обязательно) и виртуальных вызовов может возникать дублирование логики в классах реализации.
Возникает такой эффект из-за того, что интерфейс подразумевает некую семантику, а разные реализации могут разойтись в логике.
Бороться с этим можно запихав логику реализации в шаблонный класс, который параметризован traits.
Соответственно, шаблонный класс реализует виртуальные функции интерфейса, а за все нюансы отвечает конкретный traits.
Тут можно использовать паттерн "шаблонный метод" или по другому, в зависимости от характера нюансов реализации.
Re[4]: Интерфейсы и реализации
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 06.03.24 09:31
Оценка:
Здравствуйте, CreatorCray, Вы писали:

M>>Ну вот выбор канваса на GDI или на GDI+

CC>GDI+ лучше не использовать совсем, уж очень оно тормозное.
CC>Лучше DirectWrite backend добавить если надо всякие украшательства.

Для DirectWrite надо всё переделывать, а GDI+ на том же HDC, малой кровью добавилось


M>>У меня есть проект с движком squirrel, туда shared_ptr<IInterface> прокидывается

CC>А внутри как используется?

В смысле?


M>>Ну, может быть, но это в большинстве случаев удобно.

CC>Зависит от размеров проекта. Маленькое шевелится и даже выходит что то вроде WPO/IPO, но на большом компилер таким может подавиться.

Ну, /bigobj пришлось таки добавить, но в целом, пока справляется
Маньяк Робокряк колесит по городу
Re[4]: Интерфейсы и реализации
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 06.03.24 09:35
Оценка:
Здравствуйте, CreatorCray, Вы писали:

M>>Изначально я закладывался на то, чтобы работало даже на самой древней винде.

CC>А о каком типе софта вообще речь то кстати?
CC>Ну т.е. что оно делает?

Десктопный софт


M>> Я вообще ещё и 9X по привычке поддерживаю в некоторых проектах, но в данном проекте решил, что хотя бы WinXP SP3.

CC>А в этом есть реальная необходимость? Ну т.е. твоя клиентская база всё ещё на 32битных виндах сидит?

Мой потенциальный потребитель — человек не богатый, не компьютерщик, и дома у него легко может оказаться Win7 на x86, а то и та самая XP


M>>GDI+ прикрутил позже для большей гладкости изображаемого, и оказалось, что оно вообще тормоз (а со шрифтами так вообще откровенно хуже)

CC>Угу, вот только нафига? Cleartype шрифты обычный GDI умеет и при этом ещё и быстро.

GDI со шрифтами работает быстро, да, и при этом качество лучше, чем у GDI+, который мылить пытается, но вот линии и заливки с блюром по краям GDI+ красиво делает


M>> поэтому сделал возможность в динамике переключаться.

CC>Лучше бы не добавлял вовсе.

GDI в блюр не умеет, и это выглядит иногда ужасно
Маньяк Робокряк колесит по городу
Re[2]: Интерфейсы и реализации
От: vopl Россия  
Дата: 06.03.24 10:13
Оценка: 10 (1)
Здравствуйте, watchmaker, Вы писали:

W>... Некоторые проекты обмазываются целиком FastPimpl и не тратят место на лишние указатели, получают возможность создавать классы в том числе и на стеке, и не показывают при этом в интерфейсе как они внутри устроены. Больше действий по поддержке, но в целом терпимо.


Как то раз делал себе FastImpl (с блэкджеком конечно же ), назвал его Hidden Impl или коротко himpl. Основная задача которая на него возлагалась — фаервол компиляции без дополнительной косвенности и динамической памяти как в случае с pimpl. Получилось довольно приятно, по сравнению с наивным FastImpl удалось добиться следующих фишек
— определение тактико-технических характеристик объекта (размер, выравнивание, наличие таких/сяких типичных конструкторов/операторов, ...) автоматически, без ручного участия
— поддержка большинства вариантов наследования (кроме виртуального и множественного+полиморфного)
— поддержка полиморфных типов в иерархиях наследования (только нельзя их множественно наследовать)

из минусов
— действительно надо некоторое количество бойлерплейта отписывать
— реализация частично выходит за рамки C++, в моем случае это CMake, в нем часть функционала которую не так легко будет портировать на другие системы сборки
— немного рантайм оверхеда при разрушении сложно-наследованных объектов

пример использования можно посмотреть тут
— интерфейс https://github.com/vopl/dci-core-cmt/blob/master/include/dci/cmt/event.hpp#L15
— бойлерплейт плюсовой части https://github.com/vopl/dci-core-cmt/blob/master/src/event.cpp
— реализация https://github.com/vopl/dci-core-cmt/blob/master/src/impl/event.hpp#L15
— бойлерплейт CMake части https://github.com/vopl/dci-core-cmt/blob/master/CMakeLists.txt#L156-L198

сама библиотека тут https://github.com/vopl/dci-core-himpl
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.