C++95

Description
640K ought to be enough for anybody

Author: @cloudy_district aka izaron.github.io
Advertising
We recommend to visit
Roxman
Roxman
13,345,923 @roxman

Sharing my thoughts, discussing my projects, and traveling the world.

Contact: @borz

Last updated 4 days, 5 hours ago

HAYZON
HAYZON
6,791,449 @hayzonn

💼 How to create capital and increase it using cryptocurrency

👤 𝐅𝐨𝐮𝐧𝐝𝐞𝐫: @Tg_Syprion
🗓 ᴀᴅᴠᴇʀᴛɪsɪɴɢ: @SEO_Fam
⭐️ 𝐎𝐧𝐞 𝐋𝐨𝐯𝐞: @major
🍀 𝐌𝐲 𝐜𝐡𝐚𝐧𝐧𝐞𝐥𝐬: @kriptofo @tonfo
@geekstonmedia

Last updated 21 hours ago

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

Last updated 2 months ago

3 months, 1 week ago

#jostik #compiler

Обзор жостиков C++ *?*** (номер 0b11)

На этот раз будет аж семь жостиков - три про DIY в Clang, одно про устройство Wireshark, одно с видео, и пара про идиомы C++ ? 7️⃣ В данный момент это самый плотный набор жостиков.

1️⃣ [Clang] Сделай сам наследование в enum ?
У enum'ов в C++ нет наследования. Но это могло бы быть полезным в редких случаях, когда есть общий enum для всех кейсов, но в каких-то окружениях есть мелкие различия, которыми не хочется засорять общее место.

Пусть программа работает с большими ресторанами, куда отправляем запросы на доставку. С нашей стороны можно запросить новую доставку или отмену доставки (статус становится Adding/Cancelling и ждется ответ от сервера), после ответов от сервера статус становится Added/Cancelled/Delivered.
Большинство ресторанов не имеет возможности изменить адрес доставки (только отменять и создавать новую), но пусть какое-нибудь "Кафе Пушкинъ" ? так умеет, тогда неплохо бы иметь enum-наследник:

enum class DeliveryStatus : uint8\_t { Adding, Added, Cancelling, Cancelled, Delivered, }; enum class CafePushkinDeliveryStatus : DeliveryStatus { Moving, };

(Планируется, что при изменении заказа статус меняется Added \-> Moving \-> Added)

Я попробовал придумать дизайн для наследования ? Придумал такие правила:
1️⃣ У enum-наследника нумерация первой константы как будто она следует за последней константой родителя:

enum class First : short { None, /* = 0 */ MovedPermanently = 301, Found, /* = 302 */ SeeOther, /* = 303 */ }; enum class Second : First { NotModified, /* = 304 */ UseProxy, /* = 305 */ BadRequest = 400, Unauthorized, /* = 401 */ }; enum class Third : Second { PaymentRequired, /* = 402 */ Forbidden, /* = 403 */ InternalServerError = 500, };

2️⃣ enum-наследник наследует родительский underlying type транзитивно

static\_assert(sizeof(First) == 2); static\_assert(sizeof(Second) == 2); static\_assert(sizeof(Third) == 2); static\_assert(std::is\_same\_v<std::underlying\_type\_t<First>, short>); static\_assert(std::is\_same\_v<std::underlying\_type\_t<Second>, First>); static\_assert(std::is\_same\_v<std::underlying\_type\_t<Third>, Second>);

3️⃣ enum-родитель может неявно кастоваться к своему enum-наследнику:

inline constexpr Third kFound = First::Found;

4️⃣ Скоуп enum-наследника включает в себя все символы enum-родителей - этот пример аналогичен прошлому:

inline constexpr Third kFound = Third::Found;

То есть lookup по Third::Found находит enum constant First::Found (имеет тип First), потом на нем делается implicit cast к типу Third ?

Сделал proof of concept в Clang - ссылка на дифф?
(Это базовый вариант - нет тестов, не супер красивый код, не рассмотрена возможность множественного наследования, и так далее...)

Код компилятора жесткий, есть много хаков чтобы например различать a ? new enum E : int{} от foo(a, enum E : int{}).
Также под флагами поддерживается нестандартный синтаксис, например можно писать enum E : int *p; что равносильно enum E : int; E *p; (прикол от microsoft)

ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ ?

4 months, 2 weeks ago
4 months, 2 weeks ago
5 months, 2 weeks ago

#story

Обзор на работу в большой компании ?

