LINUX.ORG.RU

Функциональщина на C++

 , ,


0

5

По мотивам: Си с классами

бери с++20 с концепциями, корутинами и ренжами. игнорируй всё из с++17, сфинае, не пиши упоротые шаблоны, вообще шаблоны старайся не писать, и всё будет ок.
концепции уже вроде работают, ренжи тоже есть, корутины ещё не подъехали, но в будущем пригодятся, генераторы там всякие, всё такое. ещё будет проще потом перелезть на экзекюторы и т.д. потом ещё модули затащишь.

Как эффективно учиться? (комментарий)

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

Конкретно мне интересен функционально-процедурный подход к написанию кода на крестах, что-то похожее на Rust, только без абсурдной помешанности на безопасности памяти, так сказать «си с плюшками», но совсем НЕ «си с классами», как было в упомянутом треде. Для примера: Qt и UE — это примеры плохой архитектуры в данном контексте. Например, fstream — это плохая реализация файловых операций, поскольку скатывается в классы и исключения, в ней даже нельзя без исключений получить конкретную ошибку файловых операций.

Итак: какие есть конкретные хорошо проработанные приемы и библиотеки для писания на крестах в функционально-процедурном стиле?

★★★★
Ответ на: комментарий от EugeneBas

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

slackwarrior ★★★★★
()
Последнее исправление: slackwarrior (всего исправлений: 1)
Ответ на: комментарий от slackwarrior

сбоку относительно чего? первой целью Страуструпа при создании плюсов было именно добавление ООП

особенно не нужны развесистые иерархии и мучения с наследоваеием

проблемы неосиляторов, они такие

EugeneBas ★★
()
Последнее исправление: EugeneBas (всего исправлений: 1)
Ответ на: комментарий от kvpfs

И в 90% софта допустимо просто завалиться с abort() при какой-то сильно непонятной ситуации, на этом Раст стоит

Ты забываешь, что почему-то 90% софта не спешат переписывать на Rust. Rust сделан специально для написания браузера, именно там вкладка может просто завалиться и перезапуститься, потому что большая часть состояния у нее все равно хранится на сервере. Хотя на самом деле тот же Firefox не любит перезапускаться, а вместо этого предпочитает терпеть до последнего. Потому выяснилось, что через такую особенность поведения можно удаленно выполнять код, и тогда возникла идея Rust. Хотя по прежнему большая часть браузера писана на C++, а в хроме и вовсе нет раста, только на уровне идеи.

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

В С++20 мы можем получить что-то типа borrow checker с помощью концептов + проверок clang-tidy. Так что за Rust остаются только макросы и встроенный пакетный менеджер. Что хорошо, но погоды не делает.

aist1 ★★★
()
Последнее исправление: aist1 (всего исправлений: 1)
Ответ на: комментарий от EugeneBas

проблемы неосиляторов, они такие

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

aist1 ★★★
()
Ответ на: комментарий от anonymous

Опять поплыл до первого сообщения. Бедняга, иди телом зарабатывать на таблетки)0

anonymous-angler ★☆
()
Ответ на: комментарий от EugeneBas

признак плохого дизайна кода

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

так-то он вообще не нужен

Без dynamic_cast не получится сделать интерфейсы, которые очень даже хорошо используются в той же Java.

aist1 ★★★
()
Ответ на: комментарий от aist1

mechanical sympathy

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

Так значит ты создал свою систему метапрограммирования для целей оптимизации скорости выполнения?

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

Что-то я сколько пишу на Си — не могу вспомнить, чтобы язык прямо-таки «навязывал» мне связанные списки. Связанные списки почему-то любят описывать в книжках, но даже та же Java прекрасно строит структуры данных на базе массивов, которые в онной весьма эффективно хранятся. А в паскале вообще всё на базе массивов делали, даже черезчур много.

ETL никуда не отходит, так как кроме колоночных хранилищ появляются и другие модели представлений: FTS, пространственные индексы, графы, кодирование смежный распределений вероятностей и т.п. Не таблицами едиными

