Pull to refresh
4
0
Send message

Спасибо, интересно! Однако, предлагаю не обижать std::format_to в сравнении с sprintf: для sprintf буфер аллоцируется заранее, и с той же возможностью преаллоцировать буфер имеет смысл и std::format_to померять тоже. Я из любопытства померял - плюс добавил строчку на 10 миллионов итераций - получается примерно так (новая колонка с преаллоцированным буфером для std::format_to - предпоследняя):

iterations stringstream sprintf format_to fmt_to(2) format

1 14.50 3.20 4.60 0.30 0.40
2 2.55 0.50 0.50 0.30 0.30
5 1.66 0.34 0.30 0.22 0.28
10 1.16 0.26 0.24 0.19 0.21
100 0.86 0.21 0.19 0.16 0.19
1000 0.82 0.20 0.17 0.15 0.18
10000 0.77 0.19 0.16 0.14 0.17
100000 0.74 0.17 0.14 0.13 0.15
1000000 0.77 0.18 0.17 0.14 0.18
10000000 0.74 0.17 0.14 0.13 0.16

Извините за поехавшее форматирование - не знаю, как это можно здесь исправить. Мерял с компилятором msvc, Release, все оптимизации - на скорость.

Код для новой колонки - как для sprintf:

std::string str(100, '\0');
std::format_to(str.data(), "Number {} is great!", numbers[i]);

Интересно - однако, я не понял, как ":" поступает с типами операндов и своего результата. В примерах либо слева и справа тот же тип (целое, строка) или (более интересный второй случай) справа - вызов функции, возвращающей void, и затем ";". Второй случай намекает, что тип правого параметра диктует тип результа. Первый случай намекает, что тип левого параметра - тоже в деле. Хорошо, а если справа - целое, а слева - строка? Скорее всего, произойдет ошибка приведения типов ( хотя, можно и пофантозировать, что ошибки не будет, а типом результата будет тип-сумма целого и строки, int | string, как можно делать на TypeScript-е). А если справа и слева - пользовательские типы в каких-то отношениях subtype / supertype?

В плане accessibility - есть довольно интересная разрабока по диктовке математических формул - на случай, если вам будет любопытно: https://devblogs.microsoft.com/math-in-office/math-dictation/

Отлично, спасибо! Я себе сделал попроще, по мотивам той презентации, буду иметь в виду, если понадобится улучшить.

Антон, спасибо - вопрос: используется ли в этом проекте идиома / утилита fast pimpl (вы рассказывали о ней в презентации "C++ трюки из Такси" 2.5 лет назад)? Я заглянул в пару-тройку хедеров и не наткнулся на использование (пока). Предполагаю, что не используется - т.е. C++ утилита / приём весьма общего назначения, полезная в одном крупном C++ проекте, с которым вы работали, в другом крупном C++ проекте (в той же фирме) оказалась не так уж полезна?

Хотел бы прокоментировать

1. "Они могли бы добавить в язык атрибуты, упрощающие контроль и за выделением/освобождением памяти, и за использованием объектов на разных уровнях приоритета, чтобы не ловить это исключительно через Driver Verifier в процессе выполнения"

и

2. "Нет, вместо этого они предпочли писать и отлаживать весь системный код на C/C++ примитивными дедовскими методами, а теперь убедили себя, что нет ничего лучше, как переписать его на Rust".

По 1: приведу один пример того, что не только могли, но и делали. Вы, наверно, никогда не сталкивались с майкрософтовским SAL (Standard Annotation Language). Этот "язык аннотаций" для C, C++ был впервые внедрён, насколько я помню, в нулевые годы - и выпускался тогда в составе DDK (Dirver Development Kit) как хороший инструмент для повышения качества third party драйверов. https://learn.microsoft.com/en-us/cpp/code-quality/understanding-sal - хотя ныне, оставаясь полезным для кода в стиле C, этот инструмент кажется мне недостаточно современным для C++ кода. Современный msvc инструментарий включает майкрософтовский статический анализатор C++ кода, синхронизованный с рекомендацими C++ Core Guidelines (и, скорее всего, некоторыми стандартными атрибутами в двойных квадратных скобках - проверю этот аспект, когда мне это будет более кстати), и, в дополнение к этому, интеграцию с анализатором clang-tidy. Visual Studio предоставляет относительно удобный UI для использования этих возможностей. VS Code поддерживает эти возможности в какой-то мере тоже (читаю сейчас блог-пост про использование clang-tidy с VS Code).

По 2: это не так, в Microsoft активно используются статические анализаторы кода и другие методы формальной верификации (выше я коснулся только отдельных примеров, как такое использование возможно при применинии msvc toolchain), вот обзорная лекция по этому вопросу (я остановил на слайде, где перечисляются такие проекты / инструменты с 2001-го по 2022-й - для верификации в основном критического кода системного уровня): https://youtu.be/GEsvGGp0jyQ?t=935. Про переписывание на Rust - это ещё один инструмент; исходная заметка, которую мы комментируем, указывает только несколько таких проектов, пока имеющих относительно маленький размах, насколько я могу судить.