В начале этого года я покинул Яндекс, где проработал чуть больше 4 лет ?‍?
Хотя там была ровная братва среди коллег и «интересные проекты»™️, я внял мудрости современного философа - «за деньги да» ? Но обо всем по порядку...

Часть1️⃣ Рассказ бывалого балабола

Я накидал субъективный обзор ? на плюсы и минусы
**1️⃣* *Hard skills
За несколько первых месяцев реальной работы в мощном темпе выучиваешь в разы больше, чем за годы «саморазвития» до этого ?
За долгий срок работы можно порешать задачи из широкого спектра, испытать все виды инцидентов, и в целом это интересный опыт ? Можно смотреть курсы ШАД, код крутых коллег, внутренние ресурсы.
Нельзя сказать, чтобы у каждого было супер крутое знание какого-либо языка - это не самоценность, а просто инструмент выполнения задачи. Нередко встречаются ржаки по типу как на этом скрине, что чела закинули в команду где пишут на Х и он тоже выучил Х за 21 день (можно догадываться про уровень понимания)?
Компании не нужно, чтобы ты был rockstar - большие проекты делаются сотнями людей, и адекватный менеджмент ориентируется на их средний уровень, а также резервирует время под возможные переделки
Если же менеджмент неадекватный, то будут требовать сделать космолет за неделю, что будет не под силу даже Леннарту Поттерингу ?
К вопросу о том - можно ли считать себя "ылитой", если ты работаешь в FAANG или под них косящих ? - имхо это звучит неуместно, когда речь идет о супер формализованном процессе найма workforce (результат каждого собеса это буквально true или false) с "good enough" компетенцией, чтобы закрывать стандартные таски. Так вижу.

**2️⃣* *Soft skills
Если есть желание, софт скиллы тоже неплохо прокачиваются. Хотя их можно качать бесконечно - если какой-то алгоритм или фичу можно выучить раз и навсегда, то взаимодействие с людьми это то, чему можно учиться всю жизнь ?
Обычный "работяга" - низшее звено пищевой цепочки. То, что он делает какие-то таски - это значит что топы определили направление, менеджеры поборолись за скоуп, а тимлид нарезал таски из глубокого знания codebase.

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

Это тот случай, когда "негативный опыт - тоже опыт". Так как многое завязано на человеческий фактор, комфортное пребывание во многом зависит от твоих коллег и еще намного больше от рукля.
За все время у меня было 5 руклей, со всеми я супер ровно сработался, кроме одного. Так оно работает - чтобы у тебя были задачи с высоким импактом и рост, необходимое условие чтобы рукль (и дальше по цепочке вверх) этого тоже хотели. Некоторое время я с ним промучался, но ничего не помогало и все было настолько плохо, что мне даже ставили встречу с HR с неприятным разговором ? Так что совет - лучше сразу уходить, если есть негатив с руклем, а не пытаться это исправить.
Я чуть не перешел к кенту в стартап, но по итогу просто ротировался в другой под-отдел и неплохо поработал в компании еще 1.5 года ?

ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ ?

5 months, 4 weeks ago

#jostik #compiler

Обзор жостиков C++ *?*** (номер 0x00000002)

1️⃣ Сделай сам requires-clause для полей класса ?

Шаблоны C++ развиваются в сторону упрощения - многое было сделано, чтобы избавиться от enable_if и ручной специализации шаблонов.

Одна из фичей - сейчас можно наложить trailing requires-clause на класс и на метод, чтобы ограничить их использование:

template<typename T> requires std::copyable<T> && std::movable<T> struct Blow { void make\_job() requires (sizeof(T) > 100) { /* ... */ } };

Логичным следующим шагом должно стать ограничение на поле класса.

Такое опциональное наличие поля было бы полезно везде, где из-за свойств шаблонной сущности меняется размер класса. Например делетеры в умных указателях (как std::unique_ptr). Или отсутствие поля "size", если он указан в шаблоне (как std::span).

Супер синтетический пример, как это должно работать у вектора, который на стеке или куче в зависимости от параметра:

```
template
class JanusVector {
private:
// static vector, N is fixed
std::array arr_ requires (N > 0);

// dynamic vector, N is not fixed
std::size_t size_ requires (N == 0);
T* data_ requires (N == 0);
};

// ...

int main() {
JanusVector vec1;
static_assert(sizeof(vec1) == 4 * 228);

JanusVector vec2;
static_assert(sizeof(vec2) == 16);
}
```