Колоночные БД в том числе позволяют делать запросы с многомерными результатами, со сложными полями, вроде тех же пространственных координат. Полнотекстовый поиск тоже не является какой-то сверхъестественной задачей, хотя ElasticSearch и хорошо оптимизирован конкретно под нее.

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

Колоночные БД представляют собой такую оптимизацию. По крайней мере для большинства аналитических запросов, которые представляют собой выборку по небольшому числу столбцов со сложными связями между этими столбцами. На аппаратном уровне это означает обработку векторными инструкциями столбца с однотипными данными. Ты то же самое делаешь, что ли?

ПСД в closure для main memory, причем там и близко нет никакой mechanical sympathy

Большинство контейнеров являются B+-деревьями, которые таки являются оптимизацией под процессор.

byko3y ★★★★
() автор топика
Ответ на: комментарий от aist1

//интерфейс в стиле Java
struct MyInterface {
   virtual void doSomething() = 0;
};

struct MyImplementation: MyInterface {
   MyImplementation() = default;
   void doSomething() override {
       std::cout << "hello" << std::endl;
   }
}

MyInterface* i = new MyImplementation();

в каком месте тут понадобился динамик каст?

EugeneBas ★★
()
Ответ на: комментарий от byko3y

Мерзотнейшая статья переливания из пустого в порожнее

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

но на реальном железе полное перемещение таблицы оказывается эффективнее работы с мелкими узлами.

Не всё так просто. Попробуй запили in-memory KV store на обычной хэш-таблице на пару десятков GB и удивишься откуда-ни-возьми появляющимся задержкам, ломающим тебе красивый хвост распределения и перцентили.

Структуры данных с амортизированной константной вставкой, будь-то std::vector или std::unordered_map, не применяются там, где есть ограничения на время выполнения операции в худшем случае. Тут деревьям нет альтернативы. Хотя они на чтение значительно хуже.

Что-то я сколько пишу на Си — не могу вспомнить, чтобы язык прямо-таки «навязывал» мне связанные списки.

Под связными списками тут имелась в виду ссылочная модель данных в куче. В случае std::map<K, V> у тебя и K, и V будут размещены в памяти внутренних узлов данных. В случае mylib_map_add(map_t, void* K, void* V), K и V у тебя в куче, и к ним еще нужно прыгнуть сначала. А хочешь инлайнинга данных, то мономорфизируешь mylib_map_add() для каждой пары типов сам. Можешь макросами. Но я бы не стал.

В С ты еще как-то можешь сам что-то мономорфизировать, макросами там или дополнительным кодогенератором. А вот Java тебе этого просто не даст. Там нельзя в память ходить, кроме как через объектные графы (та же ссылочная модель) или массивы.

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

Поддержка Structure-of-array есть, да. Но ты слишком узко видишь возможности железа. Давай я тебе объясню по-другому. Ты ссылаешься на колоночные БД и применяемую ими оптимизацию. ОК. Ты знаком с Apache Arrow? Я имею в виду хорошее такое знакомство, с интерналсами, а не просто «там где-то применяют».

aist1 ★★★
()
Ответ на: комментарий от aist1

Обычно оно реализуется просто с помощью еще одного системного атрибута временной метки в таблице, по которому навешивается индекс (или нет) и модифицируются входящие запросы чтобы искать «от и до»

У MySQL/MariaDB несколько вариантов темпоральности, в том числе по версиям записей (system versioning). Вот эта статья меня убедила в том, что в InnoDB, как и в Oracle, изменения записей хранятся именно в виде разностной информации, а не в виде полной копии записи:

http://www.vldb.org/pvldb/vol10/p781-Wu.pdf