Ваша история про статистику смертности в автокатастрофах в США до и после внедрения подушек безопасности меня заинтересовала! Я поискал подтверждения, но пока не нашёл.

Нашёл вот такой график. Внедрение подушек безопасности происходило между примерно второй половиной 80-х (всё более широкое внедрение технологии, продолжавшей совершенствоваться; закон был принят в 1991-м, с отложенным вступлением в силу) и 1998-м годом (закон полностью вступил в силу). Красная кривая (смерти на милю) шла вниз, но не драматично (заметен небольшой зубец более резкого падения около 1992-го года - более заметный на оранжевой кривой, смерти на миллион населения). Главное - я не вижу отскока.

Аттрибуция графика: By Dennis Bratland - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=66179446

Annual US traffic  fatalities per billion vehicle miles traveled (VMT) (red), per million  people (orange), total annual deaths (light blue), VMT in 10s of  billions (dark blue) and population in millions (teal), from 1921 to  2017
Annual US traffic fatalities per billion vehicle miles traveled (VMT) (red), per million people (orange), total annual deaths (light blue), VMT in 10s of billions (dark blue) and population in millions (teal), from 1921 to 2017



Начал читать, имея только шапочное знакомство с Rust - глаза зацепились за фрагмент в "Плюсы Rust", "... и без сборщика мусора" (как будто это что-то хорошее :) ). Тут вероятно было бы уместно упомнятуть "строгий контроль использования памяти, не опирающийся на сборщик мусора". В C++ нет сборщика мусора (кстати, часть языка, позволявшая интеграцию сборщика мусора, существовала некоторое время в стандарте - убрали), но нет и строго контроля, легко выстрелить в ногу. В D есть строгий контороль, но есть и сборщик мусора. В дизайне Rust сознательно отказались от сборщика мусора, что имеет далекоидущие последствия из-за более сложных мер по обеспечению строгого контроля.

Да, магические числа (размер, выравнивание болванки std::aligned_storage - кстати, этот класс из стандарта убирают в C++23) в хедере; при несоответсвии правильным числам (размеру и выравниванию класса Impl, реализации) static_assert выдаёт правильные числа в диагностике (трюк, по-моему, в презентации Антона описан). То, что выглядит не очень - сходная цена при ограниченном применении. Я подумывал о более широком применении FastPImpl с генерацией скучного кода, но - поскольку применяю не часто, пока не сделал.

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

