Про type-safe union разговор закрыли за невозможностью получить оный без шаблонов, правильно?
lpd>Писать код чтобы продемонстрировать полиморфизм, и его отличия от шаблонов?
Нет, просто чтобы можно было сопоставить два листинга рядом и увидеть разницу.
S>>Т.е. мы создаем result в динамической памяти? Дергаем хип и создаем лишнюю косвенность там, где можно было обойтись передачей/перемещением значения.
lpd>Ну все, фанатика move-семантики я переубедить не берусь.
Когда сказать нечего начинают ярлыки развешивать.
lpd>В каждом конкретном случае такую проблему можно решить без умных указателей — вариантов решений много, включая подсчет ссылок.
Во-первых, подсчет ссылок -- это уже умные указатели и есть.
Во-вторых, изначально речь шла про unique_ptr, который, в обычных случаях (т.е. со штатным deleter-ом), стоит столько же, сколько обычный голый указатель, и не требует ручного контроля за временем жизни. Но у вас же какие-то религиозные предубеждения против шаблонов и умных указателей. Потому остается закатывать Солнце вручную.
Простите, но с такими взглядами можно было программировать где-то до 1994-1995-х годов. С тех пор уже больше 20 лет прошло. Пора уже научиться пользоваться благами цивилизации.
Здравствуйте, night beast, Вы писали:
NB>и чем замена ссылки на вариант лучше исходного кода? (кроме добавления лишнего мува)
Тем что можно вынести реализацию в cpp, лишнего мува там вроде нет, когда ты вызываешь к-тор reply_t, тебе нужно использовать move, без него будет копирование.
Вариант с variant в к-торе будет выглядеть так же, в точке использования, так как к-тор std::variant не помечен как explicit. Т.е. и там и там будет reply_t foo(std::move(value));
Здравствуйте, chaotic-kotik, Вы писали:
NB>>и чем замена ссылки на вариант лучше исходного кода? (кроме добавления лишнего мува)
CK>Тем что можно вынести реализацию в cpp, лишнего мува там вроде нет, когда ты вызываешь к-тор reply_t, тебе нужно использовать move, без него будет копирование.
с чего бы вдруг? там передача по ссылке.
CK>Вариант с variant в к-торе будет выглядеть так же, в точке использования, так как к-тор std::variant не помечен как explicit. Т.е. и там и там будет reply_t foo(std::move(value));
и потом этот variant будет муваться в член класса. итого 2 мува.
Здравствуйте, so5team, Вы писали:
S>Здравствуйте, chaotic-kotik, Вы писали:
CK>>мой вариант он проигнорировал, так что, делаем выводы
S>А разве был какой-то вариант? Передача std::variant прямо в конструктор reply -- это так себе выбор. Поскольку хранение result-а именно в виде variant -- это, по сути, деталь реализации reply_t. Не есть хорошо, что эта деталь уже торчит наружу. Но прибивать к ней гвоздями еще и конструктор reply... Кому как, короче говоря.
У тебя весь код живет в hpp, т.е. наружу торчит вообще все. Т.е. представить что код, использующий reply_t не знает про std::variant — невозможно.
std::variant — штука из стандартной библиотеки, не вижу особой проблемы в том, чтобы передавать его. Ты же не пытаешься спрятать std::string как деталь реализации?
S>Этот пример достаточно компактен и понятен. Как раз то, что нужно для ленивого обсуждения на форуме. И то, даже аналог этого простого случая никто вменяемо не без шаблонов переписать не смог (ну нельзя же вот это
Даже у этого примера есть преимущество перед твоим. Он будет работать везде. Твой вызовет проблемы даже на некоторых х86 дистрибутивах linux (c gcc 4.x), не говоря уже про всякие контроллеры.
Здравствуйте, night beast, Вы писали:
CK>>Тем что можно вынести реализацию в cpp, лишнего мува там вроде нет, когда ты вызываешь к-тор reply_t, тебе нужно использовать move, без него будет копирование.
NB>с чего бы вдруг? там передача по ссылке.
Здравствуйте, chaotic-kotik, Вы писали:
S>>А разве был какой-то вариант? Передача std::variant прямо в конструктор reply -- это так себе выбор. Поскольку хранение result-а именно в виде variant -- это, по сути, деталь реализации reply_t. Не есть хорошо, что эта деталь уже торчит наружу. Но прибивать к ней гвоздями еще и конструктор reply... Кому как, короче говоря.
CK>У тебя весь код живет в hpp, т.е. наружу торчит вообще все. Т.е. представить что код, использующий reply_t не знает про std::variant — невозможно.
Не нужно выдавать свои проблемы с фантазией за объективные факторы:
То, что все это в hpp-файле не играет никакой роли, когда речь заходит о публичных и приватных полях/методах класса.
CK>Даже у этого примера есть преимущество перед твоим. Он будет работать везде.
Будет ли он работать -- это еще вопрос прямизны рук разработчика.
CK>Твой вызовет проблемы даже на некоторых х86 дистрибутивах linux (c gcc 4.x), не говоря уже про всякие контроллеры.
Ну что поделать, придется кросскомпиляцией заниматься.
Здравствуйте, chaotic-kotik, Вы писали:
NB>>я тебе просто показал, откуда дополнительный мув взялся
CK>я не распарсил, думал что ты имеешь ввиду что в первом случае std::move для параметра использовать не нужно CK>и что передается по ссылке?
в исходном коде мув один. в твоем -- два (один неявный)
Здравствуйте, chaotic-kotik, Вы писали:
CK>ты хочешь сделать вид, что в месте вызова on_success <variant> не подключен?
Может пора дурочку выключить? Если вам действительно что-то непонятно про разницу в использовании некоторого типа A в реализации некоторого типа B, и в интерфейсе этого типа B, то спросите что именно непонятно.
Или для вас принципиально то, чтобы в заголовочном файле с определением reply_t вообще из стандартных заголовочных файлов ничего не было подключено (т.е., чтобы pimpl во все поля)? Тогда ваша же идея на счет использования std::variant прямо в конструкторе оказывается несостоятельна. Вы уж определитесь.
S>>Ну что поделать, придется кросскомпиляцией заниматься.
CK>занимался ли ты на практике чем-то подобным? так чтобы с зависимостями и сборкой пакетов под разные дистрибутивы?
Что вы тянете, сразу длину в сантиметрах пишите. Ну или в миллиметрах, чтобы еще солиднее было.
A>>Там не было желания общаться конструктивно.
S>По вашим сообщениям с фразами "Сам дурак" и "Ну это я прикололся" было заметно, что у вас нет желания общаться конструктивно.
"Сам дурак" произносится, когда сначала было слово "Дурак".
Да, фейспалм — это тоже "Дурак".
A>>>>Давай рассказывай, почему в данном случае нельзя обойтись одним битом вместо всех наворотов.
A>>Я сделаю вот так: A>>
A>>struct result_t {
A>> bool successful ;
A>> A a_;
A>> B b_;
A>> C c_;
A>> D d_;
A>> E e_;
A>>// Конструктор/деструктор и пр. лабуда.
A>>};
A>>
S>Ожидаемо. Сейчас даже не будем говорить за оверхед по памяти/времени, т.к. в данном конкретном случае это не существенно. S>Как вы собираетесь обеспечить гарантии того, что в случае неудачного результата программист не будет обращаться к полям a_, b_ и c_.
Ремнём по жопе. Этот способ всегда работает.
Есть другой способ — присваивать данным осмысленные значения даже в случае неуспеха
(например пустые массивы, нулевые строки и т.п.).
И пусть обращается. Программа продолжит работать. И даже правильно.
И даже не нужна специальная обработка ошибок.
Кстати, по поводу оверхеда. По моему опыту у темплейтов оверхеда больше.
Потому что копи-пастинг со всеми вытекающими.
S>Ну а тема дальнейшего расширения набора результатов в вашем подходе вообще открывает какие-то бездны фейспалма.
Сам дурак (это Вам за фейспалм).
В данном случае результат может быть либо успешный, либо неуспешный.
Какое тут расширение? Зачем?
Вы вообще, попробуйте сформулировать, что такое "результат",
и зачем Вы на каждый новый результат новый тип плодите.
A>>А если совсем-совсем жалко места (ну это совсем скупердяем надо быть) мы умеем использовать слово union.
S>Судя по тому, что вы уже написали, вы вряд ли можете что-то использовать что-то сложнее bool-а и int-а.
Опять хамите, а потом удивляетесь, что сам дурак. — Сам дурак.
A>>Поэтому мне не нужно решать тех проблем, которые есть в случае с темплейтами.
S>И какие же это проблемы?
Ну очевидно же — расплодили типов на ровном месте.
A>>Зачем Вы опять делаете навороты на ровном месте?!
S>Это не навороты. Это опыт. Не только наш.
Это ненужные навороты. Так говорит опыт. И не только наш.
Здравствуйте, alpha21264, Вы писали:
A>Да, фейспалм — это тоже "Дурак".
Вы сначала разберитесь кто вам фейспалмы показывает, а потом "Дураками" раскидывайтесь. А то ведь складывается впечатление, что вы с кем-то другим пытаетесь говорить.
A>Ремнём по жопе. Этот способ всегда работает.
Очевидно недостаточно, раз столько багов в софте. Или в вашем софте багов нет?
A>Есть другой способ — присваивать данным осмысленные значения даже в случае неуспеха A>(например пустые массивы, нулевые строки и т.п.).
Вы представляете, а при использовании sum types (это которые и есть std::variant) этого делать не нужно. Прогресс таки не стоит на месте.
A>И пусть обращается. Программа продолжит работать. И даже правильно.
Это сейчас больше на молитвы похоже.
A>Кстати, по поводу оверхеда. По моему опыту у темплейтов оверхеда больше. A>Потому что копи-пастинг со всеми вытекающими.
Это у вас представления о шаблонах из начала 90-х. Но в вашу криокамеру, наверное, ничего нового не попадает.
A>В данном случае результат может быть либо успешный, либо неуспешный.
Это вам так кажется.
A>Ну очевидно же — расплодили типов на ровном месте.
По сравнению с перечислением вообще всех полей в одном месте -- это не то, что не проблема, это даже не мелкие неприятности.
A>Это ненужные навороты. Так говорит опыт. И не только наш.
Судя по коду из начала 90-х у вас и опыт оттуда же. Между тем в мире нового опыта накоплено масса. На основании которого делают folly::Expected, boost::outcome и предложения по добавлению дешевых исключений в C++. Казалось бы с чего бы это?
S>Может пора дурочку выключить? Если вам действительно что-то непонятно про разницу в использовании некоторого типа A в реализации некоторого типа B, и в интерфейсе этого типа B, то спросите что именно непонятно.
У меня в проекте тысячи единиц трансляции, допустим этот класс reply_t должен использоваться в десяти, от которых зависит еще 100, я подключаю reply.h с этим классом, а там — #include <variant>, соответственно, все это добро подключается в 100 разных единиц трасляции. При этом совершенно фиолетово где используется variant, в реализации, или в реализации и интерфейсе.
S>Или для вас принципиально то, чтобы в заголовочном файле с определением reply_t вообще из стандартных заголовочных файлов ничего не было подключено (т.е., чтобы pimpl во все поля)? Тогда ваша же идея на счет использования std::variant прямо в конструкторе оказывается несостоятельна. Вы уж определитесь.
Мне принципиально называть вещи своими именами. Не важно, где используется шаблон. Зависимость это зависимость. По этому, с моей точки зрения, мой вариант лучше, т.к. он позволяет уменьшить reply.h, спрятав часть кода в cpp файл.
Если бы я писал этот класс для своего текущего проекта, ***_result_t были бы наследниками абстрактного класса и в реализации класса был бы уникальный указатель на base_result_t вместо variant. Это позволило бы, например, иметь реализации base_result_t отличные от failure_result_t и success_result_t, причем даже такие, о которых "знает" только какой-то один компонент. Можно было бы поменять реализацию без перекомпиляции сотни единиц трансляции, которые зависят от reply_t.
S>Что вы тянете, сразу длину в сантиметрах пишите. Ну или в миллиметрах, чтобы еще солиднее было.
С восхищением смотрю на людей, успешно что-то кросс-компилирующих, особенно в нетривиальных случаях. Я ни разу не смог настроить CI так, чтобы без докера и отдельных серверов с ARM и Solaris для сборки под ARM и Solaris.
Здравствуйте, chaotic-kotik, Вы писали:
CK>У меня в проекте тысячи единиц трансляции, допустим этот класс reply_t должен использоваться в десяти, от которых зависит еще 100, я подключаю reply.h с этим классом, а там — #include <variant>, соответственно, все это добро подключается в 100 разных единиц трасляции. При этом совершенно фиолетово где используется variant, в реализации, или в реализации и интерфейсе.
Если определение одного типа сказывается на таком количестве единиц трансляции, то у вас проблемы и без шаблонов.
Ну и ваш вариант с передачей std::variant в конструктор напрямую здесь ничего не исправит.
CK>Мне принципиально называть вещи своими именами. Не важно, где используется шаблон. Зависимость это зависимость. По этому, с моей точки зрения, мой вариант лучше, т.к. он позволяет уменьшить reply.h, спрятав часть кода в cpp файл.
Вы сейчас точно в трезвом уме? Предлагаемый вами вариант выглядел бы так:
И ничего бы вы с "зависимостью" от std::variant не сделали бы.
CK>Если бы я писал этот класс для своего текущего проекта, ***_result_t были бы наследниками абстрактного класса и в реализации класса был бы уникальный указатель на base_result_t вместо variant. Это позволило бы, например, иметь реализации base_result_t отличные от failure_result_t и success_result_t, причем даже такие, о которых "знает" только какой-то один компонент. Можно было бы поменять реализацию без перекомпиляции сотни единиц трансляции, которые зависят от reply_t.
Да, если бы были сотни единиц трансляции, вам было бы пофигу на возможных наследников base_result_t и полноту обработки этих наследников, тогда да, можно было бы и без шаблонов. По счастью, у нас другая ситуация.
Здравствуйте, so5team, Вы писали:
S>Если определение одного типа сказывается на таком количестве единиц трансляции, то у вас проблемы и без шаблонов. S>Ну и ваш вариант с передачей std::variant в конструктор напрямую здесь ничего не исправит.
Я не утверждал что он исправит. Поинт был в том, что зависимость есть зависимость и если используешь шаблоны, то приходится размещать код в hpp файлах, а значит увеличивать количество зависимостей и связность, даже если зависимости в реализации, а не в интерфейсе.
Для твоей библиотеки (ссылка выше была), это в общем-то и не проблема. Если бы мне нужно было использовать ее в своем big-ass проекте, я бы просто подключил ее в одном срр и показал наружу какой-нибудь удобный мне интерфейс. Но вот для вещей, которые активно используются внутри проекта практически везде, мы так не делаем. Вот открыл сейчас хедер в рабочем проекте, а там первые 200 строк вот такого:
namespace Foo {
namespace Bar { class A; class B; }
namespace Buzz {
class C;
...
}
}
и на ревью, если ты что-то изменил в хедере, у тебя первым делом спросят — а можно ли было обойтись без этого изменения?
S>Вы сейчас точно в трезвом уме? Предлагаемый вами вариант выглядел бы так: S>
Здравствуйте, chaotic-kotik, Вы писали:
CK>Для твоей библиотеки (ссылка выше была), это в общем-то и не проблема. Если бы мне нужно было использовать ее в своем big-ass проекте, я бы просто подключил ее в одном срр и показал наружу какой-нибудь удобный мне интерфейс. Но вот для вещей, которые активно используются внутри проекта практически везде, мы так не делаем. Вот открыл сейчас хедер в рабочем проекте, а там первые 200 строк вот такого:
Очень и очень хрупкий подход, особенно для кодовых баз, которые живут и активно эволюционируют больше 5 лет.
Но если компиляция занимает часы, то альтернативы придумать не так уж и просто.
CK>Я этот код воспринял как toy example, честно говоря.
Не совсем. Это кусочек из маленького proof-of-concept проекта объемом в 1.5KLOC. На таких масштабах выводить иерархию result-ов -- это оверкилл. Даже если объем этого проекта увеличится в 100 раз, то и в этом случае проблем с шаблонами быть не должно.
Здравствуйте, chaotic-kotik, Вы писали:
CK>Для твоей библиотеки (ссылка выше была), это в общем-то и не проблема. Если бы мне нужно было использовать ее в своем big-ass проекте, я бы просто подключил ее в одном срр и показал наружу какой-нибудь удобный мне интерфейс. Но вот для вещей, которые активно используются внутри проекта практически везде, мы так не делаем. Вот открыл сейчас хедер в рабочем проекте, а там первые 200 строк вот такого:
CK>namespace Foo {
CK>namespace Bar { class A; class B; }
CK>}
Здравствуйте, smeeld, Вы писали:
S>Заметил, что у большинства практикующих C++ достаточно негативное отношение к шаблонам в CPP. Речь идёт не о использовании std или boost, которые состоят из таковых процентов на 80, а о написании кода с активным применением параметризации функций и типов. Это многими считается чуть ли не признаком недостатка квалификации, типа матёрый плюсовик всегда найдёт способ расписать без создания параметризованных типов, и вообще найдёт способ писать на C++ как можно проще. Это вообще что такое? Если принять это мнение за справедливое, то получается, что std и boost писали джуны несмышлённые и не знающие как просто писать на CPP?
В соседней теме вести с полей о потенциальных нововведениях в c++20, и там довольно много всяких страшных слов для "шаблонных хейтеров" , выделю наиболее ужасные на мой взгляд:
Контракты и друзья
Концепты (без друзей)
Что нового можно писать в шаблонах и чем это полезно
constexpr virtual foo()
Reflection
Куда можно будет засунуть восклицательный знак и чем это может быть полезно
тотальное нашествие constexpr в стандартную библиотеку
Самым ужасающим будет (скорее всего в c++20 не войдет, позже) Reflection. По сравнению с ним — текущие "шаблоны" — просто детский лепет. Так что, "большинству практикующих C++" придется либо переключить негатив c "шаблонов" на более ужасные вещи, либо, если они уж и впрямь сильно практикуют — расширить, включив туда, кроме "шаблонов" — еще и эти новые страшилки
Здравствуйте, smeeld, Вы писали:
S>Заметил, что у большинства практикующих C++ достаточно негативное отношение к шаблонам в CPP.
Ну, на счет большинства — это ты погорячился, имхо.
S>Речь идёт не о использовании std или boost, которые состоят из таковых процентов на 80, а о написании кода с активным применением параметризации функций и типов. Это многими считается чуть ли не признаком недостатка квалификации, типа матёрый плюсовик всегда найдёт способ расписать без создания параметризованных типов, и вообще найдёт способ писать на C++ как можно проще. Это вообще что такое? Если принять это мнение за справедливое, то получается, что std и boost писали джуны несмышлённые и не знающие как просто писать на CPP?
Святая церковь тоже поначалу выступала против электричества: было большое количество травм и несчастных случаев. И вообще, без него вполне можно было обходиться — и воды натаскать, и скотину покормить. А электричество нужно было только тем, кто хотел повыпендриваться.
--
Не можешь достичь желаемого — пожелай достигнутого.