this->notes.
Kanalga Telegram’da o‘tish
О разработке, архитектуре и C++. Tags: #common, #cpp, #highload и другие можно найти поиском. Задачки: #poll. Мои публикации: #pub. Автор и предложка: @vanyakhodor. GitHub: dasfex.
Ko'proq ko'rsatish4 531
Obunachilar
+124 soatlar
-87 kunlar
-2030 kunlar
Postlar arxiv
4 531
#cpp
1. Женя писал про попытку закоммитить в clang реализацию
assume и про то, почему не получилось. Даже как-то и несмешно уже.
2. А Саша писал у себя про self-move. Почитайте. Там ещё в комментах есть интересное.
3. Тут краткий и понятный поинт про то, почему лучше использовать std::make_unique вместо прямой передачи указателя в конструктор std::unique_ptr. В целом понятно.
А тут можно посмотреть аналогичный ответ про std::make_shared (разница гораздо более ощутимая, но не такая однозначная).
4. Небольшая статья про использование aligned new.
5. Доклад Herb Sutter про видение обобщённого программирования на C++ аж из 2017ого, вполне, тем не менее, актуальный, т.к. многое из рассказанного ещё не завезли.
6. Небольшая статья с заметками про лямбды.
Такой вот минипостик получился.4 531
#cpp
Сходил на встречу российской РГ21. Накидаю инфы (что-то вы уже видели, но лишним, думаю, не будет).
В C++23:
- починили некорректные срабатываения
static_assert(false):
if constexpr (constexpr_condition) {
…
} else {
static_assert(false, “message”);
}
- починили range based for. Link на proposal;
- добавили static operator[] (в записи приконый пример с таким оператором для енамов);
- монадические операции для std::expected;
- дополнения к алгоритмам ranges (например эффективная итерация по ключам/значениям мап и много другого), но из-за этого ренджи из 23его стандарта сильно несовместимы с ренджами из 20ого. Не оч понятно, что это значит. Подождём и потрогаем;
- std::stacktrace и std::print;
- constexpr много всего (например std::unique_ptr).
С++26:
Пока глобально планируется примерно вот так:
- library support for coroutines;
- executors;
- networking;
- pattern matching;
- reflection;
- и всякие другие штуки.
Конкретно:
- #embed (емнип приехал из C23);
- пытаются пропихнуть std::get для агрегатов;
- stacktrace for exceptions в комитете приняли с вопросами, будут поправлять;
- ABI ломать пока не будут, не за чем, говорят.
Хех. Прикиньте, С++23 это вот в этом году. А мы только недавно на 20й стандарт перешли🥴🥴🥴4 531
#highload #cpp #go
Досмотрел всё интересующее из майского highload, так что докидываю к прошлому посту (и пару докладов с плюсовых конф).
1. Как мы готовили поиск в Delivery Club. Иван Максимов (link).
Тут интересно, потому что я много работаю над штуками вокруг нашего поиска (не непосредственно качеством, а именно около). Когда ты чуть-чуть в теме, смотреть интереснее, что там чуваки намутили.
2. Хайлоад, которого не ждали. Дмитрий Самиров (link).
Тут аналогично, потому что чуваки намутили какую-то доставку продуктов из Я Еды. Почти Лавка. Только у нас круче.
3. Как мы устроили переезд 10+млн строк С++ кода на новый стандарт. Никита Старичков (link).
4. Статический анализ кода++. Анастасия Казакова (link).
5. Компилируем 200 000 файлов быстрее, чем distcc. Александр Кирсанов (link).
Это интересно с точки зрения, что в ВК намутили миллион каких-то своих способов компилироваться из PHP в C++ эффективнее. И по чуть-чуть чуваки эту свою инфру развивают. Прикольно посмотреть, что нового они там придумывают.
6. Почему всё сложно с оптимизацией и что с этим делать. Константин Шаренков (link).
В целом доклад с вопросами, но мб вам зайдёт. Мне не то чтобы прям, но выше среднего.
7. Go Map Internals. Егор Гришечко (link).
И плюсовые из закромов:
8. C++14 Reflections Without Macros, Markup nor External Tooling. Antony Polukhin (link).
Блин, оч крутой доклад с CppCon 16ого года, который я как-то пропустил. Антон рассказывает про
magic_get/boost::pfr и всякие мозговзрывающие штуки, которые там использовались.
9. Back to Basics: C++ API Design. Jason Turner (link).
10. Метаклассы в C++17: фантастика? Реальность! Сергей Садовников (link).
Мб вам что-то на последних конфах понравилось? Можно поделиться в комментариях.4 531
#common #highload #cpp
Пару дней назад вышли записи прошломайского Highload++ Foundation 2022. Накидаю докладов из того, что уже посмотрел.
1. Как выжать 1 000 000 RPS. Андрей Аксёнов (link).
Классический доклад в своей нестандартной манере про бенчмарк Sphinx.
2. Поиск GPS-аномалий среди сотен тысяч водителей. Александр Царьков (link).
3. Как мы ускорили Яндекс Go на несколько секунд. Денис Исаев (link).
Я немного упоминал этот доклад в /155 п.4. Просто и прикона.
4. Экскурсия в бэкенд Интернета вещей. Владимир Плизга (link).
Прикона было узнать, какие задачи в целом решают с помощью IoT. Правда я так до конца и не понял, что это такое.
5. Авито: root cause detector. Юрий Дзюбан (link).
Интересная попытка автоматизировать расследование проблем.
6. Избавляемся от кэш-промахов в коде для x86-64. Евгений Буевич (link).
Я не поклонник вот этих низкоуровневых штук, но может вам будет интересно. И заодно тогда можно глянуть LLVM Optimization Remarks by Ofek Shilon с осеннего CppCon 2022 (link).
7. Как наладить CI-процесс в монорепозитории. Алина Власова (link).
8. Настраиваем инцидент-менеджмент: от хаоса до автоматизации. Сергей Бухаров (link).
Тут скорее было интересно посмотреть, что у других. Имхо, у нас лучше.
Основные вопросы к системе это: насколько нужен скрайбер; почему оценкой импакта на бизнес занимаются разработчики в процессе инцидента; и зачем есть secondary дежурный, почему их только двое?
4 531
#cpp
Посмотрел доклад Andrei Alexandrescu на CppCon 2021. Накидаю приконых фактов оттуда:
- предлагает засунуть в стандарт
template <typename… Ts> struct type_sequence {};
потому что если этого не сделать, каждый будет писать это сам (и вероятно с помощью tuple/variant, что концептуально не всегда то, что нужно). В таком виде со списками типов предлагали работать и на CppRussia 2019 с примерами других функций, которые могут понадобиться при работе со списками типов, что выглядит вообще-то довольно приятно и в ногу со временем. И рядом Alexandrescu предлагает три базовые функции в классической манере для работы с металистом типов: head, tail и cons (создать лист из элемента и другого листа). Всё как в Lisp.
- и депрекейтнуть integer_sequence, потому что это не последовательность целых чисел, а последовательность значений какого-то типа. Вместо него ввести value_sequence, потому что это семантически честнее.
Из интересного ещё можно отметить такое. Предположим есть функция
template <typename T, typename… Ts, typename U>
int f(T, U, const Ts&…);
и её вызовы
f(1, 2); // Ts=<>
f<int, int>(1, 2, 3); // Ts=<int>
f<int, int, double>(1, 2, 3, “4”); // Ts=<int, double>
Тип U всегда сможет вывестись! Причём из параметра, а не указанного списка типов в шаблоне. В первом случае это int по понятным причинам. Во втором это тоже int. В третьем же Ts выводится как <int, double>, а U — исходя из параметра. Причина в том, что если variadic pack начал матчится, он не остановится и идёт до конца (жадный матчинг). Получается, в таком кейсе у нас нет возможности указать тип U компилятору явно, ведь он всегда будет выводится из параметра.
Соответственно, если у вас есть что-то такое
template <typename... Ts, typename T = int>
T f(Ts…);
тип T может быть только int, потому что в него никогда ничего не сматчится.
Соответственно, если у вас пак типов функции находится не в конце, то он всегда должен быть указан явно:
template <typename... Ts, typename T>
int f(Ts... values, T value);
f(1); // Ts=<>
f<int, double>(1, 2, 3); // Ts=<int, double>
f<int, int>(1, 2); // error, потому что не хватает одного аргумента
Всё это приводит к забавным кейсам вроде такого
template <typename... Ts, typename... Us>
int f(Ts... ts, Us... us);
Тут Us всегда выводится из параметром (deduced), тогда как Ts должно быть указано явно. Можете попробовать понять для себя, чему они будут равны в следующих случаях:
f(1);
f(1, “2”);
f<int, char>(1, ‘2’);
f<int, char>(1, '2', “three”);
Или может быть такой угар:
template <typename... Ts, typename... Us, typename T>
int f(Ts..., Us..., T);
При любом вызове Us будет пустым паком. Что бы вы не делали. Все компиляторы это кушают, но стандарт говорит, что если один пак всегда пустой, то это no diagnostic required (грубо говоря, нельзя так).
Ещё есть такие штуки как std::tr2::bases и std::tr2::direct_bases, которые возвращают тайплист всех базовых классов указанного и все непосредственные базовые классы указанного соответственно.
Хотя вообще доклад не об этом : ) Так что посмотрите.4 531
#common
Ещё немного полезностей из личного опыта.
Ещё когда в начале второго курса в каком-то рандомном курсе по python я столкнулся с регулярками, подумал, что это оч крутой инструмент, который, тем не менее, вряд ли часто применяется (потому что так с большинством знаний, к сожалению). Как же я был глуп. Сейчас я пользуюсь ими чуть ли не каждый день, пусть часто и хватает какого-то минимального базового набора. Ниже небольшой список примеров, где они пригодились:
- я не оч люблю codesearch в ide, потому что сижу в clion, который, как известно не самый быстрый (в том числе потому что у него проблемы с индексацией мало-мальски больших проектов). У меня это скорее прокачаный блокнот, по которому я делаю только Ctrl+F в рамках файла. Но когда надо поискать что-то в более глобальной окрестности, я пользуюсь внутренним сервисом для поиска по монорепе. И он поддерживает регулярки (как для строк, так и имён файлов), что вообще-то довольно удобно.
Кажется, недавно github обновил у себя поиск и можно делать что-то похожее.
- во внутреннем userver, в отличие от опенсорсного, есть кодогенерация (статья на хабре, где про это можно почитать), которая позволяет не писать бойлерплейт для раскладывания полей в структурки и обратно. Ты просто задаёшь схему в формате yaml, по которой валидируется реквест/респонс. И там есть возможность для строковых переменных задавать паттерны регулярками, по которым так же будет проводиться валидация. Если что-то не прошло, клиент получает 4хх автоматически.
- иногда в поиске бывают примеры не самой релевантной выдачи, которые обусловлены особенностями реализации. И иногда их проще закрыть чем-то вроде блеклиста, в котором можно поюзать регулярки для обобщения каких-то кейсов и неразрастания блеклиста.
Недавно обходил деревья dfs’ом, что вызывает какие-то детские эмоции радости, потому что после десятков (и может даже сотен) раз, когда он писался для спшных тасок, это знание перестало быть бесполезным с точки зрения применения в проде.
Рядом у нас ещё есть много разных bfs’ов для разных кейсов. А когда-то видел bfs для специфического обхода какой-то графической сетки.
Короче алгоритмы на собесах даже как-то обоснованы (даже k раз приходилось делать что-то с двумя указателями).
В статье про обработку ошибок я упоминал, что не нужно ловить всё подряд (
catch (…) {} или ловить std::exception). Опровержений первому я пока не увидел, но ловить std::exception оказалось полезно. Кейс примерно такой: мы умеем явно ловить только ответы ручек от других сервисов, которые описаны в api, но это не всё, что сервис может ответить, т.к. иногда есть неописанные 5xx или технические 429/другие 4хх. И хочется, чтобы неуспешный запрос не влиял на доступность сервиса, который этот запрос и делает. Но мы согласны деградировать некоторую функциональность. Тут как раз хорошим вариантом будет ловить std::exception.
============================
Сейчас верхнеуровнево пытаемся намутить курсов на новый семестр, примерно с нуля. Занимает оч много времени (из-за чего я тут притих). Но есть ощущение, что получится даже не очень плохо. Может попозже чем-нибудь в этом месте поделюсь.4 531
В этом году я начал пытаться писать сам время от времени вместо тупого вкидывания ссылочек на разные материалы, просто потому что так полезнее и мне, и вам.
Вспомним, что тут появилось за 2022й год (особенно полезно, если вы пришли на канал чуть позже и не видели чего-то), а после подведу какие-то итоги.
Посты:
- необычные возможности C/C++;
- o chaos engineering;
- об аллокаторах в других языках;
- macros tricks;
- о векторах;
- о хеш-таблицах;
- о вероятностных структурах данных;
- о кешах;
- про рефакторинг;
- backend for frontend;
- про ub;
- впечатления от C++ Zero Cost Conf 2022;
- деревья в алгоритмах;
- про полезности из личного опыта;
- немного из метапрограммирования;
- пару кейсов, связанных с грамматикой языка;
- про эпохи;
- про cpp2;
- статический лист для сборки динамических аллокаций;
- чуть-чуть про софтскиллы;
- о подходах к реализации дженериков (помню, что торчу пост про go);
- про пагинацию;
- про лямбды;
- про компиляцию в общих чертах;
- RVO/NRVO;
- пачки пропозалов: раз, два;
- пачки рандомных фактов: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14;
О сборщиках мусора:
- основные алгоритмы;
- G1GC в java;
- другие сборщики в java;
- python gc;
- сборщики в других языках;
- о сборке мусора и C/C++.
Статьи:
- об устройстве популярных аллокаторов в C++;
- об интересных структурах данных;
- обработка ошибок и C++.
Задачки:
- про использование variadic templates;
- хз про что это но вот.
Примерно понимаю, о чём хочу написать ещё минимум одну статью и около миллиарда постов, так что контент в следующем году будет. Другой вопрос, что нагрузки становится всё больше, потому, вероятно, писать буду реже. Или чаще. Хз короч.
Рандомные факты про жизнь.
Закончил универ, прокачался на работе и в целом доволен тем, чем сейчас занимаюсь. Немного участвую в образовании тут-там. Хотя может и много. По крайней мере универа в следующем семестре будет явно больше, чем в некоторые семестры в студенческое время. Оч интересно и оч сложно.
Завёл @dzikart и пытаюсь поддержать жизнь в @memesfromhole.
Набил ещё три татухи и научился жонглировать (кек).
Не умер, что особенно приятно.
Временно на каникулы. Не болейте.
4 531
#cpp
RVO/NRVO 1/2.
Давно хотел рассказать про всякие оптимизации, которые делаются в плюсах. Тут будет пост про RVO/NRVO, а позже про что-нибудь ещё.
Может в начале будут какие-то неявные допущения или грубые формулировки, но к концу поста постараюсь донести все детали, чтобы картина сложилась.
Для начала про терминологию в стандарте. Формально есть общий термин copy elision, который в целом о том, чтобы не делать лишних копий объекта и создавать его сразу там, где нужно. Например ниже примеры copy elision:
T t = T(); // not T() + operator= but only T();
T t = T(T(T(T(T(T(T(1))))))); // not T(1) + many T(const T&) + operator= but only T(1)
Вот что говорит стандарт ([class.copy.elision]):
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects.
Т.е. компилятор в праве применять эту оптимизацию в обход copy-/move-кторов, даже если в них или деструкторе есть какие-то сайдэффекты.
Так же тут есть 4 пункта. В первом говорится, что тип инициализирующего выражения и тип переменной с итоговым результатом должны совпадать. Это главное условие для RVO/NRVO.
RVO/NRVO это частные случаи copy elision, которые происходят в результате возврата чего-либо из функции. Далее мы будем пользовать примерно таким классом, чтобы понять, что происходит с объектами:
#define TELL(str) { std::cout << str << std::endl; }
struct S {
S() TELL("def")
S(const S&) TELL("cp")
S(S&&) TELL("mv")
};
Так же есть такой термин как temporary materialization – принцип, согласно которому prvalue-выражение не создаётся физически в памяти до тех пор, пока оно не будет присвоено в не prvalue-объект.
Return Value Optimization по сути и есть пример temporary materialization. Посмотрим на базовый пример (godbolt):
S f() { return S(); }
S s = f();
Тут у нас prvalue-выражение S(), благодаря чему мы можем не создавать переменную в момент его появления в return и присвоить создать лишь в s. И из всех интересных нам конструкторов будет вызван лишь конструктор по умолчанию. cv-квалификаторы в сигнатуре функции в данном случае нам ничего не портят (gb).
А если мы можем (не)явно сконструировать возвращаемый тип из выражения в return? Добавим в S такой конструктор:
S(std::string&&) TELL("str&&")
и собственно пример (gb):
S f() { return std::string(10, '1'); }
S s = f();
Всё хорошо, даже не смотря на то, что тип в return и в сигнатуре не совпадают. По факту компилятор представляет выражение в return как S(std::string(10, ‘1’)), что тоже является prvalue. Даже в таком случае всё в порядке (gb):
S f() {
std::string s(10, '1');
return s;
}
Всё корректно. Т.к. компилятор справляется тут даже сделать неявный move переменной s. Причём это всё ещё RVO.
Стоит понимать, что с C++17 RVO — гарантированная штука и даже не оптимизация.
Давайте поймём, как оно работает. У компилятора есть адрес места в памяти, где должен оказаться результат выполнения функции (называется return slot). Когда выполняются условия для RVO, компилятор может сразу создать объект из вашего return в нужном месте в памяти. Т.е. это скорее про свойства инициализации объектов.
Named Return Value Optimization уже оптимизация (причём не гарантированная, тут компиляторы делают кто что может). Базовый пример (gb):
S f() { S s; return s; }
Или пример посложнее, где в S появился int x (gb):
S f() { S s; s.x = 1; return s; }
void gg(S& s) { s.x = 1; }
S g() {
S s; gg(s); return s;
}
Компилятор всё ещё может создавать локальную переменную s в функциях сразу в return slot итогового объекта.
Для NRVO правда volatile для локальной переменной всё портит (gb).
Почему важно знать об этих штуках? Потому что NRVO можно сломать (gb):
S f() {
S s;
return std::move(s); // return S&&
}
Теперь компилятора обязан создать объект и мувнуть его, потому что это то, о чём вы его попросили явно. Так же можно думать, что у выражения в return и в сигнатуре теперь разные типы (S&& и S).4 531
#cpp
1. Хочется укрепить понимание того, что же такое модификаторы доступа (
public, protected, private), потому что в последнее время вижу тут-там некоторое непонимание у молодых особей, изучающиъ C++.
Это именно модификаторы доступа. Они всего лишь не дают напрямую обратиться к члену/методу класса. Но это не значит, что вы совсем не можете работать со скрываемыми объектами.
class A {
struct T {};
public:
T getT() { return T{}; }
};
// A::T t; // ce
A a;
// A::T t = a.getT(); // ce
auto t = a.getT(); // ok
В двух первых случаях мы напрямую пытались обращаться к типу, спрятанному за модификатором доступа (и не получилось). В третьем всё ок: мы не трогаем тип напрямую -> можем с ним работать. Причём тип лишь недоступен напрямую, но мы его видим снаружи (видимость и доступность это разные штуки).
Так же модификаторы доступа это концепция времени компиляции, потому, если не думать, можно ловить неприятные штуки:
struct A {
virtual void f() = 0;
};
struct B : A {
private:
void f() override { std::cout << 1; }
};
A* a = new B();
a->f();
Вроде как f() в B помечена private, но т.к. это требование проверяется на компиляции, а виртуальные штуки работают в рантайме, будет успешно выведена единица. Кстати, интересно, могут ли статанализаторы такое трекать как говнокод🤔 Не пробовал. Но может вы потрогаете и расскажете.
2. В C++11 появились типы фиксированного размера. И почему-то стало принято юзать их вместо стандартных int, long long, и прочей братвы. Мне не очень понятно почему. Ведь вам на самом деле редко нужны типы фиксированного размера. Часто вам хватает знания, что конкретный тип удовлетворяет каким-то ограничениям сверху/снизу. И часто можно просто поставить какой-нибудь static_assert на то, что у вас CHAR_BIT равен 8 или сколько вам там нужно. Короч кмк это специфический инструмент, непонятно почему ставший popular.
3. Можно почитать Женин довольно понятный пост про ABI и почему его не хотят ломать (там много ссылочек с доп инфой).4 531
#common
Про компиляцию в общих чертах.
Представим выражение (строка кода):
pos = init + rate * 60
Что с ним произойдёт при компиляции?
👉 Лексический анализ — читаем поток символов и группируем их в лексемы (значащие последовательности). Для каждой лексемы получаем токен вроде <имя, значение>.
<id, 1> <=> <id, 2> <+> <id, 3> <*> <60>
id в данном случае указывает, что информацию о токене нужно искать в глобальной таблице символов (там инфа о всех встреченных объектах). Номер 1/2/3 — индекс объекта в этой таблице.
<60> можно представить в более общем виде как <number, 4>, но для простоты не будем.
Такой набор токенов и отправляется на следующий этап.
👉 Синтаксический анализ позволяет построить из токенов промежуточное древовидное представление, которое описывает грамматическую структуру потока токенов (обычно тут речь о синтаксическом дереве или ast). Получаем что-то такое:
=
/ \
<id, 1> +
/ \
<id, 2> *
/ \
<id, 3> 60
👉 Семантический анализ использует синтаксическое дерево и таблицу символов на семантическую согласованность с правилами языка. Тут ещё может собираться информация о типах сущностей, после чего она может сохраняться в дереве для последующего использования. Например, если язык позволяет неявные приведения типов, то он может дополнить дерево например так:
=
/ \
<id, 1> +
/ \
<id, 2> *
/ \
<id, 3> int_to_float
\
60
Или если у операндов некоторого оператора типы не совпадают, и язык запрещает неявные преобразования, уже на этом этапе можно кинуть ошибку.
👉 Генерация промежуточного кода.
В процессе трансляции исходного кода в целевой, компилятор может несколько раз генерировать различные промежуточные представления. В нашем примере можем получить что-то такое:
t1 = int_to_float(60)
t2 = id3 * t1
t3 = id2 + t2
id1 = t3
👉 Оптимизация кода.
Конечно, сгенерированный выше код кажется нам довольно неестественным. Его хочется упростить. Это и происходит на этапе оптимизации (пока это машинно-независимые процессы). Можем получить следующее:
t1 = id3 * 60.0
id1 = id2 + t1
Стоит понимать, что оптимизация (хотя это скорее трансформация) -- всегда эвристический процесс, который ничего не гарантирует и пытается улучшить какой-то основной/несколько критериев, возможно жертвуя другими. Часто, например, можно хотеть уменьшить количество инструкций. Хотя, если у вас какая-то встраеваемая система, вы можете хотеть использовать меньше памяти. Но никто не гарантирует, что не станет хуже.
Ещё такой процесс может проходить несколько раз, т.к. какие-то сделанные оптимизации открывают возможность для новых.
👉 Генерация кода из промежуточного кода получает код целевой платформы (например на асм). Тут могут происходить локальные оптимизации, исходя из того, что умеет конкретная архитектура.
Важной задачей также является грамотная работа с памятью (кого в какие регистры поместить, и т.п.).
Понятно, что это довольно упрощённая схема, т.к. в реальных компиляторах какие-то этапы могут отличаться, быть сильно более сложными или совсем отсутствовать. Может позже посмотрим на то, как это происходит с плюсовым кодом.
Если верхнеуровнево, то компиляторы для плюсов состоят из несколько крупных этапов:
1. Frontend: препроцессинг, лексический, синтаксические и семантический анализ, построение high level intermediate representation (hir).
2. Middleend + backend: оптимизации hir, оптимизации mir, оптимизации lir, кодогенерация.
Frontend для каждого языка свой. Middleend и backend работаю с ir, что позволяет переиспользовать их для разных языков.
Послушать про то, как работают компиляторы для C++, можно в плейлисте. Правда речь о toolchain, но от этого только интереснее : )
Ещё можно посмотреть пару лекций про LLVM IR. И почитать пару постов на похожую тему в @cxx95: раз, два.
P.S. пример из dragonbook.4 531
#cpp
Чуть-чуть о сборке мусора на C/C++.
Почитайте тут небольшую статью на хабре про какую-то базовую имплементацию.
Если кратко, то:
- есть глобальное хранилище всех выделенных объектов;
- есть хранилище корневых объектов;
- с каждым объектом хранится какая-то метаинформация;
- есть жёсткие (которые влияют на сборку объекта) и слабые (которые нет) ссылки;
- есть какой-то способ проверки достижимости объектов. В Unreal Engine например надо пользоваться макросами
UPROPERTY [1], с помощью которых сборщик мусора ходит по указаным в макросах указателям и тем самым получает ссылки на объекты, которые связаны с текущим. В случае статьи выше применяется хак, что все интересующие сборщик мусора объекты лежат в начале рассматриваемого объекта, что позволяет пройтись по ним, сделать reinterpret_cast и сделать что требуется (это не всегда работает, но тут чисто proof of concept).
Boehm-gc это прям классический сборщик мусора. Тут прям чуваки реализовали аналог malloc, после которого ничего не нужно удалять. Концептуально всё примерно так, как описано выше (лист блоков памяти, мета в хедере выделяемых объектов, mark-sweep), но сильно сложнее : ) Не будем останавливаться (код читать не советую: стандартное месиво директив препроцессора, C и других радостей жизни).
Sutter в своём проекте gcpp вводит такие объекты как deferred_heap, deferred_ptr и deferred_allocator.
deferred_heap -- регион памяти с объектами, на которые можно уметь указывать с помощью deferred_ptr. Вы можете создавать объекты с помощью метода make<T>() и уничтожать их на конкретной куче изолированно с помощью collect(). Концептуально вы можете завести такую кучу на множество объектов одного типа, на каждый объект свой или на любое подмножество объектов/типов. Как вам удобно.
deferred_ptr почти фулл такой же, как std::shared_ptr за тем исключением, что он умеет в отложенное удаление в зависимости от того, что же происходит с его кучей (ну и там немного по-другому реализован aliasing constructor). Куча кстати фиксируется единожды и поменять её указатель не может.
deferred_allocator это обёртка для своей кучи для использования в контейнерах.
По факту проект Sutter’а это подсчёт ссылок с удалением недоступных циклов. Однако когда “классические” сборщики мусора концептуально управляют сырой памятью, deferred_heap это скорее об управлении существующими объектами (в основном корректным вызовом деструкторов неиспользуемых объектов).
Код у него довольно понятный и читаемый. Можете почекать.
И оч много разной инфы в readme. Тоже можно посмотреть.
Тут можете почитать про проблемы (хех), которые могут возникнуть из-за наличия штук для сборщика мусора в стандарте C++11-20.
[1]. Сборка мусора в UE.4 531
#common #cpp
Микропост.
1. Женя написал крутой пост про способы ускорения компиляции. Докину ещё пост на хабре про hidden friends.
2. Тут собрали какое-то количество предложений по улучшению structure binding. Есть довольно приятные и интересные штуки, которые даже комитет рассматривал, но пока, как я увидел, ничего никуда не протащили (хотя обсуждения местами ведутся довольно давно). Можно посмотреть для расширения сознания.
3. Можете посмотреть очередной доклад Аксёнова про то, как не говнокодить (ничего конкретного вы не услышите; просто набросы).
4. Если приглядеться во множество примитивных типов в Java, можно увидеть отсутствие unsigned типов. Вроде как это потому что один из авторов языка считает, что мало какие разработчики на C способны рассказать, как работает арифметика с unsigned числами, поэтому такого в Java нет в принципе. Странный поинт честно говоря, но факт есть факт.
5. Какой-то прикольный симулятор тимлида. После работы прогером можно поиграть в менеджера. Хех.
4 531
#cpp
Лямбды 2/2.
7. Init capture optimisation.
Если у вас есть какой-то код вроде
std::find_if(v.begin(), v.end(),
[&pref](const auto& s) {
return s == pref + “bar”;
});
то вычисление pref + “bar” будет делаться на каждой итерации. Потому лучше вынести его в список захвата:
std::find_if(v.begin(), v.end(),
[str = pref + “bar”](const auto& s) {
return s == str;
});
что позволит немного сэкономить на вычислениях.
8. С C++17 лямбды могут быть constexpr.
auto f = []() constexpr {
return sizeof(void*);
};
std::array<int, f()> arr = {};
Аналогично с consteval с C++20.
9. Вот тут можно почитать, как написать удобный хелпер Overloaded для работы с std::variant и std::visit с помощью наследования от лямбд.
10. C С++20 лямбды умеют в конструирование по умолчанию, что помогает не передавать их в места вроде делитеров для умных указателей или как компараторы в конструкторы std::set/std::map и отдавать только тип в шаблон.
11. Забавный факт.
template <auto = []{}>
struct X {};
X x1;
X x2;
static_assert(!is_same_v<decltype(x1), decltype(x2)>);
Этот код успешно скомпилируется, т.к. при каждом обращении к типу, используется новое выражение []{}, которое имеет абсолютно новый тип относительно прошлых выражений []{}. Соответственно каждый раз шаблон X будет инстанцирован разными типами. И потому типы переменных не совпадают.
Например, если у вас часто есть переменные, которые нужны только для получения сайдэффектов их конструктора, вам нужно сгенерировать некоторое уникальное имя. Тут и может пригодиться подобное.4 531
#cpp
Лямбды 2/2.
7. Init capture optimisation.
Если у вас есть какой-то код вроде
std::find_if(v.begin(), v.end(),
[&pref](const auto& s) {
return s == pref + “bar”;
});
то вычисление pref + “bar” будет делаться на каждой итерации. Потому лучше вынести его в список захвата:
std::find_if(v.begin(), v.end(),
[str = pref + “bar”](const auto& s) {
return s == str;
});
что позволит немного сэкономить на вычислениях.
8. С C++17 лямбды могут быть constexpr.
auto f = []() constexpr {
return sizeof(void*);
};
std::array<int, f()> arr = {};
Аналогично с consteval с C++20.
9. Вот тут можно почитать, как написать удобный хелпер Overloaded для работы с std::variant и std::visit с помощью наследования от лямбд.
10. C С++20 лямбды умеют в конструирование по умолчанию, что помогает не передавать их в места вроде делитеров для умных указателей или как компараторы в конструкторы std::set/std::map и отдавать только тип в шаблон.
11. Забавный факт.
template <auto = []{}>
struct X {};
X x1;
X x2;
static_assert(!is_same_v<decltype(x1), decltype(x2)>);
Этот код успешно скомпилируется, т.к. при каждом обращении к типу, используется новое выражение []{}, которое имеет абсолютно новый тип относительно прошлых выражений []{}. Соответственно каждый раз шаблон X будет инстанцирован разными типами. И потому типы переменных не совпадают.
Например, если у вас часто есть переменные, которые нужны только для получения сайдэффектов их конструктора, вам нужно сгенерировать некоторое уникальное имя. Тут и может пригодиться подобное.
Endi mavjud! Telegram Tadqiqoti 2025 — yilning asosiy insaytlari 