Недавно мной была вкинута идея в разнорабочую группу по C++, пока без эффекта ?

Решил для прикола сделать реализацию этой фичи на Clang ?
(Ссылки: как настроить сетап для clang, как создать свой keyword)

Дифф за пару вечеров получился не очень большим (он на gist.github.com), состоит из таких кусков:
1️⃣ В парсере разрешить парсить выражение trailing requires-clause после полей класса.
2️⃣ Сохранять это выражение в класс FieldDecl, вычислять его значение в методе isRequiresClauseSatisfied.
3️⃣ Добавить проверки в LayoutBuilder'е, чтобы не вкомпилировать storage под неудовлетворяющие поля.
4️⃣ Диагностировать обращения к неудовлетворяющим полям (создается ошибка компиляции).
5️⃣ Осторожно проинстанцировать выражение во время инстанциации шаблона ? Непроинстанцированные зависимые константные выражения невычисляемы!

Вот так можно делать proof of concept фичи ? Это самый базовый пример, еще не рассмотрено несколько концептуальных вопросов, например:
1️⃣ Разрешить ли одинаковые имена у полей, если они никогда не скомпилируются вместе в одном классе?
2️⃣ Нужно ли помещать поле в скоуп выражения, для такого:

// типа поле `kek\_` скомпилится если у него будет метод `foo()` T kek\_ requires requires { kek\_.foo(); };

3️⃣ Всякие мелкие баги, наподобии того что в реализации не запрещено брать адрес &Foo::kek у нескомпилировавшегося поля kek.

В КОММЕНТАРИЯХ ЕЩЕ ТРИ ЖОСТИКА C++ ?

6 months, 2 weeks ago

#compiler

День, когда умер CRTP ?

По исходникам проектов на C++ можно определять, примерно в какое время был написан код ?‍? В истории C++ был революционный стандарт C++11, еще чуть менее революционный C++20 (по убыванию используемости - concepts, ranges, coroutines, modules), и проходные между ними (C++14, C++17).

В C++23 есть единственная крупная фича, ради которой стоило его ждать - deducing this ? Единственное место, где нужно про него читать - пропозал в стандарт, там есть всё - мотивация, обоснование всех тонкостей дизайна, примеры использования, разбор всех корнер кейсов, и прочее. Все другие объяснения из интернета принципиально менее полны ? Я сюда не буду копипастить примеры, они есть в пропозале.

Long story short, в каждом методе используется неявный "нулевой" параметр this, эта фича добавляет возможность явно указать этот параметр, причем необязательно с исходным типом (можно заиспользовать шаблон). Это оказалось очень нужным - уменьшает дублирование методов, убирает необходимость в CRTP, позволяет удобнее рекурсивные лямбды, и прочее ?

Конечно, у deducing this есть ряд drawbacks ?‍♂️
1️⃣Есть ряд спецэффектов по типу shadowing'а переменной базового класса (в пропозале они разобраны)
2️⃣ Методы становятся шаблонными со всеми вытекающими - Clang известен тем, что в отличие от MSVC не особо проверяет код шаблона до инстанциации
3️⃣ В коде начинается треш с категориями значений, нужно понимать разницу между auto, auto&&, decltype(auto), и специально добавили срань как std::forward_like.

Реализация deducing this наконец-то появилась в LLVM/Clang 18, который зарелизился 6 марта ? К сожалению, релиз получился всратым, и баги не пофикшены до сих пор ? Поэтому его пока никто не использует.

Но уже сейчас можно изучать вопрос - как лучше всего подготовиться к использованию этой фичи и гарантировать, что все перестанут писать "по-старому"? ?
В этом нам поможет libclang! Мы можем написать простые python-тесты на исходный код (о них писал тут?), и отлавливать устаревший код.

Первым делом нужно, чтобы в проекте генерировался compile_commands.json. Это простой список команд для компилятора, его описание тут, он нужен для многих вещей, например для автокомплита.
Его создание поддерживается в CMake, для этого в корневом CMakeLists.txt надо добавить:

set(CMAKE\_EXPORT\_COMPILE\_COMMANDS ON)

Потом надо поставить пакеты из этого поста? Код всего теста можно посмотреть тут, дальше я опишу что он делает.

ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ ?

6 months, 4 weeks ago

#jostik

Обзор жостиков C++ ? (номер sizeof(char))

(Предисловие)
Эту картинку я сделал месяц назад, и хотел отправить в WG21 (Working Group 21, он же Комитет C++) как предложение для логотипа ? Это буква "С" и два плюса.

