#longread
У
https://habr.com/ru/post/665632/
У
std::unique_ptr есть минус - он аллоцирует память в куче. А что, если разработать его аналог с памятью на стеке?.. Об этом можно прочитать тут!https://habr.com/ru/post/665632/
Хабр
Концепция умного указателя static_ptr<T> в C++
В C++ есть несколько "умных указателей" - std::unique_ptr , std::shared_ptr , std::weak_ptr . Также есть более нестандартные умные указатели, например в boost 1 : intrusive_ptr , local_shared_ptr . В...
👍3
C++95
#longread У std::unique_ptr есть минус - он аллоцирует память в куче. А что, если разработать его аналог с памятью на стеке?.. Об этом можно прочитать тут! https://habr.com/ru/post/665632/
#madskillz
Fast Pimpl
Концепцию из статьи (насколько понял, достаточно необычную) народ принял с трудом, в комментариях было бурное обсуждение и иногда непонимание.
В комментариях затронули тему Fast Pimpl (понятия, не сильно связанного со статьей). Это как раз жутко боянистая концепция.
PImpl используется, чтобы скрыть детали реализации и/или ускорить компиляцию. До его использования код в хидере выглядит так:
То есть вместо самого объекта в структуре лежит буфер памяти под этот объект, и рулят именно им.
В интернете есть много реализаций этой идиомы (по гуглежу "Fast Pimpl"), и есть обёртки, которые хороши собой 🍬
Fast Pimpl
Концепцию из статьи (насколько понял, достаточно необычную) народ принял с трудом, в комментариях было бурное обсуждение и иногда непонимание.
В комментариях затронули тему Fast Pimpl (понятия, не сильно связанного со статьей). Это как раз жутко боянистая концепция.
PImpl используется, чтобы скрыть детали реализации и/или ускорить компиляцию. До его использования код в хидере выглядит так:
#include <third_party/json.hpp>Стандартный подход заключается в замене
struct Value {
third_party::Json data_;
};
T на std::unique_ptr<T>, потому что он разрешает использовать incomplete class:namespace third_party { struct Json; }
struct Value {
std::unique_ptr<third_party::Json> data_;
};
Проблема, которая из-за этого возникает - аллокация объекта в куче, это замедляет рантайм PImpl-ового варианта ⏱ Поэтому используется подход Fast Pimpl:struct Value {
std::aligned_storage<sizeof(T), alignof(T)> data_;
};
(вместо sizeof(T) и alignof(T) надо руками ввести нужную цифру)То есть вместо самого объекта в структуре лежит буфер памяти под этот объект, и рулят именно им.
В интернете есть много реализаций этой идиомы (по гуглежу "Fast Pimpl"), и есть обёртки, которые хороши собой 🍬
👍3
#compiler
extern "C" - на что в действительности это влияет?
Примерно 9 лет назад я пробовал писать игры на C++. Для скриптов я использовал Lua. Он поставлялся в виде статической библиотеки (
В языке C перегрузки методов нет, и в объектный файл попадает "чистое" название метода. В C++ попадает "запутанное", это называется name mangling:
Однако что в остальном? Сам код внутри
Сам Стандарт показывает примеры, из которых видно, что код скомпилируется по правилам C++: [dcl.link] (там есть классы).
Погрепав исходники компилятора, можно точно сказать, что компиляция внутри
Вывод из этого такой: хидеры библиотеки на C должны быть достаточно простыми, чтобы в C и в C++ они компилировались одинаково.
extern "C" - на что в действительности это влияет?
Примерно 9 лет назад я пробовал писать игры на C++. Для скриптов я использовал Lua. Он поставлялся в виде статической библиотеки (
lua.lib для Windows) и header-файлов. Так как Lua написан на C и lua.lib собран компилятором C, то чтобы все слинковалось, надо было подключать хидеры так:extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
В то время я не понимал, почему надо делать именно так. Конечно, спустя много времени стало понятно, что дело в перегрузке методов. Перегруженные методы надо как-то отличать между собой.В языке C перегрузки методов нет, и в объектный файл попадает "чистое" название метода. В C++ попадает "запутанное", это называется name mangling:
void hello(int, char) -> hello (в С)
void hello(int, char) -> _Z1helloic (в C++)
extern "C" нужен как раз для того, чтобы методы и переменные, объявленные внутри него, имели "чистое" имя (код на godbolt)Однако что в остальном? Сам код внутри
extern "C" компилируется как C++ или как C? Мнение "C это подмножество C++" ошибочно: два языка развиваются независимо. В C есть то, чего нет в C++, например VLA.Сам Стандарт показывает примеры, из которых видно, что код скомпилируется по правилам C++: [dcl.link] (там есть классы).
Погрепав исходники компилятора, можно точно сказать, что компиляция внутри
extern "C" происходит по правилам C++, и аффектится только условие "надо манглить или нет": исходник Clang.Вывод из этого такой: хидеры библиотеки на C должны быть достаточно простыми, чтобы в C и в C++ они компилировались одинаково.
👍8
#books
Обзор книги "Advanced C and C++ Compiling" 📚
(можно скачать тут - https://news.1rj.ru/str/progbook/6109)
Книги по программированию часто разочаровывают. Они подобны пирамиде - большую долю занимает элементарщина и копипаст документации в переводе Гоблина; в середине находятся менее боянистые книги, но их меньше; и на вершине находится что-то действительно интересное.
Эта книга, которую я читал в прошлом году, имеет очень большую редкость.
В ней описаны роли линкера (linker) и загрузчика (loader) и их работа в очень глубоких деталях (а также компилятора (compiler), но в меньшей степени). Есть много картинок, описаний форматов, команд Linux и Windows, и прочего.
Книга подробная вплоть до того, что объясняет, по какой схеме надо правильно версионировать свои динамические библиотеки! 🤯 (понятно, что это очень специфическое занятие)
Я бы рекомендовал для чтения первые несколько глав, а также главы 12 и 13 (для Linux-разработчиков)
Обзор книги "Advanced C and C++ Compiling" 📚
(можно скачать тут - https://news.1rj.ru/str/progbook/6109)
Книги по программированию часто разочаровывают. Они подобны пирамиде - большую долю занимает элементарщина и копипаст документации в переводе Гоблина; в середине находятся менее боянистые книги, но их меньше; и на вершине находится что-то действительно интересное.
Эта книга, которую я читал в прошлом году, имеет очень большую редкость.
В ней описаны роли линкера (linker) и загрузчика (loader) и их работа в очень глубоких деталях (а также компилятора (compiler), но в меньшей степени). Есть много картинок, описаний форматов, команд Linux и Windows, и прочего.
Книга подробная вплоть до того, что объясняет, по какой схеме надо правильно версионировать свои динамические библиотеки! 🤯 (понятно, что это очень специфическое занятие)
Я бы рекомендовал для чтения первые несколько глав, а также главы 12 и 13 (для Linux-разработчиков)
Telegram
Книги для программистов
Advanced C and C++ Compiling (2014)
Автор: Milan Stevanovic
Количество страниц: 334
Научиться писать код на C / C++ - это только первый шаг. Чтобы быть серьезным программистом, вы должны понимать структуру и назначение двоичных файлов, создаваемых компилятором:…
Автор: Milan Stevanovic
Количество страниц: 334
Научиться писать код на C / C++ - это только первый шаг. Чтобы быть серьезным программистом, вы должны понимать структуру и назначение двоичных файлов, создаваемых компилятором:…
❤3👍1
#story
Как готовятся задачи к олимпиадам по программированию? (Часть 1/2)
Наверное, много кто пробовал решать задачки: на codeforces.com, школьных олимпиадах, ACM ICPC, и в других местах. Большинство людей использует C++ для решения.
Я раньше очень увлекался этим занятием (называется "проблемсеттинг"), и даже эпизодически готовил задачи для codeforces и школьных олимпиад (подготовил примерно 15-20 задач).
Это занятие оплачивается достаточно символически. Поэтому в основном этим занимаются студенты - бывшие топовые олимпиадники, которые еще не закончили университет и не нашли работу. Потом им надоедает и их сменяет другое поколение студентов.
Покажу на примере одной задачи, как происходит подготовка: Codeforces Round #704 - E. Почти отказоустойчивая база данных
(Эта задача самая сложная в том раунде, но у нее простое описание)
Как готовятся задачи к олимпиадам по программированию? (Часть 1/2)
Наверное, много кто пробовал решать задачки: на codeforces.com, школьных олимпиадах, ACM ICPC, и в других местах. Большинство людей использует C++ для решения.
Я раньше очень увлекался этим занятием (называется "проблемсеттинг"), и даже эпизодически готовил задачи для codeforces и школьных олимпиад (подготовил примерно 15-20 задач).
Это занятие оплачивается достаточно символически. Поэтому в основном этим занимаются студенты - бывшие топовые олимпиадники, которые еще не закончили университет и не нашли работу. Потом им надоедает и их сменяет другое поколение студентов.
Покажу на примере одной задачи, как происходит подготовка: Codeforces Round #704 - E. Почти отказоустойчивая база данных
(Эта задача самая сложная в том раунде, но у нее простое описание)
C++95
#story Как готовятся задачи к олимпиадам по программированию? (Часть 1/2) Наверное, много кто пробовал решать задачки: на codeforces.com, школьных олимпиадах, ACM ICPC, и в других местах. Большинство людей использует C++ для решения. Я раньше очень увлекался…
Как готовятся задачи к олимпиадам по программированию? (Часть 2/2)
Подготовка задачи ведется на сайте polygon.codeforces.com. Это самая продвинутая платформа, долгое время была практически единственной. Другие платформы (Яндекс.Контест, CodeChef, etc.) со временем потырили у нее фичи - валидацию, встроенную систему контроля версий, и прочее.
Для программок (генераторы, чекеры, etc.) используется библиотека testlib.h.
Процесс подготовки задачи состоит из этих пунктов (могут идти в произвольном порядке, кроме п.1):
(1) Придумывается не-баянистая идея для задачи в краткой формулировке.
(2) Создается художественное описание задачи в LaTeX - пример, это называется "легенда".
(3) Пишутся генераторы тестов, таких программок может быть несколько - пример 1, пример 2. Они могут запускаться с разными параметрами.
(4) Пишется валидатор, он нужен для проверки, что сгенерированные тесты имеют правильный формат и удовлетворяют ограничениям из условия задачи - пример.
(5) Список тестов создается через движок шаблонов Freemarker. Можно написать скрипт, который потом преобразуется в список запусков генераторов - пример из документации. В нашей задаче 100 тестов - список.
(6) Надо написать решение задачи. Есть одно эталонное правильное решение - пример, но надо также выдумать и написать все возможные неправильные решения, чтобы смотреть, смогут ли они пройти все (или почти все) тесты, и скорректировать тесты от этого.
(7) Чекер проверяет решение участника на правильность. На polygon есть дефолтные чекеры для простых задач, но если в задаче может быть несколько правильных ответов, то нужен свой чекер - пример.
Это дело достаточно серьезное, поэтому проводится перекрёстное код-ревью задач вместе с коллегами-проблемсеттерами и вычитка легенды.
Также примерно 5-15 человек разных рейтингов прорешивают задачу, не зная заранее решения. Они могут придумать неожиданное решение попроще, или заслать неправильное решение - все эти кейсы рассматриваются отдельно.
По закону больших чисел, на одну из десятков задач могут происходить факапы такого рода:
(1) Много участников додумались до решения проще авторского и реальная сложность задачи преувеличена
(2) Задача имеет слабые тесты, из-за чего проходит много "плохих" решений.
(3) Авторское решение неправильное.
Первые два типа факапов это еще ничего, а третий тип это полный треш, и на codeforces контест из-за этого просто отменяют (точнее, делают "нерейтинговым", и участвовать в нем нет смысла).
Подготовка задачи ведется на сайте polygon.codeforces.com. Это самая продвинутая платформа, долгое время была практически единственной. Другие платформы (Яндекс.Контест, CodeChef, etc.) со временем потырили у нее фичи - валидацию, встроенную систему контроля версий, и прочее.
Для программок (генераторы, чекеры, etc.) используется библиотека testlib.h.
Процесс подготовки задачи состоит из этих пунктов (могут идти в произвольном порядке, кроме п.1):
(1) Придумывается не-баянистая идея для задачи в краткой формулировке.
(2) Создается художественное описание задачи в LaTeX - пример, это называется "легенда".
(3) Пишутся генераторы тестов, таких программок может быть несколько - пример 1, пример 2. Они могут запускаться с разными параметрами.
(4) Пишется валидатор, он нужен для проверки, что сгенерированные тесты имеют правильный формат и удовлетворяют ограничениям из условия задачи - пример.
(5) Список тестов создается через движок шаблонов Freemarker. Можно написать скрипт, который потом преобразуется в список запусков генераторов - пример из документации. В нашей задаче 100 тестов - список.
(6) Надо написать решение задачи. Есть одно эталонное правильное решение - пример, но надо также выдумать и написать все возможные неправильные решения, чтобы смотреть, смогут ли они пройти все (или почти все) тесты, и скорректировать тесты от этого.
(7) Чекер проверяет решение участника на правильность. На polygon есть дефолтные чекеры для простых задач, но если в задаче может быть несколько правильных ответов, то нужен свой чекер - пример.
Это дело достаточно серьезное, поэтому проводится перекрёстное код-ревью задач вместе с коллегами-проблемсеттерами и вычитка легенды.
Также примерно 5-15 человек разных рейтингов прорешивают задачу, не зная заранее решения. Они могут придумать неожиданное решение попроще, или заслать неправильное решение - все эти кейсы рассматриваются отдельно.
По закону больших чисел, на одну из десятков задач могут происходить факапы такого рода:
(1) Много участников додумались до решения проще авторского и реальная сложность задачи преувеличена
(2) Задача имеет слабые тесты, из-за чего проходит много "плохих" решений.
(3) Авторское решение неправильное.
Первые два типа факапов это еще ничего, а третий тип это полный треш, и на codeforces контест из-за этого просто отменяют (точнее, делают "нерейтинговым", и участвовать в нем нет смысла).
#creepy и #video
Тупое нововведение в C++23: копирование с помощью auto{}
Что нужно сделать, чтобы скопировать объект, особенно в шаблонном коде? Зная, что объявление переменной с типом
Комментарии к видео интереснее самого видео, все люди ловят фейспалм и пишут комментарии как "в чем вообще проблема копировать как раньше", "почему не сделали в виде
Тупое нововведение в C++23: копирование с помощью auto{}
Что нужно сделать, чтобы скопировать объект, особенно в шаблонном коде? Зная, что объявление переменной с типом
auto это всегда копия, можно сделать так:void func(const auto& something) {
auto copy = something;
use(copy);
}
Что, если мы не хотим объявлять новый объект, а создать копию "на месте"? Тогда можно сделать так (decay нужен потому что исходный тип может быть ссылочным и/или иметь cv-квалификаторы):void func(const auto& something) {
use(std::decay_t<decltype(something)>{copy});
}
А что, если мы считаем что C++ еще недостаточно сложный? То в С++23 теперь можно делать так:void func(const auto& something) {
use(auto{copy});
}
Эту информацию я узнал из канала "C++ Weekly": https://www.youtube.com/watch?v=5zVQ50LEnuQ В видео приводится не очень убедительный "мотивационный пример".Комментарии к видео интереснее самого видео, все люди ловят фейспалм и пишут комментарии как "в чем вообще проблема копировать как раньше", "почему не сделали в виде
std::copy", и прочее. В этом я с ними согласен!YouTube
C++ Weekly - Ep 323 - C++23's auto{} and auto()
☟☟ Awesome T-Shirts! Sponsors! Books! ☟☟
Upcoming Workshops:
► C++ Best Practices Workshop, CppCon, Aurora, CO, USA, Sept 13-14, 2025: https://cppcon.org/class-2025-best-practices/
► C++ Best Practices Workshop, NDC TechTown, Kongsberg, NO, Sept 22-23…
Upcoming Workshops:
► C++ Best Practices Workshop, CppCon, Aurora, CO, USA, Sept 13-14, 2025: https://cppcon.org/class-2025-best-practices/
► C++ Best Practices Workshop, NDC TechTown, Kongsberg, NO, Sept 22-23…
#books
Обзор книги "API Design for C++ " 📚
(можно скачать тут - https://news.1rj.ru/str/progbook/3214)
Эту книгу мне когда-то посоветовал тимлид соседней команды. Он от нее был в полном восторге 🤩 Однако у меня впечатления были более сдержанными 🤔
Что такое API? По определению из книги:
В книге довольно четко можно разделить "общие принципы дизайна API" и "специфические вопросы дизайна API на C++".
Первая составляющая, как философская, действительно очень хороша - есть много толковых идей, подсказок, и разбор API популярных проектов. Книга поможет понять, над какими вопросами думает Software Architect, чтобы API не развалился через несколько недель 👍
Вторая составляющая, как техническая, на мой взгляд, слабовата. Вот некоторые из минусов, которые мне не понравились:
☹️ Книга издана в 2011 году, в ней описываются фичи
☹️ Некоторые параграфы писал Капитан Очевидность, например
☹️ Про статические и динамические библиотеки отведены последние 10 страниц из 450-страничной книги, тоже с тривиальностями.
Обзор книги "API Design for C++ " 📚
(можно скачать тут - https://news.1rj.ru/str/progbook/3214)
Эту книгу мне когда-то посоветовал тимлид соседней команды. Он от нее был в полном восторге 🤩 Однако у меня впечатления были более сдержанными 🤔
Что такое API? По определению из книги:
An API is a logical interface to a software component that hides the internal details required to implement it.API окружают нас везде, даже внутри одной программы их несколько. И вот книга рассматривает вопросы дизайна API на C++.
В книге довольно четко можно разделить "общие принципы дизайна API" и "специфические вопросы дизайна API на C++".
Первая составляющая, как философская, действительно очень хороша - есть много толковых идей, подсказок, и разбор API популярных проектов. Книга поможет понять, над какими вопросами думает Software Architect, чтобы API не развалился через несколько недель 👍
Вторая составляющая, как техническая, на мой взгляд, слабовата. Вот некоторые из минусов, которые мне не понравились:
☹️ Книга издана в 2011 году, в ней описываются фичи
C++0x (рабочее название стандарта C++11), но какой-то древней редакции. Нет ни слова про auto, руками пишутся std::vector<double>::iterator, и т.д.☹️ Некоторые параграфы писал Капитан Очевидность, например
Avoid #define for constants
☹️ На действительно очень важный вопрос совместимости ABI отведена буквально 1 страница с тривиальностями. Без упоминания тулзов как abidiff и каких-то специфических советов.☹️ Про статические и динамические библиотеки отведены последние 10 страниц из 450-страничной книги, тоже с тривиальностями.
Telegram
Книги для программистов
API Design for C++ (2011)
Автор: Martin Reddy
#cpp #book #english
Язык: английский.
Целевая аудитория: опытные разработчики на С++.
Если вы уже прошли курс начинающего разработчика на С++, вам, вероятно, захочется попробовать свои силы и сделать на…
Автор: Martin Reddy
#cpp #book #english
Язык: английский.
Целевая аудитория: опытные разработчики на С++.
Если вы уже прошли курс начинающего разработчика на С++, вам, вероятно, захочется попробовать свои силы и сделать на…
👍1
#video
Марсианские ужасы 👽
Как программист я часто имею дело с "воздушными замками": пишу код который работает с абстрактными в вакууме понятиями.
Неожиданно я наткнулся на видео, где разбирают софт для марсохода NASA (nasa/fprime). Сразу же, задыхаясь от интереса, открыл видео и принялся смотреть. Настоящей глыбой является мегадевайс, для которого писался этот код.
Первые 15 минут видео составляет унылое обозрение CI и велопарка .bash-скриптов для билда. Оттуда мы узнаем, что по авторитетной оценке анализатора lgtm.com репозиторий является редкой годнотой с качеством
Одухотворившись этими фактами, я превознесся в ожидании обзора кода, достаточно качественного для поиска марсианской цивилизации рептилоидов. Однако со своих небес я грохнулся как Стас Барецкий с лестницы.
В полном ужасе я обнаружил обзор на ядреную дедову смесь C89 и C++11, а от количества code smell у меня отклеились обои. Я стал свидетелем такого преступного программирования как:
Свои кривые реализации строки и вектора (буфера)
Константы задаются через
Комментарии к видео интереснее самого видео - работники разных European Space Agency подтверждают экстремистский уровень программирования в "научной" и "государственной" сфере.
Марсианские ужасы 👽
Как программист я часто имею дело с "воздушными замками": пишу код который работает с абстрактными в вакууме понятиями.
Неожиданно я наткнулся на видео, где разбирают софт для марсохода NASA (nasa/fprime). Сразу же, задыхаясь от интереса, открыл видео и принялся смотреть. Настоящей глыбой является мегадевайс, для которого писался этот код.
Первые 15 минут видео составляет унылое обозрение CI и велопарка .bash-скриптов для билда. Оттуда мы узнаем, что по авторитетной оценке анализатора lgtm.com репозиторий является редкой годнотой с качеством
C/C++ кода A+.Одухотворившись этими фактами, я превознесся в ожидании обзора кода, достаточно качественного для поиска марсианской цивилизации рептилоидов. Однако со своих небес я грохнулся как Стас Барецкий с лестницы.
В полном ужасе я обнаружил обзор на ядреную дедову смесь C89 и C++11, а от количества code smell у меня отклеились обои. Я стал свидетелем такого преступного программирования как:
Свои кривые реализации строки и вектора (буфера)
ObjBase::~ObjBase() {} // не делают ~ObjBase() = default
void resetSer(void);
ExternalSerializeBuffer(ExternalSerializeBuffer& other);
ExternalSerializeBuffer(ExternalSerializeBuffer* other);
const char* operator=(const char* src);
(void)snprintf(buffer, size, ...
В коде использование reinterpret_cast в 169 местахКонстанты задаются через
enum
Я думаю, надо сделать аудит NASA на предмет коррупционного сговора с lgtm.com о качестве кода. А также можно не бояться восстания машин, потому что Терминатор скорее будет страдать деменцией и стрелять себе в ноги, чем в Сару Коннор.Комментарии к видео интереснее самого видео - работники разных European Space Agency подтверждают экстремистский уровень программирования в "научной" и "государственной" сфере.
YouTube
C++ Weekly - Exploring and Reviewing F', The Mars Ingenuity Flight Software
☟☟ Awesome T-Shirts! Sponsors! Books! ☟☟
Upcoming Workshops:
► C++ Best Practices Workshop, CppCon, Aurora, CO, USA, Sept 13-14, 2025: https://cppcon.org/class-2025-best-practices/
► C++ Best Practices Workshop, NDC TechTown, Kongsberg, NO, Sept 22-23…
Upcoming Workshops:
► C++ Best Practices Workshop, CppCon, Aurora, CO, USA, Sept 13-14, 2025: https://cppcon.org/class-2025-best-practices/
► C++ Best Practices Workshop, NDC TechTown, Kongsberg, NO, Sept 22-23…
#advice
Избыточный const снижает перфоманс ⏱
Правильное использование
Пусть у нас есть структура, которая представляет собой API-объект. Содержимое этой структуры не планируется как-то изменять после создания, поэтому с первого взгляда логично, чтобы все поля были объявлены константными:
Дело в том, что
(пример на godbolt с логами)
Также константность полей часто просто не нужна - для нашей цели достаточно, чтобы константным был сам объект (или ссылка на него), а не его поля.
Избыточный const снижает перфоманс ⏱
Правильное использование
const это большая тема в C++. Самое крутое объяснение я видел на Const Correctness, C++ FAQ, но там не показан один из минусов "избыточного" const.Пусть у нас есть структура, которая представляет собой API-объект. Содержимое этой структуры не планируется как-то изменять после создания, поэтому с первого взгляда логично, чтобы все поля были объявлены константными:
struct Widget {
const std::size_t radius;
const std::vector<Event> events;
const Element element;
};
Но это плохо, если в коде объекты этого типа перемещаются, хранятся в векторе, и так далее.Дело в том, что
const-поля нельзя мувнуть, поэтому в записи Widget w2{std::move(w1)} поля events и element скопируются.(пример на godbolt с логами)
Также константность полей часто просто не нужна - для нашей цели достаточно, чтобы константным был сам объект (или ссылка на него), а не его поля.
void Do(const Widget& widget);
👍9
#madskillz и #video
Успех применения идиомы Read-Copy-Update в C++
В видео За полгода - C++ Погода разработчик Яндекс.Погоды Дмитрий Старков рассказывает о переезде погодных микросервисов на C++ 🌤
До переезда Погода разрабатывалась на Node.JS + Memcached и отвечала за 300мс в 99 перцентиле. Это было медленно даже с кэшем, а после улучшения погодных предсказаний кэш стал сильно разъезжаться с реальными данными 👎🏻
Чтобы ускориться и улучшить утилизацию CPU, решили переписать на C++.
Для быстроты данные держатся in-memory, не в СУБД.
В памяти программа держит погодное состояние, которое регулярно читается (по GET-запросам от пользователей) и обновляется (по POST-запросам от поставщиков данных).
Обычный подход - shared_ptr:
Более быстрый подход - использование
Однако такой подход все равно был недостаточно быстрым (в видео есть графики со "спайками"). Поэтому применили lock-free идиому Read-Copy-Update. Состояние живет тут:
В видео есть описание работающей хитрой схемы, я пересмотрел часть с кодом несколько раз, чтобы полностью понять его.
Переписывание обернулось успехом - при возросшем (за время переписывания) в 1.5 раза RPS Погода отказалась от рассинхронизирующих кэшей и стала отвечать за 30мс на 99 перцентиле! ⏱
Успех применения идиомы Read-Copy-Update в C++
В видео За полгода - C++ Погода разработчик Яндекс.Погоды Дмитрий Старков рассказывает о переезде погодных микросервисов на C++ 🌤
До переезда Погода разрабатывалась на Node.JS + Memcached и отвечала за 300мс в 99 перцентиле. Это было медленно даже с кэшем, а после улучшения погодных предсказаний кэш стал сильно разъезжаться с реальными данными 👎🏻
Чтобы ускориться и улучшить утилизацию CPU, решили переписать на C++.
Для быстроты данные держатся in-memory, не в СУБД.
В памяти программа держит погодное состояние, которое регулярно читается (по GET-запросам от пользователей) и обновляется (по POST-запросам от поставщиков данных).
Обычный подход - shared_ptr:
std::shared_ptr<State> state;И использование
std::mutex с std::lock_guard для безопасного чтения/записи.Более быстрый подход - использование
std::shared_mutex и std::shared_lock (для читателей) и std::unique_lock (для писателей)Однако такой подход все равно был недостаточно быстрым (в видео есть графики со "спайками"). Поэтому применили lock-free идиому Read-Copy-Update. Состояние живет тут:
std::atomic<std::shared_ptr<State>> state;И читатель атомарно копирует объект, лежащий внутри
std::atomic, прежде чем как-то с ним работать. Писатель также атомарно изменяет объект.В видео есть описание работающей хитрой схемы, я пересмотрел часть с кодом несколько раз, чтобы полностью понять его.
Переписывание обернулось успехом - при возросшем (за время переписывания) в 1.5 раза RPS Погода отказалась от рассинхронизирующих кэшей и стала отвечать за 30мс на 99 перцентиле! ⏱
YouTube
C++ Zero Cost Conf 31 июля 2021. 1 трек.
С++ Zero Cost Conf — новая конференция, которую разработчики на C++ в Яндексе провели для всех практикующих плюсовиков. Прикладной С++ — измеримые метрики и реальные кейсы. Конференция идёт в два потока.
Телеграмм чат конференции (https://news.1rj.ru/str/Cxx_Zero_Cost_Conf).…
Телеграмм чат конференции (https://news.1rj.ru/str/Cxx_Zero_Cost_Conf).…
👍5😢1
#creepy
Двуликий inline для функций
Так вышло, что сейчас в C++ ключевое слово
(1) [dcl.inline].2: Подсказка компилятору о том, что более предпочтительна подстановка кода из функции в место вызова, чем сам вызов функции. (В компиляторе Clang в LLVM IR этот атрибут у функции называется
(2) [dcl.inline].6: Правило, что у функции может быть несколько определений (definition) за программу. Для отсутствия UB это должны быть одинаковые определения - что естественным образом соблюдается, т.к. обычно метод определен в хидере. (В компиляторе атрибут у функции называется
Получается, что у функции есть два атрибута -
Прикол в том, что:
Стандарт написан так, что
Clang выполняет именно это, т.е.
То есть получается вот что:
А вот методы, которые по Стандарту являются implicit inline functions, например:
(*) инстанциации шаблонов
(*) default и deleted члены классов, и их неявные методы
(*) методы, чей
(*)
... они только
Если бы я мог редактировать стандарт, я бы переименовал inline functions в linkonce functions, потому что текущее описание в Стандарте сильно запутывает...
Интересные вопросы:
(1) Так ли сильно влияет наличие атрибута
Влияет практически незаметно. Порог для инлайнинга у обычного метода 225 попугаев, для inlinehint-метода 325 попугаев (ссылка на код), а для реальной разницы нужно ~1000 попугаев.
То есть в 99% кейсов
(2) Почему один атрибут вытекает из другого, если они ортогональны?
Чтобы компилятор мог в translation unit заинлайнить (
Двуликий inline для функций
Так вышло, что сейчас в C++ ключевое слово
inline для функций (точнее, само понятие inline-функции) наделяет их двумя абсолютно ортогональными друг другу effective смыслами. Понятно, что один смысл вытек из другого, но все равно это раздельные сущности. Это:(1) [dcl.inline].2: Подсказка компилятору о том, что более предпочтительна подстановка кода из функции в место вызова, чем сам вызов функции. (В компиляторе Clang в LLVM IR этот атрибут у функции называется
inlinehint)(2) [dcl.inline].6: Правило, что у функции может быть несколько определений (definition) за программу. Для отсутствия UB это должны быть одинаковые определения - что естественным образом соблюдается, т.к. обычно метод определен в хидере. (В компиляторе атрибут у функции называется
linkonce_odr)Получается, что у функции есть два атрибута -
inlinehint и linkonce.Прикол в том, что:
Стандарт написан так, что
inlinehint подразумевается только у функций, где явно написан спецификатор inline.Clang выполняет именно это, т.е.
inlinehint ставится тогда и только тогда, когда есть спецификатор inline.То есть получается вот что:
inline int random() { return 4; } будет И inlinehint, И linkonce.А вот методы, которые по Стандарту являются implicit inline functions, например:
(*) инстанциации шаблонов
(*) default и deleted члены классов, и их неявные методы
(*) методы, чей
definition находится внутри definition класса(*)
constexpr- и consteval-функции (на самом деле только constexpr, т.к. consteval "испаряется" и в LLVM IR его код тупо не попадает)... они только
linkonce. Надо все равно писать ключевое слово inline, чтобы было linkonce+inlinehint.Если бы я мог редактировать стандарт, я бы переименовал inline functions в linkonce functions, потому что текущее описание в Стандарте сильно запутывает...
Интересные вопросы:
(1) Так ли сильно влияет наличие атрибута
inlinehint на компиляцию? Я слышал, что компиляторы уже давно не обращают внимание на подобные подсказки...Влияет практически незаметно. Порог для инлайнинга у обычного метода 225 попугаев, для inlinehint-метода 325 попугаев (ссылка на код), а для реальной разницы нужно ~1000 попугаев.
То есть в 99% кейсов
inline constexpr void foo() и constexpr void foo() будут вести себя одинаково, однако в clang-tidy не принимают патч на удаление redundant inline, потому что остается какой-то непонятный 1% кейсов.(2) Почему один атрибут вытекает из другого, если они ортогональны?
Чтобы компилятор мог в translation unit заинлайнить (
inlinehint) функцию, TU нужно "видеть" исходник этой функции, то есть её тело. В общем случае это невозможно обеспечить, потому что если у нас N штук TU, то будет N определений одной и той же функции и линкер сломается. Поэтому эта функция должна являться linkonce, чтобы не нарушился ODR.👍3🔥3🤔3🤯1
#compiler
Прикладные linkage types в C++ 🔗
Стандартное добавление функции в C++ происходит так: в
Чтобы определить функцию в
Посмотрим, каким будет LLVM IR (компиляторное промежуточное представление C++-кода) для
У LLVM IR есть интересный список linkage types для символов, типов много. Linkage type определяет, как ведет себя символ во время линковки в бинарник. (Символ - это функция или глобальная переменная)
▪️ У
▪️ У
▪️ У
В программе может быть несколько weak-определений одного и того же символа.
Если все определения символа являются weak, то линковщик берет рандомное определение.
Если какое-то из определений символа является strong (как у
В чем отличие
▪️ В общем случае weak-определения могут быть разными, поэтому компилятор не имеет права заинлайнить вызов weak-функции (после линковки у функции может оказаться совсем другое определение)
▪️ Но Стандарт С++ требует от программиста обеспечить, чтобы inline-функции имели идентичное определение (это естественным образом достигается, если определение находится в
▪️ Поэтому компилятор имеет право заинлайнить вызов метода
Скомпилируем объектный файл
Прикладные linkage types в C++ 🔗
Стандартное добавление функции в C++ происходит так: в
.h-файле пишется объявление функции (которое попадает в много translation unit), а в одном .cpp-файле пишется определение функции.Чтобы определить функцию в
.h-файле, чаще всего пишут спецификатор inline (это рекомендуемый путь), в несколько раз реже static.Посмотрим, каким будет LLVM IR (компиляторное промежуточное представление C++-кода) для
int sum1(), static int sum2(), inline int sum3(): ссылка на godbolt.У LLVM IR есть интересный список linkage types для символов, типов много. Linkage type определяет, как ведет себя символ во время линковки в бинарник. (Символ - это функция или глобальная переменная)
▪️ У
sum1 дефолтный linkage type external - возможно ровно 1 определение символа. Линковщик выдаст ошибку, если в программе будет 0 или >1 определения.▪️ У
sum2 linkage type internal - символ доступен только из того translation unit, где определен. Если в процессе линковки попадется одноименный символ, то линковщик просто переименует sum2, чтобы не было коллизии.▪️ У
sum3 linkage type linkonce_odr. Это типичный weak symbol, как и некоторые другие типы (linkonce, weak).В программе может быть несколько weak-определений одного и того же символа.
Если все определения символа являются weak, то линковщик берет рандомное определение.
Если какое-то из определений символа является strong (как у
sum1), то линковщик берет strong-определение.В чем отличие
linkonce от linkonce_odr?▪️ В общем случае weak-определения могут быть разными, поэтому компилятор не имеет права заинлайнить вызов weak-функции (после линковки у функции может оказаться совсем другое определение)
▪️ Но Стандарт С++ требует от программиста обеспечить, чтобы inline-функции имели идентичное определение (это естественным образом достигается, если определение находится в
.h-файле)▪️ Поэтому компилятор имеет право заинлайнить вызов метода
sum3 - программа от этого сломаться не сможетСкомпилируем объектный файл
> clang++ -c link.cppНа Linux получим объектный файл формата ELF. Используем утилиту
readelf для чтения таблицы символов. Чтобы вывести исходные имена методов (не mangled-имена), используем утилиту c++filt:> readelf -s link.o | c++filtПолучим такой вывод (опустил другие символы):
Symbol table '.symtab' contains 21 entries:В общем случае выгоднее использовать inline-методы, чем static-методы! Потому что так меньше загрязняется бинарник, и все статические переменные внутри функции живут в количестве 1 штуки, а не в N штук:
Num: Value Size Type Bind Vis Ndx Name
6: 00000000000000c0 18 FUNC LOCAL DEFAULT 2 sum2(int, int)
14: 0000000000000000 18 FUNC GLOBAL DEFAULT 2 sum1(int, int)
20: 0000000000000000 18 FUNC WEAK DEFAULT 7 sum3(int, int)
inline int* get_address() {
// `dummy` займет sizeof(int) памяти, а со static-методом было бы N*sizeof(int)
static int dummy;
// inline-метод - возвращает один и тот же указатель
// static-метод - уникальный для каждого translation unit
return &dummy;
}👍9
#madskillz
Обертка над потоком вывода 🌊
У каждой крупной компании (и в FAANG) есть своя реализации
Много где, прежде чем вывести немного видоизмененную строку в поток вывода (это объект с оператором
Попробуем сделать по аналогии с std::boolalpha "флаг" для "бесплатного" вывода строки. Мы хотим, чтобы можно было писать так:
Пусть выражение
Для удобства будем работать со всеми типами потоков. Объект
Также можно сделать так, чтобы
По ссылке на godbolt можно посмотреть, как я это сделал.
Другой подход к "бесплатному" выводу строки в нужном формате можно посмотреть в библиотеке abseil - Writing to Stream. В нем потоку отдается "легкий" объект:
Обертка над потоком вывода 🌊
У каждой крупной компании (и в FAANG) есть своя реализации
std::string и/или разных строковых утилит.Много где, прежде чем вывести немного видоизмененную строку в поток вывода (это объект с оператором
<<, например std::cout, std::stringstream), создается новая строка:log << "Value is " << valueStr.Quote();
Метод Quote() создаст новую строку с кавычками " по бокам. Такой код встречается тысячами, и вредит перфомансу ⏱, но это удобнее, чем выводить бесконечные "\"".Попробуем сделать по аналогии с std::boolalpha "флаг" для "бесплатного" вывода строки. Мы хотим, чтобы можно было писать так:
log << "Value is " << quote << valueStr;
И это было бы аналогично записиlog << "Value is " << '"' << valueStr << '"';
Как это можно сделать?Пусть выражение
((объект потока) << quote) вернет объект TQuoteWrapper, а запись ((объект TQuoteWrapper) << str) вернет исходный поток с записанным туда str.Для удобства будем работать со всеми типами потоков. Объект
quote ничего не значит и нужен только для вышеуказанной записи.inline constexpr struct{} quote;
template<typename TStream>
auto operator<<(TStream& stream, decltype(quote)) {
return TQuoteWrapper{stream};
}
Сам объект TQuoteWrapper имеет оператор <<, в котором записывает то, что нужно: template<typename TArg>
TStream& operator<<(TArg&& arg) {
return Stream_ << '"' << std::forward<TArg>(arg) << '"';
}
По ссылке на godbolt можно посмотреть, что получилось 👍Также можно сделать так, чтобы
TQuoteWrapper эскейпил символы строки (например, заменял \" на \\\").По ссылке на godbolt можно посмотреть, как я это сделал.
Другой подход к "бесплатному" выводу строки в нужном формате можно посмотреть в библиотеке abseil - Writing to Stream. В нем потоку отдается "легкий" объект:
std::cout << absl::StreamFormat("\"%s\"", valueStr);
Но есть и свои ограничения - нельзя по-полному перепахать строку (сделать escape символов) через printf-like аргумент👍9
#compiler и #story
Как написать свой компилятор? ⚙️🛠
По моему мнению, лучший туториал по созданию компилятора своего языка на C++: My First Language Frontend
Теория и практика написания компиляторов - это целый мир, который развивается уже несколько десятилетий. Энтузиасты создают свои разнообразные языки программирования (интересный список), часто в одиночку.
В туториале рассматривается, как написать компилятор для простого тьюринг-полного языка, на котором возможны такие программы:
(1) Лексический анализ - перевод исходного кода в лексемы
(2) Синтаксический анализ - перевод лексем в AST: абстрактное синтаксическое дерево (написание несложного LL(1)-анализатора языка)
(3) Кодогенерация - перевод AST в промежуточное представление LLVM IR; на этом этапе можно узнать теорию о SSA и графе потока управления
(4) Оптимизация кода - включение нескольких оптимизаторов (список оптимизаторов тут) и теория по ним
(5) Компиляция кода в объектный файл
(6) ... и по желанию многие другие вопросы: debug-символы, типы, управление памятью, стандартная библиотека...
В компиляторе языка C++ все вышеописанные куски имеют ультимативную сложность (и всякие нестандартные пункты как стадия препроцессора 🤯), но на примере простого языка можно разобраться, как работает компилятор и даже как создать свой.
Мне больше всего понравилась возможность слинковать код на другом языке с кодом на C++: сделал небольшое описание на github.
Как написать свой компилятор? ⚙️🛠
По моему мнению, лучший туториал по созданию компилятора своего языка на C++: My First Language Frontend
Теория и практика написания компиляторов - это целый мир, который развивается уже несколько десятилетий. Энтузиасты создают свои разнообразные языки программирования (интересный список), часто в одиночку.
В туториале рассматривается, как написать компилятор для простого тьюринг-полного языка, на котором возможны такие программы:
# Compute the x'th fibonacci number.По очереди разбираются типичные вопросы и pipeline:
def fib(x)
if x < 3 then
1
else
fib(x-1)+fib(x-2)
# This expression will compute the 40th number.
fib(40)
(1) Лексический анализ - перевод исходного кода в лексемы
(2) Синтаксический анализ - перевод лексем в AST: абстрактное синтаксическое дерево (написание несложного LL(1)-анализатора языка)
(3) Кодогенерация - перевод AST в промежуточное представление LLVM IR; на этом этапе можно узнать теорию о SSA и графе потока управления
(4) Оптимизация кода - включение нескольких оптимизаторов (список оптимизаторов тут) и теория по ним
(5) Компиляция кода в объектный файл
(6) ... и по желанию многие другие вопросы: debug-символы, типы, управление памятью, стандартная библиотека...
В компиляторе языка C++ все вышеописанные куски имеют ультимативную сложность (и всякие нестандартные пункты как стадия препроцессора 🤯), но на примере простого языка можно разобраться, как работает компилятор и даже как создать свой.
Мне больше всего понравилась возможность слинковать код на другом языке с кодом на C++: сделал небольшое описание на github.
👍8
#creepy
Можно ли рекурсивно вызывать метод main()? 🔄
(минутка бесполезной информации)
Когда-то давно я читал набор вопросов к собеседованию по C++, и там встретился такой: "можно ли вызывать метод main() из программы"?
зачем так делать почему бы нельзя было так сделать?
Во всех известных мне окружениях функция
Linkage функции
Стандарт говорит, что рекурсивный вызов
Однако все современные компиляторы успешно компилируют такой код! Они могут выводить warning, если компилировать с опцией
Поэтому ответ на вопрос такой - рекурсивно вызывать
Можно ли рекурсивно вызывать метод main()? 🔄
(минутка бесполезной информации)
Когда-то давно я читал набор вопросов к собеседованию по C++, и там встретился такой: "можно ли вызывать метод main() из программы"?
int main() {
int n;
std::cin >> n;
if (n != 0) {
return main();
}
return 0;
}
Казалось бы, Во всех известных мне окружениях функция
main() в действительности не является точкой входа - сначала вызывается функция, сгенерированная компилятором, которая проинициализирует глобальные переменные. А сам main() ничем не отличается от других функций.Linkage функции
main() является implementation-defined (ссылка на стандарт): обычно имя функции не манглится. Также написано, что main() не должен вызываться в программе.Стандарт говорит, что рекурсивный вызов
main() запрещен (ссылка на стандарт).Однако все современные компиляторы успешно компилируют такой код! Они могут выводить warning, если компилировать с опцией
-Wmain: ссылка на godbolt.Поэтому ответ на вопрос такой - рекурсивно вызывать
main() нельзя, но компилятор будет компилировать это, если не указывать специальные флаги как -Wmain или -pedantic.👍4🤔1
#books
Обзор книги "C++20: Get the Details" 📚
(можно скачать тут - https://news.1rj.ru/str/progbook/6082)
Стандарт C++11 по количеству и влиянию нововведений был революционным для языка, он сделал C++ фактически новым языком.
Стандарт C++20 - самый влиятельный после C++11, с кучей новых вещей.
В книге на 530 (!) страниц описываются нововведения, то есть "дифф" между C++17 и C++20. Несмотря на размер книги, воды там нет 🚱 Все написано по делу, прилагаются самые подробные объяснения и примеры кода.
Чувствуется, что в книгу вложено огромное количество труда. Почти все вещи объяснены лучше, чем "в интернете" (на
Книгу можно читать как справочник - интересующую главу за раз.
Некоторые нововведения, к сожалению, еще не поддержаны нормально (как модули), или редко используемы (как корутины), поэтому стоит помнить, что редко используемые фичи забываются быстро.
Обзор книги "C++20: Get the Details" 📚
(можно скачать тут - https://news.1rj.ru/str/progbook/6082)
Стандарт C++11 по количеству и влиянию нововведений был революционным для языка, он сделал C++ фактически новым языком.
Стандарт C++20 - самый влиятельный после C++11, с кучей новых вещей.
В книге на 530 (!) страниц описываются нововведения, то есть "дифф" между C++17 и C++20. Несмотря на размер книги, воды там нет 🚱 Все написано по делу, прилагаются самые подробные объяснения и примеры кода.
Чувствуется, что в книгу вложено огромное количество труда. Почти все вещи объяснены лучше, чем "в интернете" (на
cppreference.com и подобных сайтах). Для "больших" нововведений описываются предпосылки и другая редкая информация.Книгу можно читать как справочник - интересующую главу за раз.
Некоторые нововведения, к сожалению, еще не поддержаны нормально (как модули), или редко используемы (как корутины), поэтому стоит помнить, что редко используемые фичи забываются быстро.
Telegram
Книги для программистов
C++20: Get the Details (2021)
Автор: Rainer Grimm
Количество страниц: 530
В своей книге автор детально описывает новые фичи C++20 - модули, концепции, диапазоны и сопрограммы. Например, новая библиотека диапазонов позволяет выполнять алгоритмы непосредственно…
Автор: Rainer Grimm
Количество страниц: 530
В своей книге автор детально описывает новые фичи C++20 - модули, концепции, диапазоны и сопрограммы. Например, новая библиотека диапазонов позволяет выполнять алгоритмы непосредственно…
👍7
#advice
"Это как std::vector, но на 20% круче" 🕶
Часто приходится иметь дело с множеством объектов, и также как-то их переупорядочивать, находить в них объект по ключу, и так далее - в зависимости от бизнес-сценария.
Пусть у нас есть объекты класса
Я несколько раз успешно применял подход - унаследоваться от std::vector и определить там все нужные методы. Получается примерно так:
"Это как std::vector, но на 20% круче" 🕶
Часто приходится иметь дело с множеством объектов, и также как-то их переупорядочивать, находить в них объект по ключу, и так далее - в зависимости от бизнес-сценария.
Пусть у нас есть объекты класса
Meal (блюда ресторана 🍝), тогда обычно несколько блюд представляют так:std::vector<Meal> meals;И также где-то находятся методы, которые делают то, что нужно
void SortAlphabetically(std::vector<Meal>& meals);Это подход уменьшает читабельность кода, потому что множество объектов живет как бы в отрыве от своих методов.
void SortByCostDesc(std::vector<Meal>& meals);
const Meal* FindMealById(const std::vector<Meal>& meals, std::string_view id);
Я несколько раз успешно применял подход - унаследоваться от std::vector и определить там все нужные методы. Получается примерно так:
class MealList : public std::vector<Meal> {
public:
static MealList ParseFromJson(const nlohmann::json& json);
void SortAlphabetically();
void SortByCostDesc();
const Meal* FindMealById(std::string_view id) const;
private:
MealList() = default;
};
И определения методов находятся в соответствующем .cpp-файле.MealList держит все методы "при себе" и при этом сохраняет всю остальную семантику std::vector (например, возможность range-based for loop).#story #retro
Путь Александра Степанова - автора STL в C++ 🔆
Для современных детей смартфоны существовали всегда, и кнопочные телефоны - это что-то древнее и не стоящее внимания, а проводные телефоны - и вовсе атрибут фильмов про СССР.
Так же для программистов, которые начали свой путь 10-15 лет назад, STL существовал всегда. Однако STL имеет интересную историю.
Я наткнулся на страницу в Википедии про Александра Степанова - автора STL и прочитал ее, много интервью с ним, и некоторые его книги. Коллекция этих материалов есть на stepanovpapers.com.
У Александра, на мой взгляд, биография начинается крайне необычно:
⬛️ Родился в 1950 году в Москве
⬛️ В 1972 году закончил мехмат МГУ
⬛️ С 1972 по 1976 год работал программистом (разработка мини-компьютера для управления гидроэлектростанциями)
⬛️ В 1977 году эмигрировал в США
Я слабо представляю себе, сколько в 1972 (!) году в мире было полноценных программистов, которые реально писали свои дебаггеры, линкеры, операционные системы реального времени. Наверное, в то время это были единичные специалисты.
Также мне было интересно, каким образом можно было эмигрировать из СССР в США в 1977 году с таким важным бэкграундом, учитывая истории про необычные побеги в те годы. К сожалению, ни в одном интервью этот вопрос не затрагивается 🙁 Лишь какие-то избитые фразы как в интервью 2003 года:
Можно почитать это интервью - там присутствуют приколы, например вопрос «расшифровывается ли STL как "Stepanov and Lee"?»; а также факт, что первые идеи про обобщенное программирование пришли Александру в 1976 году в полубессознательном состоянии, когда он находился в больнице с пищевым отравлением.
Также в интервью Александр высказывает мнение о ненужности ООП и Java (определенный как
Из книг мне больше понравился Notes for the Programming course at Adobe 2005-2006.
На первых страницах Александр описывает, как он в 1972-1976 годах сам себя научил принципам хорошего кода.
Сначала он писал проект (например, дебаггер), через несколько месяцев переписывал его с нуля с знанием всех ошибок дизайна, затем это повторялось, и так далее.
Затем он писал проекты не как попало, а с учетом принципов (размер функции не более 20 строк, выделение общего кода в общие функции и т.д.). Этот эксперимент был крайне удачным и упростил разработку.
Александр заметил, что чем больше программа, тем меньше у нее application-specific (или non-general) кода и больше general кода.
В современных десктопных приложениях содержание non-general кода намного меньше чем 1%. Сейчас это самоочевидно, если посмотреть на объем кода операционной системы и Qt/WinForms. Но в то время это было далеко не так очевидно, потому что разработка велась очень близко к "железу".
Сейчас обобщенное программирование - обычное дело, но в те времена это было довольно контркультурное движение. Оно не соответствует концепции "top-down design", потому что это утверждение о том, что можно хорошо спроектировать какой-то компонент без особых знаний о том, как этот компонент будет использоваться 🤔
Из упомянутой выше книги можно прочитать первые несколько глав - там находится очень редкая информация о том, как правильно дизайнить обобщенные классы (как
Путь Александра Степанова - автора STL в C++ 🔆
Для современных детей смартфоны существовали всегда, и кнопочные телефоны - это что-то древнее и не стоящее внимания, а проводные телефоны - и вовсе атрибут фильмов про СССР.
Так же для программистов, которые начали свой путь 10-15 лет назад, STL существовал всегда. Однако STL имеет интересную историю.
Я наткнулся на страницу в Википедии про Александра Степанова - автора STL и прочитал ее, много интервью с ним, и некоторые его книги. Коллекция этих материалов есть на stepanovpapers.com.
У Александра, на мой взгляд, биография начинается крайне необычно:
⬛️ Родился в 1950 году в Москве
⬛️ В 1972 году закончил мехмат МГУ
⬛️ С 1972 по 1976 год работал программистом (разработка мини-компьютера для управления гидроэлектростанциями)
⬛️ В 1977 году эмигрировал в США
Я слабо представляю себе, сколько в 1972 (!) году в мире было полноценных программистов, которые реально писали свои дебаггеры, линкеры, операционные системы реального времени. Наверное, в то время это были единичные специалисты.
Также мне было интересно, каким образом можно было эмигрировать из СССР в США в 1977 году с таким важным бэкграундом, учитывая истории про необычные побеги в те годы. К сожалению, ни в одном интервью этот вопрос не затрагивается 🙁 Лишь какие-то избитые фразы как в интервью 2003 года:
Я покинул СССР, потому что не любил советскую власть, а Microsoft - это советская власть в программировании.Однако вернемся к интервью и книгам. Любое интервью будет интересным, потому что раскрывает какие-то малоизвестные факты. Александр вообще создатель обобщенного программирования как такового, до C++ он реализовал этот подход в языках Scheme и Ada.
Можно почитать это интервью - там присутствуют приколы, например вопрос «расшифровывается ли STL как "Stepanov and Lee"?»; а также факт, что первые идеи про обобщенное программирование пришли Александру в 1976 году в полубессознательном состоянии, когда он находился в больнице с пищевым отравлением.
Также в интервью Александр высказывает мнение о ненужности ООП и Java (определенный как
money oriented programming (MOP))Из книг мне больше понравился Notes for the Programming course at Adobe 2005-2006.
На первых страницах Александр описывает, как он в 1972-1976 годах сам себя научил принципам хорошего кода.
Сначала он писал проект (например, дебаггер), через несколько месяцев переписывал его с нуля с знанием всех ошибок дизайна, затем это повторялось, и так далее.
Затем он писал проекты не как попало, а с учетом принципов (размер функции не более 20 строк, выделение общего кода в общие функции и т.д.). Этот эксперимент был крайне удачным и упростил разработку.
Александр заметил, что чем больше программа, тем меньше у нее application-specific (или non-general) кода и больше general кода.
В современных десктопных приложениях содержание non-general кода намного меньше чем 1%. Сейчас это самоочевидно, если посмотреть на объем кода операционной системы и Qt/WinForms. Но в то время это было далеко не так очевидно, потому что разработка велась очень близко к "железу".
Сейчас обобщенное программирование - обычное дело, но в те времена это было довольно контркультурное движение. Оно не соответствует концепции "top-down design", потому что это утверждение о том, что можно хорошо спроектировать какой-то компонент без особых знаний о том, как этот компонент будет использоваться 🤔
Из упомянутой выше книги можно прочитать первые несколько глав - там находится очень редкая информация о том, как правильно дизайнить обобщенные классы (как
std::vector), и о том, как должны были бы выглядеть разные места C++ с точки зрения Александра (но Страуструп запретил). Потом в книге начинается переизобретение std::move (книга 2006 года), и это читать не имеет смысла.👍7
#story #carbon
Язык Carbon как наследник С++ - о чем молчат его авторы? 🕵️
Многие уже слышали о языке Carbon, который Google представляет как "наследник C++". Всего лишь за несколько дней туда уже наставили 16k лайков.
О нем можно рассказать с трех точек зрения: (1) почему его делают, (2) что в нем планируется, (3) что может быть дальше.
🤔 Почему его делают?
По словам авторов, основной проблемой C++ является огромная сложность внесения в него изменений.
Это так - комитет по стандартизации C++ страдает бюрократизмом, ограничен жесткими рамками, и по сути бОльшая часть нормальных пропозалов по всевозможным причинам выбрасывается в мусорку (это можно описать в отдельном посте).
Окончательный баттхерт произошел в феврале 2020 года, когда комитет отказался ломать ABI (подробнее об этом можно почитать тут), тогда стало ясно что C++ продолжит развиваться черепашьим темпом, и Google стал подпольно создавать свой C++ 2.0
🤔 Что в нем планируется?
Самое главное - то, что C++ и Carbon могут подключать хидеры друг друга, то есть в наличии двунаправленная совместимость языков (как между Java и Kotlin). Я подозреваю, что это сделано через патч в Clang, который при виде файла с нужным расширением по-нужному парсит его в промежуточное представление, а дальше всё вместе оптимизируется.
На гитхабе есть документация, которой уже столько, что тянет на небольшую книгу. Я был поражен объемом того, на что замахнулись авторы 🤐, например:
(*) около языка: менеджер пакетов, сильная стандартная библиотека (если там будет как в Python, то я балдю бом бом), быстрый процесс "стандартизации", и прочее...
(*) сам язык: pattern matching (как в Rust), рефлексия (пока доки нет, но будет), traits (тоже как в Rust), работа с типами, в будущем memory safety, и прочее...
(*) полно мелочей: адекватная иерархия наследования, классы final по умолчанию, проверка на NRVO, и прочее...
🤔 Что может быть дальше?
Здесь я опишу факты, в которых я +- уверен, а выводы на их основе можно сделать самому 👻
(1) У Google есть гигантский админресурс, чтобы продвинуть любой нужный партии язык в массы - реклама на C++-конвенциях, массовый выпуск курсов (например на Coursera), обучение в вузах...
Так что при желании язык можно продвигать "нерыночными" способами (вряд ли Go выстрелил бы сам в "дикой природе").
(2) У Google есть технический ресурс, чтобы создать C++ 2.0 и не сильно обоср*ться, потому что многие очень активные контрибьюторы в Clang и в Стандарт C++ работают в Google.
(3) Самое печальное: "Экономика" развития C++ и Carbon будет кардинально отличаться.
Дело в том, что если в C++ контрибьютят в основном "подвижники", крайне заинтересованные в основном нематериально люди, то Carbon разрабатывается корпорацией.
При работе в FAANG (и в десятках компаний под них косящих) важно не то, как ты делаешь, а что ты делаешь.
Повышение при прочих равных получает обычно не чувак, который круто знает C++, хорошо делает задачи и покрывает тестами, а тот, кто выкатил какую-нибудь хрень с высоким visibility и показал красивые графики. И обычно всем пофигу что там нет тестов или код говно - выкатилось, ну и хорошо.
Поэтому есть риск, что из-за личных планов "выкатить что-то крутое, показать манагеру, и пойти на повышение" Carbon может проэволюционировать в какое-то болото.
Язык Carbon как наследник С++ - о чем молчат его авторы? 🕵️
Многие уже слышали о языке Carbon, который Google представляет как "наследник C++". Всего лишь за несколько дней туда уже наставили 16k лайков.
О нем можно рассказать с трех точек зрения: (1) почему его делают, (2) что в нем планируется, (3) что может быть дальше.
🤔 Почему его делают?
По словам авторов, основной проблемой C++ является огромная сложность внесения в него изменений.
Это так - комитет по стандартизации C++ страдает бюрократизмом, ограничен жесткими рамками, и по сути бОльшая часть нормальных пропозалов по всевозможным причинам выбрасывается в мусорку (это можно описать в отдельном посте).
Окончательный баттхерт произошел в феврале 2020 года, когда комитет отказался ломать ABI (подробнее об этом можно почитать тут), тогда стало ясно что C++ продолжит развиваться черепашьим темпом, и Google стал подпольно создавать свой C++ 2.0
🤔 Что в нем планируется?
Самое главное - то, что C++ и Carbon могут подключать хидеры друг друга, то есть в наличии двунаправленная совместимость языков (как между Java и Kotlin). Я подозреваю, что это сделано через патч в Clang, который при виде файла с нужным расширением по-нужному парсит его в промежуточное представление, а дальше всё вместе оптимизируется.
На гитхабе есть документация, которой уже столько, что тянет на небольшую книгу. Я был поражен объемом того, на что замахнулись авторы 🤐, например:
(*) около языка: менеджер пакетов, сильная стандартная библиотека (если там будет как в Python, то я балдю бом бом), быстрый процесс "стандартизации", и прочее...
(*) сам язык: pattern matching (как в Rust), рефлексия (пока доки нет, но будет), traits (тоже как в Rust), работа с типами, в будущем memory safety, и прочее...
(*) полно мелочей: адекватная иерархия наследования, классы final по умолчанию, проверка на NRVO, и прочее...
🤔 Что может быть дальше?
Здесь я опишу факты, в которых я +- уверен, а выводы на их основе можно сделать самому 👻
(1) У Google есть гигантский админресурс, чтобы продвинуть любой нужный партии язык в массы - реклама на C++-конвенциях, массовый выпуск курсов (например на Coursera), обучение в вузах...
Так что при желании язык можно продвигать "нерыночными" способами (вряд ли Go выстрелил бы сам в "дикой природе").
(2) У Google есть технический ресурс, чтобы создать C++ 2.0 и не сильно обоср*ться, потому что многие очень активные контрибьюторы в Clang и в Стандарт C++ работают в Google.
(3) Самое печальное: "Экономика" развития C++ и Carbon будет кардинально отличаться.
Дело в том, что если в C++ контрибьютят в основном "подвижники", крайне заинтересованные в основном нематериально люди, то Carbon разрабатывается корпорацией.
При работе в FAANG (и в десятках компаний под них косящих) важно не то, как ты делаешь, а что ты делаешь.
Повышение при прочих равных получает обычно не чувак, который круто знает C++, хорошо делает задачи и покрывает тестами, а тот, кто выкатил какую-нибудь хрень с высоким visibility и показал красивые графики. И обычно всем пофигу что там нет тестов или код говно - выкатилось, ну и хорошо.
Поэтому есть риск, что из-за личных планов "выкатить что-то крутое, показать манагеру, и пойти на повышение" Carbon может проэволюционировать в какое-то болото.
👍8😢2🤔1