#story
Информационное насилие: курс молодого бойца🔫
Я долго думал, стоит ли писать такой пост, но решил это сделать. Ведь обсуждаться будет не человек, а идеи, которые несутся в массы, а также условия, которые позволяют это делать. Этот текст - не какой-то "лох-патруль", а серьезный пост.
Есть такой деятель, как Андрей Викторович Столяров. Дело в том, что многие люди, которым доводится контактировать с ним, испытывают спектр различных эмоций. Неполный список групп людей, постигших мудрость:
1️⃣ Жители linux.org.ru и прочих форумов. Запомнился розыском CMS на C/C++, критикой термина GNU/Linux, и просто комментариями с нестандартными речевыми оборотами в адрес тех, с кем он несогласен.
2️⃣ Студенты ВМК МГУ. Запомнился ведением занятий по Jabber во время ковида (об этом позже), неодобрительными отзывами на дипломные работы, неуважением к студентам с Windows и/или Code::Blocks на ноутбуке. Также благодаря ему первокурсники учат Pascal.
3️⃣ Его ученики. Запомнились пранкерским разоблачением Rust (rustmustdie.com), в ответ на что неравнодушные люди выпустили разоблачение разоблачения Rust (habr). Затем джихад был объявлен Сишке (cmustdie.com), но это уже было никому не интересно.
4️⃣ Зрители АйТиБороды, посмотревшие 4-часовое интервью. К сожалению, интервьюер не развел его на срач ИРЛ, а то бы просмотров было в 10 раз больше.
У Столярова есть idée fixe - теория информационного насилия. Можно считать, что это новая философская школа мысли, но по факту это просто другое агрегатное состояние "философии" Александра Гельевича Дугина - эклектичный микс разных аксиом. Это рационализаторство собственных предпочтений, оформленное в виде "как бы" философии.
Интересно, что, так сказать, magnum opus Столярова можно посмотреть в формате видеороликов - ссылка на YouTube.
Арсенал цитат из этих видеороликов:
"I don't like spam" - исторический обзор возникновения спама.
"Об основателях рунета" - про каких-то ноунеймов, с которым личные счета из-за смерти ЖЖ.
"Спам из мэрии Москвы" - про чудо спамерской инженерной мысли и письмо в суд.
"В чью пользу авторское право?" - жуткий срыв покровов над книжными издательствами.
"Безобидный гипертекст и коварный джаваскрипт" - просто бриллиант, можно разбирать на цитаты.
(ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ)
Информационное насилие: курс молодого бойца
Я долго думал, стоит ли писать такой пост, но решил это сделать. Ведь обсуждаться будет не человек, а идеи, которые несутся в массы, а также условия, которые позволяют это делать. Этот текст - не какой-то "лох-патруль", а серьезный пост.
Есть такой деятель, как Андрей Викторович Столяров. Дело в том, что многие люди, которым доводится контактировать с ним, испытывают спектр различных эмоций. Неполный список групп людей, постигших мудрость:
У Столярова есть idée fixe - теория информационного насилия. Можно считать, что это новая философская школа мысли, но по факту это просто другое агрегатное состояние "философии" Александра Гельевича Дугина - эклектичный микс разных аксиом. Это рационализаторство собственных предпочтений, оформленное в виде "как бы" философии.
Интересно, что, так сказать, magnum opus Столярова можно посмотреть в формате видеороликов - ссылка на YouTube.
Арсенал цитат из этих видеороликов:
"Пилотный выпуск"Некоторые видео лучше посмотреть самому, так как они просто необычные:
- Информационное насилие - такая штука, исследованием которого я занимаюсь с 97 года.
- Браузер вообще не предназначен для просмотра видео, для этого есть другие программы, а браузером нужно смотреть гипертекст.
"О запрещенных книгах"
- Я признаюсь, я даже "Mein Kampf" читал, и знаете, книжка-то так себе, не умел Гитлер книжки писать.
- Какая-то непонятная шушера за меня решает, что мне читать, а что не читать.
"I don't like spam!"
- Был такой сервис - телеконференция NNTP. Нету больше этих конференций, они в районе 2000 года благополучно загнулись, потому что там оказалось невозможно устроить защиту от спама.
- Тут речь идет не только о рекламе. Многие пытались посылать такое всем получателям своей адресной книги: "А котеночек никому не нужен?" - да не нужен, блин, мне котеночек, и никому из них не нужен котеночек!
"История с телеграмом"
- Я вам открою один секрет. Меня нет в телеграме, меня нет в вотсапе, меня нет вконтактике, меня нет в фейсбуке. В этом зловредном ютубе я появился только потому, что Навальный объявил конкурс, иначе бы меня и здесь не было.
- Мне интересно вот что: когда ВКонтакте отжали у Павла Дурова, хоть бы кто-нибудь стёр свой аккаунт?
- Я ничего не имею против Павла Дурова, ну кроме того что он, зараза, уже вторую проприетарную систему поднимает. <...> Выглядит он красиво, презентабельно, хороший парень такой. Айтишник, опять же.
"В чью пользу авторское право?"
- Информация как таковая не может иметь денежной стоимости.
"Про DRM, Всемирную паутину, ..."
- Техническая стандартизация это особый и особо опасный вид международного терроризма. <...> Такие организации, как ISO, это на самом деле террористические группы.
"I don't like spam" - исторический обзор возникновения спама.
"Об основателях рунета" - про каких-то ноунеймов, с которым личные счета из-за смерти ЖЖ.
"Спам из мэрии Москвы" - про чудо спамерской инженерной мысли и письмо в суд.
"В чью пользу авторское право?" - жуткий срыв покровов над книжными издательствами.
"Безобидный гипертекст и коварный джаваскрипт" - просто бриллиант, можно разбирать на цитаты.
(ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ)
Please open Telegram to view this post
VIEW IN TELEGRAM
#story
Как сделать программу для читов в играх⌨️
«Я буду устанавливать сейчас все игры»
- Сашко «Компьютерный Монстр»
Когда-то давно я познакомился с программой ArtMoney. Эта программа шла на диске к журналу "Лучшие Компьютерные Игры". Она была очень простой, и я быстро смог настроить бесконечную валюту в игре Spore.
В ArtMoney нужно было указывать процесс игры, затем ввести значение игровой валюты. Программа проверяла всю память и запоминала адреса, где было это значение.
Потом надо было вернуться в игру, что-нибудь сделать, чтобы значение валюты изменилось, и указать новое значение в ArtMoney.
Адреса фильтровались (оставлялись только равные новому значению), и процесс повторялся, пока не останется единственный адрес, в котором можно поменять значение на нужное.
Я решил поисследовать, как работают такие программы. Так как я не пишу под Windows, то исследую аналог для Linux, особой разницы не должно быть😁 Это программа scanmem.
В этой программе также можно искать нужный адрес с переменной. Можно искать не только конкретное значение, но еще промежуток значений, а также "значение по адресу больше/меньше/равно/не равно чем было в прошлый раз" и так далее.
Такое бывает нужно, если например в игре есть "полоска здоровья" без числового значения, про которое известно, что оно становится больше/меньше чем в предыдущий раз.
Проверяется, что
Проверка происходит через
У процесса игры надо увидеть его виртуальное адресное пространство. Это память, которую игра использует и где надо будет фильтровать адреса. Увидеть регионы непрерывно занимаемой памяти может любой (в смысле не только суперюзер), прочитав файл
Те, кто не программируют активно под Linux, могут удивиться двум специфичным вещам:
1️⃣ Очень многие данные о системе/процессе можно увидеть, прочитав особые файлы, хотя видно что это НЕ "стандартные" файлы, данные в них генерируются на ходу! Это концепция "everything is a file". 😐
2️⃣ Программы реально завязываются на формат вывода линуксовых тулз/файлов, в этом ничего страшного нет. Например, весь Android завязан на вывод тулзы
В первом способе (как бы вы думали?) снова открывается файл, на этот раз
Во втором способе можно читать память через ptrace:
ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ
Как сделать программу для читов в играх
«Я буду устанавливать сейчас все игры»
- Сашко «Компьютерный Монстр»
Когда-то давно я познакомился с программой ArtMoney. Эта программа шла на диске к журналу "Лучшие Компьютерные Игры". Она была очень простой, и я быстро смог настроить бесконечную валюту в игре Spore.
В ArtMoney нужно было указывать процесс игры, затем ввести значение игровой валюты. Программа проверяла всю память и запоминала адреса, где было это значение.
Потом надо было вернуться в игру, что-нибудь сделать, чтобы значение валюты изменилось, и указать новое значение в ArtMoney.
Адреса фильтровались (оставлялись только равные новому значению), и процесс повторялся, пока не останется единственный адрес, в котором можно поменять значение на нужное.
Я решил поисследовать, как работают такие программы. Так как я не пишу под Windows, то исследую аналог для Linux, особой разницы не должно быть
В этой программе также можно искать нужный адрес с переменной. Можно искать не только конкретное значение, но еще промежуток значений, а также "значение по адресу больше/меньше/равно/не равно чем было в прошлый раз" и так далее.
Такое бывает нужно, если например в игре есть "полоска здоровья" без числового значения, про которое известно, что оно становится больше/меньше чем в предыдущий раз.
Проверяется, что
scanmem
был запущен из-под суперюзера (sudo scanmem
), без этого нельзя "следить" за другим процессом (если он не является процессом-"ребенком").Проверка происходит через
getuid() == 0
- uid суперюзера равен 0.У процесса игры надо увидеть его виртуальное адресное пространство. Это память, которую игра использует и где надо будет фильтровать адреса. Увидеть регионы непрерывно занимаемой памяти может любой (в смысле не только суперюзер), прочитав файл
/proc/<pid>/maps
:cat /proc/<pid>/maps # вывод в терминалВывод примерно такой:
559b8c5d1000-559b8c5f2000 rw-p 00000000 00:00 0 [heap](подробнее об этих файлах можно прочитать тут)
7faa72001000-7faa72023000 rw-p 00000000 00:00 0
7faa72023000-7faa72055000 r--p 00000000 08:30 3023 /usr/lib/locale/C.UTF-8/LC_CTYPE
7faa726e0000-7faa726e1000 rw-p 0002d000 08:30 11854 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7faa726e1000-7faa726e2000 rw-p 00000000 00:00 0
7ffeb2f53000-7ffeb2f74000 rw-p 00000000 00:00 0 [stack]
scanmem
просто открывает этот файл (через fopen) и парсит каждую строку. Оставляются только регионы с возможностью записи (флаг w
). Обычно этого флага нет у загруженных секций .text
(исполняемый код бинарника) и .rodata
(константные данные).Те, кто не программируют активно под Linux, могут удивиться двум специфичным вещам:
wpa_supplicant
(для поиска WiFi).scanmem
ждет, пока юзер введет текущее значение из игры. После ввода значения вызывается ptrace для слежки за другим процессом (target
- process id процесса игры), из-за этого процесс игры остановится (кинет сигнал SIGSTOP
):ptrace(PTRACE_ATTACH, target, NULL, NULL)Затем можно читать память процесса. Делать это можно двумя способами.
В первом способе (как бы вы думали?) снова открывается файл, на этот раз
/proc/<pid>/mem
.Во втором способе можно читать память через ptrace:
ptrace(PTRACE_PEEKDATA, target, cur_address, NULL)
Посмотрим на первый способ. В первый раз в этом файла читается вся память из занимаемых регионов, через pread. Из чужой памяти загружается 1 мегабайт (брать больше смысла нет), запоминаются все адреса с нужным значением, затем читается следующие 1 мегабайт, и так далее, пока не прошерстят всю память.ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ
Please open Telegram to view this post
VIEW IN TELEGRAM
#madskillz
Нестандартные представления строк✨
В "стандартном" C++ есть три основных представления для строк. Не будем учитывать "составные" классы (как
=====
1️⃣
=====
2️⃣
Маленькие строки полностью помещаются на стек (это называется small string optimization), но пока проигнорируем это.
=====
3️⃣
Не обязательно верно то, что😁 Ведь
=====
Класс
4️⃣
Запись
=====
В бизнес-логике со строками есть проблема. Иногда в коде надо делать много составных строк. Например, для создания строки - "версии" программы нужно сложить несколько строк-частей:
В кодовой базе LLVM есть решение, которое сложно для понимания, но мы его разберем:
5️⃣
Трудности начинаются на уровне названия класса, как у не-носителя английского😁 Я так и не понял смысл названия.
Вообще, трудности сначала были со словом
Теперь посмотрим на слово
</конец бесполезного абзаца>
Этот класс опасный: он полагается на стремное правило Reference Lifetime Extension, а также на не менее стремное правило, что объекты, созданные для использования в full-expression, не удаляются до конца выполнения этого full-expression (сформулировал как смог).
Функция должна принимать
😁
😟 )
Нестандартные представления строк
В "стандартном" C++ есть три основных представления для строк. Не будем учитывать "составные" классы (как
std::stringstream
), у которых нет уникальных концепций.=====
const char*
- просто указатель на начало строки где-то в памяти. Обычно если итерироваться по указателю, то когда-то достигнем нулевой байт \0
(нуль-терминатор), который указывает на конец строки. Все строковые алгоритмы Си завязаны на признак \0
как на конец строки.=====
std::string
- класс строки, владеющий памятью для нее в куче. Запись std::string s = "abcd";
значит, что где-то в куче занята память под байты abcd\0
. Известно, std::string
гарантированно нуль-терминирован (начиная с C++11).std::string_view
- класс строки, не владеющий памятью. Представляет собой пару const char* s
(начало строки) и size_t len
(длину строки).Не обязательно верно то, что
*(s + len) == '\0'
. std::string_view
указывает не на всю строку, а только на какую-то ее часть.=====
Класс
std::string
поведением похож на контейнер std::vector<char>
. Можно посмотреть на какие-нибудь неклассические контейнеры, чтобы создать новые строковые классы, которых нет в стандартном C++.SmallString
- класс строки, владеющий памятью для нее, с поведением как у SmallVector<char>
. Реализован в LLVM.Запись
std::string s1;Дает два объекта
SmallString<256> s2;
s1
и s2
, у которых одинаковый набор методов, но s2
хранится на стеке, если размер строки не превышает 256 символов (планируется, что так будет в 99.9% случаев). Если размер все-таки превысили, то строку начинают хранить в куче.=====
В бизнес-логике со строками есть проблема. Иногда в коде надо делать много составных строк. Например, для создания строки - "версии" программы нужно сложить несколько строк-частей:
Major + "." + Minor + "." + VersionPatchВ этом случае происходит создание 3 (!) лишних "временных" строк с аллокациями памяти, то есть делается строка
Major + "."
, потом строка (Major + ".") + Minor
и так далее. Более того, итоговая строка (4-я по счету) тоже по сути лишняя, если мы хотели сразу записать итог в какой-нибудь файл, а не хранить результат сложения.В кодовой базе LLVM есть решение, которое сложно для понимания, но мы его разберем:
Twine
- класс "сумма строк". Документация по Twine, но больше информации в исходнике.Трудности начинаются на уровне названия класса, как у не-носителя английского
Вообще, трудности сначала были со словом
string
. До того, как я начал программировать, у меня это ассоциировалось со стрингами, которые носил Борат. У этого слова куча значений, пусть в нашем случае это будет шнур
.Теперь посмотрим на слово
twine
. У него тоже вагон значений, пусть в нашем случае это будет бечёвка
, пнятненько?</конец бесполезного абзаца>
Этот класс опасный: он полагается на стремное правило Reference Lifetime Extension, а также на не менее стремное правило, что объекты, созданные для использования в full-expression, не удаляются до конца выполнения этого full-expression (сформулировал как смог).
Функция должна принимать
Twine
по константной ссылке:void foo(const Twine& T);А подавать туда Twine нужно не отходя от кассы, чтобы сработало правило RLE:
foo(Twine(Major) + "." + Minor + "." + VersionPatch);Благодаря правилу про full-expression, все составные части строки "живы" на стеке, пока не выполнится вызов
foo
.Twine
внутри себя выглядит как бинарное дерево. У него два "ребенка":Child LHS;Каждый ребенок это указатель на какой-нибудь строковой объект:
Child RHS;
const char
, или std::string
, или std::string_view
, или другой Twine
("поддерево"). Также для удобства поддерживаются числа union ChildПРОДОЛЖЕНИЕ В ПЕРВОМ КОММЕНТАРИИ (у телеграма ограничение по размеру постов
{
const Twine *twine;
const char *cString;
const std::string *stdString;
/* ... */
int decI;
/* ... */
};
Please open Telegram to view this post
VIEW IN TELEGRAM
#opensource
Обзор на Boost🆘
Boost это широко известный набор библиотек для C++. Boost оказал большое влияние на развитие C++, но что осталось от его влияния в 2023 году?
Чтобы не копаться в библиотеках самому, можно почитать про разработку в Boost в крутой книге крутого Антона Полухина, которой уже прилично лет, но ее держат в актуальном состоянии.
Надеюсь, не проспойлерю книгу (на сайте все равно есть исходники примеров), но очень, очень много чего в Boost вошло в стандарты C++, и вы половину книги будете читать про то, как работают классы
Эта часть Boost отдала свои жизненные соки Стандарту C++ и перестала быть интересной (кроме как тем, кто пишет на C++ старого стандарта и не может перейти на более новый стандарт).
Далеко не все куски Boost находятся в ажурном состоянии. Сейчас в Boost состоит 169 библиотек, во многом независимых друг от друга. Практически у всех библиотек есть какие-то реальные проблемы из этих:
1️⃣ Не обновлялась с ~2006 года. Да, некоторые библиотеки просто написаны сумрачным гением тысячу лун назад и заброшены.
2️⃣ Стала неактуальной после вхождения в Стандарт C++. Это описал выше.
3️⃣ Повторяет другие библиотеки по функциональности. Бывают приколы как тупо
4️⃣ Есть нишевая библиотека вне Boost с лучшим функционалом. Можно сравнить Boost.JSON и nlohmann/json.
В целом Boost так себе в нишевых библиотеках, количество контрибьюторов в отдельную библиотеку намного ниже, чем в популярный проект.
Иногда кто-то хочет усугубить проблему и добавить библиотеку по типу
Видимо, делаются попытки с уверенностью, что Boost сам по себе типа как бы бренд, и библиотека становится лучше как бы самим фактом наличия в Boost... Что не так.
5️⃣ Лютая дичь и эрзац-компилятор. Это легендарные библиотеки-монстры, которые выглядят очень странно, потому что нестандартными способами обходят ограничения компиляторов, или просто "чем хуже тем лучше". Я бы отнес их использование к ненормальному программированию.
Например, Boost.Hana для метапрограммирования
Есть библиотеки для имитации
6️⃣ Не лучшая техническая реализация. Некоторые фанаты open source верят в миф, что стоит проекту бытоваться открытым, как тут же появляются "тысячи глаз", которые следят за его качеством. На деле никому это нафиг не нужно это вряд ли так, и большой вопрос, где качество кода в среднем лучше.
Из тех библиотек, что я активно исследовал:
Boost.ScopeExit - в другом опенсорсном проекте есть реализация подобной штуки без необходимости писать
Boost.SmallVector - официально стырен из LLVM, а не придуман уникально.Почему бы тогда не использовать библиотеку LLVM?
Boost.DynamicBitset - по состоянию на 2018 год оно использовало захардкоженные таблицы, чтобы искать количество бит в числе или типа того.
Я туда сделал коммиты (github) и ускорил некоторые методы в 2 раза, если система поддерживает интринсики как
Offtop:в 2018 году я исследовал dynamic bitset, потому что программировал тогда шахматы с кастомным размером доски (не 8x8) и std::bitset<64> не подходил. Но в итоге забросил идею, а запрограммировал шахматы только в 2022 году без dynamic bitset ( мой лонгрид на хабре ).
🤔 Таким образом, в 2023 году Boost может быть не лучшим выбором для активного использования!
Обзор на Boost
Boost это широко известный набор библиотек для C++. Boost оказал большое влияние на развитие C++, но что осталось от его влияния в 2023 году?
Чтобы не копаться в библиотеках самому, можно почитать про разработку в Boost в крутой книге крутого Антона Полухина, которой уже прилично лет, но ее держат в актуальном состоянии.
Надеюсь, не проспойлерю книгу (на сайте все равно есть исходники примеров), но очень, очень много чего в Boost вошло в стандарты C++, и вы половину книги будете читать про то, как работают классы
boost::shared_ptr<T>
и boost::string_view
. Работают они почти так же, как канонические std::XXX
, но иногда отличия бывают (в книге рассказано, какие именно).Эта часть Boost отдала свои жизненные соки Стандарту C++ и перестала быть интересной (кроме как тем, кто пишет на C++ старого стандарта и не может перейти на более новый стандарт).
Далеко не все куски Boost находятся в ажурном состоянии. Сейчас в Boost состоит 169 библиотек, во многом независимых друг от друга. Практически у всех библиотек есть какие-то реальные проблемы из этих:
Boost.Variant
и Boost.Variant2
.В целом Boost так себе в нишевых библиотеках, количество контрибьюторов в отдельную библиотеку намного ниже, чем в популярный проект.
Иногда кто-то хочет усугубить проблему и добавить библиотеку по типу
Boost.Lua
(еще одну к овер9000 библиотекам про Lua), но к счастью количество библиотек растет не так быстро.Видимо, делаются попытки с уверенностью, что Boost сам по себе типа как бы бренд, и библиотека становится лучше как бы самим фактом наличия в Boost... Что не так.
Например, Boost.Hana для метапрограммирования
struct Person {Boost.Spirit как LL-парсер, который притом header-only (поэтому собирается по 10 минут).
BOOST_HANA_DEFINE_STRUCT(Person,
(std::string, name),
(int, age)
);
};
Есть библиотеки для имитации
std::move
до C++11 и прочие попытки перепрыгнуть выше крыши.Из тех библиотек, что я активно исследовал:
Boost.ScopeExit - в другом опенсорсном проекте есть реализация подобной штуки без необходимости писать
BOOST_SCOPE_EXIT_END
в конце.Boost.SmallVector - официально стырен из LLVM, а не придуман уникально.
Я туда сделал коммиты (github) и ускорил некоторые методы в 2 раза, если система поддерживает интринсики как
__builtin_popcount
.Offtop:
Please open Telegram to view this post
VIEW IN TELEGRAM
#story
Сделай свой std::flat_set из C++23 📦
В C++23 были приняты новые контейнеры
В интернете почти нет нормальных объяснений их содержимого. Многие из тех, что есть, скорее запутывают. Там приводятся всякие ненужные размышления на тему локальности памяти, бенчмарки, итераторы, и так далее.
А на деле реализация этих контейнеров простая, ее может сделать каждый. Из готовой реализации можно уже самому понять свойства этого класса.
API flat_set почти не отличается от set. Добавлены новые конструкторы и перегрузки
Можно просто заменить тип на новый и почти наверняка это скомпилируется.
Новые контейнеры являются адаптерами. Это такие контейнеры, которые сами не делают ничего "тяжелого", а только держат у себя другой контейнер и все методы пробрасывают туда. В C++ уже было три таких контейнера -
Пусть у нас есть😝
0️⃣ Итераторы
1️⃣ Конструктор
2️⃣ Вставка
😁
3️⃣ Удаление
Действуют по одинаковому принципу с
4️⃣ Слияние двух flat_set
В некоторых случаях надо делать слияние двух отсортированных векторов. Это стандартный алгоритм, его часто спрашивают на собеседованиях.
Если нужно вставить несколько элементов, то можно вызвать такой метод, который вставит каждый элемент по отдельности за
🙂
Сделай свой std::flat_set из C++23 📦
В C++23 были приняты новые контейнеры
std::flat_set
, std::flat_map
(и их multi-варианты), аналогично уже существующим std::set
и std::map
.В интернете почти нет нормальных объяснений их содержимого. Многие из тех, что есть, скорее запутывают. Там приводятся всякие ненужные размышления на тему локальности памяти, бенчмарки, итераторы, и так далее.
А на деле реализация этих контейнеров простая, ее может сделать каждый. Из готовой реализации можно уже самому понять свойства этого класса.
API flat_set почти не отличается от set. Добавлены новые конструкторы и перегрузки
insert
. Заменены редкие методы: node_type extract(...)
insert_return_type insert(node_type&& nh);
то есть те, где юзер должен был работать с вершинами красно-черного дерева.Можно просто заменить тип на новый и почти наверняка это скомпилируется.
Новые контейнеры являются адаптерами. Это такие контейнеры, которые сами не делают ничего "тяжелого", а только держат у себя другой контейнер и все методы пробрасывают туда. В C++ уже было три таких контейнера -
stack
, queue
, priority_queue
:template<class T, class Container = std::deque<T>> class stack;
template<class T, class Container = std::deque<T>> class queue;
flat_set
(и ему подобные) такой же адаптер:template<class Key, class Compare = less<Key>, class KeyContainer = vector<Key>> class flat_set;То есть множество представляется (по умолчанию) в виде отсортированного вектора (но можно дать и другой класс -
static_vector
, small_vector
).Пусть у нас есть
std::vector<T> data
. Попробуем реализовать для него все основные методы flat_set
сами, в упрощенном виде (без кастомных компараторов и прочего) begin/end/...
, методы empty()/size()/max_size()
Подобные методы не рассматриваем, они просто перенаправляют вызовы в сам контейнерdecltype(auto) begin() noexcept { return data.begin(); }
flat_set(container_type cont);
Конструкторов там примерно 24 штуки на разные случаи жизни. Можно реализовать случай, когда дается контейнер, который изначально не отсортирован и там могут быть повторяющиеся элементы - с использованием std::unique:data = std::move(cont);Сложность этого метода
std::sort(data.begin(), data.end());
auto last = std::unique(data.begin(), data.end());
data.erase(last, data.end());
O(NlogN)
insert(const value_type& x)
Нужно найти место, куда вставить новый элемент. Если он уже существует, то вставлять не нужно. Это можно делать бинарным поиском через std::lower_bound и вставкой в вектор в этом место.auto lower = std::lower_bound(data.begin(), data.end(), x);Сложность этого метода
if (lower == data.end() || *lower != x) {
data.insert(lower, x);
}
O(N)
в общем случае и амортизированный O(logN)
в случае вставки в конец. "Амортизированный" - потому что можно попасть на реаллокацию вектора erase(const key_type& x)
и прочие методы (find
, contains
, ...)Действуют по одинаковому принципу с
insert
- найти место этого элемента бинарным поиском и что-то с этим сделать.auto lower = std::lower_bound(data.begin(), data.end(), x);Сложность этого метода
if (lower != data.end() && *lower == x) {
data.erase(lower);
}
O(N)
в общем случае и O(logN)
в случае удаления с конца.В некоторых случаях надо делать слияние двух отсортированных векторов. Это стандартный алгоритм, его часто спрашивают на собеседованиях.
Если нужно вставить несколько элементов, то можно вызвать такой метод, который вставит каждый элемент по отдельности за
O(N)
, с итоговой сложностью до O(N^2)
в среднем:void insert(InputIterator first, InputIterator last);Но если есть знание, что вставляемые элементы
[first, last)
уже отсортированы, то можно вызвать другой метод с "мусорным" объектом в начале, который сделает слияние, это будет работать за O(N)
, что может быть быстрее:void insert(sorted_unique_t, InputIterator first, InputIterator last);Такую задачу можно решить на Leetcode - Merge Sorted Array
Please open Telegram to view this post
VIEW IN TELEGRAM
#madskillz
Сделай свой эмулятор процессора Motorola 68000🖥
В свободное время программисты могут заниматься странными вещами. Например, я сделал эмулятор процессора Motorola 68000. Сейчас там поддержано 94% инструкций и все нужные абстракции, это заняло ~2500 строк кода.
Мне было интересно, рекомендую это делать всем, кто хочет лучше понять работу процессора🍅
m68k (сокращенно) это процессор, сильно обогнавший свое время. Он использовался на протяжении десятилетий в компьютерах Macintosh, Amiga, Atari, приставке Sega Mega Drive, и прочих устройствах.
1️⃣ Представление в C++
В архитектуре процессора уже есть элементы 32-битовости, но с ограничениями.
Всего есть 16 регистров 32-битных (и 1 регистр 16-битный).
Несмотря на то, что "адресные" регистры (
Процессор поддерживает зачаток виртуализации для многозадачных систем - обращение к регистру
🔍 registers.h - представление регистров
Процессор может что-то читать/писать по адресам
Эмулятор имеет дело с интерфейсом. Запись/чтение могут спровоцировать ошибку по любой причине (например, чтение по нечетному адресу). Я не использую исключения C++ в эмуляторе - объект ошибки возвращается из методов.
🔍 i_device.h - интерфейс записи/чтения памяти
"Текущее состояние" эмулятора, которое можно менять, можно представлять так:
🔍 target.h - представление операнда в инструкциях
Последнее, самый большое представление - у инструкций. У них есть "тип" инструкции и все нужные параметры. Ассемблер очень "ортогональный", поэтому представление в виде набора переменных подходит лучше всего.
🔍 instructions.h - представление инструкций
2️⃣ Как реализовать и протестировать инструкции
Каждая инструкция занимает 2 байта. Иногда могут потребоваться 2/4 байта дополнительных данных после инструкции (обязательно четное число).
Декодирование инструкции можно написать глядя на крутую таблицу от GoldenCrystal (сайт регулярно лежит, в комментариях к посту есть PDF).
Краткое описание инструкции можно читать в этой markdown-документации. Иногда этого недостаточно, тогда можно читать длинное описание в этой книге.
Самое важная часть - тестирование. Небольшая ошибка в каком-нибудь статусном флаге может привести к катастрофе во время эмуляции. Когда программа большая, ее становится легко сломать в неожиданном месте, поэтому нужны тесты на все инструкции.
Мне очень помогли тесты из этого репозитория. На каждую инструкцию есть 8000+ тестов, которые покрывают все возможные случаи. Суммарно тестов чуть больше миллиона.
Они могут находить даже самые мелкие ошибки - нередко бывает ситуация, что не проходятся ~20 тестов из 8000.
Например, инструкция
Продолжение в комментариях (эмуляция программ)
Сделай свой эмулятор процессора Motorola 68000
В свободное время программисты могут заниматься странными вещами. Например, я сделал эмулятор процессора Motorola 68000. Сейчас там поддержано 94% инструкций и все нужные абстракции, это заняло ~2500 строк кода.
Мне было интересно, рекомендую это делать всем, кто хочет лучше понять работу процессора
m68k (сокращенно) это процессор, сильно обогнавший свое время. Он использовался на протяжении десятилетий в компьютерах Macintosh, Amiga, Atari, приставке Sega Mega Drive, и прочих устройствах.
В архитектуре процессора уже есть элементы 32-битовости, но с ограничениями.
Всего есть 16 регистров 32-битных (и 1 регистр 16-битный).
Несмотря на то, что "адресные" регистры (
A0
-A7
) 32-битные, по факту для адреса берутся младшие 24 бита. То есть адресуется пространство в 16 мегабайт памяти.Процессор поддерживает зачаток виртуализации для многозадачных систем - обращение к регистру
A7
по факту будет обращением либо к USP
, либо к SSP
, в зависимости от флага в статусном регистре.Процессор может что-то читать/писать по адресам
0x000000 - 0xFFFFFF
(24 бита), не обязательно это будет физическая память. Иногда запись в определенные адреса будет влиять на периферийные устройства. Поведение определяется "шиной".Эмулятор имеет дело с интерфейсом. Запись/чтение могут спровоцировать ошибку по любой причине (например, чтение по нечетному адресу). Я не использую исключения C++ в эмуляторе - объект ошибки возвращается из методов.
"Текущее состояние" эмулятора, которое можно менять, можно представлять так:
struct TContext {Операнды в инструкциях ("цели") могут указывать на адрес в памяти/регистр большим количеством способов. Для этих способов можно выделить примерно такой интерфейс с общим методом чтения/записи данных:
NRegisters::TRegisters& Registers;
NMemory::IDevice& Memory;
};
Последнее, самый большое представление - у инструкций. У них есть "тип" инструкции и все нужные параметры. Ассемблер очень "ортогональный", поэтому представление в виде набора переменных подходит лучше всего.
Каждая инструкция занимает 2 байта. Иногда могут потребоваться 2/4 байта дополнительных данных после инструкции (обязательно четное число).
Декодирование инструкции можно написать глядя на крутую таблицу от GoldenCrystal (сайт регулярно лежит, в комментариях к посту есть PDF).
Краткое описание инструкции можно читать в этой markdown-документации. Иногда этого недостаточно, тогда можно читать длинное описание в этой книге.
Самое важная часть - тестирование. Небольшая ошибка в каком-нибудь статусном флаге может привести к катастрофе во время эмуляции. Когда программа большая, ее становится легко сломать в неожиданном месте, поэтому нужны тесты на все инструкции.
Мне очень помогли тесты из этого репозитория. На каждую инструкцию есть 8000+ тестов, которые покрывают все возможные случаи. Суммарно тестов чуть больше миллиона.
Они могут находить даже самые мелкие ошибки - нередко бывает ситуация, что не проходятся ~20 тестов из 8000.
Например, инструкция
MOVE (A6)+ (A6)+
(обращение к регистру A6
делается с пост-инкрементом) должна работать не так, как я реализовал, поэтому я сделал костыль, чтобы работало корректно.Продолжение в комментариях (эмуляция программ)
Please open Telegram to view this post
VIEW IN TELEGRAM
#opensource
Создание своих плагинов для Vim👩💻
Я не люблю холивары, в том числе на тему текстовых редакторов. У людей есть баг - они рационализируют свой выбор и убеждения.
В худших случаях это вытекает в поиск того, за чей счет бы самоутвердиться, и приписывании другой стороне ложных свойств. Так, многие любители не-Vim уверены, что в Vim нет автокомплита, а многие любители Vim бывают уверены в каких-то вовсе страшных вещах насчет не-Vim.
Я сознательно отказываюсь от рационализации своего выбора. Например, довод о "быстроте редактирования" в Vim неактуален в обстановке, когда программист намного дольше читает уже написанный код или обсуждает решение, чем пишет новый код.
Vim👩💻 (позже его форк Neovim👩💻 ) для меня основной редактор с 2016 года, а полностью сменил гендер "акклиматизировался" я где-то за год (за сравнимое время можно полностью выучить любой другой редактор).
В Vim каждый сам собирает свою "IDE" из 15-20 плагинов и кучи настроек.
Vim я открываю прямо на удаленной машине - потому что работаю в виртуалке.
Для интереса можно сделать свои специфические плагины, чтобы "IDE" стала удобнее. На Neovim плагины можно писать на языке Lua. Чтобы понять, что нужно делать, можно для начала почитать эту и эту доки, но в процессе написания все равно надо много гуглить.
API плагинов мне понравился - если хорошо знать Vim, то в программировании плагинов не надо учить никакие внутренние костыли (другое дело, что в самом Vim полно костылей). Плагин может делать все что угодно многими способами. Добавлять можно новые команды, действия по нажатию хоткеев, колбеки на разные "события" (такие как "сохранение файла" и многие другие).
Сейчас я для работы использую три самодельных плагина, которые очень помогают решать задачи, которые раньше делал ручным способом. Они специфические для компании😁
1️⃣ Ссылка на CodeSearch
🔍 Гифка (10.7 MB)
😱 Исходник
Раньше надо было копировать текст в терминале (выделение мышью и
Это конечно никуда не годится, лучшее решение - встроить CodeSearch например в плагин Telescope😁
По исходнику видно, насколько много костылей в Vim. Например, "слово под курсором" ищется так:
2️⃣ Вставка на Pastebin
🔍 Гифка (13.3 MB)
😱 Исходник
Есть тулза, чтобы отправлять файлы в нечто вроде местного Pastebin. Чтобы вставить кусок логов или кода, приходилось выделять их, делать временный файл, вставлять выделенное, сохранять текст, вызывать тулзу в другой вкладке... Сейчас можно просто выделить текст (или не выделять, если нужно вставить весь файл) и нажать
Видно еще больше костылей: отсутствие многих базовых функций (их надо писать самому), выделенный текст копируется в регистр
3️⃣ Автоматический code style при сохранении
🔍 Гифка (44.1 MB)
😱 Исходник
Используется тулза clang-format для форматирования кода, но с локальными хаками и особенностями. Если код не удовлетворяет код-стайлу, то падает проверка на CI, и приходится запускать тулзу, потом смотреть, что она нормально отработала, закрывать и открывать файл, и так далее... Сейчас эта тулза запускается сама, каждый раз после сохранения файла, меняя только нужные участки кода.
Таким образом, можно написать свои плагины, чтобы уменьшить боль от ручного труда. Это, конечно, базовые плагины, и не сравнятся с монстрами наподобии Telescope или Gitsigns. К "большим" плагинам можно делать свои плагины.😐
Гифки записывал через blue-recorder, визуализация нажатий на кнопки через screenkey.
Создание своих плагинов для Vim
Я не люблю холивары, в том числе на тему текстовых редакторов. У людей есть баг - они рационализируют свой выбор и убеждения.
В худших случаях это вытекает в поиск того, за чей счет бы самоутвердиться, и приписывании другой стороне ложных свойств. Так, многие любители не-Vim уверены, что в Vim нет автокомплита, а многие любители Vim бывают уверены в каких-то вовсе страшных вещах насчет не-Vim.
Я сознательно отказываюсь от рационализации своего выбора. Например, довод о "быстроте редактирования" в Vim неактуален в обстановке, когда программист намного дольше читает уже написанный код или обсуждает решение, чем пишет новый код.
Vim
В Vim каждый сам собирает свою "IDE" из 15-20 плагинов и кучи настроек.
Vim я открываю прямо на удаленной машине - потому что работаю в виртуалке.
Для интереса можно сделать свои специфические плагины, чтобы "IDE" стала удобнее. На Neovim плагины можно писать на языке Lua. Чтобы понять, что нужно делать, можно для начала почитать эту и эту доки, но в процессе написания все равно надо много гуглить.
API плагинов мне понравился - если хорошо знать Vim, то в программировании плагинов не надо учить никакие внутренние костыли (другое дело, что в самом Vim полно костылей). Плагин может делать все что угодно многими способами. Добавлять можно новые команды, действия по нажатию хоткеев, колбеки на разные "события" (такие как "сохранение файла" и многие другие).
Сейчас я для работы использую три самодельных плагина, которые очень помогают решать задачи, которые раньше делал ручным способом. Они специфические для компании
Раньше надо было копировать текст в терминале (выделение мышью и
Ctrl+C
), идти на сайт внутреннего поиска кода, вставлять текст Ctrl+V
, экранировать спецсимволы... Сейчас можно выделить текст (или не выделять, если нужно найти одно слово под курсором) и нажать Ctrl+S
(или ввести команду :CodeSearch
, но хоткей быстрее), чтобы вывелась правильная ссылка.Это конечно никуда не годится, лучшее решение - встроить CodeSearch например в плагин Telescope
По исходнику видно, насколько много костылей в Vim. Например, "слово под курсором" ищется так:
vim.fn.expand('<cword>')
, а позиции начала и конца выделения это '<
и '>
.Есть тулза, чтобы отправлять файлы в нечто вроде местного Pastebin. Чтобы вставить кусок логов или кода, приходилось выделять их, делать временный файл, вставлять выделенное, сохранять текст, вызывать тулзу в другой вкладке... Сейчас можно просто выделить текст (или не выделять, если нужно вставить весь файл) и нажать
Ctrl+P
(или ввести команду :Paste
), чтобы плагин все это сделал и показал ссылку.Видно еще больше костылей: отсутствие многих базовых функций (их надо писать самому), выделенный текст копируется в регистр
"
или 0
, а путь до текущего файла можно вытащить через vim.fn.expand('%')
- это все надо помнить или очень сильно читать документацию.Используется тулза clang-format для форматирования кода, но с локальными хаками и особенностями. Если код не удовлетворяет код-стайлу, то падает проверка на CI, и приходится запускать тулзу, потом смотреть, что она нормально отработала, закрывать и открывать файл, и так далее... Сейчас эта тулза запускается сама, каждый раз после сохранения файла, меняя только нужные участки кода.
Таким образом, можно написать свои плагины, чтобы уменьшить боль от ручного труда. Это, конечно, базовые плагины, и не сравнятся с монстрами наподобии Telescope или Gitsigns. К "большим" плагинам можно делать свои плагины.
Гифки записывал через blue-recorder, визуализация нажатий на кнопки через screenkey.
Please open Telegram to view this post
VIEW IN TELEGRAM
#opensource
Обзор исходников Telegram Desktop💬
Для меня Telegram - основное средство связи со всеми коллегами и знакомыми. Поэтому я решил посмотреть в исходники десктопного Telegram и возможно что-нибудь переделать под себя.
Эти исходники находятся тут - https://github.com/telegramdesktop/tdesktop, там же внизу ссылки на инструкции по билду Telegram.
Я попробовал сбилдить Telegram для Linux на двух ноутбуках и на виртуалке, и у меня не получилось, вплоть до того что навернулось окружение рабочего стола на одном ноутбуке😶 Сначала чинил кривые скрипты сборки, потом пытался обновить старую версию Glib, и так далее.
Зато получилось с полпинка сбилдить его для Windows😁 Вспомнил как выглядит Visual Studio: скриншот.
Исходники самого Telegram сравнительно небольшие, но вместе с ним качается и билдится из исходников куча хрени (~25 либ включая ffmpeg и Qt, в размере несколько гигабайтов).
Десктоп написан на движке Qt, с довольно хорошим кодом, он сравнительно небольшой. После исследования известны такие факты:
1️⃣ Что-нибудь "сломать" в самом Телеграме не выйдет, так как десктоп сделан правильно - это всего лишь красивая оболочка над Telegram API, которая получает и посылает все данные из сервера.
Например, когда мы видим статус
2️⃣ Можно написать свой десктоп или даже консольный Телеграм, но на это сомнительное в своей полезности занятие уйдет слишком много времени.
3️⃣ Если собрать "свой" Телеграм и зайти туда хоть раз, то твой аккаунт ставят на счётчик ставят под колпак. За действиями с аккаунта будут следить, в случае разных приколов (спам, накрутка, другой абьюз) будет вечный бан. Об этом честно предупреждают. 🔫
Поменять работу сервера Telegram не выйдет, но можно кастомизировать клиент под свои приколы или под приколы компании, если это рабочий инструмент (вместо такого сизифова труда, как создание своего мессенджера с нуля).
Я сделал такую мелкую фичу: в написанном сообщении обнаруживаются все http-ссылки и заменяются на укороченный вариант из clck.ru. Это может быть нужно для безопасности, если в компании есть свой сервис url shortener для внутренних ссылок, но не хочется тратить по 5-10 секунд для вбивания каждой ссылки.
К счастью, Qt это одна из лучших библиотек C++ на свете, поэтому там есть почти всё свое. Контейнеры (как
Единственный минус - Qt очень заточен под асинхронность, поэтому пришлось сделать какой-то фокус (с 3-го раза), чтобы поставить барьер для ожидания ответов на http-запрос.
✨ Примерный коммит, который включает короткие урлы (с параллельными походами!), выглядит так. (И еще такой кусок кода во "внешней" либе). Там работа со строками.
🔍 😐 Гифка с результатом
Урлы из гифки:
исходник1, исходник2, исходник3
https://clck.ru/34A7Hv, https://clck.ru/3vyXS, https://clck.ru/8f7h6
При желании можно достаточно сильно кастомизировать Telegram, чтобы в нем показывалось больше информации (например, соответствие профиля юзера и его аккаунта во внутренней сети компании, его должность, и т.д.)
Обзор исходников Telegram Desktop
Для меня Telegram - основное средство связи со всеми коллегами и знакомыми. Поэтому я решил посмотреть в исходники десктопного Telegram и возможно что-нибудь переделать под себя.
Эти исходники находятся тут - https://github.com/telegramdesktop/tdesktop, там же внизу ссылки на инструкции по билду Telegram.
Я попробовал сбилдить Telegram для Linux на двух ноутбуках и на виртуалке, и у меня не получилось, вплоть до того что навернулось окружение рабочего стола на одном ноутбуке
Зато получилось с полпинка сбилдить его для Windows
Исходники самого Telegram сравнительно небольшие, но вместе с ним качается и билдится из исходников куча хрени (~25 либ включая ffmpeg и Qt, в размере несколько гигабайтов).
Десктоп написан на движке Qt, с довольно хорошим кодом, он сравнительно небольшой. После исследования известны такие факты:
Например, когда мы видим статус
last seen recently
вместо точного времени (есть такая настройка), то десктоп действительно не знает точного времени - сервер отдает ему именно такой статус.Поменять работу сервера Telegram не выйдет, но можно кастомизировать клиент под свои приколы или под приколы компании, если это рабочий инструмент (вместо такого сизифова труда, как создание своего мессенджера с нуля).
Я сделал такую мелкую фичу: в написанном сообщении обнаруживаются все http-ссылки и заменяются на укороченный вариант из clck.ru. Это может быть нужно для безопасности, если в компании есть свой сервис url shortener для внутренних ссылок, но не хочется тратить по 5-10 секунд для вбивания каждой ссылки.
К счастью, Qt это одна из лучших библиотек C++ на свете, поэтому там есть почти всё свое. Контейнеры (как
QVector
, QList
, QString
), регексы, сеть, и очень многое другое. Поэтому такие задачи делаются сравнительно без проблем.Единственный минус - Qt очень заточен под асинхронность, поэтому пришлось сделать какой-то фокус (с 3-го раза), чтобы поставить барьер для ожидания ответов на http-запрос.
Урлы из гифки:
исходник1, исходник2, исходник3
https://clck.ru/34A7Hv, https://clck.ru/3vyXS, https://clck.ru/8f7h6
При желании можно достаточно сильно кастомизировать Telegram, чтобы в нем показывалось больше информации (например, соответствие профиля юзера и его аккаунта во внутренней сети компании, его должность, и т.д.)
Please open Telegram to view this post
VIEW IN TELEGRAM
#creepy
Как выбить из C++ настоящий адрес объекта🌃
Недавно коллега искал причины какого-то бага. У нас система многопоточная с большим количеством объектов, поэтому было решено вместе с логами выводить адрес объекта в памяти, к которому относится лог. Это выглядело примерно так:
😁 То есть фильтр логов по адресу показывал явно не все логи, которые должны были быть. После исследования нашли, что такие куски кода:
😒
Класс
Но есть тот факт, что виртуальные классы нормально работают со смещенными указателями. Если указатель когда-то начнет указывать не на тот адрес, то он все равно разрушится по правильному адресу при вызове виртуального деструктора.
😐 Для информации я прочитал две крутые статьи:
C++ vtables - Part 1 - Basics
C++ vtables - Part 2 - Multiple Inheritance
Оттуда узнаем такие факты (верные для 64-битного Linux с включенным rtti):
1️⃣ vtable pointer указывает не на начало vtable, а смещен на 16 байт от начала. Первые 8 байт это число 2️⃣ Компилятор генерирует особые методы под названием thunk, которые фиксят смещение указателя
Продолжение в комментарии - нахождение реального адреса!
Как выбить из C++ настоящий адрес объекта
Недавно коллега искал причины какого-то бага. У нас система многопоточная с большим количеством объектов, поэтому было решено вместе с логами выводить адрес объекта в памяти, к которому относится лог. Это выглядело примерно так:
std::shared_ptr<CallService> callService = ...;Спустя некоторое время оказалось, что созданные объекты пропадают с концами
LOG_INFO("blah blah blah " << callService); // выведет адрес `callService` в конце
void Foo::Bar(std::weak_ptr<IListener> listener) {где
LOG_INFO("add listener " << listener.lock().get());
}
listener
это указатель на callService
(а класс IListener
- предок класса CallService
), выводит этот же адрес со смещением, в данном случае было на +4 байта вперед Класс
CallService
имел множественное наследование, а из-за этого указатели на базовые типы могли указывать со смещением. На простом примере:struct Foo { int i; };Это логично, потому что указатель должен давать доступ к под-объектам без всяких приколов, то есть вызов
struct Bar { short s; };
struct Baz { char c; };
struct All : Foo, Bar, Baz {};
// ...
All all; // &all == 0x7ffdfa3ce770
Foo* foo = &all; // foo == 0x7ffdfa3ce770
Bar* bar = &all; // bar == 0x7ffdfa3ce774
Baz* baz = &all; // baz == 0x7ffdfa3ce776
bar->s
должен работать одинаково, без разницы куда указывает bar
. Таким образом, затея коллеги полностью провалилась, полностью.Но есть тот факт, что виртуальные классы нормально работают со смещенными указателями. Если указатель когда-то начнет указывать не на тот адрес, то он все равно разрушится по правильному адресу при вызове виртуального деструктора.
// IEdible - виртуальный класс, (с `virtual ~IEdible()`)То же самое верно для других виртуальных методов. Если взять указатель на предок, и он будет смещенным, то вызов метода все равно отработает корректно, а точнее - неявный параметр
// struct Mango : IFruit, IEdible
std::unique_ptr<IEdible> edible;
{
std::unique_ptr<Mango> mango = std::make_unique<Mango>();
std::cout << mango << std::endl; // вывод "0x56366c90beb0"
edible = std::move(mango);
}
std::cout << edible << std::endl; // вывод "0x56366c90beb8"
// удаляется объект именно по правильному адресу "0x56366c90beb0"!
// с вызовом ~Mango()
this
будет пофикшен.struct IEdible {Таким образом можно попробовать найти "настоящий" адрес объекта виртуального класса
virtual ~IEdible() = default;
virtual void Eat() = 0;
};
struct Mango : IFruit, IEdible {
void Eat() override { Eaten = true; }; // используется неявный `this`
bool Eaten = false;
};
// ...
IEdible& e = ...; // смещенный указатель
e.Eat(); // работает корректно!
C++ vtables - Part 1 - Basics
C++ vtables - Part 2 - Multiple Inheritance
Оттуда узнаем такие факты (верные для 64-битного Linux с включенным rtti):
top_offset
(смещение относительно реального объекта), вторые 8 байт это указатель на объект typeinfo
this
на реальный и потом вызывают нужный метод. В примере выше e.Eat()
на самом деле вызовет thunk, который сместит this
на -8
(это top_offset
) и потом вызовет Mango::Eat()
.Продолжение в комментарии - нахождение реального адреса!
Please open Telegram to view this post
VIEW IN TELEGRAM
#compiler
Простые тесты для C++ кода на Python🍄 🍄 🍄 🍄 🍄
Часто хочется покрыть тестами код на C++ в проекте - не логику, которую описывает код, а сам исходный код. Сейчас для этого есть приличное количество чекеров в clang-tidy. Можно написать свой чекер, который все равно будут ревьюить много месяцев и КПД всего этого занятия близок к нулю. Для специфических проверок надо что-то колхозить самому.
Примерных проверок может быть много:
1️⃣ Запрет на использование
2️⃣ Запрет на использование старого API в новом коде во время масштабного рефакторинга.
3️⃣ Проверка, что в начале каждого файла находится копипаста лицензии проекта.
Можно придумать проверку на примере лямбд😊 Пусть у нас есть такой код:
Если переменная используется только внутри лямбды, то ее можно вкостылить прямо в capture list. Тогда вместо двух верхних строк примера будет такая строка, дающая аналогичный результат:
Попробуем сделать тест на такие кейсы😁 Если мы для этого используем библиотеки clang, то логика такая:
1️⃣ Получаем AST (Abstract Syntax Tree) из исходного кода.
2️⃣ Лямбды в AST это вершины
3️⃣ "Объявление переменной" в AST это вершина
4️⃣ "Использование переменной" (в каком-то месте) это вершина
5️⃣ Если все "использования переменной" находятся внутри какого-то одного и того же
6️⃣ Делаем рекурсивный обход AST с корня и держанием указателя на "текущую лямбду", и делаем проверку на пункт 5.
Этот тест можно сделать на👩💻 C++: вкостылить новый чекер в локальной сборке clang-tidy или просто сделать программу на libclang. Но можно сделать то же самое на 👩💻 Python и кода будет меньше в несколько раз, и это делается быстрее - менее муторно. Python хорошо подходит для быстрого написания разных тестов.
Поддерживается libclang в Python, и после просмотра примеров можно поставить его себе:
Также не хватает некоторых очевидных фичей, например получения родительской ноды: в курсорах есть пара ссылок на другие курсоры (типа родительские, двух видов), но они работают неправильно.
Кроме тестов можно писать другие тулзы, например "кодогенераторы" - которые сгенерируют какой-нибудь исходник на основе существующего кода. Про кодогенераторы можно почитать лонгрид.
Еще можно делать "исправляторы" исходников - которые берут AST, что-то туда дописывают и сохраняют в другой файл, а компилятор имеет дело с уже поправленным AST (то есть с этим другим файлом).
Простые тесты для C++ кода на Python
Часто хочется покрыть тестами код на C++ в проекте - не логику, которую описывает код, а сам исходный код. Сейчас для этого есть приличное количество чекеров в clang-tidy. Можно написать свой чекер, который все равно будут ревьюить много месяцев и КПД всего этого занятия близок к нулю. Для специфических проверок надо что-то колхозить самому.
Примерных проверок может быть много:
typeid(x).name()
, потому что он дает mangled имя, с советом использовать костыль, который отдаст demangled имя.Можно придумать проверку на примере лямбд
int counter = 15;В этом примере лямбда использует внешнюю переменную, которая влияет на логику.
const auto addEvent = [&counter](int number) {
if (counter > 0) {
// do something...
--counter;
}
};
// ... call the lambda
addEvent(1337);
Если переменная используется только внутри лямбды, то ее можно вкостылить прямо в capture list. Тогда вместо двух верхних строк примера будет такая строка, дающая аналогичный результат:
auto addEvent = [counter = 15](int number) mutable {Такой же подход работает для переменных любых типов. Если интересно, что происходит внутри лямбд, то можно почитать целую книгу про них.
Попробуем сделать тест на такие кейсы
LambdaExpr
.VarDecl
.DeclRefExpr
.LambdaExpr
, а "объявление переменной" находится вне этого LambdaExpr
, то тест должен упасть, потому что данную переменную можно всунуть в capture list лямбды.Этот тест можно сделать на
Поддерживается libclang в Python, и после просмотра примеров можно поставить его себе:
pip install pytestи сделать такой простой тест, где реализуется описанная проверка для лямбд. Можно в директории рядом сохранить тестовый файл source.cpp и проверить, что тест падает:
pip install clang
pip install libclang
python3 -m pytest test.pyВывод:
E Failed: These variables can be declared in lambda capture:libclang на Python выглядит нормально, но неприятно то, что для понятия "нода AST" не к месту придумали новый термин "курсор".
E "counter" (at source.cpp:3:44), to lambda at source.cpp:4:88
Также не хватает некоторых очевидных фичей, например получения родительской ноды: в курсорах есть пара ссылок на другие курсоры (типа родительские, двух видов), но они работают неправильно.
Кроме тестов можно писать другие тулзы, например "кодогенераторы" - которые сгенерируют какой-нибудь исходник на основе существующего кода. Про кодогенераторы можно почитать лонгрид.
Еще можно делать "исправляторы" исходников - которые берут AST, что-то туда дописывают и сохраняют в другой файл, а компилятор имеет дело с уже поправленным AST (то есть с этим другим файлом).
Please open Telegram to view this post
VIEW IN TELEGRAM
#youtube
Поваренная книга пирата: создание своего торрент-клиента 🏴☠️
https://youtu.be/usVMq7LDW1Y
Когда есть желание написать о чем-то прикольном, обычно я делаю это в двух форматах:
1️⃣ Статья на Хабр - кондовые лонгриды, которые можно читать, помолясь и перекрестясь.
2️⃣ Этот канал - небольшие приколы, которые быстро читаются.
Сейчас я решил попробовать новый формат - а именно формат видео на ютубе.
Первое видео получилось длиной в полчаса, оно в основном про создание своего торрент-клиента на Python и побочные вещи. Могу сказать, пока я готовил его, я посмотрел с десяток других видео про торренты на ютубе (в том числе типа дофига технические), там близко нет того уровня проникновения, как у меня.
Возможно, такой формат будет более удачным. Так можно накидать неограниченный объем информации (как на Хабре) и оставаться неформальным (как в Телеге)😁
Заранее прошу прощения за мое чувство юмора, и дисклеймер: старайтесь не пиратить, не бухать, не накуриваться; а если очень хочется, то делайте так, чтобы никто не узнал😡
Поваренная книга пирата: создание своего торрент-клиента 🏴☠️
https://youtu.be/usVMq7LDW1Y
Когда есть желание написать о чем-то прикольном, обычно я делаю это в двух форматах:
Сейчас я решил попробовать новый формат - а именно формат видео на ютубе.
Первое видео получилось длиной в полчаса, оно в основном про создание своего торрент-клиента на Python и побочные вещи. Могу сказать, пока я готовил его, я посмотрел с десяток других видео про торренты на ютубе (в том числе типа дофига технические), там близко нет того уровня проникновения, как у меня.
Возможно, такой формат будет более удачным. Так можно накидать неограниченный объем информации (как на Хабре) и оставаться неформальным (как в Телеге)
Заранее прошу прощения за мое чувство юмора, и дисклеймер: старайтесь не пиратить, не бухать, не накуриваться; а если очень хочется, то делайте так, чтобы никто не узнал
Please open Telegram to view this post
VIEW IN TELEGRAM
YouTube
ПОВАРЕННАЯ КНИГА ПИРАТА: СОЗДАНИЕ СВОЕГО ТОРРЕНТ-КЛИЕНТА
Исследуем протокол BitTorrent и делаем свой торрент-клиент на Python с asyncio.
Ссылки:
Канал в Телеграме - www.tgoop.com/cxx95
Исходный код - github.com/Izaron/pidorrent
Сайт протокола BitTorrent - www.bittorrent.org
Сбор статистики в DHT - andv.medium.com/веселимся…
Ссылки:
Канал в Телеграме - www.tgoop.com/cxx95
Исходный код - github.com/Izaron/pidorrent
Сайт протокола BitTorrent - www.bittorrent.org
Сбор статистики в DHT - andv.medium.com/веселимся…
#madskillz
Как правильно выполнить любой код до и после main()🏃
Функция
Почти всегда этот код - инициализация статических объектов, которая выполняется до
В этом коде можно делать что угодно (читать файлы, записывать в поток и т.д.), окружение там полностью готово. Я часто использую лямбды:
Пример на godbolt 1, godbolt 2.
😀 Есть крутой лонгрид (со схемами) про то, что происходит до и после
Но все описание там "примерное", потому что поведение зависит от компилятора и его версии🖥
В моем случае некоторые действия находятся в другом положении схемы (увидел по
⌨️ Для того, чтобы "вызвать функцию до
А именно
Здесь слова "constructor" и "destructor" не относятся к классам в C++, это исторические названия еще до C++.
Пример на godbolt с ctor-ами и dtor-ами
Однако здесь есть типичный прикол с😁
👍 Пример по ссылке выше, скомпилированный на
❌ Более старый релиз
😠 Это связано с тем, что
А компилятор данной версии так скомпилировал, что кторы выполняются ДО НЕГО и пытаются обратиться к неинициализированному объекту.
Функция
Мораль думайте сами👍
❔ Для чего может быть полезна такая штука? Лучше описать как "вопрос-ответ"
1️⃣
Вопрос: В чем принципиальное отличие этого кода:
2️⃣
Вопрос: Зачем это все надо, почему нельзя этот кусок кода поместить в
Ответ: Иногда нужно выполнить кусок кода, только если мы линкуем какую-то библиотеку или подключаем хедер. Например, если в библиотеке хэширования есть расчет хитрых данных для хэшей на старте программы в
Да точно это же и происходит для
3️⃣
Вопрос: Какие жесткие проблемы решает эта запись?
Ответ: В кторах и дторах можно указывать приоритет😐 Чем он выше, тем позже выполнится ктор (и соответственно раньше дтор).
Пример на godbolt.
Это решает артиллерийский выстрел в ногу под названием Static Initialization Order Fiasco. Очередность выполнения инициализаций обычно зависит от порядка линковки, а с приоритетом стало можно это нормально разруливать!😎
В остальном проблемы линковки все еще остаются легендарными - можно почитать пример особо опасной проблемы, о которой писал раньше: https://www.tgoop.com/cxx95/76
Как правильно выполнить любой код до и после main()
Функция
int main()
не является настоящей "точкой входа" в программе. До того, как зайти туда (и после него), программа выполняет много другого кода, в том числе юзерского.Почти всегда этот код - инициализация статических объектов, которая выполняется до
main()
. Программа:std::vector<int> MakeVector() {Выведет такое:
std::cout << "do MakeVector" << std::endl;
return {1, 2, 3};
}
std::vector<int> VECTOR = MakeVector();
int main() {
std::cout << "do main" << std::endl;
}
do MakeVectorПример на godbolt
do main
В этом коде можно делать что угодно (читать файлы, записывать в поток и т.д.), окружение там полностью готово. Я часто использую лямбды:
Пример на godbolt 1, godbolt 2.
main()
: Linux x86 Program Start Up.Но все описание там "примерное", потому что поведение зависит от компилятора и его версии
В моем случае некоторые действия находятся в другом положении схемы (увидел по
gdb
), и еще есть дополнительная вложенность - есть функции для отдельных .cpp-файлов, а там внутри уже вызовы для инициализации переменных.main()
", можно пометить ее специальным атрибутом.А именно
__attribute__((constructor))
или менее вырвиглазно [[gnu::constructor]]
.Здесь слова "constructor" и "destructor" не относятся к классам в C++, это исторические названия еще до C++.
Пример на godbolt с ctor-ами и dtor-ами
Однако здесь есть типичный прикол с
std::cout
x86-64 clang trunk
по состоянию на 13.06.2023
работает нормально.x86-64 clang 16.0.0
дает сегфолт в кторах и будет работать только если поменять там на printf
- ссылка на godbolt.std::cout
и подобные объекты инициализируются в конструкторе статического объекта std::ios_base::Init (объявление этого объекта находится в хедере <iostream>
).А компилятор данной версии так скомпилировал, что кторы выполняются ДО НЕГО и пытаются обратиться к неинициализированному объекту.
Функция
printf
таких спецэффектов не имеет, там просто перенаправление в нужный системный вызов (syscall) без регистрации и СМС.Мораль думайте сами
Вопрос: В чем принципиальное отличие этого кода:
[[gnu::constructor]] void foo() {от этого:
// приколы
}
struct Dummy {или более укуренного этого:
Dummy() { /* приколы */ }
};
Dummy dummy;
const auto dummy = []{Ответ: Никакого, кроме более удобной записи.
// приколы
return nullptr;
}();
Вопрос: Зачем это все надо, почему нельзя этот кусок кода поместить в
int main()
???Ответ: Иногда нужно выполнить кусок кода, только если мы линкуем какую-то библиотеку или подключаем хедер. Например, если в библиотеке хэширования есть расчет хитрых данных для хэшей на старте программы в
[[gnu::constructor]]
, то не надо париться с тем, как и что вызвать в main()
, можно просто линковать библиотеку и заработает.Да точно это же и происходит для
std::cout
, как мы только что увидели выше!Вопрос: Какие жесткие проблемы решает эта запись?
Ответ: В кторах и дторах можно указывать приоритет
Пример на godbolt.
Это решает артиллерийский выстрел в ногу под названием Static Initialization Order Fiasco. Очередность выполнения инициализаций обычно зависит от порядка линковки, а с приоритетом стало можно это нормально разруливать!
В остальном проблемы линковки все еще остаются легендарными - можно почитать пример особо опасной проблемы, о которой писал раньше: https://www.tgoop.com/cxx95/76
Please open Telegram to view this post
VIEW IN TELEGRAM
#theory
Замороженные корутины🧊
Am Ende bleib ich doch alleine
Но в конце я остаюсь один
Die Zeit steht still und mir ist kalt
Время остановилось, и мне холодно
Этот пост больше теоретический, чем практический - просто эта тема сложная, чтобы даже иметь какой-то minimal viable product😱
Я много работал с двумя фреймворками, которые со всеми плюсами имели в некоторых местах очень неудобный процесс разработки:
1️⃣ Разработка "тасок" по триггеру
"Таска" это такая микро-программа на Python, которая должна запускаться по какому-то триггеру и делать что-то обычно небольшое: отправка email, создание тикетов, http-запрос в сервис, и прочее👦 Сервис выполняет эти программы на каком-нибудь свободном сервере.
Иногда таска может быть посложнее - например, создать и запустить другие таски и ждать их выполнения. В таких случаях исходная таска "замораживается" (переходит в состояние "Wait") и прекращает свою работу, а после финиширования дочерней таски возобновляет работу, снова на каком-нибудь свободном сервере🖥
Есть проблема - система не может как бы "продолжить" код таски с той точки, где было ожидание таски. Был придуман нереальный костыль, который выглядит примерно так:
В "контексте" можно ставить флажки, чтобы имитировать разные стадии таски, еще туда можно сохранять списки/строки/числа✍️
Наконец, оповещение сервису о том, что текущей таске надо дождаться другую таску, делается через бросание исключения (🤔
При таком подходе надо очень аккуратно писать код и много думать, если таске нужно делать что-то немного нетривиальное: например запуск дочерних тасок в цикле, пока одна из них не завершится успешно🙁 Попробуйте сами наколхозить такое, будет треш.
2️⃣ Микросервисы в Apphost
По ссылке открыто описано что такое Apphost - фреймворк для микросервисов (на Python или C++).
Он решает многие старые проблемы, но появляется новая проблема - теперь программа не может просто так сделать HTTP-запрос.
Нужно разбивать всю программу на микроскопические куски кода, которые принимают некие "входящие" объекты и формируют "исходящие", но не более того. Все походы во внешние сервисы делает Apphost. Эти куски кода выполняются на разных серверах.
Та часть, которая раньше делалась мгновенно, сейчас делается в 10 раз медленнее из-за настройки конфигов, жесткого обдумывания "графа" выполнения, и так далее.
❓ Использование корутин
Неприятности в сервисах, указанных выше, теоретически можно решить через корутины. Я когда-то писал о них в канале. Это функции, которые могут приостанавливать свое выполнение, а затем продолжать с той же точки.
На самом деле, проще всего изучать корутины в простых языках - здесь статья про корутины в Lua. Общий подход не меняется.
Потом здесь можно почитать, как корутины реализованы в C++ (начиная с C++20) - это очень сложные статьи, их можно читать несколько недель, зато появляется максимальное понимание, как компилятор преобразовывает код.
Суть идеи, которая решит проблемы, которые я описал - замороженные корутины: программа выполняется на одном сервере, потом останавливается, ожидает возобновления, и выполняется на каком-нибудь другом сервере с той точки, где была остановлена😎
ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ
Замороженные корутины
Am Ende bleib ich doch alleine
Но в конце я остаюсь один
Die Zeit steht still und mir ist kalt
Время остановилось, и мне холодно
Этот пост больше теоретический, чем практический - просто эта тема сложная, чтобы даже иметь какой-то minimal viable product
Я много работал с двумя фреймворками, которые со всеми плюсами имели в некоторых местах очень неудобный процесс разработки:
"Таска" это такая микро-программа на Python, которая должна запускаться по какому-то триггеру и делать что-то обычно небольшое: отправка email, создание тикетов, http-запрос в сервис, и прочее
Иногда таска может быть посложнее - например, создать и запустить другие таски и ждать их выполнения. В таких случаях исходная таска "замораживается" (переходит в состояние "Wait") и прекращает свою работу, а после финиширования дочерней таски возобновляет работу, снова на каком-нибудь свободном сервере
Есть проблема - система не может как бы "продолжить" код таски с той точки, где было ожидание таски. Был придуман нереальный костыль, который выглядит примерно так:
def on_execute(self):Сервис сохраняет "контекст" между разными запусками таски, а метод
if not self.Context.config_stage:
self.Context.config_stage = True
param1 = ...
param2 = ...
task = sdk.CreateTask(param1, param2, ...) # создаем дочернюю таску №1
raise sdk.WaitTask(task)
if not self.Context.run_stage:
self.Context.run_stage = True
param = ...
task = sdk.CreateTask(param, ...) # создаем дочернюю таску №2
raise sdk.WaitTask(task)
on_execute
каждый раз запускается сначала ♻️В "контексте" можно ставить флажки, чтобы имитировать разные стадии таски, еще туда можно сохранять списки/строки/числа
Наконец, оповещение сервису о том, что текущей таске надо дождаться другую таску, делается через бросание исключения (
raise
), чтобы это "прорвалось" за пределы def on_execute
(удобнее, чем просто return
, если вызываются всякие вложенные методы) При таком подходе надо очень аккуратно писать код и много думать, если таске нужно делать что-то немного нетривиальное: например запуск дочерних тасок в цикле, пока одна из них не завершится успешно
По ссылке открыто описано что такое Apphost - фреймворк для микросервисов (на Python или C++).
Он решает многие старые проблемы, но появляется новая проблема - теперь программа не может просто так сделать HTTP-запрос.
Нужно разбивать всю программу на микроскопические куски кода, которые принимают некие "входящие" объекты и формируют "исходящие", но не более того. Все походы во внешние сервисы делает Apphost. Эти куски кода выполняются на разных серверах.
Та часть, которая раньше делалась мгновенно, сейчас делается в 10 раз медленнее из-за настройки конфигов, жесткого обдумывания "графа" выполнения, и так далее.
Неприятности в сервисах, указанных выше, теоретически можно решить через корутины. Я когда-то писал о них в канале. Это функции, которые могут приостанавливать свое выполнение, а затем продолжать с той же точки.
На самом деле, проще всего изучать корутины в простых языках - здесь статья про корутины в Lua. Общий подход не меняется.
Потом здесь можно почитать, как корутины реализованы в C++ (начиная с C++20) - это очень сложные статьи, их можно читать несколько недель, зато появляется максимальное понимание, как компилятор преобразовывает код.
Суть идеи, которая решит проблемы, которые я описал - замороженные корутины: программа выполняется на одном сервере, потом останавливается, ожидает возобновления, и выполняется на каком-нибудь другом сервере с той точки, где была остановлена
ПРОДОЛЖЕНИЕ В КОММЕНТАРИЯХ
Please open Telegram to view this post
VIEW IN TELEGRAM
#cringe
Про SJW в C++ комьюнити🤡
Настало время обратить внимание на такую суперважную для Барнаула (Алтайский край) проблему, как борьбу за социальную справедливость в комьюнити по C++.
В начале 2022 года я отправил патч в Clang
После чего забил на него на месяц, а когда вернулся чтобы исправить его, то обнаружил что ревьюер забанен, хотя он профессионал - у него много статей, выступлений и тд.
Я написал ему на почту - что произошло и кто может отревьюить патч. Оказалось, что сообщество его "отменило" aka "закэнселило"🤡
В 2011 году он, видимо, последовал совету Трампа ("grab them by the pussy" ), после чего получил две уголовки и попал в почетный список Форбс "U.S.'s sex offender registry".
Зачем об этом знать и какое отношение это имеет к C++?
Оказывается (как он мне написал), в 2021 году группировка с includecpp.org об этом факте узнала и начала попытки "отменить" еб*ря-террориста со всего интернета, и оффлайн тоже.🔫
По этой причине он, например, не выступал на CppCon 2021 - для этого борцуны в твиттере угрожали Гербу Саттеру, а также выгнать Microsoft из комитета по C++.
И соответственно из LLVM его также выгнали - якобы по причине нарушение "Code of Conduct".
Далее произошел небольшой прикол (это было начало 2022 года)😁
- Я в ответ написал ему, что понимаю его - меня также "отменяют", ограничивая в правах пожрать в макдаке и оплатить онлифанс, хотя я даже не трогал маленьких девочек.
- На что он бомбанул и ответил в общих чертах, что тут как раз все правильно, SJW ни при чем, наши страны находятся в состоянии войны, тебя разъ*бут как Ирак, зато ты после этого получишь аналог Плана Маршалла😱
От такой щедрости я почувствовал мем с пингвином. Особо не ответил ему, но надеюсь что он больше не показывает своего Маршалла местным Лолитам😁
Однако вернемся к нашим маленьким фанатам экстремизма. Сайт includecpp.org выглядит максимально клоунским.
Достаточно посмотреть на "code of conduct" и пооткрывать твиттеры в ссылках под "the moderation and administration team includes", чтобы начать подозревать, что в западных странах есть проблемы с доступностью психотерапии для населения.
На мой взгляд, корнем проблемы является то, что комьюнити по C++ не объединено никаким бизнес-интересом - развитие языка и компиляторов не идет слишком быстро.
Из-за этого, когда толпе нечего делать, они находят всякие причины поссориться с кем-либо, и особенно крикливые объединяются в организованные группировки. В целом это оставляет плохое впечатление и желание дистанцироваться от любого рода "тусовок".
Похожие драмы происходят в Rust и прочих языках, и мне все-таки кажется что нормальный человек не будет таким заниматься.
Я пофантазировал и представил себе "Моральный кодекс C++-программиста в 2030 году", через несколько лет посмотрим насколько я попал🚬
1️⃣ Запрет паттерна проектирования "фабрика" и слов в коде как
2️⃣ Одобрямс легализации насвая в стране, истинная вера в то что это "менее вредно чем алкоголь", прослушивание учёных которые доказывают это "научными фактами"
3️⃣ Подписывание бумажки "я признаю Rust Foundation террористической организацией за вторжение в Linux Kernel и оккупацию 20% Stackoverflow"
4️⃣ Переменные наподобии номера гендера хранить в int16, потому что int8 не будет хватать
5️⃣ Не менее 28.2% C++-программистов должны составлять иностранные специалисты из беднейших регионов мира, с такой же зарплатой как у всех
6️⃣ Мультиязычность - одна часть проекта должна быть на C++, другая на Python, третья на Delphi, четвертая на JavaScript и т.д. Реклама, что такой подход делает проект более технологично богатым. Всякие утверждения, что некоторые языки не совместимы друг с другом и что моноязычный C++-проект проще разрабатывать - языковой шовинизм.
7️⃣ Из-за того, что комитет по C++ когда-то попытался поглотить комитет по C, каждый C++-программист теперь должен платить репарации C-программистам. За утверждения, что C и C++ родственные языки, имеющие общую историю и происхождение, которые совместимы в одном проекте, следует немедленный кэнселинг.
Про SJW в C++ комьюнити
Настало время обратить внимание на такую суперважную для Барнаула (Алтайский край) проблему, как борьбу за социальную справедливость в комьюнити по C++.
В начале 2022 года я отправил патч в Clang
После чего забил на него на месяц, а когда вернулся чтобы исправить его, то обнаружил что ревьюер забанен, хотя он профессионал - у него много статей, выступлений и тд.
Я написал ему на почту - что произошло и кто может отревьюить патч. Оказалось, что сообщество его "отменило" aka "закэнселило"
В 2011 году он, видимо, последовал совету Трампа (
Зачем об этом знать и какое отношение это имеет к C++?
Оказывается (как он мне написал), в 2021 году группировка с includecpp.org об этом факте узнала и начала попытки "отменить" еб*ря-террориста со всего интернета, и оффлайн тоже.
По этой причине он, например, не выступал на CppCon 2021 - для этого борцуны в твиттере угрожали Гербу Саттеру, а также выгнать Microsoft из комитета по C++.
И соответственно из LLVM его также выгнали - якобы по причине нарушение "Code of Conduct".
Далее произошел небольшой прикол (это было начало 2022 года)
- Я в ответ написал ему, что понимаю его - меня также "отменяют", ограничивая в правах пожрать в макдаке и оплатить онлифанс
- На что он бомбанул и ответил в общих чертах, что тут как раз все правильно, SJW ни при чем, наши страны находятся в состоянии войны, тебя разъ*бут как Ирак, зато ты после этого получишь аналог Плана Маршалла
От такой щедрости я почувствовал мем с пингвином. Особо не ответил ему, но надеюсь что он больше не показывает своего Маршалла местным Лолитам
Однако вернемся к нашим маленьким фанатам экстремизма. Сайт includecpp.org выглядит максимально клоунским.
Достаточно посмотреть на "code of conduct" и пооткрывать твиттеры в ссылках под "the moderation and administration team includes", чтобы начать подозревать, что в западных странах есть проблемы с доступностью психотерапии для населения.
На мой взгляд, корнем проблемы является то, что комьюнити по C++ не объединено никаким бизнес-интересом - развитие языка и компиляторов не идет слишком быстро.
Из-за этого, когда толпе нечего делать, они находят всякие причины поссориться с кем-либо, и особенно крикливые объединяются в организованные группировки. В целом это оставляет плохое впечатление и желание дистанцироваться от любого рода "тусовок".
Похожие драмы происходят в Rust и прочих языках, и мне все-таки кажется что нормальный человек не будет таким заниматься.
Я пофантазировал и представил себе "Моральный кодекс C++-программиста в 2030 году", через несколько лет посмотрим насколько я попал
work
, job
и прочее, так как это угнетает права безработныхPlease open Telegram to view this post
VIEW IN TELEGRAM
C++95
billy.png
#books
Обзор книги "Practical Binary Analysis" (2019 г.) 📚
(можно скачать PDF тут)
На фотографии внештатный корреспондент паблика "C++95" Билли, прочитавший данную книгу.
Его отзыв: "Oh yeah, это точно стоит three hundred bucks! ASM WE CAN!"📞
Я давно ничего не писал из-за занятости, но сейчас смог🫡
Эта книга посвящена разнообразному анализу бинарников, то есть исполняемых программ без соответствующего исходного кода. В ней очень много информации, больше чем в любой другой книге из #books - ее реально читать неделями.
Там есть знания, которые могут пригодиться разве что reverse engineers и vulnerable researchers, но и другим тоже может быть интересно🤔
В1️⃣ главе дается анатомия бинарника и как он вообще появляется на свет - как в книге про компиляцию.
Во2️⃣ и в 3️⃣ главах описываются форматы исполняемых файлов ELF (на Linux) и PE (на Windows)
В4️⃣ главе строится своя тулза для загрузки бинарника с помощью libbfd. В книге вообще есть упор на создание собственных анализаторов бинарников.
В крутой5️⃣ главе решается задача из CTF по поиску "флага" в бинарнике, на примере которой применяются десятки подходов и тулз, чтобы шарить еще лучше.
В6️⃣ главе, которая на мой взгляд является центральной, показывается фундамент анализа и дизассемблирования 🖥
Там народу являют жестокую правду - в общем случае нет какого-то единого алгоритма дизассемблирования.
Более тупые дизассемблеры (как objdump) имеют линейный алгоритм - тупо расшифровывают ассемблерные команды от начала до конца.
Так как компиляторы могут встраивать вспомогательные данные прямо в код (особенно Visual Studio), то можно например напороться на jump table. По факту это список адресов, но дизассемблер об этом не знает и нарасшифрует в этом месте ассемблерные команды👍
Ничего не упадет, потому что в x86 "плотный" набор команд и практически любая последовательность байтов представляет собой валидный ассемблер👍
Более умные дизассемблеры (как IDA Pro) имеют рекурсивный алгоритм - они расшифровывают как бы настоящий код и только те места, куда управление доказанно может перейти, например через jmp. Там свои сложности с неявным control flow, когда в коде есть jump table или vtable, которые неявно указывают куда передавать управление, но это решаемо.
Дизассемблеры восстанавливают функции довольно условно (в ассемблере "функции" это вообще слишком большая абстракция) - оптимизирующий компилятор может так навертеть, что код принадлежащий функции может оказаться размазанным по всему бинарнику, и разные "функции" могут переиспользовать какой-то один кусок ассемблера👍
Чтобы определить, где находится условная "функция", дизасм ищет "сигнатуры", то есть паттерны ассемблерных инструкций, которые часто используются в начале/конце функции.
Пример сигнатуры в неоптимизированном x86 это "пролог"
Очень сложно восстанавливать структуры данных (C/C++), дизасмы часто не пытаются этого делать☔️
Для более простого анализа часто используется промежуточное представление (IR, Intermediate Representation) ассемблера - например, вместо исходника где могут быть сотни разновидностей инструкций x86/ARM, можно получить IR где единицы разновидностей. IR бывает несколько, например REIL.
Дизасм восстанавливает код, который делает примерно то же, что ассемблер. Обычно выглядит он все равно довольно ужасно, но дизасмы стараются🥺 Например, по эвристикам (то есть через три костыля) определяются простые циклы (for/while), математические формулы, и прочее.
ПРОДОЛЖЕНИЕ В КОММЕНТАРИИ
Обзор книги "Practical Binary Analysis" (2019 г.) 📚
(можно скачать PDF тут)
На фотографии внештатный корреспондент паблика "C++95" Билли, прочитавший данную книгу.
Его отзыв: "Oh yeah, это точно стоит three hundred bucks! ASM WE CAN!"
Я давно ничего не писал из-за занятости, но сейчас смог
Эта книга посвящена разнообразному анализу бинарников, то есть исполняемых программ без соответствующего исходного кода. В ней очень много информации, больше чем в любой другой книге из #books - ее реально читать неделями.
Там есть знания, которые могут пригодиться разве что reverse engineers и vulnerable researchers, но и другим тоже может быть интересно
В
Во
В
В крутой
В
Там народу являют жестокую правду - в общем случае нет какого-то единого алгоритма дизассемблирования.
Более тупые дизассемблеры (как objdump) имеют линейный алгоритм - тупо расшифровывают ассемблерные команды от начала до конца.
Так как компиляторы могут встраивать вспомогательные данные прямо в код (особенно Visual Studio), то можно например напороться на jump table. По факту это список адресов, но дизассемблер об этом не знает и нарасшифрует в этом месте ассемблерные команды
Ничего не упадет, потому что в x86 "плотный" набор команд и практически любая последовательность байтов представляет собой валидный ассемблер
Более умные дизассемблеры (как IDA Pro) имеют рекурсивный алгоритм - они расшифровывают как бы настоящий код и только те места, куда управление доказанно может перейти, например через jmp. Там свои сложности с неявным control flow, когда в коде есть jump table или vtable, которые неявно указывают куда передавать управление, но это решаемо.
Дизассемблеры восстанавливают функции довольно условно (в ассемблере "функции" это вообще слишком большая абстракция) - оптимизирующий компилятор может так навертеть, что код принадлежащий функции может оказаться размазанным по всему бинарнику, и разные "функции" могут переиспользовать какой-то один кусок ассемблера
Чтобы определить, где находится условная "функция", дизасм ищет "сигнатуры", то есть паттерны ассемблерных инструкций, которые часто используются в начале/конце функции.
Пример сигнатуры в неоптимизированном x86 это "пролог"
push ebp; mov ebp,esp
и "эпилог" leave; ret
Есть и более сложные кейсы, когда делается "tail call", а оптимизированные функции могут не иметь пролога и/или эпилога, поэтому ошибок может быть от 20% и больше.Очень сложно восстанавливать структуры данных (C/C++), дизасмы часто не пытаются этого делать
Для более простого анализа часто используется промежуточное представление (IR, Intermediate Representation) ассемблера - например, вместо исходника где могут быть сотни разновидностей инструкций x86/ARM, можно получить IR где единицы разновидностей. IR бывает несколько, например REIL.
Дизасм восстанавливает код, который делает примерно то же, что ассемблер. Обычно выглядит он все равно довольно ужасно, но дизасмы стараются
ПРОДОЛЖЕНИЕ В КОММЕНТАРИИ
Please open Telegram to view this post
VIEW IN TELEGRAM