Собственно, все многоверсионные СУБД тем или иным образом оптимизируют обновление записей, иначе бы безумно жрали память и диск. В таком виде это близко к персистентным структурам. И темпоральные данные в таком случае будут запрашщиваться медленнее, чем актуальные версии (именно это происходит в Oracle в обычных таблицах со старыми снимками-транзакциями). К сожалению, беглый поиск не дал ответа, как же все-таки на самом деле хранятся несколько версий записей в случае активной темпоральности.

Но даже если индекс есть, мы всё равно не сможем эффективно реализовать ветки таким образом. Тут уже нужны CoW-деревья, а не таблицы.

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

byko3y ★★★★
() автор топика
Ответ на: комментарий от EugeneBas

Добавь множественное виртуальное наследование и необходимость instanceof() и даункастига. Как тут без dynamic_cast?

aist1 ★★★
()
Ответ на: комментарий от byko3y

Вот эта статья меня убедила в том, что в InnoDB, как и в Oracle, изменения записей хранятся именно в виде разностной информации, а не в виде полной копии записи:

Статью посмотрю сегодня, интересно, спасибо.

Дело тут вот в чем. Хранить версии в виде дельт или команд обновления не сложно. Сложно её читать в таком виде, если у тебя не ПСД. Т.е., вопрос в том, материализованы ли версии разу или нет. В ПДС на основе CoW-деревьев — материализованы и могут читаться. В Git — не материализованы и надо делать checkout.

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

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

Ты ответь про Arrow, я тебе на примере Arrow покажу, чем Мемория отличается.

aist1 ★★★
()
Ответ на: комментарий от aist1

int mylib_map_add(map_t* map, void* key, void* value);
Как мне тут композицией контейнеров управлять? Тут только поинтеры в кучу (вложенные связные списки). Никакой тебе кэш-локальности

map_t, то есть таблица-ассоциативный массив, является «полем» агрегирующей структуры. Если map_t имеет статичный размер, то можно статично объявить struct с ней. В куцой системе типов Си не существует типов динамического размера, а потому «поле» динамического размера придется эмулировать арифметикой над указателями. Тогда при необходимости изменения размера mylib_map_add выдает определенный код и уже другая функция изменяет размер таблицы.

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

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

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

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

byko3y ★★★★
() автор топика
Ответ на: комментарий от aist1

Ты знаком с Apache Arrow? Я имею в виду хорошее такое знакомство, с интерналсами, а не просто «там где-то применяют»
Ты ответь про Arrow, я тебе на примере Arrow покажу, чем Мемория отличается.

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

byko3y ★★★★
() автор топика
Ответ на: комментарий от anonymous

уже давно аппаратура подстраивается и эмулирует достаточно примитивную сишную модель памяти

А ведь царь в какой-то степени прав. При всей его неадекватности в целом. Все ОС-и писаны на Си, отсюда индустрия и пляшет в режиме бесконечной совместимости с совместимостью.

byko3y ★★★★
() автор топика
Ответ на: комментарий от EugeneBas

первой целью Страуструпа при создании плюсов было именно добавление ООП

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

byko3y ★★★★
() автор топика
Ответ на: комментарий от aist1

Попробуй запили in-memory KV store на обычной хэш-таблице на пару десятков GB и удивишься откуда-ни-возьми появляющимся задержкам, ломающим тебе красивый хвост распределения и перцентили

Вообще-то одни из самых быстрых ассоциативных массивов на GPGPU, обрабатывающие миллиарды ключей в секунду на одной машине, как раз сделаны на хэш-таблицах. Но да, там статика, перемещение памяти на таких нагрузках чудовищно бьет по производительности. Впрочем, даже при перемещении есть возможность сохранения старой таблицы для доступа на чтение. А вот с записью уже ничего не сделаешь, только деревья или что-то комбинированное аля B+, B*, etc.

А вот Java тебе этого просто не даст. Там нельзя в память ходить, кроме как через объектные графы (та же ссылочная модель) или массивы

Во: https://github.com/xaionaro-go/atomicmap

