Пора на свалку


    Никогда не думал, что это случится со мной, но, похоже, я выгорел. А ещё мне стрёмно. Да, это ещё одна статья про выгорание.


    Я тут на днях смотрел на свою RSS-читалку и заметил, что под тегом «C++» у меня где-то три сотни непрочитанных статей. Я не прочитал ни одной статьи по плюсам с прошлого лета, и мне офигенно. Я не написал ни строчки осмысленного кода на плюсах за последние три месяца, с тех пор, как распустили отдел, где я работал, и мне просто супер. Я позволил себе хотеть больше никогда не писать на плюсах, и у меня появились крылья.


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


    Короче, да, я выгорел. И я не знаю, что делать дальше.


    Я очень люблю плюсы. Помню, как схомячил учебник Стивена Праты за неделю поездок в школу в седьмом классе (ну хоть что-то хорошее от дороги в полтора часа в один конец). Помню, как читал распечатку туториала по ACE (помнит ли сейчас кто-то ACE?) на какой-то школьной экскурсии классе в девятом. Потом у меня появился КПК, куда я мог залить chm'ку Саттера и читать её на пьянке у друга на даче (куда меня почему-то больше не приглашали) классе в 11-м. Примерно тогда же я уже и начал зарабатывать первые программистские деньги — снова спасибо плюсам. Мои самые светлые воспоминания того периода — бессонные ночи и встречи рассвета под чтение того же Саттера, Мейерса, Александреску, каких-то статей на RSDN, под написание собственного кода. Я был наивным и не думал о будущем, но я ощущал, что оно у меня есть, и что я его кую вот прямо сейчас. Что все эти бессонные ночи и кодомарафоны на сутки и больше — это моё.


    Я люблю то время и благодарен мирозданию за то, что оно у меня было.


    Но теперь пришла усталость.


    Просто надоело ходить по минам. Нет, не по минному полю, а именно по минам, потому что на минном поле есть участки без мин. 17 лет назад, когда я начинал, писать свой вектор было весело и прикольно, и я мог это сделать за полчаса, а сейчас мне нужно несколько дней и постоянная консультация со стандартом. Тварь я дрожащая или право имею? Получу UB тут или нет? А тут? А тут? Да что там вектор, мы не так давно с khim довольно долго обсуждали, как написать строку, и едва смогли определиться с тем, каким может (или должен) быть тип элементов строки, чтобы не было алиасинга.


    Просто надоело смотреть на чужие культи. На моей позапрошлой работе они все были так молоды, вся жизнь впереди хорошие чуваки, и они хорошо знают своё дело — data science, статистику, предметную область, что угодно — но на плюсах они просто отстреливают себе ноги. Тяжело смотреть. И это бессмысленно. Там не нужно то, что плюсы могут дать, там не нужна высокая производительность (всё равно они пишут клей), не нужна близость к железу. К счастью, очередной пришедший тимлид это понял и перевёл всех на питон. Правда, тогда мне пришлось уйти, питон — не мой язык.


    А, да, производительность — эту самую топовую производительность плюсы тоже не могут дать.


    Сравнить два POD одним memcmp? Нельзя.
    Использовать строки, где внутри char*, не думая об алиасинге? Нельзя.
    Передавать unique_ptr в функцию, рассчитывая, что оверхеда не будет, как говорят из каждого утюга? Нельзя.
    Написать свой вектор для trivially copyable/constructible/etc types, который бы не рассчитывал на то, что компилятор вырежет часть необходимых по стандарту вещей? Нельзя.
    Вообще писать современный код, пытаясь при этом быть идиоматичным, но не рассчитывая на то, что компилятор свернёт все эти слои шаблонных конструкций? Нельзя.


    Нельзя. Нельзя. Нельзя. Спасибо, но рассчитывать на достаточно умный компилятор приятнее в других языках.


    Что там ещё? Автовекторизация? Держи карман шире! Даже в простейших случаях уровня «пробежаться по строке и подсчитать количество вхождений символа» (ассоциации подкидывают название файла laba1.cpp) рукописный ассемблер оказывается быстрее того, что генерирует gcc -O3 -march=native с нестандартными расширениями и дёрганьем интринсиков (а что остаётся от плюсов с интринсиками? шедулинг регистров?). Незначительно, на 2-5%, но и я не спец в ассемблере на этом уровне. Без интринсиков и оставаясь исключительно в рамках стандарта? Тогда проигрыш в 2-3 раза. Ладно, это с кодом, про это я скоро напишу отдельную статью.


    Даже выделить место под тривиальный тип и выmemcpyть туда набор байт — нельзя без дополнительных приседаний, лайфтайм так не начнётся. До C++20. В C++20 начнётся, спасибо, починили, но когнитивная нагрузка языка от этого только выросла.


    Когнитивная нагрузка вообще только вырастает, даже когда какие-то вещи исправляют, потому что я же, мать его, специалист, я же должен знать, что починили, когда починили, и как было раньше. Да и C++ хорош поддержкой легаси, а это значит, что ты с этим легаси можешь встретиться. Например, в прошлом месяце ко мне обратился приятель с вопросом о том, как что-то сделать в C++03.


    И эта когнитивная нагрузка — она не потому, что задачи того требуют. Это не внутренняя непосредственная сложность предметной области. Это просто, ну, так исторически сложилось.


    Смотреть на трёхэтажные конструкции из костылей, кстати, тоже надоело. Было 20 способов инициализации, добавили uniform initialization syntax, стал 21 способ инициализации, но это всё ещё недостаточно uniform, поэтому в C++20 мы сделаем uniform из обычных круглых скобочек. Кстати, кто-нибудь сходу помнит правила выбора конструкторов с initializer list? Что-то там про неявные преобразования с потерей точности, но если значение известно статически, то…


    Я постарался выработать правило — если мне сходу неочевидно, что код делает, если мне приходится вспоминать стандарт, если я чувствую неуверенность, то не надо так писать код. Поэтому у нас есть uniform initialization syntax, но я пишу std::vector<int> foo(n, val), без всяких фигурных скобочек, потому что я не хочу думать, как интерпретация зависит от определения n и val. И мне стыдно так писать, потому что все посоны на раёне пишут посты в блогах, где всё красиво, с фигурными скобочками, со всеми новыми финтами.


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


    Да и проблемы возникают не только у меня.


    • Вот NeoCode написал классную статью про концепты в C++20. requires C1<T::type> || C2<T::type> не то же самое, что requires (C1<T::type> || C2<T::type>) — не прелесть ли? Ну это чтоб не расслабляться, видимо. (╯°□°)╯︵ ┻━┻
    • Или вот чувак описывает, почему при переходе на gcc-10 сломался vim. Спойлер: потому что там UB, которым компилятор пока что не пользуется, да и то не факт. Починено вот так: «The workaround vim uses to avoid these failures is to disable buffer overflow checks from being emitted by using -D_FORTIFY_SOURCE=1 define». Это vim, да. Ъ-юникс-хакеры, трушнее некуда.
    • Или вот ESR (если эти инициалы о чём-то говорят) пишет, что C кончается.
    • Или вот веселуха, например.
    • Или вот хороший перевод. И да, я видел код, который ломался из-за похожих вещей.
    • Или на днях у чуваков из PVS Studio была статья, где в комментах подискутировали, когда можно использовать memcpy/memset, а когда нельзя. Если разработчики статического анализатора ошибаются в таких довольно прямолинейных вещах, то кто и когда тогда вообще не ошибается?
    • Или вот, кстати, memset в плюсах таки пользоваться нельзя. Никогда. Лично мне потребовалось поковырять стандарт примерно час, чтобы убедить себя, что нельзя (как же так, я всегда думал, что можно!). Чуваки на канале про плюсы на фриноде сначала сказали, что можно, потом обсуждение свернулось в сторону «прост не используй memset и не думай)))». Потом, правда, ещё один чувак таки попытался разобраться в проблеме, и попытался доказать, что memset на плюсах использовать таки можно, потому что алиасинг (и что?). Но на практике, конечно, всем пофиг.

    А сколько CVE и прочих уязвимостей и падений появилось из-за того, что где-то не так с указателем поступили, где-то проверка не там была, где-то… Чёрт, как жаль, что я не сохраняю эти вещи.


    Короче, это была усталость.


    Потом я понял, что мне страшно. У меня развился паралич.


    Прошлым летом я устроился на совершенно шикарную работу, моими коллегами были умные, шарящие люди, ежедневно читающие блоги плюсистов и новые пропозалы в стандарт, обсуждающие на ланче что-то из программирования и каких-то новых фишек, а не то, как они провели выходные или какие планы на следующие. Для которых «так нельзя, это потенциальное UB» — не пустой звук, а повод потратить два дня на реализацию фичи вместо двух часов и искренне поблагодарить меня, что я обратил внимание. Но даже они делали ошибки. Я делал ошибки. Мы все делали ошибки. Они не знали то, что я считал базовыми знаниями. Я не знал то, что они считали базовыми знаниями. Никто ничего не знает. Если они не могут, то кто может?


    Мне страшно. Мне хочется забиться в угол и плакать. Я не могу быть уверенным как минимум в половине строк, что я пишу. У меня есть чувство, что я строю фекалодендритные конструкции, а убеждение хотя бы самого себя в том, что написанное имеет смысл, занимает неоправданно много времени. Что бы я ни делал, в моём коде будут UB. Я ни на что не могу повлиять. Психологи говорят, что выученная беспомощность тут где-то рядом, так что написание кода на плюсах для психики не очень полезно.


    Иронично, что больше всего уверенности у меня в наименее нужных в среднем продакшен-коде вещах — что-то там на темплейтах этакое намутить, на constexpr вот. Может, это потому, что вся эта мета-ерунда куда ближе к так близкому сердцу принципу «если оно компилируется, то оно работает».


    А, кстати о темплейтах. Рабочий проект, где каждый .cpp-файл компилируется по 5-7 минут даже без оптимизаций? Время до первой диагностики компилятора в те же 5 минут? Пердёж компилятора на десятки мегабайт в случае ошибок? Да, я сохранял в файл и замерял ради интереса. Потребление памяти компилятором в 5-10 гигов на файл? Билдсервер с 32 ядрами и 64 гигами памяти, на котором нельзя запускать больше чем этак 8 параллельных потоков компиляции? Проект на несколько десятков kloc, собирающийся на ней полчаса? Получите, распишитесь.


    И тулинг. Мне куда проще находить, на что у меня тратится память, в том же хаскеле, который, как известно, только для факториалов и годится. Системы сборки? Ха. Апгрейд компилятора для прода? Жди лет пять после релиза стандарта. Пакетный менеджер? Ха-ха. Reproducible builds? Ха-ха-ха. Все места, где я работал, на это либо вообще забивали, либо вкладывали какое-то совершенно неадекватное количество ресурсов. Я понимаю, почему так происходит, у этого всего есть абсолютно логичные и объективные причины, по-другому и выйти не могло, но я устал так жить.


    Это был страх от языка, но я пропустил его через себя. Три месяца назад меня уволили, я пришёл домой, расслабился, вот уже три месяца пишу код для себя (не на плюсах!), сделал пару мелких проектов и начал один покрупнее, и мне хорошо.


    Было хорошо.


    Совсем недавно я поймал и осознал страх другого рода, куда более экзистенциальный.


    Кто я такой? Я программист на плюсах. Я пишу код на плюсах две трети жизни. Это действительно стало огромной частью моей самоидентификации. Почему я претендую на синиора? Потому что я успел отстрелить себе миллион ног и натаскать свою нейросеть в черепушке определять, какая нога отстрелена на этот раз, по отсвету дульной вспышки. Потому что все интервью, касающиеся языка, я проходил, как тут говорят, with flying colors. Потому что я могу даже не готовиться к интервью, и всё равно его пройду. Потому что я могу показать особо интересные куски кода на гитхабе, и для меня скипнут как минимум телефонное интервью, а иногда скипали и техническое собеседование, разговаривая только о жизни или в лучшем случае о дизайне систем, релевантных для места работы.


    А так придётся выкидывать весь этот опыт в корзину и начинать почти с нуля. Ну, да, у меня есть какое-то там системное мышление, я дизайнил компиляторы, я дизайнил распределённые системы с противоречивыми требованиями, это, наверное, тоже кусок синьористости, но если меня разбудить ночью и спросить «почему ты претендуешь на синьора», я отвечу «потому что сиплюсплюс». Ко мне обращаются коллеги, когда там какой-то адовый баг или что-то не работает или надо что-то сделать на темплейтах или хорошее код-ревью бы. Это давно уже так — помню, ещё где-то на третьем курсе, когда я отрабатывал пропуски по физкультуре заплывами в бассейне, ко мне подплыл какой-то чувак и спросил: «а это ты 0xd34df00d? у меня тут вопрос по boost…»


    Это просто прорва времени и сил, которая выкидывается вникуда. Это непереносимый опыт (каламбур, ага).


    Ну вот пойду я сейчас собеседоваться на чистого хаскелиста какого-нибудь, или вообще в ресёрч. Что я там скажу? На кого вообще претендовать? На каком основании? Что-то там типы что-то там алгебра что-то там математика. Но разве это сравнится по сложности со стандартом плюсов? Разве сравнятся мои тамошние навыки с моим опытом в плюсах? 17 лет плюсов против этак лет 8-10 хаскеля. Против этак лет трёх какой-то математики (примат в вузе не считается).


    Кто я, если выкинуть плюсы?


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


    Короче, да, мне немного страшно, и я устал. Я немножко стал динозавром. Когда-то я непонимающе смотрел на людей, которые в гробу видали все новые вещи, а теперь я замечаю что-то похожее в себе. Я не нахожу в себе силы читать новые вещи о C++, и только о C++. За последние полгода я поковырял и неплохо проработал два новых для меня языка, я ежедневно либо читаю всякие матанокнижки либо решаю задачки, мой текущий проект — игрушечный proof-of-concept-язык, но меня тошнит при одной мысли о том, чтобы прочитать ещё один пропозал или даже ещё одну пережёванную статью-рерайт cppreference о каких-нибудь нововведениях в C++20.


    Собственно, сначала я думал назвать эту статью «я стал динозавром», но проблема в другом. Те люди — они, кажется, вполне себе счастливы на своём C++03 или C99, или где там у них произошла кристаллизация, а я никак не счастлив и там. Проблема в том, что, получается, моему опыту, моим навыкам, вот этому всему — этому пора на свалку. А если этому пора на свалку, то не пора ли и мне?


    И да, я ни в коем случае не хочу сказать, что плюсы — отстой. Вовсе нет, плюсы прекрасны, это я устал и кончился. Проблема не в тебе, проблема во мне.

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 1247

      +12
      RUSTJavaScript уже советовали?
        +46

        Зря зачеркнули.

          +15

          Так и в чем проблема идти в Rust? Я ни секунду не жалею об этом выборе. Хотя до этого на плюсах писал лет 8. Но от перехода не было даже никакого экзистенциального кризиса, только пара месяцев ломки по некоторым паттернам.
          Зато вот теперь смотрю на плюсы и ощущаю что то похожее на то, что ты описываешь, но уже смотрю со стороны на все это, это уже не часть меня больше.

            +11

            Rust — хороший язык. Но проблемы есть.


            1. Это всё равно выкидывание опыта.
            2. Как-то так сложилось, что раста вокруг меня нет. Вакансии в смежных областях, где можно пользоваться опытом-связями — там скорее хотят видеть плюсы.
            3. Если уж таки переходить, выкидывая опыт и игнорируя эти самые связи, то зачем ограничивать себя растом? Есть и более интересные и приятные языки.

            Хотя, конечно, раст наиболее близок к железу среди прочих языков. Но всегда можно заниматься компиляторами этих самых прочих языков.

              +10

              Ну я прошел через этот путь, но не так уж это выкидывание страшно, это все равно что избавится от legacy, это на самом деле крутое ощущение, ты будто бы с нуля все делаешь и учишься.
              Я вот в конце концов тоже из родного окружения C++/Qt ушел в неизвестность blockchain разработки. А сейчас еще и просто стало много вакансий в области системной разработки на расте, причем именно в штатах. В России то пока немного тухляк, увы.

                +4
                ушел в неизвестность blockchain разработки

                Зря я от одних идрисистов-блокчейнеров отказался с полгода назад, похоже.

                  0
                  Как же я тебя понимаю. Я не программист. Но эмоциональное выгорание — это и моя тема на данный период. Когда вообще не хочется заниматься тем чем занимался и откровенно радуешься, когда занимаешься чем-то другим, не тем, во что вложил несколько лет образования и практической деятельности. И откровенно хочется заняться всем чем угодно, а не этой работой. И таких беспощадных усилий стоит чтобы просто заставить себя что-то сделать. Подсознательно ищешь поводы просто переключиться и не делать свою работу ) Обнимашки, короче, ты не один такой.
                  И какая разница какое время, главное ведь не страдать.
                    +2
                    Мне кажется, это не эмоциональное выгорание. Это скорее объективно задолбало объективно задалбывающее. Несешь себе такой чемодан, вот и ручка оторвалась, и и чем дольше его прешь без ручки, тем меньше хочется переть и жальче бросить(
                      0
                      «Задолбало», если даже «объективно», это и есть эмоции. То, что они конструктивные и объективные, дело не меняет — автор чётко описал, что проблема именно эмоциональная.

                      И да, эти эмоции полезны — в плане изобретения нового и перехода на него.
                        +7

                        Я такого мужика в метро видел. Шел он такой, из последних сил тащил какой-то баул, и тут бах у него ручка отрывается, баул падает. Мужик сначала не понимает, потом не верит, потом краснеет, кричит на него "НУ И НЕСИ СЕБЯ САМ!!!", пинает его, разворачивается и уходит.

                        0
                        О!
                        Нас уже трое!
                        Вы мне не братья? (как в индийском кино) :)

                        И да, кресты (плюсы) я в институте «ниасилил» (1993-й год), остались всякие паскали/модулы/обероны/чуть асма(совсем чуть)/и всё остальное…

                        Последние 27 лет я либо мелкий-средний начальник, либо сисадмин, либо и то и то в одном флаконе. ББС, ФИДО, Релком,…

                        И да: если у вас, ноль-икс-чего-то-там (кажется, какой-то хардварный баг в пентиуме?) 17 — это треть жизни, то калькулятор утверждает, что мы с вами ровесники с огромной точностью… :)

                        У меня сбылась мечта — я полтора месяца работаю из дома. А т.к. у меня всё отстроено хорошо и очень хорошо, то к компу я подхожу редко и очень редко! Ибо работа — как у пожарного, в «негативе» (все работают, я готовлюсь к инцидентам, если я работаю, то кто-то или даже вся контора отдыхает и ждёт, когда я починю), и мне всегда это нравилось.

                        Так вот, в самоизоляции я с огромным удовольствием отхожу от компа, отдыхаю от него совсем. Я даже соцсети свои почти забросил.
                        А до того, лет 10 шутил, что как в Матрице: вынимаю из затылка разъём от рабочего компа, еду домой — вставляю домашний разъём… :) А последние года три 4G и мобила обеспечивали «разъём в дороге»… :)

                        Оказывается, я выгорел (ну, читал ваши сентенции и на себя примерял) профессионально. До сих пор я думал, что все эти эмоции — от конфликтов на работах, от начальников, от бедной жизни от з/п до з/п, а оказывается, корни глубже… :(

                        Спасибо за пару лишних поводов для размышлений, наблюдений, сопоставлений и рефлексии! :)
                        +1
                        Возможно глупость спрошу, не судите — а Scala? Или Java-мир совсем не интересен?
                          +2

                          Ох, Scala лично для меня лежит где-то в uncanny valley на смешении функциональщины и императивщины. Под JVM я бы скорее на Eta писал.

                            +1

                            От многих людей слышал мнение, что скала фпшнее хаскелля. И завтипы, и частичные вычисления по крайней мере в ней реализованы, а в хаскелле пока что полу-фабрикатный LH есть.

                              +4

                              Там же не завтипы, а path-dependent types, разве нет? Пародий на завтипы у меня и в хаскеле достаточно.


                              Ну и у меня, если честно, от её[_] синтаксиса => [вытекают] глаза.

                                0
                                Про Haskell не могу сказать, но Scala одно время сам хотел освоить (в связке с Hadoop и Spark) и глаза не вытекали вроде. Вот R — действительно необычен синтаксисом. Хотя, конечно, все это вкусовщина)))
                                  0

                                  Ну там можно выразить зависимую пару и зависимую функцию, значит завтипы есть :dunno:.


                                  Что до синтаксиса, то тебе ли не знать что это вкусовщина к которой быстро привыкаешь.

                                    0
                                    Ну там можно выразить зависимую пару и зависимую функцию,

                                    Ни то ни другое нельзя.

                                        +2

                                        То, что в интернете кому то что-то показалось — не аргумент, очевидно. Что суммы что произведения из примеров по ссылке — зависят от типов, а не от значений. Просто некоторые некомпетентные люди постоянно путают зависимость от типа-синглтона с зависимостью от значения.

                                          –1

                                          Можно конкретный пример на скале, и, скажем, идрисе, чтобы было видно, вот тут так можно, а вот тут так нельзя?

                                            0
                                            Можно конкретный пример на скале

                                            Нельзя, конечно. Чтобы построить такой пример, мне нужна конкретная формализованная кодировка, которая позволяет любой тип идриса преобразовать в тип на скале. Когда у меня эта кодировка есть — я тогда могу ее взять и показать, например, что это кодировка не покрывает все типы или покрывает некорректно (т.е. то что получается в результате не ведет себя как завтип). А такой кодировки никто не дал.
                                            До того как такая кодировка предоставлена вопрос в принципе бессодержателен — зав. типов в скале нет, ну просто потому что не существует причин считать, что они там есть. На данный момент просто нечего обсуждать. А если ты попробуешь сделать такую кодировку и доказать ее корректность, то отсутствие завтипов в скале тебе станет полностью очевидно, если ты конечно не полный дебил и имеешь представление о предмете в целом.


                                            А тебе, бро, лично совет — прекрати упарываться идрисами, матлогом и прочим и займись базой. Алгебра, топология, дифгем — нормально, с задачками, приложениями и т.п.
                                            Потому что из-за отсутствия базовых знаний у тебя в голове вместо понимания остается дерьмо. Смотришь в книгу — видишь фигу.

                                              0
                                              А тебе, бро, лично совет — прекрати упарываться идрисами, матлогом и прочим и займись базой. Алгебра, топология, дифгем — нормально, с задачками, приложениями и т.п.
                                              Потому что из-за отсутствия базовых знаний у тебя в голове вместо понимания остается дерьмо. Смотришь в книгу — видишь фигу.

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


                                              Просто некоторые некомпетентные люди постоянно путают зависимость от типа-синглтона с зависимостью от значения.

                                              По такому определению люди которые пытаются запилить в хаскель завтипы занимаются ерундой, ведь они как раз это и пытаются реализовать.

                                                +1
                                                Ну и я не очень понимаю, как оно тут поможет

                                                Тем, что без этого у тебя нет дисциплины, которая позволяет нормально валидировать т.н. "очевидные" утверждения. Когда тебе челиус пишет на стековерфлоу, что "вот у нас тут тип зависит от терма" — тебе в голову-то не пришло задуматься, от каких термов он зависит и от каких — должен зависеть, чтобы быть зависимым?


                                                Хинт: на месте типового аргумента в случае зависимых типов может вставать любой терм (или, как минимум, это должно быть какое-то "интересное" подмножество — тогда конечно все равно полноценных зависимых типов не будет, но будет "интересный" фрагмент). Это значит, что если твоя "зависимость" выражается "path'eм", то этот "path" должен уметь кодировать любой терм скалы (либо "интересное" подмножество термов). А оно чо кодирует? Нихера не кодирует.
                                                Это опуская другие проблемы.


                                                Вообще, отличить зав. типы от их отсутствия просто. Вот ты можешь без зависимых типов написать Vec[N], ты можешь даже написать Vec[N] таской, что N нее будет известно в компилтайме. И потом для этого Vec[N] ты сможешь даже написать vecRef(vector, n) — и ты даже сможешь статически типизировать этот vecRef и тайпсейфно его применять в том случае, если у тебя известны n и длина vec, либо только длина vec, либо только длина n. Но вот чекнуть его в случае если неизвестно ни то ни другое — уже не выйдет, ты не сможешь написать refl.

                                                  0

                                                  Мне кажется необходимость поднимать/опускать термы на уровень типов руками не делает язык завтиповым или нет, просто вопрос удобства.


                                                  Если ты покажешь пример на идрисе который по-твоему невозможно выразить на скале, я попробую представить контрпример.

                                                    +1
                                                    Мне кажется необходимость поднимать/опускать термы на уровень типов руками не делает язык завтиповым или нет

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


                                                    Если ты покажешь пример на идрисе который по-твоему невозможно выразить на скале

                                                    Напиши любой ПИ-тип, который можно использовать как ПИ-тип.

                                                      0

                                                      ниже пример

                                                      0

                                                      Как насчёт read_vec из гайда по индрису?

                                                        0
                                                          +1

                                                          Эм, а при чем тут типы? Это метапрограммирование. Естественно, на макросах зав. типы делаются без проблем — ну просто можно в компайлтайме эмитить код на идрисе и его тайпчекать.
                                                          А типов-то там и нет. И сам код — не типобезопасный. Корректность обращения к массиву там не гарантирована.


                                                          И пи-типа ты тоже не показал ArrayFixed[A, N <: Int] на место N нельзя засунуть связанную переменную. Там останется просто Int. И тогда ты сможешь типобезопасно либо получить значение из массива с известной длиной, но с неизвестным индексом, либо с известным индексом — но с неизвестной длиной. В случае если и длина и индекс неизвестна — облом.

                                                            0

                                                            Ох, я не просил прям код, просто общую идею :) Но раз так, то я почитал и не понятно flatMap(_.get(2)) зачем? Почем не просто get(2)?

                                                              0

                                                              Потому что вы создаете массив в рантайме, он может не создаться (для отрицательной длины в частности), поэтому функция парсинга массива initDynamic возвращает Option[FixedArray[..]], ну а дальше через флатмап комбиинруем две функции возвращающие Option.

                                                                0

                                                                А тьфу тупой вопрос был, чо то сбило с толку чтобы признать эту функцию. Я подумал в начале что это map по всему массиву.


                                                                А можно достать факт доказательства из get и носить его с собой пока он не понадобится опять?

                                                                  0

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

                                                                    0

                                                                    Мне не понятно какой тип у ArrayFixed.initDynamic(...), это каждый раз новый тип? Когда там в дженерик аргументах написано i.type, это как понимать?

                                                                      0

                                                                      Ну если вывести сам массив на печать то получим:


                                                                      Some(ArrayFixed(5,[I@3fa8e43a))

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


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

                                                                        0

                                                                        Ну эти рандомные числа это кишки из JVM наверное а пять оно походу просто печатает для красоты. А вот если попытаться саму скалу спросить то выдает типа такого


                                                                        value wtf is not a member of Option[Main.ArrayFixed[Int, Int(otherLen)]]


                                                                        Тут меня удивило то что в типе содержится название переменной. Не знаю как это работает, но может это и есть тот самый path dependent тип?

                                                                          0
                                                                          Ну эти рандомные числа это кишки из JVM

                                                                          Вспомнил это.


                                                                          Вот, кстати, интересно, если зависимые типы я вроде как понимаю, а синглтоны со всеми этими костылями типа SingI или дефункционализации не понимаю и понимать не хочу, это хороший или плохой знак?

                                                                            0

                                                                            5 оно берёт потому что val otherLen = "5".toInt, это легко проверить. А вот как вытащить это на уровень типов — видимо матчингом каким-то, не знаю.

                                                                        0
                                                                        Ну там собственно с докзательствами можно всякое разное мутить.

                                                                        С доказательствами там мутить нельзя ничего, т.к. тайпсейф код написать нельзя.


                                                                        technic93


                                                                        Мне не понятно какой тип у ArrayFixed.initDynamic(...), это каждый раз новый тип? Когда там в дженерик аргументах написано i.type, это как понимать?

                                                                        i.type это некоторый подтип Int, проиндексированный i.

                                                                          0

                                                                          проиндексированный по значению i, а как если оно рантайм или по переменной i? типа что она otherLen, как выше писал Int(otherLen).

                                                                            0
                                                                            а как если оно рантайм или по переменной i?

                                                                            Оно формально по значению, но у вас нет возможности доказать равенство значений кроме как по is-a (с-но может быть ситуация когда значения одинаковые — а типы разные). В этом и проблема. Если бы было можно — были бы зависимые типы.

                                                          0
                                                          По такому определению люди которые пытаются запилить в хаскель завтипы занимаются ерундой, ведь они как раз это и пытаются реализовать.

                                                          На самом деле не совсем это, потому что синглтоны в хаскеле давно возможны — собственно, для этого достаточно GADTs. Даже DataKinds не нужны, это синтаксическое удобство (ну как индуктивные типы вместо чёрчеподобной кодировки через ∀), синглтоноподобные кодировки формально ничего от неё не потеряют.


                                                          Но да, среднее отношение к синглтонам — «ужас и костыли, пока не завезут нормальные завтипы».


                                                          А вообще на самом деле всё очень просто. Есть formation rule, который описывает, какие типы можно реализовывать в данной теории типов. Оно, упрощая, следует шаблону


                                                          Г ⊢ T₁ : s₁        Г, x:T₁ ⊢ T₂ : s₂
                                                          ------------------------------------
                                                                   Г ⊢ Пx:T₁. T₂ : s₃

                                                          А дальше всё зависит от того, какие s₁ и s₂ допустимы (ну и как определяется s₃).


                                                          Если и s₁, и s₂ могут быть только * (то есть, типы), то получается базовое STLC. Можно показать, что П-байндер ни на что не влияет, и переписать тип в привычное T₁ → T₂ со стрелочкой.


                                                          Если s₁ и s₂ кроме того оба одновременно могут быть (где * : □), то получится λ_ω (кажется, я всё время забываю формальные названия для этих частных случаев — короче, с типами, зависящими от типов и конструкторами типов).


                                                          Если к этому добавить возможность s₁ быть , а s₂ быть *, то получится System F с конструкторами типов.


                                                          А если разрешить все комбинации (в частности, s₁ = *, s₂ = □), то мы получим calculus of constructions, то есть, зависимые типы с полиморфизмом и конструкторами типов.


                                                          Там везде при этом s₃ = max(s₁, s₂), и там тоже есть варианты, но не суть.


                                                          При этом всём DataKinds — это возможность создавать новые сорта с очень фиксированным количеством обитателей, не более.

                                                            0

                                                            Боюсь мой браузер не в состоянии отрендерить это правильно, у меня вместо части символов — квадраты.

                                                              +3

                                                              Лол, я не учёл, что символ для sorts очень похож на подстановочный символ для отсутствующего глифа. Короче, так и задумано.

                                                                0

                                                                Век живи, век учись, никогда его не встречал.


                                                                Кстати, звездочки же забанили, теперь надо писать Type, правда?)

                                                                  0

                                                                  В хаскеле — да (ну, на это есть экстеншн, по крайней мере, но я не знаю, приняли его уже или нет). Но это относительно стандартное теоретикотиповое обозначение, насколько я могу судить.


                                                                  Просто в хаскеле есть значок * для умножения, а в теории типов нет никакого умножения на термах.

                                              +1

                                              Мне раньше нравилась Scala. Бросил и возвращаться не хочу:


                                              1. Bloat. Современный софт ужасен в этом плане, и Scala — явный тому пример. Я как-то чинил багу в парсере разметки в Lift Framework. Парсер жил в одном файле строк на 600. Компиляция этого файла занимала пару минут и порождала более 600 класс-файлов, т.е. более одного класса на строчку.


                                              2. Очень тормозной компилятор, даже с fsc.


                                              3. Побочные эффекты в неожиданных местах. Многие библиотеки выглядят чисто функциональными, но не являются потоко-безопасными, к примеру. Referential Transparency? Забудте.


                                              4. Громоздкий синтаксис. Чтобы написать банальный Maybe нужно непростительно много букв и неочевидных фич языка.



                                              Хороший язык должен снижать сложность, Scala эту сложность только преумножает.

                                                0
                                                Компиляция этого файла занимала пару минут и порождала более 600 класс-файлов, т.е. более одного класса на строчку.

                                                Это пофиксили в scala 2.12, теперь scala-лямбды компилируются в java-лямбды внутри класс-файла, а не в отдельные классы.


                                                Громоздкий синтаксис. Чтобы написать банальный Maybe нужно непростительно много букв и неочевидных фич языка

                                                А чем стандартный Option не угодил?

                                                  0

                                                  Есть подозрение, что вы со скалой очень давно общались. Компилятор достаточно быстрый (если не упороться шейплесом), библиотеки чистые (все эти tapir/rho/typed-schema + finch/http4s + IO/ZIO + doobie/quill + ...), синтаксис в третьей скале куда более читаемый имхо:


                                                  trait Functor[F[_]] 
                                                    def [A,B](fa: F[A]) map (f: A => B): F[B]
                                                  
                                                  sealed trait Option[+A]
                                                  case object None extends Option[Nothing]
                                                  case class Some[+A](value: A) extends Option[A]
                                                  
                                                  given Functor[Option] with
                                                    def [A,B](fa: Option[A]) map (f: A => B): Option[B] = fa match 
                                                      case None => None
                                                      case Some(x) => Some(f(x))
                                                  
                                                  object Main extends App
                                                    println("Hello")
                                                    println(Some(2).map(_*2))
                                                  
                                                    +1
                                                    вы со скалой очень давно общались

                                                    Последний раз я писал код на Scala в 2013 году.


                                                    синтаксис в третьей скале куда более читаемый имхо:

                                                    Да, это выглядит лучше. В Scala 2.10 все операции реализовывались через переопределение методов трейта, что выглядело очень громоздко и требовало повторения сигнатур по 3 раза. И всё же Haskell заметно проще, компактнее и приятнее глазу:


                                                    class Functor f where
                                                      map :: f a -> (a -> b) -> f b
                                                    
                                                    data Option a = None | Some a
                                                    
                                                    instance Functor Option where
                                                      map None _ = None
                                                      map (Some x) f = Some (f x)
                                                    
                                                    main = putStrLn "Hello" >> print (Some 2 `map` (*2))

                                                    В какой-то момент я осознал, что при написанию кода на Scala я на самом деле транслирую свои Haskell идеи в синтаксис Scala. Почему бы не писать сразу на Haskell?

                                                      0
                                                      Последний раз я писал код на Scala в 2013 году.

                                                      То есть очень давно, я угадал


                                                      В какой-то момент я осознал, что при написанию кода на Scala я на самом деле транслирую свои Haskell идеи в синтаксис Scala. Почему бы не писать сразу на Haskell?

                                                      Чтобы пользоваться благами жвм, в основном. Библиотеки/рантайм в который вложено просто в 1000 раз больше денег, крутче частичные вычисления,… Вот это всё. А в остальном это действительно про трансляцию хаскель кода на скала синтаксис.




                                                      Пока вам отвечал, узнал, что в скала3 появился облегчённый синтаксис:


                                                      enum Option[+T] {
                                                        case None       extends Option[Nothing]
                                                        case Some(x: T) extends Option[T]
                                                      }

                                                      Хуже ML, но лучше чем то, что было

                                                        0
                                                        Чтобы пользоваться благами жвм, в основном. Библиотеки/рантайм в который вложено просто в 1000 раз больше денег

                                                        Можно же взять eta!

                                                          0

                                                          Учитывая что я про неё только краем уха слышал (а еще слышал, что вне гхц жизни нет), то шансы что это действительно альтернатива очень малы.

                                                            0

                                                            Я её сам не пробовал, но, как я понял, это что-то очень близкое к гхц, только темплейт хаскеля нет.


                                                            Хотя интересно, как они там без линзочек.

                                            0

                                            Где же ее работу то на скале найти. Только в Берлине/Мюнхене/Тель Авиве.

                                              +1

                                              Да даже в москве хватает. Плюс, у нас в чатике ходит мем что любая java вакансия это секретная скала вакансия.

                                                +2

                                                Ну после кризиса будем посмотреть :-) Скалу очень люблю, но в моем случае Скала вакансия оказалась скрытой Джава тим-лид вакансией

                                        +41
                                        Это выкидывание контрпродуктивного опыта (опыта ходьбы по граблям). А продуктивный опыт — знание алгоритмов, умение писать быстрый код с учётом знания того, как работает процессор и память, и т.п. — это всё остаётся.
                                          +9

                                          На мой взгляд это не выкидывание опыта. Rust молодой язык и явно не для начинающих. Думаю, подавляющее число программистов на нём как раз бывшие C++-ники. Т.е. вы будете вариться ровно в той же среде и собеседование будете проходить возможно на том же С++. Но чем больше вы ждёте, тем дальше эти группы будут расходиться и тем менее важным будет ваш опыт.

                                            0
                                            На мой взгляд это не выкидывание опыта. Rust молодой язык и явно не для начинающих. Думаю, подавляющее число программистов на нём как раз бывшие C++-ники

                                            Забавно, я слышал про Rust жалобы ровно противоположного характера: мол, среди растоманов програмистов на Rust дофига выходцев из Javascript, Python и Ruby.

                                              +1

                                              Вероятно человек не трогал раст со времен 1.0

                                                0
                                                И не только эти ЯП, а также Java!
                                              0
                                              Попробуйте программировать микроконтроллеры, опыт он и есть опыт
                                                0
                                                Мне 40 лет.
                                                Я 3 раза выкидывал свой опыт, начав в 2003-м с Delphi.
                                                P.S. Я и сейчас иногда пишу на D7/FP если надо быстро накорябать что-то оконное и работающее под Windows и Linux. Но клиент как правило уже привык смотреть на результаты через браузер.
                                                  0
                                                  Простите-с, а что такое «D7/FP»? Гугл меня не понял (вообще), однако иногда возникает жгучая необходимость «накорябать что-то оконное и работающее под Windows и Linux».
                                                    0
                                                    Гугл в такое не умеет. D7 — это Delphi 7 (последняя версия до того, как какая-то муха укусила разработчиков и они ударились в .NET), FP — это FreePascal (хотя думаю, скорее, Lazarus).
                                                      0
                                                      Ого, и правда не умеет, удивлен :) Спасибо, не знал что он кросс-платформенный. Возможно и стоит взять на заметочку, но блин, Делфи, даже как-то не знаю. У меня знакомый паскалист есть, 20 000 свои в месяц имеет, говорит больше уже просто не сделать, при этом занимается фотографией и у самого оборудования стоимостью с машину, просто запросы небольшие. Не знаю как он еще не сгорел, владея почти мертвым языком, наверное только благодаря фотографии и держится. Есть тут на Хабре, надеюсь не увидит мой коммент, а-то как-то неловко будет если честно :)
                                                        0
                                                        Ну сейчас пошло небольшое оживление. Весь старый код на Delphi теперь с небольшими улучшениями можно применить на iOS/Android. При этом среда разработки в разы шустрее, чем глючный Android Studio. Но всё равно он популярность Android Studio не обретёт.
                                                          0

                                                          RAD Studio таки глючнее Android Studio. На больших проектах, например, автодополнение вешает IDE на десяток секунд, после чего ничего не выдаёт. В Idea поиск вариантов дополнения, видимо, делается в другом потоке, и его хотя бы отменить можно.
                                                          И плюс глючный компилятор, выдающий internal error без указания, где ошибка.
                                                          И, насколько я помню давние попытки товарищей написать что-то на делфи под андроид, hello world имел apk под 15МБ и запускался секунд 5.

                                                            0
                                                            Ого, Делфи под Android? Это как это? Компилит код с С и запускает его через NDK, или как? Должны же быть библиотеки андройдоские, а они только сишные, ну под яву само собой.
                                                              0
                                                              Делфи уже давно под Андроид, iOS, Mac OS, Линукс и даже веб :)
                                                              Под Андроид несколько фреймворков, под веб пяток.
                                                                0
                                                                Понятно, только как это работает вообще? Андроид на Яве написан, SDK его понятно тоже, Котлин это по сути Ява и есть, просто немного с другим синтаксисом, а Делфи-то как? Просто транслирует свой код в Яву или С?
                                                                  +1
                                                                  Там нативный год генерится, насколько я знаю. Причём не знаю как сейчас, но несколько лет назад он всегда генерился подо все платформы. Только под «не заказанные» — генерилась заглушка, радостно сообщавшая, что платформа не поддерживаются. Пользователи Хромбуков с автоматическим транслятором из ARM-в-x86 (который включался бы, если бы заглушки не было) — были в восторге.
                                                                    0
                                                                    Ого, не, ну похвально конечно если это на самом деле так, и они генерят нативный код в обход виртуальной машины…
                                                                    P. S. Извиняюсь, я оооочень долго отвечаю на комментарии…
                                                    0
                                                    > Это всё равно выкидывание опыта
                                                    Раз не хочется выкидывать опыт, и хочется всё же оставаться в C++, оставайтесь. Ездите на встречи комитета C++, заставьте их исправить недостатки C++, которые вы видите (можно через РГ21 пытаться stdcpp.ru ). Участвуйте в проектах (и пишите свои), которые активно используют новые фичи C++, которые фиксят недостатки старых. По поводу UB, возникающих из всяких memcpy: есть подвижки: www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4303.html, en.cppreference.com/w/cpp/utility/launder, wg21.link/P0476. Советую этот блог: blog.regehr.org, автор почти все свои посты посвящает своим попыткам сделать C и C++ точнее (и его компиляторы). Пишет разнообразные новые инструменты для этого. Например, blog.regehr.org/archives/1667, blog.regehr.org/archives/1619, blog.regehr.org/archives/1520, blog.regehr.org/archives/1393, blog.regehr.org/archives/1292, blog.regehr.org/archives/1128, blog.regehr.org/archives/1678, blog.regehr.org/archives/631. Используйте свои знания, чтобы сделать лучше альтернативы C++, такие как Rust. Или создайте свою
                                                      +1

                                                      Ну вот, кстати, писать тулинг было бы неплохим вложением ресурсов и опыта. Хоть в EDG иди, правда, они на плюсах пишут.


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

                                                        0

                                                        А нету попыток как-то изолировать легаси? Т.е. вместо того чтобы заинклюдить все и потом перемылавать адскую смесь из разных стилей из разных эпох, как то все приводить сначала к промежуточному представлению. По сути компилятор это и делает наверное, т.е. зачем оставлять какие-то костыли обратной совместимости мне не понятно.

                                                          0

                                                          В комитете были какие-то разговоры об эпохах, но всё заглохло, насколько я могу судить.


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

                                                            0
                                                            А вот с людьми тяжелее, людям всё равно, похоже, нужно уметь читать код в разных стилях, пусть он даже раскидан по разным файлам или модулям или что там.
                                                            В ближайшие лет 5-10, в любом случае, толп новичков же не прибавится — всё равно будут те, кто и так уже C++ знают его использовать в основном будут.

                                                            А вот лет через 10 можно и компилятор упростить и жизнь людям… заранее предсказать «когда», будет невозможно, конечно, но если это будет включаться опциями компилятора — можно собрать статистику по разным open-source проектам и в крупных компаниях…

                                                            Ну, и, в добавление ко всему: сам факт того, что тебе нужно включать «режим устаревшего C++» будет давить — ну не любят люди ретроградами быть, на самом-то деле.

                                                            Так что версионирование с явным включением версий — вполне себе разумный путь к «чистке языка». Да, небыстрый… а что вообще в C++ происходит быстро?
                                                              0

                                                              У меня есть такое нехорошее чувство, что через 10 лет плюсы будут по большей части там, где много легаси. А как относятся люди к ретроградству в конторах с легаси — вопрос риторический, ИМХО.


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


                                                              Короче, я настроен как-то пессимистичнее.

                                                      0

                                                      Я тоже по этому пути прошел.
                                                      Занимался одним интеграционным продуктом одной известной комании (неважно какой продукт, какая компания). Тоже добрался до топа.
                                                      А в конце получилась похожая история, несочетание меня и этой софтвари.
                                                      И да, было горестно бростать все нажитое, выученное.
                                                      Сейчас получаю давно забытое удовольствие от программирования. python, я ж его просто люблю. Я так соскучился по забытому чувству, когда что-то пишешь-пишешь, а оно бац, и сразу работает. И это с моим-то счастьем (счастье мое в том, что я вылавливаю больше ошибок, чем вся команда. Хотя иногда кажется, что ошибки вылавливают меня.)
                                                      Получаю сейчас раза в 3 меньше. И совсем не жалею. Может когда и дорасту до прежних рейтов, может нет, совсем неважно. Убитых лет жалко.
                                                      Уверен, что у тебя тоже все получится. Нажитый опыт, он не весь по языку. В этом опыте еще много чего полезного и уникального. Не надо себя недооценивать.
                                                      Есть куча свежих, веселых, умных языков, которые решают С++шные задачи.

                                                        0
                                                        Есть куча свежих, веселых, умных языков, которые решают С++шные задачи.
                                                        А можно примеры? А то ищу, на каком бы языке пострадать фигней в области написания «выскопроизводительного» кода.
                                                        Требования просты:
                                                        1. Без GC.
                                                        2. Без виртуальной машины.
                                                        3. C-like синтаксис, но без кучи странных символов в неожиданных местах, как в плюсах.
                                                        4. Пакетный менеджер и система сборки из коробки.
                                                        5. Кроссплатформенный — должен собираться и работать на MacOS и Linux.
                                                        6. Желательно с уже имеющейся библиотекой GUI, не являющейся оберткой над Qt/GTK.
                                                        7. Желательно с существующей библиотекой для работы с сетью.
                                                        Что смотрел:
                                                        1. D — он не взлетел за почти два десятка лет своего существования, посему вкладываться в него нецелесообразно.
                                                        2.Rust — перспективно, но пока вроде с GUI там сложно.
                                                        3. Swift — пока не разобрался, насколько у него хорошо с Linux, сборкой без Xcode и GUI на Linux.
                                                          0
                                                          на каком бы языке пострадать фигней
                                                          в области написания «выскопроизводительного» кода.

                                                          Попробуйте CUDA или OpenCL, уж страдать — так страдать. Но это, конечно, не универсальные языки. К тому же, очень близкие к С++ и С соответственно.


                                                          На википедии есть таблица сравнения языков программирования. Насколько я её помню, единственный язык, подходящий под большинство ваших критериев — это C.

                                                            0

                                                            Иногда может быть проще реализовать разные части на разных языках ведь.

                                                              0

                                                              Требования немножко противоречивые. 1+2 против 6. Т.е.или низкоуровневый, для системных работ — против UI. Я бы не смешивал.
                                                              Если брать первые требования, то Go на ум приходит.


                                                              Кстати, зачем С++ выдумали поверх С? Для ООП. А ООП какие проблемы решает? DRY проблемы. Не хочется много раз что-то повторять.
                                                              В UI подобные проблемы везде. А в системном программировании поменьше. Linux © — тому хороший пример.

                                                          +10
                                                          Кто б сомневался, что растоманы в тред набегут :))))
                                                            0
                                                            Это плохо?
                                                              0
                                                              Это смешно, скорее :)
                                                                +6

                                                                А ещё в тред набежали симаны, сиплюсманы, идрисманы, скаломаны и ещё кто только не набежал.


                                                                Мне кажется, у вас избирательная наблюдательность.

                                                              +2
                                                              Раст — самый любимый язык по опросам на стэковерфлоу за последние несколько лет, и оттуда он не уйдёт :)
                                                                +1

                                                                И что, людям пишущим/интересующимся одним системным языком в треды других теперь не заходить? Сишники и плюсовики в растотредах присутствуют в количестве, и мне это кажется вполне хорошим явлением. Также как, скажем джависты в тредах про kotlin/scala/clojure/etc или хаскелисты в тредах про scala/rust/idris/coq/etc. Для нормального разработчика вполне здоровая тема интересоваться смежными темами, а не закукливаться в своём болоте.

                                                                –1
                                                                Человек пишет, что не хочет программирование, просто не может об этом сказать прямо. А вы ему те же яйца только в профиль толкаете. Человеку программирование банально НА-ДО-Е-ЛО. Нужно заняться чем-то другим в жизни. Да, так бывает. Постичь программирование — это не вершина и не смысл жизни, просто один из многих видов деятельности человека
                                                                  +2

                                                                  Там ниже писали, что я что-то хорошо и выразительно написал — нет, вот нифига не хорошо и не выразительно.


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



                                                                  Просто конкретный инструмент надоел.

                                                                    0
                                                                    Другой инструмент — это шило на мыло. Через несколько месяцев, может пару лет, вы придете к тому же, что у вас сейчас
                                                                      +1

                                                                      Почему же?


                                                                      Тем более, что некоторыми из других инструментов я тоже пользуюсь не год и не два.

                                                                        0
                                                                        Они для вас не основные. В этом разница. Я когда-то давно был TeXником. Разбирался во всех этих стадиях работы TeXа и прочее. Там, конечно, не C++, но что-то похожее, со всеми этим стадиями работы этого чудо-юда.

                                                                        Ощущение — было близко к тому, что у Вас по отношению к C++.

                                                                        Ущёл, кстати, не потому что выгорел (до этого дойти не успел), а потому что за программирование пообещали больше платить, чем для разработку систем макросов.

                                                                        Но теперь я и к C++ отношусь проще и понимаю, что если попытаюсь достичь вот самых-самых вершин мастерств… получу вот ту же самую боязнь и ненависть, что и в случае с TeXом.
                                                                          0

                                                                          Ну как не основные… Дома все пет-проекты почти только на хаскеле, на последней работе хаскеля с плюсами было где-то пополам, на предпоследней были чисто хаскелевские проекты. Да, плюсы, наверное, «основнее», но это отношение скорее где-то 60:40, а не 95:5.


                                                                          А теха мне хватило с точки зрения ковыряния чужих пакетов при написании статей.

                                                                  –3
                                                                  > Так и в чем проблема идти в Rust?

                                                                  Ну вот я очень плохо представляю себе, как писать в ООП без полноценного наследования реализации. Оно так или иначе вылазит в любом серьёзном проекте.
                                                                  Можно, конечно, вспомнить, как это решалось на C. Но необходимость возиться с подобным пугает.
                                                                    +2

                                                                    Наследование реализации переоценено. Что на расте, что на хаскелле, пишем безо всякого наследования, и код только чище получается.


                                                                    Да что там, у меня в сишарп проектах которые я последнее время пишу 0 (ноль) сеттеров у классов, и никакого наследования. И я бы не сказал что у меня там кривая архитектура получается или я там код не переиспользую. Стало только чище и проще.

                                                                      0
                                                                      Композиция? А в шарпе для неё есть такой же сахар, как в го, когда компилятор сам ищет, к какому классу относится метод?
                                                                        0
                                                                        Композиция? А в шарпе для неё есть такой же сахар, как в го, когда компилятор сам ищет, к какому классу относится метод?

                                                                        Лучше. В C# есть делегаты и явные интерфейсы.

                                                                        Да и вообще голанг весьма бедный язык на «сахар».
                                                                          0
                                                                          Не специалист в шарпе, но, кажется, и делегаты и явные интерфейсы с композицией не связаны.
                                                                      +4
                                                                      Ну вот я очень плохо представляю себе, как писать в ООП без полноценного наследования реализации.
                                                                      «Наследование реализации» — есть зло. Я сейчас глянут — у нас в проекте, несмотря на использование C++ и много всяких интересных вещей, нет ни одного класса, который бы наследовал откуда-то реализацию.

                                                                      Оно так или иначе вылазит в любом серьёзном проекте.
                                                                      Ядро Linux — это несерьёзный проект, я так полагаю?

                                                                      Можно, конечно, вспомнить, как это решалось на C. Но необходимость возиться с подобным пугает.
                                                                      А это-то тут причём? GObject — он вообще о другом. Там поддержка скриптовых языков и рефлексия основную сложность создают.

                                                                      А если просто писать, придерживаясь COI, то никаких проблем отсутствие «полноценного наследования реализации» не создаёт.
                                                                        +1
                                                                        > Ядро Linux — это несерьёзный проект, я так полагаю?

                                                                        Под «вылазит» я имел в виду «удобно применить».

                                                                        И я по умолчанию считаю, что все случаи, когда наследование некорректно из-за заметно разного характера объектов (или из-за того, что их включается больше одного или переменное количество), уже исключены. А вот если надо (повторю соседний пример) из 100 методов переопределить только 5 — тут-то и начинается.

                                                                        Linux характерен тем, что в нём таки легко уйти от подобных желаний — суть объектов заметно разная. Но, например, в сокетах уже есть очень заметное сходство, и то, что разделение сделано включением реализации в общий объект — вопрос стиля, а не жёсткой необходимости.

                                                                        Или вот n_tty_ioctl_helper, если не нашёл свой случай, вызывает tty_mode_ioctl с теми же аргументами — почему нельзя это считать аналогом перекрытой виртуальной функции?

                                                                        > А если просто писать, придерживаясь COI, то никаких проблем отсутствие «полноценного наследования реализации» не создаёт.

                                                                        Создаёт, когда надо в отдельных реализациях менять только малую часть функциональности.

                                                                        В моём основном проекте сейчас, навскидку, 3 таких иерархии — гарантированно (альтернатива — массовое тупое дублирование), и ещё 2 — немного лучше выглядят с наследованием, чем со включением.

                                                                        > Я сейчас глянут — у нас в проекте, несмотря на использование C++ и много всяких интересных вещей, нет ни одного класса, который бы наследовал откуда-то реализацию.

                                                                        Уровень языка тут к вопросу не относится. А вот специфика проекта — да, в полный рост.

                                                                          +3
                                                                          А вот если надо (повторю соседний пример) из 100 методов переопределить только 5 — тут-то и начинается.
                                                                          Не начинается, а заканчивается. Потому что если у вас 100 методов, из которых нужно перекрыть пять — то у вас отвратительный дизайн и гарантированная куча ошибок. И да, я понимаю почему так сделано в macOS для Macintosh 128K или Windows 1.0 для IBM PC (куда, в оригинальной версии, вставало только 512KiB).

                                                                          Да, это позволяет получить что-то работающее в очень малом количестве кода… но в современном мире — это очевидный антипаттерн и так делать не нужно. Количество глюков, которые возникают в разных программах из-за того, что никому неизвестно когда какие методы используются и какие «тонкие зависимости» между ними (перекрывая метод A, не забудьте перекрыть также методы B, C, и D… причём все эти описания хорошо если в документации, а не в коде)… вы это всерьёз сейчас предлагаете в язык, построенный вокруг гарантий безопасности тащить?

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

                                                                          Или вот n_tty_ioctl_helper, если не нашёл свой случай, вызывает tty_mode_ioctl с теми же аргументами — почему нельзя это считать аналогом перекрытой виртуальной функции?
                                                                          Можно считать. Но это не является наследованием реализации, потому что отсутствует основная «фишка» наследования реализации: возможность меняя поведение функции A «получить в нагрузку» изменение поведения другой, никак визуально не связанной с первой в точке перекрытия, функции B.

                                                                          В моём основном проекте сейчас, навскидку, 3 таких иерархии — гарантированно (альтернатива — массовое тупое дублирование), и ещё 2 — немного лучше выглядят с наследованием, чем со включением.
                                                                          Ну да. Выглядит красиво. Иногда. Но… вы можете выбирать — выстрелить себе в руку или в ногу. А попадание в голову — идёт бонусом бесплатным.

                                                                          Нафиг-нафиг. Нечего такого инструменту в безопасном языке делать.

                                                                          Уровень языка тут к вопросу не относится. А вот специфика проекта — да, в полный рост.
                                                                          Вот вообще ни разу. Если у вас в каком-то классе образовалось больше 10-20 виртуальных методов — то это чёткий признак того, то вы что-то сделали не так. И вам нужно или поделить этот объект на части, либо сделать часть методов невиртуальными, а скорее всего — и то и другое: сделать небольшое чисто виртуальных методов в одном объекте и кучу невиртуальных — в другом.

                                                                          Появление такой каши — это явный признак того, что вы делаете непонятно что и у вас просто нет точного понимания чего вы хотите получить в результате, вот и всё.
                                                                            0
                                                                            > Не начинается, а заканчивается. Потому что если у вас 100 методов, из которых нужно перекрыть пять — то у вас отвратительный дизайн и гарантированная куча ошибок.

                                                                            Как минимум в одной из упомянутых иерархий это просто факт вот такой вот реальной сложности той сущности. Все попытки упростить её за последние ~10 лет провалились.

                                                                            Разделить, да, можно. Но это будет на 147% искусственное разделение и в результате будем терять в понятности (начнут возникать неестественные связи) и производительности.

                                                                            > никому неизвестно когда какие методы используются и какие «тонкие зависимости» между ними (перекрывая метод A, не забудьте перекрыть также методы B, C, и D… причём все эти описания хорошо если в документации, а не в коде)…

                                                                            Вот именно таких связей, как ни странно:), нет.

                                                                            > вы это всерьёз сейчас предлагаете в язык, построенный вокруг гарантий безопасности тащить?

                                                                            Что опаснее — честно перекрыть 5 методов из 100, или копировать реализацию в 10 потомков, а потом, сделав правку, изменить только в 8 из них? Всего-то забыли поменять white_foo() на red_foo()…

                                                                            Описанные проблемы дизайна никак не влияют напрямую на безопасность, только опосредованно — в каком случае какой из них будет давать меньше бойлерплейта и копипасты.

                                                                            > Нет, это не вопрос стиля. Это вопрос безопасности. Если вы перекрыли один метод в объекте, где их 100… то вы понятия не имеете, какие методы станут работать по другому. В принципе.

                                                                            Хорошо бы такие слова подтверждать каким-то реальным обоснованием :)

                                                                            > Можно считать. Но это не является наследованием реализации, потому что отсутствует основная «фишка» наследования реализации: возможность меняя поведение функции A «получить в нагрузку» изменение поведения другой, никак визуально не связанной с первой в точке перекрытия, функции B.

                                                                            И как это «не меняет»? Я изменяю обработку в tty_mode_ioctl => изменяется поведение n_tty_ioctl_helper.
                                                                            В первой никак не написано, что её вызывает вторая. Узнать это можно только сканом по исходникам.

                                                                            > Появление такой каши — это явный признак того, что вы делаете непонятно что и у вас просто нет точного понимания чего вы хотите получить в результате, вот и всё.

                                                                            См. выше. И я хочу получить таки обоснование больше, чем на уровне «мамой клянусь» :)
                                                                              0
                                                                              Как минимум в одной из упомянутых иерархий это просто факт вот такой вот реальной сложности той сущности. Все попытки упростить её за последние ~10 лет провалились.
                                                                              100 методов, все перекрываются, но группами по 5? Я хочу это видеть, извините. Вот 100 методов из который 10-20 могут быть перекрыты, а 80-90 — реализовываются через них, а вот такое как у вас… очень странно

                                                                              Вот именно таких связей, как ни странно:), нет.
                                                                              Если такого нет, то наследование реализации не нужно. Типажей вполне достаточно.

                                                                              > вы это всерьёз сейчас предлагаете в язык, построенный вокруг гарантий безопасности тащить?

                                                                              Что опаснее — честно перекрыть 5 методов из 100, или копировать реализацию в 10 потомков, а потом, сделав правку, изменить только в 8 из них? Всего-то забыли поменять white_foo() на red_foo()…
                                                                              А кто сказал про копирование? Вам нужно всего-то включить в себя «стандартную» реализацию и сделать 100 однострочных методов, которые будут вызывать «стандартную реализацию».

                                                                              Да, выглядит не слишком красиво, кажется, что вы что-то «не то» делаете… потому что так и есть… но безопасно.

                                                                              Если что-то забудите пробросить — компилятор возмутится, не бойтесь.

                                                                              Описанные проблемы дизайна никак не влияют напрямую на безопасность, только опосредованно — в каком случае какой из них будет давать меньше бойлерплейта и копипасты.
                                                                              Количество бойлерплейта непринципиально, пока ошибки в нём отлавливает компилятор.

                                                                              И как это «не меняет»? Я изменяю обработку в tty_mode_ioctl => изменяется поведение n_tty_ioctl_helper.
                                                                              В первой никак не написано, что её вызывает вторая.
                                                                              Зато во второй написано, что она использует первую. Вот прямо в названии. _helper — это, очевидным образом, обёртка вокруг другой функции. Да и, собственно, нет ничего удивительного в том, что когда ты меняешь более низкоуровневую функцию — более высокоуровневая начинает вести себя иначе. qsort если изменить что с программой на C будет?

                                                                              Это нормально. А вот «наследование имплементации» всё переворачивает с ног на голову — меняется значение функции в потомке, а функция предка — начинает работать по другому.

                                                                              См. выше. И я хочу получить таки обоснование больше, чем на уровне «мамой клянусь» :)
                                                                              Тут вопрос не в «мамой клянусь», а в нарушении подходов структурного программирования. Когда более высокоуровневая вещь влияет на более низкоуровневую.
                                                                            0

                                                                            Так и для этого наследование реализации в смысле ООП не нужно.


                                                                            В простейшем базовом случае у вас будет запись (совсем как структура в С), где перечислены какие-то функции. Хочется отнаследоваться, переопределить пять из них и добавить три — в простейшем базовом случае включаете имеющуюся запись в вашу, добавляете в вашу три новых функции, переопределяете пять нужных вам во включённой. В небазовом случае, если охота повыпендриваться, начинаете применять паттерны вроде trees that grow.

                                                                              0
                                                                              Нет основной фичи: возможности при попытке выстрелить в ногу изящно отстрелить руку и остаться без головы.

                                                                              Как в вашем подходе перекрытие одной из функций изменит, незаметно для вас, поведение другой функции?
                                                                          0
                                                                          Наследование реализации полезно в строго очерченной ситуации: а)безусловно имеется отношение IS-A и б)есть нетривиальный объем общего для иерархии наследования поведения. Классика — оконный интерфейс.
                                                                          Когда а или б не выполняются, то наследованию реализации есть лучшие альтернативы.
                                                                            0
                                                                            Классика — оконный интерфейс.
                                                                            Ага, конечно. И именно потому что оконный интерфейс так хорошо на это ложится в Turbo Pascal 1.0 for Windows имеются для этого отдельные языковые конструкции (не совпадающие с обычными виртуальными функциями), а в MFC или QT — куча макросов и/или других наворотов.

                                                                            Наслоедование реализации — это костыль. Который позволяет иногда написать чуть меньше кода за счёт разкого уменьшения надёжости этого кода.

                                                                            Неудивитеьно, что в языке «помешанном» на безопасности его не поддерживают.

                                                                            Это всё равно как жаловаться на то, что в веганском кафе с выбором стейков очень плохо…
                                                                              0
                                                                              Особые конструкции в TP оставим на совести Филипа Кана, они к делу не относятся.
                                                                              По сути, Вы передергиваете и, простите, впадаете в дискуссионный раж. Или Вы не понимаете, что куча макросов и наворотов в MFC и, прости господи, Qt служат — это особенности подкласса для _той самой унаследовванной реализации_, которая связывает код на C++ с C-вселенной GUI? В ATL/WTL это, кстати, уже сделано на CRTP и выглядит существенно приличнее. Может я и погорячился с однозначно высказанным «полезно*, но то что это *удобно* доказывает тот факт, что ООП, понимаемое как программирование с помощью ключевых слов class, virtual и override получило такое широкое распространение. Если что, я писал оконные процедуры на C, потом писал их с помощью windowsext.h и хорошо помню это наследование „вручную“.
                                                                              0
                                                                              > есть нетривиальный объем общего для иерархии наследования поведения

                                                                              Ну вот я почему-то наблюдаю такие ситуации сильно чаще, чем обратные — которые и так по умолчанию решаются включением.

                                                                              Страуструп когда-то сказал «Наследовать ли самолёт от двигателя? А будет ли у вашего самолёта два двигателя?»

                                                                              Меня этот вопрос заботит в другом варианте: есть ситуация, когда уже есть нечто, грубо говоря, со 100 методами, а более специфичные реализации заменяют поведение только в малой части аспектов (пусть это будет 5 методов). В текущем проекте есть несколько таких мест. Переписывать их всех на реализацию остальных 95 (опять же условно, но пусть будут такие цифры) пробросом во включённый объект… честно не хочется.
                                                                              И да, это не оконный интерфейс, это сильно другая задача — хотя последствия сходны.
                                                                                0
                                                                                Ну вот я почему-то наблюдаю такие ситуации сильно чаще, чем обратные — которые и так по умолчанию решаются включением.

                                                                                Видимо, такая у Вас предметная область. А в другой могло бы быть совсем иначе.
                                                                                Споры не имеют смысла.
                                                                                В целом, главная слабость объектно-ориентированного подхода в том, что нужно выделить правильные сущности в предметной области, а это трудно. И требовать такого от прогера-Васи бессмысленно. Хрестоматийный пример о трудности реализации метода Fly у класса Penguin связан с тем, что критерии отнесения объекта к классу Bird не имеют ничего общего с возможностью полета.
                                                                                  0

                                                                                  А нельзя ли пробросить не 95 а наоборот 5? Или эти 5 отличий из 100 каждый раз разные.

                                                                                    +1
                                                                                    В том и дело, что разные.
                                                                                      0
                                                                                      Тот факт, что у вас вообще в одном мест собралось 100, потенциально перекрываемых, методов — это плохой признак. Очень плохой. Это значит что вы плохо понимаете что делаете.
                                                                                      0
                                                                                      Меня этот вопрос заботит в другом варианте: есть ситуация, когда уже есть нечто, грубо говоря, со 100 методами, а более специфичные реализации заменяют поведение только в малой части аспектов (пусть это будет 5 методов).
                                                                                      Разбить на 20 объектов в каждом из которых по 5 методов, основной объект собирается из них. Делов-то.

                                                                                      Если же у вас эти 100 методов «перемешаны» и активно вызывают друг друга…

                                                                                      Давайте я сейчас напишу тезис, а вы над ним подумаете. Вот только реально крепко, без «да ну, что это за фигня».

                                                                                      Наследование реализации — это ещё один способ трансформации классического спагетти-кода в нечто, что выглядит как структурный код.

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

                                                                                      И да, это не оконный интерфейс, это сильно другая задача — хотя последствия сходны.
                                                                                      Спагетти-код возникает всегда, когда вы недостаточно чётко представляете себе задачу, которую решаете. Это от типа исходной задачи не зависит.
                                                                                        –1
                                                                                        > Давайте я сейчас напишу тезис, а вы над ним подумаете. Вот только реально крепко, без «да ну, что это за фигня».

                                                                                        Это хороший полемический приём, но он в таком виде никогда не работает. Сработал бы он только в том случае, если бы был заметно глубже разъяснён. И не только для меня (в личном порядке я бы вообще не просил).

                                                                                        Iʼve seen some shit, как говорится. Далеко не весь, конечно; но многое. И подтверждения тотальности вашего вывода что-то не нахожу даже с фонарём.

                                                                                        > Наследование реализации — это ещё один способ трансформации классического спагетти-кода в нечто, что выглядит как структурный код.

                                                                                        Если эта претензия на основании соседних примеров с two(), возвращающем 4… ну да, тому, кто привык к структурному программированию, сложно понять все связи в такой конструкции — приходится упрощать.
                                                                                        Но он точно так же будет иметь проблемы, например, от неверно работающего коллбэка. Коллбэки тоже запретитть?

                                                                                        > давайте лучше устроим кашу, когда никто не может сказать какой кусок кода вызывает какой-нибудь другой кусок кода и зачем

                                                                                        Прямо нарушая контракты? Давайте. Только зачем? У меня обычно цель противоположная — чтобы работало :)

                                                                                        Хотя, если задача заставить 1000 условных индусов выдать на гора что-то, с чем они справятся… спасибо, я понял, почему так делают в Go.
                                                                                    0

                                                                                    Откуда столько странных мифов про Rust? Вполне есть сабтайп полиморфизм, точно так же базовый интерфейс (типаж Animal), точно такая же диспетчеризация в рантайме, если нужно.


                                                                                    fn main() {
                                                                                        let cat1 = Cat { color: Color::White };
                                                                                        let cat2 = Cat { color: Color::Black };
                                                                                        let cat3 = Cat { color: Color::Grey };
                                                                                        let dog = Dog { kind: 2 };
                                                                                        let animals: Vec<&Animal> = vec![&cat1, &cat2, &cat3, &dog];
                                                                                        for animal in animals {
                                                                                            animal.sound();
                                                                                        }
                                                                                    }

                                                                                    (https://pastebin.com/6gKVFvF5)


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

                                                                                      0
                                                                                      Потому что нет наследования реализации. Вот этого вот нету:
                                                                                      class GoodBoy {
                                                                                       public:
                                                                                        virtual int two() { 
                                                                                          return 2;
                                                                                        }
                                                                                        virtual int four() { 
                                                                                          return two() + two();
                                                                                        }
                                                                                      };
                                                                                      
                                                                                      class BadBoy : public GoodBoy {
                                                                                       public:
                                                                                        virtual int two() { 
                                                                                          return 4;
                                                                                        }
                                                                                      };
                                                                                      
                                                                                      int foo() {
                                                                                        BadBoy x;
                                                                                        return x.four();
                                                                                      }
                                                                                      Когда вы меняете одну функцию — а вместе с этим меняется поведение другой, совершенно визуально не имеющей оношение к первой, функции.

                                                                                      Обратите внимание: BadBoy совершенно никак не трогает фнкцию four, она там даже не упоминается — но она, внезапно, начинает возвращать не 4, а 8. А если бы GoodBoy был написан чуть-чуть иначе — она продолжала бы возвращать 4… а могла бы ешё и 6 вернуть. И она будет это делать даже там, где кто-то работает с GoodBoy& и four и про существование BadBoy, в принципе, не имеет представления.

                                                                                      Эта технология — это «современный GoTo»: отличный способ просто и лаконично отстрелить себе руки, ноги, голову… и другие части тела.

                                                                                      И, насколько я понимаю, в Rust — этой фичи таки действительно нет. Хотя, может быть, я действительно в Rust чего-то не понимаю.

                                                                                      При этом, я думаю, её можно имитировать руками: создать табличку виртуальных функций, руками туда занести всё, что нужно, потом руками же скопировать и поправить… Но это будет выглядеть ужасно — что, собственно и хорошо.
                                                                                        0
                                                                                        > Когда вы меняете одну функцию — а вместе с этим меняется поведение другой, совершенно визуально не имеющей оношение к первой, функции.

                                                                                        Вы нарушили контракт на первую функцию. Вторая её использует — чего вы ещё хотели, нарушив контракт?
                                                                                        Это действительно ваша основная претензия к наследованию? ;)

                                                                                        > Эта технология — это «современный GoTo»: отличный способ просто и лаконично отстрелить себе руки, ноги, голову… и другие части тела.

                                                                                        Вот я перегрузил через LD_PRELOAD или через dlopen() реализацию read(), так, что она зовёт exit() — давайте dlopen() запретим?
                                                                                        А такой случай у меня реально был (символ совпал), в отличие от мифического «two() выдаёт 4».
                                                                                          +2
                                                                                          Вы нарушили контракт на первую функцию.
                                                                                          Возможно.

                                                                                          Вторая её использует — чего вы ещё хотели, нарушив контракт?
                                                                                          Я бы хотел, чтобы изменение поведения кода одной функции не меняло поведение другой, которая с ней никак, в теле программы, не связана.

                                                                                          Вот я перегрузил через LD_PRELOAD или через dlopen() реализацию read(), так, что она зовёт exit() — давайте dlopen() запретим?
                                                                                          А такой случай у меня реально был (символ совпал), в отличие от мифического «two() выдаёт 4».
                                                                                          Мифический? Вы это серьёзно? У вас никогде не было так, что вы перекрывали какой-нибудь метод FillSoket, но ничего не работало пока вы не определите какой-нибудь SocketSize? Или, классика жанра: переопредение equals без изменерия hashCode (классическая ошибка новичков в java)? Извините, но я вам не верю. Вот просто: не верю и всё. Не бывает так.

                                                                                          Когда вы реализуете интерфейс — вы точно знаете какие методы должны быть реализованы: все. Иначе программа не скомпилируется.

                                                                                          Когда вы расширяете реализацию — никакой информации о том, какие именно методы вы должны перекрывать в тех или иных случаях у вас нет. Кроме, возможно, документации. Но её к компилятору не прикрутишь. И вот это как раз — прямое следствие того, о чём я говорю.
                                                                                          0
                                                                                          Кажется, я понял Вашу точку зрения: ничем не ограниченное наследование реализации есть зло! Трудно не согласится, Ваш пример тому иллюстрация. Но в C++ есть много чего, что в неограниченном виде есть зло.
                                                                                          Мне лениво пытаться дать точную формулировку для ограничений, надеюсь, все с теме поймут и так. Есть области, достаточно просто устроенные, для которых наследование реализации оказалось полезным. Обычно это, по словам Страуструпа, «не густой лес, а редкий кустарник». Оконный интерфейс — яркий пример.
                                                                                            +1
                                                                                            Мне лениво пытаться дать точную формулировку для ограничений, надеюсь, все с теме поймут и так.
                                                                                            Нет. Не поймут. Я когда-то, ещё школьником, обсуждал эту тему с Шенём. Когда я ещё в школе учился и ООП видел только на древнем, как говно мамонта, Turbo Pascal.

                                                                                            И он мне задал «простой» вопрос: какие ограничения нужно наложить на наследование, для того, чтобы можно было формально доказать, что программа делает то, что она должна делать по спецификации. Я честно пытался что-то придумать, но ничего, кроме «виртуальные функции не вызывают друг друга, а все вызовы осуществляются функцией, используеющей класс» придумать не смог.

                                                                                            А это уже — не наследование реализации. Это наследование интерфейса. Такое и в Rust можно сделать…

                                                                                            Оконный интерфейс — яркий пример.
                                                                                            Скорее контр-пример. Все виденные мною системы имели просто тома, которые объясняли как «правильно» нужно перекрывать те или иные функции и как добиваться того или иного результата. И, в конечном итоге, программы всё равно работали и работают с ошибками и глюками. Всё, что наворачивается в «оконном интерфейсе» (все эти MVC и прочее) — это попытка борьбы с последствиями наследования реализации. Ваша кнопка не должна непосредственно обновлять строку ввода, потому что вы всё равно забудете какой-нибудь из вариантов и вместо этого нужно изменять независимую от кнопки модель — это вот оно.

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

                                                                                            Что самое смешное — в конечном итоге кода, обычно, порождается больше, чем если бы изначально не делалось попыток «срезать углы» за счёт наследования реализации.
                                                                                              0
                                                                                              Скажем так: возможность перекрывать дефолтные обработчики событий и процедуру отрисовки в производных классах, когда вся «машинерия» взаимодействия с «системой» упростило программирования в стравнении с C API? Меня будет трудно разубедить в этом.
                                                                                              Навороты и нетривиальное взаимодействие — результат неортогональности и в целом противоречивого дизайна как самих оконных систем, так и библиотек и фреймворков.
                                                                                              Все как всегда: нет особых проблем перекрывать четко заданные точки кастомизации поведения, если интерфейс статический. Нет особых проблем перекрывать полностью замкнутые в себе «листья», такие, как обработчики конкретных событий. Менее тривиальные случаи проблематичны и плохо масштабируются при усложнении системы, что усугубляется столь распространенным не самым удачным дизайном с переплетением в одном классе разных consern'ов и т.д. и т.п.
                                                                                              Но это не основание говорить, что наследование реализации  *абсолютное зло* всегда и везде. Это просто неверно: мы спорим благодаря плодам наследования реализации.
                                                                                                +1
                                                                                                Скажем так: возможность перекрывать дефолтные обработчики событий и процедуру отрисовки в производных классах, когда вся «машинерия» взаимодействия с «системой» упростило программирования в стравнении с C API?
                                                                                                Вопрос крайне философский.

                                                                                                Меня будет трудно разубедить в этом.
                                                                                                Не очень понятно в чём и кого требуется убеждать. «Дефолтные обработчики событий» — это и есть наследование реализации. То есть ошибка проектирования. Если вы хотите чтобы ваша программа работала надёжно, то общение с ними — следует минимизировать.

                                                                                                То что вам сделали «отрыв башки» (вместо пападания в ногу) более удобным — сложно назвать прогрессом.

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

                                                                                                Но это не основание говорить, что наследование реализации *абсолютное зло* всегда и везде. Это просто неверно: мы спорим благодаря плодам наследования реализации.
                                                                                                Вы недоговорили. Мы спорим на глючащем и тормозящем Web-стайте… это да, с этим согласен. А если бы его не было — могли бы спорить в каком-нибудь FIDO с тем же успехом.

                                                                                                Только ресурсов на это требовалось бы на три порядка меньше.
                                                                                                  0
                                                                                                  Что-то много полемического задора и мало конкретики.
                                                                                                  Совершенно верно. Но решается это не упрощением создания кривого и плохо работающего кода, а созданием прослойки, которая позволит писать код несколько более удобным и безопасным образом.

                                                                                                  Пример в студию?
                                                                                                0
                                                                                                И он мне задал «простой» вопрос: какие ограничения нужно наложить на наследование, для того, чтобы можно было формально доказать, что программа делает то, что она должна делать по спецификации. Я честно пытался что-то придумать, но ничего, кроме «виртуальные функции не вызывают друг друга, а все вызовы осуществляются функцией, используеющей класс» придумать не смог.

                                                                                                Ну, чисто формально достаточно ковариантности и контравариантности (и выражения семантики в типах, конечно же). То есть, если у вас функция derived : darg1 -> ... -> dargN -> dret переопределяет функцию base : barg1 -> ... -> bargN -> bret, то нужно, чтобы все типы barg_i -> darg_i были населены, и чтобы dret -> bret был населён.

                                                                                                  0
                                                                                                  А нет. Речь извините, про конкретный Turbo Pascal, с конкретным таким ООП и виртуальными функциями. Если у вас виртуальные функции вызывают друг друга, то вы должны как-то формально описать — как изменение одной из них влияет на другую. Как это вообще, в приципе, формализовать? Не формализуя, при этом, полностью так же и всю реализацию? Сказать что фукнции не вызывают друг друга — это понятно. А вот если вызывают — то вам уже просто языка не хватит, чтобы что-то формально описать. Да, наверное, можно такой язык разработать… но это будет, во-первых, нифига не просто, а во-вторый — выйдет далеко за рамки понятий Turbo Pascal.
                                                                                                    0
                                                                                                    Перефразируя «Собачье сердце»: Друг мой, не вызывайте виртуальные функции из виртуальных функций! )))))
                                                                                                      0
                                                                                                      «Просто не делайте ошибок»? Это подход C++. В Rust — принято делать так, чтобы ошибочные конструкции было писать сложно, а правильные — легко.
                                                                                                        0
                                                                                                        В Rust — принято делать так, чтобы ошибочные конструкции было писать сложно, а правильные — легко.

                                                                                                        С первым соглашусь, со вторым — не уверен). Какое это имеет отношение к реализации виртуальных функций-членов производных классов в C++?! Мы же не о разных языках говорим, а о конкретной фиче конкретного языка.
                                                                                                        То, что это стоило бы запрещать компилятору — согласен. Но Страуструп такой мягкий человек, а крик поднимается такой всеобщий…
                                                                                                          0
                                                                                                          То, что это стоило бы запрещать компилятору — согласен.
                                                                                                          Сейчас это уж поздно запрещать. Да и изначально было проблематично. Наоборот — если ранние книги про OOP почитаете, там эта возможность как преимущество преподносится. В каком-нибудь Turbo Vision (это прямо от разработчиков самого Turbo Pascal) вы перекрываете функцию GetViewPort, чтобы другие функции того же объекта начали по другому работать.

                                                                                                          И то же самое всё — в MacOS Classic, Windows и так далее. Ну что за побочными эффектами такого подхода нужно следить — во всех учебниках написано. Только вот как за этим сделать — не написано.
                                                                                                            0
                                                                                                            Ну, создания Филипа Кана для меня никогда не были авторитетными. А то, что возможность комбинаторного взрыва проглядели — печально. Можно было бы потребовать явного квитирования вложенного виртуального вызова, но это а)полумера и б)не было бы радикальным решением. Да и заботы у комитета другие…
                                                                                                              +1
                                                                                                              Да и заботы у комитета другие…
                                                                                                              О да.

                                                                                                              Вот этот вот примерчик вспомните, да. Вызов виртуальной функции из конструктора — вызывает не функцию потомка, как в Java или том же Turbo Pascal, а вовсе даже функцию предка. Даже в том случае если все эти функции — в разных единицах трансляции. И это — стандартизованное и документированное поведение. Причём, ради этого, наворотов в компиляторе приходится устраивать… изрядно. Но тут они думали только о том, что функция потомка может, внезапно, обратиться к частям объекта, которые ещё не сконструированы. И вот чтобы тут устроить «типа безопасность» — потребовались дополнительные правила.

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

                                                                                                              А некоторые и до сих пор этого не поняли.
                                                                                      –2
                                                                                      Как поциент-поциенту. С++ тебя не отпустит, бежать куда-то с него бесполезно. Он тебя везде найдет. Всё, что ты можешь сделать — это создать «лучший С++», победив тем самым его в самом себе. Примерно так. Извини, что «на ты». На брудершафт не пили, знаю.
                                                                                      0
                                                                                      У нас контора с плюсов расползается: половина на питон, другая половина — на js.
                                                                                        +2
                                                                                        После С++ пересесть на JS??????? Да вы в своем уме? Все, что не нравится в плюсах, это еще цветочки по сравнению с JS-ом. Хуже него только 1С
                                                                                        +30
                                                                                        Пишите на С, там нет 90% головной боли, которую придумали себе разработчики С++.
                                                                                          +33

                                                                                          Безопасности там ещё меньше, язык ещё многословнее и код на нём ещё менее поддерживаем. Некоторые вещи (вроде тех, зачем были упомянутые шаблоны на 5-7 минут компиляции из поста) на нём нереализуемы вообще никак, кроме кодогенерации. Собственно, кодогенерировать его неплохо (хотя и это не всегда оптимальный вариант), писать — пожалуй, воздержусь.


                                                                                          А так можно и до ассемблера дойти, там нет 100% головной боли, которую себе придумали в более высокоуровневых языках.

                                                                                            +12
                                                                                            Безопасность в С зависит только от ваших знаний и умений. Код на нем *более* поддерживаем, чем на С++, потому что миллионы темплейто-генерированный-лямбда-переопределенных операторов читаются много хуже нормального кода на С.
                                                                                            Шаблоны на 5-7 минут генерации в С просто бессмысленны. Если вам так уж захотелось сгенерировать одинаковых функций для разных типов — то возьмите XMacro. Но если вы забрались так глубоко то с вашим кодом что-то не так.

                                                                                            Насчет читаемости и понимания:
                                                                                            У меня на работе 116 000 строк сишного кода в основном репозитории. Пока не было случая, чтобы новичок приходя в команду спрашивал, что конкретно делает вооот этот блок кода.

                                                                                            Зато один единственный С++ проект, является кладезью непонятных мест и сомнительного кода. Кроме того этот 1500-строчный проект компилируется почти столько же, сколько остальной код.
                                                                                              +25
                                                                                              Безопасность в С зависит только от ваших знаний и умений.

                                                                                              Ну да, чтобы писать код без багов, достаточно просто 100%-ой концентрации 100% времени у 100% программистов, работающих над проектом. Делов-то.


                                                                                              Код на нем более поддерживаем, чем на С++, потому что миллионы темплейто-генерированный-лямбда-переопределенных операторов читаются много хуже нормального кода на С.

                                                                                              Нормальный код на С будет куда многословнее нормального кода на плюсах. Вам просто придётся куда больше читать.


                                                                                              Шаблоны на 5-7 минут генерации в С просто бессмысленны. Если вам так уж захотелось сгенерировать одинаковых функций для разных типов — то возьмите XMacro.

                                                                                              Нет, мне хочется не этого.


                                                                                              Представьте себе, что вы делаете фреймворк для построения графов обработки данных (ну как gstreamer, если вам это о чём-то говорит). И представьте себе, что вы хотите иметь минимальный рантайм-оверхед на всё это дело, потому что десятки (а иногда и единицы) наносекунд конвертируются в прямую прибыль.


                                                                                              Так вот, в плюсах с темплейтами вы можете иметь статический полиморфизм (через CRTP) и что-то вроде ООП, который в компилтайме компилятором разворачивается. И вместо указателя на следующий узел в графе у вас параметр шаблона — тип объекта, которому надо передать эти самые данные. Компилятор этот тип видит, он видит вызываемую функцию, и может её подставить в точку вызова, избежав оверхеда от вызова функции, добавив возможность последующих оптимизаций после инлайнинга, и так далее.


                                                                                              Более того, так как граф строится на этапе компиляции, вы можете на этом же этапе делать всякие оптимизации. Например, копировать данные только тогда, когда у одного source'а больше одного sink'а, и хотя бы один из этих sink'ов меняет данные. Или, например, можете проверки делать, что граф вообще имеет смысл.


                                                                                              Как это сделать на С с такими же характеристиками производительности?


                                                                                              У меня на работе 116 000 строк сишного кода в основном репозитории. Пока не было случая, чтобы новичок приходя в команду спрашивал, что конкретно делает вооот этот блок кода.
                                                                                              Зато один единственный С++ проект, является кладезью непонятных мест и сомнительного кода. Кроме того этот 1500-строчный проект компилируется почти столько же, сколько остальной код.

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

                                                                                                +5
                                                                                                В худшем — примеры лапшекода на С с функциями на тыщу-другую строк и управлением через goto, у меня тоже есть

                                                                                                То есть вы сравниваете откровенный говнокод на С со структурированным С++ и заявляете, что С-плохой? Почему такой код попал в репозиторий проекта? У вас нет ревью кода? Бегите оттуда.

                                                                                                  +14

                                                                                                  Ну так с тем вашим проектом на плюсах точно то же самое.


                                                                                                  Кстати, вопрос про то, как сделать ту штуку с графами на С, был абсолютно серьёзным, мне это правда интересно.

                                                                                                    +1

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

                                                                                                      +17

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

                                                                                                        –7
                                                                                                        можно просто писать функции, делающие осмысленную работу. И тогда разница от inline будет неизмерима )
                                                                                                          0

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

                                                                                                            0
                                                                                                            Я на плюсах не пишу уже лет десять и возможно компиляторы достигли невероятного прогресса в оптимизации кода в последнее время, больше чем за предыдущие 30 лет. Хотя «Без интринсиков и оставаясь исключительно в рамках стандарта? Тогда проигрыш в 2-3 раза.» (ассемблеру) – наверное все же нет
                                                                                                              0

                                                                                                              У нас на расте есть код, который от инлайна десятка ключевых функций выигрывает в несколько раз по производительности.

                                                                                                                –2

                                                                                                                Это проблемы раста, такое было даже на примитивных итераторах.

                                                                                                                  0

                                                                                                                  При чем тут раст? Это был пример к высказыванию "инлайнинг все равно ни на что не влияет". В плюсах такой же эффект будет, пример автора с wc и то как там несмог компилятор заинлайнить std::min тому пример.

                                                                                                                    0

                                                                                                                    К тому, что тот же код из clang при этом всё нормально инлайнил.

                                                                                                                      +1

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

                                                                                                                        –6

                                                                                                                        Это доказывает, что при нормальном компиляторе инлайнинг не нужен, а если инлайнинг где-то на что-то влияет — значит компилятор требует доработки.

                                                                                                                          0

                                                                                                                          То есть gcc требует доработки, так и запишем.


                                                                                                                          Не то, чтобы я считал, что он идеален и в нем нечего усовершенствовать, но позиция немного странная.

                                                                                                                            +1
                                                                                                                            До тех пор, пока такой доработанный компилятор не появился (и его даже не видно на горизонте), инлайн нужен.
                                                                                                                            А так-то common lisp — лучший язык, просто компилятор доработать надо.
                                                                                                                        0

                                                                                                                        Там далеко не всё так однозначно с std::min.
                                                                                                                        Во первых, Вы точно про wc а не про подсчёт расстояния Левенштейна? Во-вторых, если да то дело там совсем не в инлайне.

                                                                                                                          0

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

                                                                                                                            +1
                                                                                                                            Там много ньюансов.

                                                                                                                            Для MSVC 2005 они таки важны. А Chrome долгое время этой версией собирался.

                                                                                                                            Для современных clang, gcc и, вроде бы, даже MSVC — inline уже не нужен. Но есть ньюанс.

                                                                                                                            В заголовочном файле

                                                                                                                            inline ... foo(...) {
                                                                                                                              ...
                                                                                                                            }
                                                                                                                            


                                                                                                                            нужно менять, внезапно, не на
                                                                                                                            ... foo(...) {
                                                                                                                              ...
                                                                                                                            }
                                                                                                                            
                                                                                                                            а вовсе даже на
                                                                                                                            static ... foo(...) {
                                                                                                                              ...
                                                                                                                            }
                                                                                                                            


                                                                                                                            И вот по историческим причинам люди предпочитают не
                                                                                                                            использовать static в заголовочных файлах — даже если речь не идёт про MSVC.
                                                                                                                              0

                                                                                                                              Окей, не буду спорить. В случае раста развешивание #[inline] / #[inline(always)] / #[inline(never)] на функциях вызывающихся в горячих tight loops вот прям очень сильно влияло на производительность.


                                                                                                                              Просто не думаю, что гцц настолько всемогущ что его эвристики всегда идеально определяют, надо инлайнить или нет. Возможно, для этого используется другой кейворд, __магические_аттрибуты__ или ещё что, но смысл ведь остается прежним.

                                                                                                                                0
                                                                                                                                Просто не думаю, что гцц настолько всемогущ что его эвристики всегда идеально определяют, надо инлайнить или нет.

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

                                                                                                                                  0

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

                                                                                                                                +2

                                                                                                                                Это другие инлайны, они влияют на линковку и, условно, то, ругнётся ли линкер, если одна и та же функция определена в нескольких файлах или нет (поэтому khim и пишет про заголовочники).


                                                                                                                                К собственно инлайнингу ключевое слово inline не имеет отношения уже очень давно, компиляторы в своих эвристиках дают ему очень малый вес. Да, мне удавалось сконструировать пример, когда наличие слова inline влияло на инлайнинг, но он был очень искусственным, и на практике я с таким не встречался.

                                                                                                                        +1
                                                                                                                        Хотя «Без интринсиков и оставаясь исключительно в рамках стандарта? Тогда проигрыш в 2-3 раза.» (ассемблеру) – наверное все же нет

                                                                                                                        Это про автовекторизацию. С ней всё плохо.

                                                                                                                      0

                                                                                                                      Можно. Можно даже руками сделать весь этот инлайнинг, сведя сложность написания кода от O(n) к O(n!) (потому что теперь вы просто руками прописываете те варианты, которые раньше за вас генерировал компилятор).


                                                                                                                      Темплейты, конечно, ад, но не настолько, чтобы это было проще.

                                                                                                                  –2
                                                                                                                  Кстати, вопрос про то, как сделать ту штуку с графами на С, был абсолютно серьёзным, мне это правда интересно.

                                                                                                                  Совсем так же, наверное, никак.
                                                                                                                  Но вообще-то это выглядит как вычислительная задача, которую можно решить без ООП, шаблонов и прочих извратов наворотов. И даже может работать быстрее.
                                                                                                                  И даже
                                                                                                                  применять только после 10+ лет опыта!!!
                                                                                                                  можно goto использовать для ускорения.

                                                                                                                  Кроме Си/C++ есть ещё и третий язык для расчётов:
                                                                                                                  Fortran. Думали — Питон, Луа, Матлаб,… — нее!!!

                                                                                                                  И вообще-то можно взять другую модель задачи и по-другому её формализовать, и тогда решение будет плохим на С++, и потребуется какой-нибудь Erlang/Elixir.

                                                                                                                  Для глубокой оптимизации м.б. особое железо, GPGPU, FPGA, assembly language, переписывание сетевого стека и частей ОС на asm + AVX2, ОСРВ и прочая наркомания, которой можно позаниматься за слишком большие деньги.

                                                                                                                  Обычно имеет смысл дополнить имеющийся код на С++ другим, написанным на других языках.
                                                                                                                    +1
                                                                                                                    Но вообще-то это выглядит как вычислительная задача, которую можно решить без ООП, шаблонов и прочих извратов наворотов.

                                                                                                                    Это не расчёты. Да, конечно, там внутри в графе есть расчётные узлы, и там как раз шаблоны не нужны, и их там нет. Шаблоны нужны, чтобы это всё потом склеить вместе.


                                                                                                                    Но просто так, из любви к искусству, шаблоны применять не стоит. Был у меня другой проект, где надо было закодить быстрый random forest руками — там нет никакой вариативности задачи, поэтому никаких шаблонов, никакого ООП, просто один файл строк на 300-500 с несколькими функциями, которые просто перемалывают числа.

                                                                                                                    +1
                                                                                                                    На С реализуется точно также, как и в С++. Вы можете перенести проверки из рантайма в компилтайм за счет наличия дополнительной апосториорной информации о поведении графа. На С пишется парсер, который используя эту же информацию кодогенерирует оптимизированный под структуру графа код С.
                                                                                                                    Такое решение будет быстрее компиляции С++, но слишком задачеспецифичное. С++ предлагает для этого более универсальный интерфейс шаблонов, но за это надо платить многим из того, что вы описываете в своей статье.
                                                                                                                    Может вам суждено запилить свой собственный ЯП, за которым будущее? Так сказать погружение в бездну ужаса С++ длиной в 17 лет, как поиск новых, лучших решений на пользу всему человечеству.
                                                                                                                      +5
                                                                                                                      На С пишется парсер, который используя эту же информацию кодогенерирует оптимизированный под структуру графа код С.

                                                                                                                      Это уже сильно не «точно так же».


                                                                                                                      Ну и писать компиляторы я люблю (не зря мы там обсуждали DSL для этого всего), но писать на С парсер/оптимизатор/кодген я бы точно не стал. Зачем? Есть куда более приятные языки для этого.

                                                                                                                      0
                                                                                                                      Такие вещи решаются кодогенерацией по модели соответствующей предметной области. Но это отдельная магия с повышенными требованиями к спецификациям.
                                                                                                                    +5
                                                                                                                    Поддержу.
                                                                                                                    Есть большой внутренний сишный продукт, где вручную написано ООП (наследование, полиморфизм, перегрузка) и куча диких макросов, потому что иначе 99% кода будет обёрткой над 1% логики. Это к тому, что Си многословен для высокоуровневых действий. Наверное, 20 лет назад плюсы не годились для эмбеда, поэтому выбрали си.

                                                                                                                    И есть драйвера 300-500 kloc от разных восточных друзей, с функциями на 10-20 kloc. Это к тому, что, программисты, способные написать сложный драйвер, не способны сделать это хорошо.
                                                                                                                      –1
                                                                                                                      Представьте себе, что вы делаете фреймворк для построения графов обработки данных
                                                                                                                      Как это сделать на С с такими же характеристиками производительности?

                                                                                                                      1. Та же самая кодогенерация;
                                                                                                                      2. Ренейминг символов сборочным скриптом перед линковкой + LTO;
                                                                                                                      3. JIT через библиотеку;
                                                                                                                      4. Обработка графа батчами с шедулером — так ещё и быстрее будет за счёт локальности кешей и возможности применения векторных инструкций.
                                                                                                                        +4
                                                                                                                        Та же самая кодогенерация

                                                                                                                        А кодогенератор на чём? И почему бы генерировать не LLVM IR, скажем?


                                                                                                                        Ренейминг символов сборочным скриптом перед линковкой + LTO

                                                                                                                        Я, если честно, даже не представляю, как это будет работать. Особенно если у вас что-то вроде


                                                                                                                        if (monday)
                                                                                                                          runGraphA();
                                                                                                                        else
                                                                                                                          runGraphB();

                                                                                                                        JIT через библиотеку

                                                                                                                        То есть, вы будете делать свой язык? Это я люблю и готов делать за бесплатно, но зачем тут С?


                                                                                                                        Обработка графа батчами с шедулером — так ещё и быстрее будет за счёт локальности кешей и возможности применения векторных инструкций.

                                                                                                                        В этой задаче баланс между latency и throughput очень сильно смещён в сторону latency.

                                                                                                                        0
                                                                                                                        Как это сделать на С с такими же характеристиками производительности?

                                                                                                                        Это достигается до определенной степени макросами и всякими хитростями. В ядре такой код я видел. Вообще хоть на С и сложнее добиваться полиморфизма и inline, язык просто на порядок проще и примитивнее. По количеству строк будет реально не в разы больше. Код будет тупее, но будет меньше всяких конструкторов копирования, move, запрещения копирирования. Или, например, сравите насколько читаем какой-нить STL/boost или linux kernel.
                                                                                                                        Но это моя очень субъективная оценка.
                                                                                                                        Из очень производительного и хорошо читаемого С++ кода был http-сервер с мой позапозапрошлой работы github.com/yandex-load/phantom — он довольно легко работал в 2-3 раза быстрее nginx на то время. Автор принципиально не использовал ни строчки из STL из-за непредсказуемых аллокаций и всяких лишних указателей на parent в std::map, например.
                                                                                                                          +3
                                                                                                                          По количеству строк будет реально не в разы больше.

                                                                                                                          По моим оценкам лучший способ сделать это на С — не делать это на С, а кодогенерировать С.


                                                                                                                          Автор принципиально не использовал ни строчки из STL из-за непредсказуемых аллокаций и всяких лишних указателей на parent в std::map, например.

                                                                                                                          STL'ные контейнеры мягко скажем не фонтан с точки зрения производительности, вполне разумное решение.


                                                                                                                          Но вот какой-нибудь std::find_if или std::enable_if вполне себе норм, они точно ничего не аллоцируют.

                                                                                                                          0
                                                                                                                          а чисто ради спортивного интереса — можно где нибудь этот «gstreamer на С++ шаблонах» пощупать?
                                                                                                                            +2

                                                                                                                            Увы, нет. Это очень внутренняя штука.

                                                                                                                            –1
                                                                                                                            Ну да, чтобы писать код без багов, достаточно просто 100%-ой концентрации 100% времени у 100% программистов, работающих над проектом. Делов-то.


                                                                                                                            достаточно
                                                                                                                            1. юнит-тесты писать
                                                                                                                            2. написать и оттестировать ядро системы, где есть опасные моменты, в Си это в основном работа с памятью. Если есть ядро, которое будет оттестировано на 100%, будут защищены все места работы с памятью, то концентрация необходимая при использовании данного ядра может быть уже не 100%.

                                                                                                                            И всегда покрывайте написанный код юнит и интеграционными тестами, каким бы гуру вы не были. Это сократит в дальнейшем время на поиск багов многократно.
                                                                                                                              +4
                                                                                                                              1. юнит-тесты писать
                                                                                                                              2. написать и оттестировать ядро системы, где есть опасные моменты

                                                                                                                              "В ядре Linux, разыменование указателя перед его проверкой на NULL позволило компилятору удалить эту проверку, создав уязвимость в системе." © как раз статья про UB


                                                                                                                              Nerf Unit test this. В смысле — нет. Не достаточно.

                                                                                                                                –2
                                                                                                                                Очевидно что достаточно иметь юнит тест который этот NULL туда будет передавать. И это UB прекрасно всплывает, так же как проблемы в десятке других мест где проверки на NULL просто нет.
                                                                                                                                  +3

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

                                                                                                                                    +1

                                                                                                                                    Сделаю Maybe :evil:

                                                                                                                                      –1
                                                                                                                                      Если функция не ожидает на вход null то я сделаю ее аргументом ссылку, а не указатель. И тогда это up to caller проверять что он не разыменует null перед передачей аргумента дальше.

                                                                                                                                      Во всех остальных случаях проверка на null нужна.
                                                                                                                                        0
                                                                                                                                        А если по ссылке передается объект, в поле которого сидит указатель, и выше по коду уже была проверка этого поля на nullptr?

                                                                                                                                        Просто assert(obj->field)?

                                                                                                                                        if (!obj->field) return?

                                                                                                                                        /* obj->field isn't supposed to be null here */?
                                                                                                                                          0
                                                                                                                                          Прямого доступа к полям объекта быть не должно. Должна быть геттер-функция (от этого можно отойти для простых структур, но указатель явно к таким не относится). Если предполагается что она всегда должна вернуть валидный объект то она должна возвращать ссылку, делать внутри себя проверку на null и кидать исключение если что не так. Если предполагается что кейс где поле нулевое валиден, то геттер возвращает nullptr а получающий его код сам решает как обработать null.
                                                                                                                                          +1

                                                                                                                                          Это всё, конечно, хорошо, но мы вроде как о C говорили, а там ссылок нет.

                                                                                                                                            0
                                                                                                                                            Там без вариантов — проверять на null везде, либо вводить какие-то naming convention по принципу «если имя указателя заканчивается суффиксом _safe то можно его не проверять» и надеяться на то что ее все будут соблюдать. Неплохая иллюстрация для того почему C++ намного лучше C.