С FastPImpl у меня есть опция (которую, впрочем, я пока не применял) подменять реализацию класса Xyz::Impl моком в случае, если я собираю тест для кода, использующего Xyz. Просто, вместо либы, реализующей настоящий Xyz::Impl, при линковке подставить мок-либу (ну или просто добавить в код теста #include на хедер-онли реализацию мока Xyz::Impl). Вероятно, с модулями тоже можно проделывать хаки такого рода, по крайней мере, при определённой дисциплине использования модулей.

В плане обмена опытом рекомендую посмотреть начало (и конец, с ответами на вопросы по конкретной теме) вот этой презентации, то есть, её части, посвящённые FastPImpl: https://www.youtube.com/watch?v=mkPTreWiglk&t=1046s (перемотано 2 мин 20 сек от начала - сразу к делу).

Disclaimer: я с Антоном Полухиным не знаком и к Яндексу отношения не имею, просто с интересом просмотрел эту презентацию и использовал изложенные идеи реализации Fast Pimpl, когда мне это понадобилось (не найдя ничего лучше на тот момент). Сейчас посмотрел написанный тогда код - определение template-класса FastPImpl (помощника) занимает примерно 25 строк довольно прямолинейной логики. Они включают в себя определения делегирующего конструктора и деструктора (а не операторов new, delete), проверку соответствия размера и выравнивания во время компиляции (с помощью static_assert), удобные аксессоры для использующего класса. Move-конструктор и move-присваивание тоже делегируются, а копирующие я делитнул (и до сих пор ни один из классов, скрывающих таким способом реализацию, не потребовал семантики копирования). Использую как раз в ситуациях вроде описанной вами - для изоляции публичного объявляния класса от реализации (когда это имеет смысл) с нулевым / минимальным оверхеадом (зависит от того, включена ли опция link time code generation в билде). Использующие классы - публичные интерфейсы своих скрытых за FastPImpl реализаций - имеют некоторое количество тривиального бойлерплейта, но - терпимо.

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

Оставляю терминологический комментарий: приём, с которого вы начинаете заметку, и который вы назваете "цепочкой", общепринято называется "телескопическая сумма", "телескопический ряд" (https://ru.wikipedia.org/wiki/Телескопический_ряд) - калька с telescoping sum. Имеется в виду физическая аналогия в виде старинного телескопа или складной подзорной трубы из входящих друг в друга трубок.

Да (пардон, я пропустил ваш ответ тогда), практическая польза -- очень важное мерило, я с этим согласен. Дисбаланс в наших мнениях, возможно, здесь (цитирую вашу последнюю реплику): "лично меня интересует не процесс и не все подряд результаты, а только те, которые можно применить на практике". Мне процесс и промежуточные результаты инетересны в некоторых областях безотносительно к применению-на-практике-прямо-сейчас. Упражнение ума (изредка всё-таки получается интересные идеи применять на практике самому), плюс -- элемент future proof (ориентироваться в нововведениях заранее).

Практичность, внедрение чего попроще впереди более продуманного с заделом на будущее подхода -- иногда имеет неприятный длинный хвост, опять приведу пример: дженерики в Java. Их ввели в язык с значительной задержкой (возможно, кстати, не без влияния конкурирующего C#, в котором дженерики появились раньше), и получившийся результат не изящен (читаю книгу по Kotlin - там проблему систематически исправляют, но не обходится без заусенцев на стыковках с Java). В момент разработки первой версии языка нужная теория (и практические наработки из других языков) были наготове -- но сделали (правомерно, вероятно) выбор в пользу более раннего релиза более простой, практичной системы без дженериков. То же с Go, я не слежу за ситуацией (вводят там дженерики в новых версиях или нет) -- повторение похожей истории лет 20 после выпуска первой версии Java. С C#/.NET вышло более гладко -- базу для введения более органичный дженериков теоретически разработал (и сделал прототипную реализацию) учёный Don Syme (знаменитый также, как автор и популяризатор F#) -- а стартовали, опять же, без дженериков, "практично".

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

«Алгебраические эффекты» человеческим языком - перевод на Хабре вводной статьи прикладного программиста, который ближе к концу поясняет, что с Хаскелем он не знаком и потому научные статьи ему непонятны, он рассматривает тему с практическими примерами на JavaScript (ссылка на оригинал статьи для удобства). Более недавняя статья другого программиста с примерами на OCaml: Friendship ended with Monads: Testing out Algebraic effects in OCaml for Animations.

Научные статьи, рассматривающие алгебраические эффекты и альтернативные подходы (монадные трансформеры) в рамаках Хаскеля: Monad Transformers and Modular Algebraic Effects (What Binds Them Together), Freer Monads, More Extensible Effects. Я такие статьи, скорее, просматриваю - с Хаскелем шапочно знаком, но не программирую на нём.

Уместна ссылка на давнюю статью автора Koka: Algebraic effects for Functional Programming.

Моё поверхностное мнение: мне бы на Koka было бы удобнее программировать, чем разбираться с монадными трансформерами на Хаскеле. Koka - компактный язык, чётко иллистрирующий идею как "чисто" программировать в функциональном стиле с эффектами ("чисто" в том смысле, что нотация эффектов явная, и при этом не особо навязчивая). В плюс к этому - такое программирование хорошо укладывается у меня в голове (я "вижу", что будет происходить в машинном коде при выполнении - и при желании могу разобраться в сгенерированном компилятором Koka коде на стандартном C, реализуещем нужную семантику благодаря фиче C longjump, насколько я понимаю). Ну и -- при желании я этот код на C мог бы встроить в свою программу (на C++); не уверен, что получилось бы легко с встраиванием кода на Хаскеле.

С Rust сравнивать, наверно, можно, но осторожно - размах не тот (Rust - промышленный язык, Koka - скорее, демонстрационный, ну, как сравнивать Rust и Idris).

Пока нет, но вы оборвали цитирование.

... "However, we have plans for future improvements: since we know statically that only mutable references are able toform a cycle, we could generate code that tracks those datatypes at run time and may perform a more efficient form of incremental cycle collection."

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

Если вы продолжаете работать над проектом, обратите, пожалуйста, внимание на правильность английского в идентификаторах, названиях файлов - IsXyzExists плохо звучит. Уберите.Is или замените на Does, убрав s на конце (DoesXyzExist).

(я не автор поста) По-моему, смысл & - создать временный объект ValuePointer, с вызовом guard в конструкторе и unguard в деструкторе. Ну а * - собственно взять завёрнутое в этот временный ValuePointer значение, но! - с дополнительными приспоблениями, реализованными в guard / unguard (ну, потоковая безопасность, что-то такое). Польза!

Конечно, невнимательный прикладной программист может случайно забыть добавить *& -- и не получить пользы. Код будет как бы работать, но не совсем: что-то крэшнется, что-то полетит не туда. Ну а кто сказал, что профессия программиста - простая и лёгкая? Программист должен быть внимательным!

В pdf-е этого переиздания не хватает обложки с портретом Киселёва - её можно увидеь здесь (издание - не коммерческое, стоимость бумажной копии равна затратам на производство этой копии на одном из print-on-demand сервисов):
https://www.amazon.com/Геометрия-Киселёву-Russian-Петрович-Киселёв/dp/1684748127/

1
23 ...

Information

Rating
Does not participate
Registered
Activity