Это не жава, но Go очень близок по модели памяти к жаве, и потому прием тот же — структура массивов вместо массива структур. Привет колоночным БД. А массивы уже в JVM/CLR/Go работают очень эффективно как по процессору, так и по памяти.

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

Ладно, я попробую объяснить так. Arrow внутри ораганизована в виде линейного списка чанков, каждый из которых представляет набор колонок. Там сбоку приделан векторизирующий генератор выражений на LLVM, который умеет делать быстрые фильтры для вышележащего SQL-уровня (predicate pushdown). Чанки в таблицу можно дописывать, их можно перезаписывать целиком и т.п. Но ориентировано оно все на линейное чтение + bitmap/hash index. Так как это — основной механизм табличных вычислений для in-memory аналитики.

В Мемории похожая гибридная блок-колоночная модель данных, удобная для векторизации, но там базовая структура данных BTree (не конкретно b-tree, а из этого семейства), а не список чанков, как в Arrow. Т.е., можно нарисовать так.

Дальше, если ты сам когда-нибудь делал, например, динамический массив с помощью BTree, то любую продвинутую структуру данных, которая сводима к массивам, я могу запихнуть в Меморию. А добавляя в узлы дерева дополнительную информацию, я могу организовывать самые разные индексы. Например, у меня в проекте есть сжатое пространственное дерево для задач классификации. И много чего еще, что в табличную модель Arrow не влезет.

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

Ну, и последнее. В Мемории есть кроссплатформенная абстракция над epoll/kqueue/IOCP/AIO/io_uring для высокопроизводительного IO на бустовский файберах. Я его пока еще не использую по прямому назначению, но буду.

aist1 ★★★
()
Ответ на: комментарий от byko3y

Это не жава, но Go очень близок по модели памяти к жаве, и потому прием тот же — структура массивов вместо массива структур. Привет колоночным БД. А массивы уже в JVM/CLR/Go работают очень эффективно как по процессору, так и по памяти.

Ну я этим профессионально занимался. Нет, удовольствие не из приятных, костылять в Java эти абстракции над массивами. А сколько там UB по дороге возникает....

aist1 ★★★
()
Ответ на: комментарий от aist1

Хранить версии в виде дельт или команд обновления не сложно. Сложно её читать в таком виде, если у тебя не ПСД. Т.е., вопрос в том, материализованы ли версии разу или нет. В ПДС на основе CoW-деревьев — материализованы и могут читаться. В Git — не материализованы и надо делать checkout

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

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

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

А уж как я это прочувствовал... Когда-то Мемория начинала писаться именно на С, когда была по задумке еще файловой системой. Never again.

aist1 ★★★
()
Ответ на: комментарий от byko3y

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

Это да. Оптимизации по месту проблемы не представляют, если это CoW-tree.

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

Вот если тебе нужно много долгоживущих версий, то тут начнутся проблемы. А если ты хочешь несколько веток времени (бранчи для data sandboxing), то ты этого без CoW-btree как базовой структуры данных не получишь.

Во времена, когда базы данных сидели на HDD, CoW-btree-подобные схемы хранилища для БД не применялись, так как они быстро приводят к сильной фрагментации линейных структур, убивая scan. На SSD оно совсем по другому, и тут я не знаю, что сейчас делают или задумывают.

aist1 ★★★
()
Последнее исправление: aist1 (всего исправлений: 1)
Ответ на: комментарий от aist1

В Мемории похожая гибридная блок-колоночная модель данных, удобная для векторизации, но там базовая структура данных BTree (не конкретно b-tree, а из этого семейства), а не список чанков, как в Arrow. Т.е., можно нарисовать так

Во многих СУБД, как то Oracle, MS SQL, MySQL, и в нереляционных HAN и ClickHouse (и многих других), таблицы опционально или всегда отсортированы по некоторому ключу. Прямо как у тебя на картинке. Да, это весьма эффективный подход для OLAP, и Arrow здесь лишь «одна из».