Но вспомнив комитетские приколы, решил не делать ?
Какой-нибудь товарищ комиссар Рабочей Группы по многообразию не принял бы ее, аргументировав что для репрезентации нужна широкая цветовая палитра, а вместо одного из крестов, например, символ зороастризма ? Зачем это делать, если он сам не живет так, как говорил Заратустра - непонятно. Слишком многим людям форма важнее содержания.

Поэтому это будет новая ава ?

1️⃣ Strong-типы ?

Теория о них тут, реализация на гитхабе.
Strong-типы это обертки над базовыми типами (int, double и пр.), которые не могут конвертироваться в другие типы. Это полезно, чтобы не путать аргументы и понимать цель значения из имени типа.

Например, вместо такого конструктора:

Rectangle(double width, double height);

Можно сделать такой:

Rectangle(Width width, Height height);

Где Width и Height - обертки над double, и их нельзя перепутать

Strong-типам можно придавать разные "скиллы" - например Addable, Comparable, Hashable и прочие, чтобы соответственно работали операции сложения, сравнения или можно было положить тип в хэшмапу.

Эти типы "бесплатные" для рантайма - компилятор выбрасывает шелуху и работает с базовым типом ?

Раньше я их не принимал (если ты путаешься в параметрах, то вместо 15 параметров нужен builder pattern ?), но сейчас наоборот, без них жить сложнее - это неплохая вещь.

2️⃣ Упакованные типы ?

Как известно, у типов есть выравнивание:

struct Biba { uint16\_t a; /* 2 bytes */ /* padding 6 bytes */ uint64\_t b; /* 8 bytes */ }; static\_assert(sizeof(Biba) == 16); static\_assert(alignof(Biba) == 8);

Но во время парсинга пакетов всяких протоколов это может мешать, потому что никаких padding'ов между полями не предусмотрено. Также во время работы с сетью супер важно оптимизировать память. Тогда надо убирать выравнивание (alignment) ?

Это можно сделать разными способами, самый удобный такой:

\#pragma pack(push, 1) struct Boba { uint16\_t a; /* 2 bytes */ uint64\_t b; /* 8 bytes */ }; \#pragma pack(pop) static\_assert(sizeof(Boba) == 10); static\_assert(alignof(Boba) == 1);

(Этот pragma pack выглядит странно, потому что у него есть типа внутренний стек)
Команда pack(push, N) значит что все типы будут иметь alignment максимум N байт, а pack(pop) убирает это.

Менее удобный способ (потому что надо ставить каждому классу вручную) это вешать на структуру __attribute__((packed)).
Это немного по-другому работает внутри компилятора, но итог один - alignment равный единице.

Способ, который не работает - спецификатор alignas, он может только увеличить дефолтный alignment. Оно используется для подгона структуры под кэш-линию (64 байта) или SIMD-инструкции (16/32/64 байта).

А как это работает на уровне ассемблера? ? Компилятор не будет генерировать дичь с переносами байтов (как для bitfield), там формируется честный unaligned access.
Как оказывается, на x86 никаких сюрпризов нет. Оно всегда поддерживало unaligned access (работающий медленнее чем aligned).
А на других архитектурах может произойти треш - надо беречься ?

ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ ?

8 months, 3 weeks ago

#books

Обзор книги "Modern Parallel Programming with C++ and Assembly Language" (2022 г.) ?

(можно скачать PDF тут)

Как можно понять, книга посвящена "параллельному программированию".
Но тут имеется в виду не та параллельность когда есть много CPU (и используются мьютексы, etc.), а параллельность внутри одного CPU, а если точнее, то вся книга про SIMD (single instruction, multiple data) ?

Сейчас стандартные типы данных имеют размер 8/16/32/64 бит (соответственно это byte/word/dword/qword), они "нативно" поддерживаются потому что сами регистры общего назначения у процессора имеют размер 64 бита ?

Но в процессорах часто есть регистры на 128, 256 и даже 512 бит (соответственно это xmmword/ymmword/zmmword в x86) ? В эти регистры "упаковываются" значения стандартного размера и над ними затем делаются групповые операции.

Проще показать на примере - пусть мы суммируем float'ы:

// float* z, const float* x, const float* y, size\_t n for (size\_t i = 0; i < n; i++) z[i] = x[i] + y[i];

То SIMD-версия на 256-битных регистрах могла бы выглядеть так (с поправкой на конкретный компилятор, т.к. эти интринсики не специфицированы в Стандарте С++):