byko3y ★★★★
() автор топика
Ответ на: комментарий от aist1

Ну, например, 1000 — это уже очень много. И версии быстро выметаются сборщиком мусора.
Вот если тебе нужно много долгоживущих версий, то тут начнутся проблемы. А если ты хочешь несколько веток времени (бранчи для data sandboxing), то ты этого без CoW-btree как базовой структуры данных не получишь

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

Во времена, когда базы данных сидели на HDD, CoW-btree-подобные схемы хранилища для БД не применялись, так как они быстро приводят к сильной фрагментации линейных структур, убивая scan. На SSD оно совсем по другому, и тут я не знаю, что сейчас делают или задумывают

На SSD на самом деле ситуация почти та же самая — произвольное чтение в 20-50 раз медленнее последовательного:

https://www.hardwareluxx.ru/index.php/artikel/hardware/storage/50695-test-i-o...

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

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

Мемория сейчас — это storage-технология с развитой системой типов сверху. Плюс, площадка для разработки RISC-V акселераторов для ускорения операций с данными.

aist1 ★★★
()
Ответ на: комментарий от byko3y

Но я могу быть неправ.

Давай подождем пару-тройку месяцев, я приберусь в коде и выкачу демо. Всё можно будет посмотреть и оценить. Там всё на виду, и сразу по асимптотическим оценкам будет видно, что оно может, а что — нет.

На SSD на самом деле ситуация почти та же самая — произвольное чтение в 20-50 раз медленнее последовательного:

Ну QD1 же. Ставим QD32 и читаем на полной скорости. Но есть нюанс. Нужны файберы :)

Фишка в том, что NVMe SSD в этом смысле уже как сеть. Нужен AIO для эффективной утилизации.

aist1 ★★★
()
Ответ на: комментарий от aist1

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

anonymous
()
Ответ на: комментарий от byko3y

Rust сделан специально для написания браузера,

А ява специально сделана для программирования встроенных устройств и даже буквально кофеварок.

anonymous
()
Ответ на: комментарий от byko3y

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

anonymous
()
Ответ на: комментарий от aist1

Писать в функциональном стиле на С++ можно будет, но тебе нужны будут ФСД и внешний кодогенератор для добавления в твой проект синтаксического сахара, чтобы можно было идеоматические вещи типа ADT использовать более натурально.

Ты фактически сказал нельзя :) Если уж настолько заморачиваться то проще взять функциональный язык генерирующий C++ типа https://github.com/felix-lang/felix

anonymous
()
Ответ на: комментарий от byko3y

Ты забываешь, что почему-то 90% софта не спешат переписывать на Rust.

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

anonymous
()
Ответ на: комментарий от aist1

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

EugeneBas ★★
()
Ответ на: комментарий от byko3y

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

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

EugeneBas ★★
()
Ответ на: комментарий от EugeneBas

проблемы неосиляторов, они такие

Проблемы неосиляторов композиции и прослушавших что Степанов про ООП говорил да, безусловно такие.

А плюсы давно переросли ООП и стали немножко мультипарадигменными, о чем говорил Страуструп, и что опять прослушали неосиляторы :)

slackwarrior ★★★★★
()
Последнее исправление: slackwarrior (всего исправлений: 1)
Ответ на: комментарий от slackwarrior

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

EugeneBas ★★
()
Ответ на: комментарий от anonymous

А ява специально сделана для программирования встроенных устройств и даже буквально кофеварок

Да. Потом натягивали эту сову на разные глобусы. Напоминаю, что реально годной для разработки сервисов (единственная успешная ниша) она стала в районе 1.4-1.5, после интенсивной доработки стандартной либы.

byko3y ★★★★
() автор топика
Ответ на: комментарий от anonymous

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

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

byko3y ★★★★
() автор топика
Ответ на: комментарий от byko3y

Они много понаписывали на расте, но лишь часть из этого кода слили в Firefox

Они сразу заявили что движок экспериментальный, и думаю если бы его писали на C++ то тоже забрали бы только часть кода.

anonymous
()
Ответ на: комментарий от EugeneBas

т.е. ты очевидно неправильно дизайнишь свой код

С чего ты взял? Таких вещей полно в компонентном программировании.

aist1 ★★★
()
Ответ на: комментарий от anonymous

Ты фактически сказал нельзя :)

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

А Felix — язычок прикольный. Надо иметь в виду, благодарю.

aist1 ★★★
()
Ответ на: комментарий от EugeneBas

если тебе нужен dynamic_cast, то ты хочешь кастить предка к насленику, т.е. ты очевидно неправильно дизайнишь свой код

В стандартной библиотеке C++ dynamic_cast используется в 4 местах:

  1. https://en.cppreference.com/w/cpp/error/rethrow_if_nested

  2. (dynamic_pointer_cast) https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

  3. https://en.cppreference.com/w/cpp/locale/use_facet (в локали хранятся locale::facet*, а нужно вернуть Facet&)

  4. https://en.cppreference.com/w/cpp/io/manip/emit_on_flush

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

Type erasure же. Boost Any делает даункастинг без dynamic_cast, но это просто очень частный случай type erasure: положил-достал.

aist1 ★★★
()
Ответ на: комментарий от byko3y

уже давно аппаратура подстраивается и эмулирует достаточно примитивную сишную модель памяти

А ведь царь в какой-то степени прав.

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

Алсо, какой-то совершенно альтернативной аппаратной модели лично я не видел.

LamerOk ★★★★★
()
Ответ на: комментарий от anonymous

Но шансы перейти в режим кобола у си тоже весьма высоки.

C уже лет 20 как перешёл на этот уровень. Короткий всплеск эмбедщины уже затух.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

если вынести за скобки мелкотравчатые детали вроде барьеров памяти и прочей атомарности.

А вот не надо выносить за скобки. Модель памяти — очень важная вещь. Явная модель памяти в С появилась относительно недавно, а до этого железо подстраивалось под софт.

aist1 ★★★
()
Ответ на: комментарий от aist1

появилась относительно недавно, а до этого железо подстраивалось под софт

Я не понял этого высказывания. С появлением перестало подстраиваться? Подстраивалось раньше созданием сегментных моделей адресации?

Асло,

железо подстраивалось под софт

Железо, неспособное исполнять какой-либо софт, никому не нужно. Железо ВНЕЗАПНО делают, чтобы на нём пускать софт. Как и наоборот - софт пишут, чтобы пускать его на железе.

Сама постановка вопроса выглядит как ложная дилемма яйца и курицы.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

Раньше скорее софт подстраивался, вон для той же сегментной адресации и указатели разные приходилось использовать, но с какого-то времени уже только железо и подстраивается под софт, это не только памяти касается, те же процессоры уже реально в железе очень далеки от сишного представления о процессорах застрявшего на уровне PDP-11.

anonymous
()
Ответ на: комментарий от EugeneBas

а если тебе нужен dynamic_cast, то ты хочешь кастить предка к насленику, т.е. ты очевидно неправильно дизайнишь свой код

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

На вскидку придумался такой сценарий (не помню, реальный или нет): абстрактный фреймворковый класс веб-страницы – Page; фреймворковый подкласс со спец-обработкой – ErrorPage; все остальные страницы. На плюсах я не использую RTTI и поэтому завёл бы какой-нибудь флажок/enum Page::getType(); а в JVM информация о типах в рантайме есть всегда, так что проще проверить (page instanceof ErrorPage) чем дополнительные сущности городить.

Ну и наконец есть довольно забавный пример – когда внешне вроде не даункаст, а по сути – он самый, родимый:

try {
    ...
} catch (std::system_error& e) {
    ...
} catch (ещё_какой_нибудь_подкласс_std_exception& e) {
    ...
} catch (std::exception& e) {
    ...
}
dimgel ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.