// представим что `n` делится на 8 for (size\_t i = 0; i < n; i += 8) { \_\_m256 x\_vals = \_mm256\_loadu\_ps(&x[i]); // грузим x[i..i+8] в один регистр \_\_m256 y\_vals = \_mm256\_loadu\_ps(&y[i]); // грузим y[i..i+8] в другой регистр \_\_m256 z\_vals = \_mm256\_add\_ps(x\_vals, y\_vals); // вычисляем z[i..i+8] в третьем \_mm256\_storeu\_ps(&z[i], z\_vals); // выгружаем z[i..i+8] в память }

Код выше работает быстро, решительно, в разы быстрее "наивного" варианта.
Общий flow такой - в "длинный" регистр выгружается мини-массив чисел (в примере выше массив из 8 float'ов), и ускорение достигается за счет того, что процессор не тратит время на чтение одних и те же опкодов, а сразу делает нужную операцию.

Так как процессоры сейчас гига сложные (я наклал кирпичей даже когда делал эмулятор m68k 45-летней давности!), то таких "групповых операций" наделали много. Можно, например, вычислять еще z[i] = min(x[i], y[i]), или y[i] = x[2*i] + x[2*i+1], или даже быстро переставить элементы z[i] = x[y[i]], и так далее.

Книга посвящена только архитектуре x86 (архитектуры как ARM не рассматриваются).
SIMD-расширений в x86 есть несколько. Сначала в 1997 году появился MMX от Intel, потом в 1998 году 3DNow от AMD, и так далее, многие давно устарели и не выпускаются.
Книга посвящена только сравнительно новым SIMD-расширениям AVX (2011 год), AVX2 (2013 год) и AVX-512 (2017 год).

Глава 1️⃣ описывает базу SIMD.
В главах 2️⃣8️⃣ по одному шаблону описываются фичи AVX / AVX2 / AVX-512:
1️⃣ Описывается какая-нибудь платиновая задача - найти минимум/среднее в массиве, перемножить матрицы, применить свёртку, etc.
2️⃣ Приводится портянка кода на C++: "наивная реализация" vs "реализация на SIMD", с нудным описанием что откуда идёт.
3️⃣ Приводится бенчмарк, наивная реализация проигрывает SIMD в среднем в 10-15 раз.
В главе 9️⃣ описывается как можно было бы сделать портабельную SIMD-программу - для этого в x86 есть опкод cpuid, по которому можно узнать поддерживаемые SIMD-расширения и еще много что.
В главе 1️⃣0️⃣ неплохо описывается ~~боян~~ архитектура процессора x86-64 вместе с этими SIMD-регистрами в 256/512 бит.
В главах 1️⃣1️⃣1️⃣8️⃣ описывается то же, что в главах 2-8, но на ассемблере... Я это не читал ?
В главе 1️⃣9️⃣ описывается здравый смысл, то есть гайд по SIMD-оптимизациям, общая тема - оптимизировать надо не всё подряд, а только то что видно в профайлере, потому что SIMD-код понимать трудно и легко ошибиться.
В аппендиксах есть инфа как ставить вижуэл студио и ссылки на доки... ?

В целом полезная книга, можно почитать для общего развития. Только нужно иметь в виду:
1️⃣ Некоторые задачи лучше решаются через GPU, а не через SIMD на CPU (который ускорит лишь в единицы раз, а не в сотни).
2️⃣ Современный компилятор может сам сгенерировать SIMD-код (но это бабка надвое сказала).
3️⃣ Сначала профайлер, потом оптимизации.

We recommend to visit
Roxman
Roxman
13,345,923 @roxman

Sharing my thoughts, discussing my projects, and traveling the world.

Contact: @borz

Last updated 4 days, 5 hours ago

HAYZON
HAYZON
6,791,449 @hayzonn

💼 How to create capital and increase it using cryptocurrency

👤 𝐅𝐨𝐮𝐧𝐝𝐞𝐫: @Tg_Syprion
🗓 ᴀᴅᴠᴇʀᴛɪsɪɴɢ: @SEO_Fam
⭐️ 𝐎𝐧𝐞 𝐋𝐨𝐯𝐞: @major
🍀 𝐌𝐲 𝐜𝐡𝐚𝐧𝐧𝐞𝐥𝐬: @kriptofo @tonfo
@geekstonmedia

Last updated 21 hours ago

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

Last updated 2 months ago