Обновить
128K+

C++ *

Типизированный язык программирования

205,77
Рейтинг
Сначала показывать
Порог рейтинга

Мы кое-что спрятали 👀

В честь выхода PVS-Studio 8.0 — а это поддержка новых языков и серьёзные улучшения анализатора — мы решили устроить небольшую охоту.

Нами спрятано несколько пасхалок. Всем, кто активно использует анализатор, будет несложно отыскать хотя бы одну из них. Если же вы еще не пробовали проверять свой проект, самое время. Забирайте пробную лицензию и участвуйте в охоте.

Первые 10 человек, которые найдут спрятанные нами пасхалки и напишут нам, получат две бумажные книги по C++ в подарок. Не забудьте указать в сообщении, где вы отыскали нашу пасхалку.

Как искать? Просто! Скачивайте PVS-Studio, анализируйте свой код — и, возможно, найдёте кое-что интересное по дороге.

👉 Подробные условия и как забрать приз — здесь

Теги:
+3
Комментарии0

Я создала красивый GUI для Qemu для запуска древних MacOS начиная с версии 9.0 до 10.5 Leopard. Для удобной эмуляции для простых пользователей ПК. Доступен для всех Linux, в том числе Raspberry Pi, chromeOS, SteamDeck и Steam Machine. Программа создана на Qt6 и C++, из-за чего программа потребляет максимум полтора килобайта ОЗУ.

Я не просто создала программу и выложила на GitHub и всё, я настроила автоматическую сборку бинарного файла на сервере, поэтому вам не нужно компилировать. Бинарная сборка в одном файле AppImage доступна для всех. Работает как минимум на Debian 13 (я проверяла), так что программа будет работать и на Ubuntu, и на Fedora, и на ArchLinux

Теги:
+7
Комментарии1

Привет, Хабр!

Я хотел бы рассказать о моем главном проекте - операционная система. Заранее прошу сильно не судить, это моя первая статья на хабаре :)

Операционной системе я дал название FuturaOS. Она является проприетарной ос с собственной файловой системой, оконным менеджером и бесконечным холстом. Несмотря на то, что проект базируется на коде ToaruOS (которая, в свою очередь, черпает вдохновение из идей UNIX), файловая система, загрузчик, экосистема приложений, графический стек и уникальные подсистемы FuturaOS разработаны с нуля и закрыты. Также база ToaruOS была очень сильно переделана.

Одна из главных фич - это Infinite Mode, бесконечный холст в духе Figma, который работает на собственном языке разметки IDSL (возможно в будущем я смогу рассказать подробнее про этот простой язык разметки). Вместо статичных обоев у нас живая солнечная система: планеты с пиксельными текстурами, орбитами, системами освещения. Причем это не просто картинка - объекты масштабируются с при зуме и двигаются вокруг Солнца на своих орбитах. На данный момент ядро поддерживает три режима работы с окнами: классический, тайлинг для продуктивности и упомянутый Infinite. Композитор построен на собственной реализации Yutani, а оконный менеджер позволяет переключаться между режимами прямо на лету, без перезагрузки сессии.

Отдельного упоминания заслуживает проделанный огромный путь по поддержке железа. Мы смогли запустить Doom и Quake через аппаратный рендеринг (DRM/KMS), хотя в самом стеке еще много работы. Порты USB 3.0 работают стабильно, а USB 2.0 находятся в разработке (как бы странно это не звучало - спустя месяцы работы у меня заработали порты SS, однако порт 2.0 на данный момент не получает питание на моем устройстве). Звуковая подсистема построена на HDA и работает. Также реализован свой загрузчик, работающий напрямую с UEFI, без GRUB и без VESA-заглушек, что дает четкий и быстрый вывод изображения без ошибок.

Ключевая особенность системы — это собственная файловая система FFS (Futura File System), которая поддерживает шифрование, Copy-on-Write и снапшоты. Это не просто абстракция над ext4, а самостоятельный проект с уникальными свойствами, как у ZFS или Btrfs, но заточенный под небольшие и быстрые устройства.

Для разработчиков у нас есть SDK, позволяющий собирать сторонние приложения, а пакеты распространяются в формате .fos. Программа установки пакетов и магазин приложений уже присутствуют, хотя ещё является незаконченной.

Сейчас самым острым вопросом остается поддержка полноценных Wi-Fi и Bluetooth адаптеров. На данный момент Wi-Fi стек работает в бета-режиме с поддержкой ряда чипсетов и уже умеет сканировать сети, но полноценное подключение к WPA2-сетям требует доработки.

Впереди у нас ещё много работы: планируется расширить поддержку ACPI, сделать мониторинг температуры и заряда батареи, а также включить кроссплатформенные жесты для тачпада и нативной поддержки USB-адаптеров

Теги:
+4
Комментарии9

Элегантность кода благодаря C++23

Продолжим разбирать приёмы рефакторинга и посмотрим, как std::ranges::views::enumerate помогает сделать код более элегантным.

В статье "C++: Пиши, сокращай, оптимизируй" я рассматривал рефакторинг и оптимизацию кода за счёт объединения циклов. В итоге я остановился на следующем варианте кода:

static TensorImpl make_contiguous_tensor(const std::vector<int64_t>& sizes)
{
  auto q = sizes.size();
  std::vector<int64_t> strides(q);
  int64_t acc = 1;
  int64_t ne = 1;
  for (auto sz : std::ranges::views::reverse(sizes))
  {
    strides[--q] = acc;
    acc *= (sz == 0 ? 1 : sz);
    ne *= sz;
  }
  //....
}

Его недостаток в том, что всё равно необходимо использовать переменную q для работы с контейнером strides. Как можно написать ещё лаконичнее, я не сообразил, но такой способ есть!

После публикации мне подсказали про enumerate. Эта штука появилась в C++23, и я как-то её пропустил. Сложно уследить за всеми нововведениями C++.

С помощью enumerate можно сразу перебирать и элементы, и их индексы:

constexpr static auto v = {'A', 'B', 'C', 'D'};
for (auto const [index, letter] : std::views::enumerate(v))
    std::cout << '(' << index << ':' << letter << ") ";

Будет напечатано: (0:A) (1:B) (2:C) (3:D).

Но нам нужен обратный порядок, и такой вариант не подходит:

for (auto const [i, sz] :
  std::views::enumerate(
    std::ranges::views::reverse(sizes)))

Этот цикл будет перебирать элементы с конца, а индексы — по возрастанию от 0. Можно сделать, чтобы значения i также шли в обратном порядке? Можно. Это делается с помощью std::views::enumerate(sizes) | std::views::reverse.

В итоге можно сократить код ещё на одну строчку:

static TensorImpl make_contiguous_tensor(const std::vector<int64_t>& sizes)
{
  std::vector<int64_t> strides(sizes.size());
  int64_t acc = 1;
  int64_t ne = 1;
  for (auto const [i, sz] : std::views::enumerate(sizes) | std::views::reverse)
  {
    strides[i] = acc;
    acc *= (sz == 0 ? 1 : sz);
    ne *= sz;
  }
  //....
}

Нельзя назвать это большим достижением, но зато на практике применили одну из новых возможностей C++23.

Что со скоростью кода? Замер показал, что в рамках погрешности его скорость не изменилось. Т.е. этот вариант работает так же, как вариант №2 — "Мой вариант одним циклом" (см. замер скорости работы в статье). Это не удивительно, так как по сути код идентичен предыдущему.

Спасибо за внимание и подписывайтесь на наш дайджест, чтобы не пропустить интересное. Например, у нас грядёт выпуск PVS-Studio 8.0 с поддержкой новых языков.

Теги:
+4
Комментарии0

Представлен первый релиз эмулятора терминала под необычным названием Shitty. Автор проекта считает его «серьёзным эмулятором терминала с глупым названием». Код решения написан с помощью ИИ-ассистента на C++23 (сbundled libstd, требующей -std=c++26) и распространяется под двойной лицензией MIT и GPL-3.0. Поддерживаются macOS и Linux.

Проект делает ставку на обеспечение низких задержек, быстрого запуска и предсказуемого потребления ресурсов: состояние терминала обрабатывается на CPU, а отрисовка выполняется через бэкенды на базе Vulkan в Linux и Metal в macOS, без использования стороннего графического тулкита. При тестировании производительности вывод 100 МБ ASCII через терминал Shitty показывает ~118 МБ/с, обгоняя alacritty (0.81 с против 0.96 секунд), kitty и ghostty. На «случайных байтах» с некорректным UTF-8 отрыв от alacritty ещё заметнее (~51 МБ/с против ~31 МБ/с). Корректность работы Shitty обеспечивается более чем 5000 тестами, собранными из десятка с лишним внешних наборов — kitty, esctest, vttests из xterm, vttest, tack, libvterm, libtsm, alacritty, ghostty, contour, konsole, mosh. Тесты выполняются с проверкой работы на реальном PTY.

Теги:
+4
Комментарии1

Всем привет. Потребовались для моего градостроя дороги, которые можно было бы удобным образом по всякому пересекать и под разными углами строить и чтобы не было это большой проблемой. В целом, все сделано было успешно, но остался артефакт (после гуглежа и обсуждения с нейронкой как я понимаю, стандартный)возникающий на перекрестках и в целом на узлах, где дороги пересекаются (на дорогу накладывается PBR материал - тайлящиеся текстуры. Я пытался сделать сверху “заплатки” и как то замаскировать это непотребство, но все стало еще хуже - наиболее отчетливо это видно если переключить отображение в режим UV. Что с этим делать, как побеждать?

image
image

Визуально в обычном режиме это конечно выглядит ужасно… Тем более непонятно, что дальше делать с разметкой перекрестка?

Теги:
Всего голосов 1: ↑1 и ↓0+3
Комментарии0

Ошибки, которых не видно в сигнатуре: зачем C++ нужен std::expected

Функция выглядит предсказуемо, пока один из вызовов не бросает исключение, о котором никто не вспомнил. Типы ошибок остаются в документации, управление расползается по try/catch, а изменение глубоко в стеке неожиданно ломает обработку выше.

Недавно в статье разобрали std::expected из C++23: как сделать ошибку частью сигнатуры, собирать цепочки через and_then и transform, разделять типы ошибок между слоями и постепенно внедрять подход в legacy-код. Заодно увидели, где std::expected действительно полезен, а где добавит лишнюю сложность.

16 июля в 20:00 продолжим тему на бесплатном уроке «Выразительный C++: кодируем намерения». На практике разберём, как переносить неявные договорённости в типы, сигнатуры и структуру программы. Присоединяйтесь.

Полный список бесплатных уроков июля смотрите в дайджесте.

Теги:
Всего голосов 4: ↑4 и ↓0+8
Комментарии1

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

Я запустил скрапер вечером, он собрал пару тысяч страниц, я ушёл спать. Утром в логах сплошные 403 и капчи, а в базе за ночь легло страниц двести. Ок, думаю, прокси спалили. Поменял пул на резидентный, накинул puppeteer-stealth, сверху ещё пачку заплаток с гитхаба. Проработало ровно до следующего вечера. Потом опять тыква.

Так продолжалось полгода. В какой-то момент мне надоело в сотый раз докидывать заплатку, и я решил разобраться, что конкретно меня палит. Не прокси. Не поведение. Сам браузер.

Осмотрим пациента

Любой стелс устроен одинаково: поверх Chrome вешается слой JavaScript, который переписывает то, на что смотрит антибот. navigator, canvas, WebGL и ещё десятки поверхностей. И вот тут первая засада.

Берём подменённую функцию и просим показать её исходник через toString(). Настоящая функция браузера отвечает [native code]. А моя заплатка честно показывает мой же JavaScript. Спалился на первой строчке.

Ладно, стелс это тоже патчит. Но детектор не дурак и лезет глубже.

  • Сверяет главный фрейм с воркером и iframe. Заплатка живёт в одном контексте, а тот же объект, вытащенный из другого, её не видит.

  • Ловит утечку Runtime.enable, по ней сразу понятно, что браузером кто-то рулит по CDP.

  • Смотрит на форму TLS-хендшейка (JA3/JA4) и сверяет её с тем, что обещает User-Agent. Заявляешь Chrome на Windows, а рукопожатие выдаёт питоновский клиент.

  • Перерисовывает один и тот же холст дважды и смотрит, стабилен ли отпечаток, или заплатка подмешивает случайный шум, которого у настоящего железа нет.

Где-нибудь да разъедется. И так будет всегда, потому что я крашу браузер сверху, а не меняю его изнутри. Это не «кривая заплатка», это тупик подхода: маскировка и то, что её просвечивает, написаны на одном языке. Тот, кто читает страницу, всегда на уровень ниже того, кто патчит её из той же страницы.

Вскрытие

Раз проблема в слое поверх браузера, надо убрать слой и лезть в сам браузер. Я взял исходники Chromium и пошёл править фингерпринт прямо в C++, в Blink, V8 и BoringSSL.

Идея простая. Каждое значение, которое читает детектор, должно быть не подменено сверху, а просто другим внутри, ровно как оно было бы на чужой машине. Тогда никакого слоя нет. Подменённый геттер это настоящий C++ геттер, поэтому toString() в любом реалме честно отдаёт [native code]. Сверка «фрейм против воркера» ничего не находит, потому что находить нечего: значение одно и то же везде, оно вкомпилировано в движок. Браузер, который изучает сам себя, видит обычный Chrome. Потому что это и есть обычный Chrome, просто с другими числами внутри.

Что заработало, а что нет

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

А что заявляю, можно проверить руками, не веря мне на слово. Я прогнал сборку по публичным

  • CreepJS: 0% headless, 0% stealth.

  • Sannysoft: всё зелёное.

  • BrowserScan: пишет «Normal».

  • Живой Cloudflare Turnstile: проходит без клика мышкой.

Всё это воспроизводится одним скриптом tools/gauntlet.py из репозитория, так что можно прогнать самому и убедиться. Персона при этом собирается когерентно: платформа, GPU, таймзона, язык, набор голосов, раскладка клавиатуры и форма TLS съезжаются в один правдоподобный Windows-девайс, а не в ме

По коду менять нечего

Движок поднимает сырой CDP на порту 9222, без утечки Runtime.enable. Я цепляю к нему свой ж остальной код работает как работал. Меняется одна строчка, адрес подключения.

Патчей тридцать четыре, каждый маленький и на одну задачу, читается за минуту. Всё открыто единому слову, соберите движок сами и проверьте, что внутри ровно то, что заявлено.

github.com/tiliondev/fortress

Теги:
Всего голосов 3: ↑3 и ↓0+5
Комментарии0

Почему ИИ-агент для кода промахивается мимо нужного метода

Несколько последних постов об ИИ-агентах, которые я читала на этой неделе, сошлись в одну мысль: агенты часто ведут себя по-разному на одной и той же задаче, и разгадка почему - находится не там, где ожидаешь. Вот конкретный пример, на котором это видно сразу.

Агент, который ищет код текстом, физически не отличает OrderService.validate от UserDto.validate: для него это просто совпадение символов «validate». Агент, который спрашивает у IDE «кто на самом деле вызывает этот метод», получает точный ответ, потому что IDE знает типы, разрешённые ссылки и видимость каждого символа. Разница между этими двумя подходами не абстрактная, она измеряется в конкретных цифрах.

Точка отсчёта: почему grep и RAG промахиваются

Кодовый агент ищет по проекту двумя способами: grep/ripgrep по содержимому файлов и векторный поиск по эмбеддингам кусков кода. На маленьком проекте оба варианта работают сносно. На enterprise-репозитории на миллионы строк с десятками модулей поведение ломается одинаково: запрос «найди использования метода validate» возвращает тысячу с лишним совпадений, из которых подавляющее большинство - другие методы с тем же именем в других классах. Векторный поиск находит куски кода, которые «похожи по смыслу», но это может быть валидация в совершенно другом домене.

Что показали цифры на 27 задачах

Мы сравнили три версии агента на 27 задачах вида «найди все места, где используется метод X» из реальных тикетов трёх внутренних репозиториев на Java и Kotlin: агент на ripgrep, агент на ripgrep с векторным RAG и агент на PSI-индексе IntelliJ (find_usagesfind_declarationclass_hierarchy вместо текстового поиска).

Вариант поискаPrecisionRecallF1Контекст, токенов$/ответripgrep0,410,820,5578 4001,12ripgrep + векторный RAG0,580,790,6792 1001,38PSI-индекс0,960,930,9416 7000,21
Вариант поискаPrecisionRecallF1Контекст, токенов$/ответripgrep0,410,820,5578 4001,12ripgrep + векторный RAG0,580,790,6792 1001,38PSI-индекс0,960,930,9416 7000,21

Precision показывает, сколько из найденного агентом оказалось реальными использованиями метода, а не случайным совпадением имён. Recall показывает, сколько из всех реальных использований в коде агент вообще нашёл, ничего не пропустив.

У ripgrep recall высокий (0,82): текстовый поиск почти никогда не пропускает совпадения по имени. Но precision низкий (0,41): больше половины найденного - чужие методы с тем же именем, и их приходится разбирать вручную. У PSI-индекса высоки обе величины (0,96 и 0,93): агент находит почти все нужные места и почти не приносит лишнего. F1 сводит обе величины в одно число, и у ripgrep он проседает именно из-за мусора в выдаче, хотя нужные места он и находит.

Отсюда пятикратная экономия контекста и в пять с лишним раз более дешёвый ответ при кратно более высокой точности: агент на PSI-индексе не читает тысячи лишних строк, которые вернул бы текстовый поиск.

Точный поиск не отменяет проверку

Даже когда агент точно нашёл все места, где используется метод, и внёс правку во все, это не гарантирует, что правка корректна: точный поиск решает только задачу «найти нужные места», а не «проверить, что код после правки работает». Поэтому прежде чем объявить об успехе, агент должен прогнать компилятор и тесты: тест зелёный, ошибок компиляции нет, покрытие не просело. Пока в выводе компилятора или тестового прогона есть хоть одна ошибка, агент обязан либо исправить её, либо сообщить о проблеме, а не писать об успехе.

Но точный поиск снимает с агента часть ручной работы: не нужно самому перепроверять по всему проекту, все ли использования метода он нашёл - эту проверку берёт на себя IDE, как это устроено в Veai, а оставшуюся часть верификации - компилятор и тесты.

Теги:
Всего голосов 8: ↑8 и ↓0+10
Комментарии2

Представлен открытый сервис NtWARden (Windows Analysis and Research Toolkit), который распознает любые вредоносы и проблемное ПО, даже если эти компоненты находятся глубоко в системе Windows.

Проект NtWARden:

  • сканирует процессы, службы, сеть, внутренние механизмы ядра;

  • обнаруживает скрытые вредоносы;

  • убивает майнеры и трояны;

  • показывает реальную картину нагрузки на процессор;

  • помогает контролировать, что находится в системе прямо сейчас: службы, сетевые соединения, скрытые процессы;

  • может подключиться к другому ПК и также отслеживать его процессы.

Теги:
Всего голосов 5: ↑5 и ↓0+8
Комментарии2

Как все так ловко ИИ пользуются... Обзоры кода делают...

Не знаю, то ли я туповат, то ли невезучий, то ли кругом сказочники. Пытаюсь исследовать, какие новые интересные виды ошибок может находить ИИ в С++ коде, о которых не знает PVS-Studio. Claude Opus то нормально мне про интересные баги рассказывает, то на ровном месте с ума сходит.

Даю файл на вход, а он тревожно:

В присланном файле есть места, где содержимое обрезано/повреждено вставкой </context>, поэтому их я не рассматриваю как «баги кода» — это артефакты форматирования. Разберу реальные логические ошибки.

Шо? Какой context? Там максимум — это #include "context.hxx". Ну ладно, давай ошибки посмотрим.

Так, парочка сообщений по делу. Например, найден лишний аргумент при использовании функции форматирования.

addErrorMessage(&sciErr, API_ERROR_CREATE_ZDOUBLE_IN_NAMED_LIST,
    _("%s: Unable to create list item #%d in Scilab memory"),
    "createComplexZMatrixOfDoubleInNamedList", _iItemPos + 1, _pstName);

В строке форматирования два спецификатора %s и %d, а передаются три аргумента: funcName, _iItemPos+1, _pstName. Да, это баг. Про подобные паттерны PVS-Studio уже знает, хотя, возможно, нужно подсказать, что addErrorMessage — это printf-подобная функция. В целом, тут неинтересно, идём дальше.

Ооо, и тут начинается...

checkListItemPosition — неправильное сравнение границы:

// check a valid item
if (_iItemPos > iItem)
{
    addErrorMessage(...);
    return sciErr;
}

Здесь проверка «позиция валидна» использует >, хотя позиции 1-based, а iItem — количество элементов. Это выглядит как граничный дефект, но по сути _iItemPos == iItem допустимо, _iItemPos > iItem — ошибка. Это в порядке — правильная граница. Не баг. (Указываю, чтобы было понятно, что я это проверил.)

Так баг или не баг? Я три раза прочитал текст и посмотрел функцию с кодом. В итоге я не только не понимаю, есть ли здесь ошибка, но и не понимаю, что Claude пытается мне сказать.

Дальше ещё хуже шарики за ролики зашли.

Баг 5 (реальный): утечка в readCommonMatrixOfPolyInNamedList и др. — нет, это API отдаёт указатели наружу. Не баг.

Это всё описание пятого бага, больше ничего нет. Комментировать — только портить.

Я не понимаю, как на всём этом можно строить хоть сколько-то надёжные процессы автоматических обзоров кода, про которые в последнее время из разных утюгов вещают.

На сегодня мне ИИ достаточно. Пойду лучше пройдусь по парку, пока погода хорошая.

Теги:
Всего голосов 14: ↑14 и ↓0+18
Комментарии6

Нарезка кода: что скрывает OrcaSlicer

Как не пропустить опасные участки кода при ревью? Можно воспользоваться инструментами статического анализа. Возьмём для примера OrcaSlicer — популярную программу, которая подготавливает 3D-модель к печати. Заглянем внутрь и посмотрим, какие сюрпризы нас ждут.

Посмотрим на одно из предупреждений статического анализатора PVS-Studio:

V1047 Lifetime of the lambda is greater than lifetime of the local variable ‘do_stop’ captured by reference. FillBedJob.cpp 250

void FillBedJob::process(Ctl &ctl)
{
  // ....
  bool do_stop = false;
  // ....
  params.on_packed = 
    [&do_stop] (const ArrangePolygon &ap)
    {
      do_stop = ap.bed_idx > 0 && ap.priority == 0;
    };
  // ....
}

Лямбда-выражение захватывает локальную переменную do_stop по ссылке, а затем сохраняется в params.on_packed. При этом do_stop уничтожается при выходе из метода process, так как заканчивается время жизни локального объекта. Если лямбда будет вызвана после выхода из этой функции-члена, произойдёт обращение к разрушенному объекту, и поведение в этой ситуации не определено.

Можно было бы сделать захват по значению, но в этой лямбде происходит перезапись переменной do_stop, а значит такой вариант не подходит. Поэтому можно сделать do_stop членом класса FillBedJob.

Это лишь один фрагмент, показывающий, что даже в работающем продукте могут быть ошибки. А другие опасные места в коде OrcaSlicer разобрали в новой статье.

Если вы тоже хотите минимизировать ошибки в своих проектах, сделайте статический анализ частью регулярного процесса, а поможет с этим PVS-Studio.

Теги:
Всего голосов 3: ↑3 и ↓0+6
Комментарии0

Илон Маск отдал приоритет видеокамерам. Радар и ультразвуковые датчики были ошибкой, от них больше помех

Мы тоже часто спорим с коллегами о выборе средства распознавания объектов. С одной стороны, лидары и ультразвук лучше камер работают в темное время суток, что безусловный плюс. С другой, освещение в 21 веке – не экзотика или всегда можно применить ИК-прожекторы. Сегодняшние нейросети неплохо справляются и в ночных условиях. Тем более, что полной темноты в наше время не бывает, видеокамерам хватает света даже от сигареты.

Сегодняшние нейросети совершают революцию в видеонаблюдении, распознавая даже мелкие объекты и даже в темноте. Наверное, поэтому Илон Маск в интервью про свои беспилотники высказал буквально следующее:

«Честно говоря, радар и ультразвуковые датчики были ошибкой. Особенно радар. Потому что радар позволяет приблизиться к решению задачи и решать задачу в большинстве случаев, кроме тех моментов, когда невозможно соединить радар и нейросеть для визуального распознавания. То есть радар и “зрение” расходятся - кто прав? По сути, нужно избавляться от радара. И как только мы избавились от радара - и кстати команда автопилота была сильно против такого решения - теперь никто не хочет возвращать радар.

Мне пришлось настоять на своем. Радару тут не место. Радар был костылем, и, если тащить костыль за собой, бежать не получится. Радар - просто сигнал. В конечном счете от радара было больше помех, чем сигналов. Иногда это было очень полезно, но помех от него было больше. От него нужно было избавляться. Как только мы убрали радар, стало очевидно, что наши нейросети гораздо хуже, чем мы думали. Радар им слишком сильно помогал. Использование компьютерного зрения предполагает, что ни на что больше полагаться нельзя, оно должно работать. Нейросеть должна работать. Большим изменением стал переход от алгоритма «Bag of points» к его интерпретации нейросетью для определения центра полосы. 

Раньше мы все это делали на «Си». Сейчас архитектура нейросети очень сложна, там много слоев. Какие-то слои удаляются, какие-то добавляются. Мы уже столько раз меняли архитектуру.”

Доступно на Ютьюб

Кстати, программисты Спецлаб тоже, в основном, всё разрабатывают на “C++”. И тоже предпочитают использование видеокамер для объяснения компьютеру окружающего мира.

Даниил Гришанин
=Спецлаб=

Теги:
Всего голосов 2: ↑2 и ↓0+4
Комментарии2

Ближайшие события

Листая на C++ Reference список принятых в C++29 фичей, увидел в нем пропозал с знакомым названием, «Thread attributes» (P2019R9). И, оказалось, действительно, я уже читал этот пропозал 4 года назад, но не в 9-й его редакции, а в самой первой. 4 года понадобилось комитету по стандартизации, чтобы принять пустячный пропозал, позволяющий задать имя и размер стека потока при его создании — востребованную фичу, реализованную в куче библиотек C++.

void f();
int main() {
  // Такой вид задания атрибутов предлагался в первой ревизии
  std::jthread P2019R1(
    f, std::thread_name("Worker"),
    std::thread_stack_size(512*1024));
  // А такой приняли 8 ревизий спустя
  std::jthread P2019R9(
    std::thread::name_hint("Worker"),
    std::thread::stack_size_hint(512*1024), f);
}

Не безумие ли это? И сколько действительно правильных пропозалов не вошло в C++ лишь по той причине, что их автор не был готов 4 года подряд защищать свое предложение, отвечая на все мелкие придирки различных подкомитетов?

Теги:
Всего голосов 5: ↑5 и ↓0+8
Комментарии3

Риски ИИ- генерируемого кода - качество, производительность, “УСПЕВАЮ ЛИ Я ЗА ТЕМПОМ” 

ИИ генерирует код быстрее, чем мы успеваем ловить его реальные проблемы.
В JPA/Hibernate это особенно больно: код компилируется, тесты зелёные — а внутри N+1 и лишние запросы. Veai проверяет такие гипотезы на фактах проекта прямо в IDE: семантические usages, реальные прогоны тестов, coverage, debugger, доступ к исходникам Hibernate.

Не “этот код, возможно, тормозит”, а проверка через запуск. Скорость растёт — контроль над качеством остаётся. Большой разбор продукта сделан в статье

Теги:
Всего голосов 2: ↑2 и ↓0+4
Комментарии0

graphics.h в 2026 году: зачем и как запустить

graphics.h — это часть библиотеки BGI (Borland Graphics Interface) родом из 1990-х. В современных IDE её нет: она несовместима с 64-битными системами и не является частью стандарта C++. Тем не менее в учебных задачах она до сих пор встречается — особенно там, где нужно быстро визуализировать алгоритм или сдать лабораторную.

Когда это оправдано

  • изучение основ C/C++ и хочется видеть результат за пределами консоли

  • разбор алгоритмов компьютерной графики

  • подготовка к экзамену по предмету, где преподаватель требует именно graphics.h

Для production-кода и серьёзных учебных проектов лучше сразу смотреть в сторону актуальных библиотек: SDL2 (2D, кроссплатформенная), SFML (ООП-подход, проще в освоении) или OpenGL/GLFW (если нужна 3D-графика и аппаратное ускорение).

О библиотеке

Адаптация для современных Windows называется WinBGIm — её разработал и поддерживает Майкл Мэйн, профессор Колорадского университета в Боулдере. Библиотека открыта для использования и модификации. Скачать можно на официальном сайте: winbgim.codecutter.org.

Если хочется сначала почитать вводный разбор — на Хабре есть статья с обзором WinBGIm, здесь же показан небольшой практический пример-скриншот: инженерная утилита с графическим выводом, которая впоследствии была переписана с использованием современного UI на C++.

Как запустить: общий принцип

Для работы используется адаптация WinBGIm — три файла: graphics.h, winbgim.h и libbgi.a.

Линкер-флаги одинаковы для всех сред:

-lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32

Известный баг: в оригинальном graphics.h на строке 302 встречается int right=0 — это вызывает ошибку компиляции. Исправляется заменой на int txtright=0.

Dev-C++

  1. Скопировать graphics.h и winbgim.h в MinGW64\x86_64-w64-mingw32\include

  2. Скопировать libbgi.a в ...\lib

  3. Tools → Compiler Options → Parameters → Linker — вставить флаги

  4. Переключить профиль компилятора на 32-bit Release

Code::Blocks

  1. Файлы — в соответствующие папки include и lib компилятора MinGW

  2. Settings → Compiler → Linker settings → Other linker options — вставить флаги

VS Code

Файлы хранятся внутри проекта. Структура:

project/
  include/  ← graphics.h, winbgim.h
  lib/      ← libbgi.a

В .vscode/tasks.json в массив args добавить:

"-I${workspaceFolder}/include",
"-L${workspaceFolder}/lib",
"-l"-I${workspaceFolder}/include",
"-L${workspaceFolder}/lib",
"-lbgi", "-lgdi32", "-lcomdlg32", "-luuid", "-loleaut32", "-lole32"
bgi", "-lgdi32", "-lcomdlg32", "-luuid", "-loleaut32", "-lole32"

Компилятор MinGW (g++) должен быть прописан в PATH.

По материалам видео-инструкции CodeWar

Тест

#include <graphics.h>
#include <conio.h>

int main() {
    int gd = DETECT, gm;
    initgraph(&gd, &gm, (char*)"");
    circle(250, 250, 100);
    getch();
    closegraph();
    return 0;
}

Должно открыться окно с белым кругом на чёрном фоне. Если компиляция падает с ошибкой cannot find -lbgi — проверьте путь до libbgi.a. Ошибка undefined reference обычно означает, что флаги линкера не подхватились.

Теги:
Всего голосов 7: ↑6 и ↓1+8
Комментарии3

Наш архитектор C++ анализатора, Юрий Минаев, выступил в прошлом году на C++ Russia с докладом "Семантика C++ изнутри компилятора".

О чём рассказал Юра:

  • о семантике C++ с точки зрения статического анализатора или фронтенда компилятора;

  • как устроена таблица символов и как происходит поиск имен, а также как семантика влияет на парсинг;

  • о том, как происходит выбор перегрузок и каким образом компилятор находит функции, которые даже не видны в текущей точке программы;

  • о шаблонах и их инстанцировании.

Посмотреть доклад можно ещё на этих площадках:

Приятного просмотра! Ждём ваши комментарии!

Теги:
Всего голосов 2: ↑2 и ↓0+6
Комментарии0

Дайте посмотреть на нормальный С++ проект, созданный вайб-кодингом

Чтобы корректировать развитие PVS-Studio я заинтересован смотреть C++ проекты, созданные с использованием генеративного AI или, по-простому, вайб-кодинга. Но вот незадача: все кругом пишут про этот самый вайб-кодинг, но я не знаю, как и где искать такие открытые проекты.

Мне попадается какая-то белиберда типа enhance-client, сгенерированная за $15. Но это даже смотреть несерьёзно. По присутствию в репозитории .obj, .iobj, .ipdb файлов и прочего мусора видно, что автор не понимает, что он делает. Проект не компилируется по разным причинам, например, из-за того, что заложен какой-то огрызок файла bytes.hpp (у массива нет конца).

Если немного поправить и проверить, что удалось собрать, то там лезут перлы вида:

void enhance::modules::autototem::run()
{
  ....
  auto env = enhance::instance->get_env();
  if (!env)
  {
    env->DeleteLocalRef(player);
    return;
  }
  ....
}

Предупреждение PVS-Studio: V522 [CWE-476, CERT-EXP34-C, SEC-NULL] Dereferencing of the null pointer ‘env’ might take place. autototem.cpp 757

Явное разыменование нулевого указателя.

Или бессмысленные сравнения значения типа int с константой 0.1f:

int sdk::minecraft_client::get_attack_cooldown() { .... }

void enhance::modules::shield_breaker::run()
{
  ....
  if (sdk::instance->get_attack_cooldown() > 0.1f)
  ....
}

Предупреждение PVS-Studio: V674 [CWE-682, CERT-FLP36-C] The ‘0.1f’ literal of the ‘float’ type is compared to a value of the ‘int’ type. shield_breaker.cpp 628

Такие ляпы нет смысла серьёзно разбирать и описывать.

Можно спросить: “А что ты хочешь от поделок за 15$?” Да, в общем-то, ничего, но вместо нормальных проектов попадаются они. Мне интересно изучить большие открытые проекты нормального качества, при написании которых активно используется GenAI. А то пока ощущение, что термин “вайб-кодинг” есть, а C++ проектов нет. Или за них стыдно? :)

Если вы знаете подобные большие проекты, то присылайте ссылки на них в комментарии. Заранее спасибо.

Предыдущие публикации по мелким проектам:

  1. Давайте заглянем в этот самый вайб-код.

  2. Ревью вайб-кода с гнильцой, который притворяется оптимизированным С++ кодом.

Теги:
Всего голосов 10: ↑10 и ↓0+12
Комментарии0

🖥 Создатель C++ разнёс вайбкодинг: “сеньоры не хотят разгребать этот мусор”

Бьёрн Страуструп, легендарный создатель C++, в новом двухчасовом интервью резко прошёлся по вайбкодингу.

Главная претензия простая: сгенерированный код пока слишком часто выглядит красиво только на демке. В реальном проекте он приносит баги, раздувает кодовую базу, плодит уязвимости и плохо поддаётся нормальной проверке.

Особенно больно это бьёт по опытным разработчикам. Им потом приходится не “магически ускоряться с ИИ”, а читать, чинить и переписывать слоп, который кто-то нагенерировал за пять минут.

Похожая история уже достала и Линуса Торвальдса. Его буквально завалили кривыми AI-отчётами по ядру Linux: вроде бы люди “помогают”, а на практике создают шум, который мешает настоящей разработке.

Если человек не понимает архитектуру, безопасность, тесты и границы задачи, то вайбкодинг превращается не в ускорение, а в генератор технического долга.

Сеньоры не боятся ИИ.

Они просто не хотят провести остаток карьеры, разгребая чужой промптованный мусор.

Полное интервью нашел в тг тут: https://t.me/cpluspluc/1451

Теги:
Всего голосов 20: ↑20 и ↓0+23
Комментарии9

В C код может выполниться ещё до main()

В Linux и GCC есть constructor-функции - они запускаются автоматически до входа в main().

Выглядит почти как магия:

Такую функцию не нужно вызывать вручную. Компилятор сам пометит её как код, который должен выполниться при старте программы.

Где это используется:

- инициализация глобального состояния

- подготовка shared libraries

- регистрация плагинов

- настройка runtime-окружения

- выполнение служебного кода до основной логики

Именно поэтому в C-программе не всегда всё начинается с main().

Иногда до него уже кто-то успел поработать.

Подсмотрел в тг про С++ : https://t.me/cpluspluc/1449

Теги:
Всего голосов 9: ↑9 и ↓0+9
Комментарии7

Всем доброго времени суток.


Я С++ разработчик из Тюмени. Хочу представить вам свой проект : "Инструментарий Мастера Подземелья" https://mastertoolkit.ru/ (тут лежит рабочий прототип)

Исходный код выложен тут : https://gitlab.com/R1nk/dungeonmastertoolkit

В данный момент чувствую выгорание над проектом. Сайт выложен год назад, но не получал ни одного фидбэка. Прошлой осенью провел сессию по днд и выявил для себя пару ошибок и недоработок.

На текущий момент внесены изменения над кодом и функционалом и не завершен в рабочую сборку.

Инструментарий для мастеров подземелья. Есть 2 вида генерации событий:

  1. Абстрактная навигация (карты таро, описание мастей и значений. где масти можно описать как положительные так и негативные последствия, а значения можно более конкретизировать. Например отряд гоблинов как препятсвие или обвал).

  2. Это события. можно описать дерево событий, из которых можно выбрать случайным способом. Например можно описать случайные квесты или встречи.

  3. Журнал приключений (в режиме Сессия туда попадают все выпадающие события и абстрактная навигация)

  4. Статистика так же работает в режиме сессии. Графики по выпавшим ситуациям.

Теги:
Всего голосов 2: ↑2 и ↓0+2
Комментарии3

Rust + LLM: новая и сильная “ОПГ” против C++ и его друзей…

Есть такая штука с Borrow Checker - он ненавидит людей. Не потому, что он плохой, а потому, что требует держать в голове графы данных всего приложения всё время, пока пишешь. Люди устают, раздражаются, и в итоге - unsafe, чувство стыда перед собой и коллегами, и понеслась. А LLM на это просто… скажем, кхм, “не важно”. Ну, надо так надо. LLM итерирует. И самое забавное - ей можно запретить unsafe прямо в промпте, и она не будет искать обходные пути(ну, может, разок другой - Opus попробует, но это ловится простым поиском по файлам, даже PVS-Studio не нужен, хотя с ним было бы фееричнее). Скорее всего, LLM просто найдёт решение в рамках safe Rust. И это круто.

Про Python и JS - там ведь основная проблема не скорость даже(и синтаксически значимые пробелы у некоторых), а то, что деплоится не программа, а среда. Интерпретатор, зависимости, версии, “а на проде другой питон”… Сейчас с линковщиком от Zig можно прямо из Windows собрать бинарник под Linux - статически, без танцев(привет musl!), одним пинком. И это не какой-то хак, это просто работает. Получаешь один файл, который либо запустится, либо нет - без сюрпризов в середине.

C++ тут проигрывает не потому, что он сложный или плохой - он интересный, мощный, с огромной историей и экосистемой. Но именно эта мощность становится проблемой когда код - генерирует LLM(можно выстрелить себе в ногу из гранатомёта, и, самое страшное - не заметить этого). Модель получает слишком много способов сделать что-то, и часть из них - UB. Rust убивает целый класс таких подходов к реализации на уровне компилятора. Это не договорённость и не линтер - это просто не скомпилируется.

Мне кажется, уникальность этой связки тут в том, что задачи разделились очень чисто: LLM думает про логику, компилятор думает про корректность. И они не мешают друг другу. В C++ - см. выше. Именно то, что делает Rust тяжёлым для людей(кроме евангелистов и стримеров), делает его почти идеальным для этой связки.

Ну и если кто был травмирован неудачным опытом с “вайб-кодингом”, и сложилось впечатление, что вся эта пляска с LLM, это не серьёзно - попробуйте отложить китайщину(MiniMax, GLM, DeepSeek...), взять фронтир-модель типа “OpenAI Codex GPT-5.5 на XtraHigh” - и дать ей хотелку в виде описания какого-нибудь серверного приложения на Rust, и просто посмотреть что будет через 15-20 минут. Забейте на клоунов со “змейкой” и прочей мелочёвкой из бенчмарков на ютубе, попробуйте реальную задачку(многохопывый релей между серверами со своим протоколом КВН например и клиентами для Win/Linux). Оно сделает. Быстро. Качественно. Это довольно странное ощущение, честно говоря, видеть такое…

Просто мысли вслух, хотелось поделиться)

-s. TekMetrics certified “Master C programmer”, July 1999

Теги:
Всего голосов 1: ↑1 и ↓0+2
Комментарии2

Тихий враг или молчаливый союзник? Коротко о выравнивании в C++. Часть 3

Мы уже разобрали базовое выравнивание полей и изучили, как наследование наслаивает данные друг на друга. Казалось бы, теперь-то всё — все ловушки изучены. Но не тут-то было! Есть у этой темы ещё одна, по-настоящему тёмная сторона, про которую не так часто говорят.

Одно короткое слово virtual полностью переписывает "геометрию" класса, внося в выравнивание свои коррективы, которые трудно игнорировать. Давайте разберёмся, что на самом деле происходит под капотом, когда выравнивание сталкивается с виртуальностью.

Теги:
Всего голосов 3: ↑3 и ↓0+4
Комментарии2

Чернобыльское лето 1986 года, когда все киевские одноклассники разъехались по разным концам СССР подальше от радиации, было для меня супер-продуктивным в смысле изучения программирования. Я ходил в контору человека по фамилии Долина, бывшего полковника танковых войск из Донецка, который переквалифицировался в компьтеризатора украинского образования. Там я работал на компьютерах MSX Yamaha, выучил программирование на Си. Компилятор назывался ASCII C (сейчас в комментах появятся умники которые будут мне говорить, что ASCII это кодировка, а я им буду кидать ссылку что это еще и японская компания).

Долине мое увлечение Си не нравилось, он хотел чтобы я больше писал программ на Бейсике, которые он демонстрировал людям из украинских министерств и студии мультфильмов (некоторые программы были графические). Кроме графики я сделал еще например программу которая фиксировала в реальном времени действия футболистов во время матча, через нажатия клавиш наблюдателем. Контора Долины была у стадиона, оттуда доносились вопли болельщиков. Долина это показывал кому-то по спортивной линии.

В конце лета я полетел на Новосибирскую Летнюю Школу Юных Программистов, где выучил ассемблер Z80 и сделал поддержку параллельного выполнения нескольких Си функций с помощью переключения контекстов в обработчике прерывания по таймеру. С сохранением регистров в дексрипторе задачи в списке задач. За это я получил диплом первой степени. По-моему дипломы вручал академик Ершов, хотя может я путаю и мы встретились с ним в академгородке куда на тоже возили.

На школе было невероятное количество комаров, а также красивая девочка из Томска, которая мне нравилась, и ее подружка, которой нравился я. Из Украины еще был юный гений из Харькова, который постоянно спорил со мной, что персоналки фигня, а мейнфреймы - это круто. Так как я успел поработать и на мейнфреймах, споры были довольно развесистые.

Еще там я увидел первые советские программы западного качества - редактор tor(?), программу низкоуровневой работы с диском и оконный отладчик. Они были написаны на Си и ассемблере аккуратно, как примеры в западных книжках. Советский код который я видел до этого (и большинство после этого) был написан тяп-ляп. Писали эти программы местные аспиранты которые были также преподавателями школы.

Помимо этих программ я привез на флоппи-дисках со школы CP/M (хуже файловая система чем в MSX-DOS), среду Turbo Pascal, интерпретатор Lisp, компилятор Nevada Fortran, еще два компилятора Си (Aztec C и BDS C) и даже компилятор с подмножества языка Ada, который я знал теоретически, но никогда не использовал.

29 лет спустя, в 2017 году я приехал на ту же новосибирскую школу в качестве инструктора по Verilog и FPGA. Еще там был Борис Файфель который учил детей Лиспу или чему-то такому.

Теги:
Всего голосов 21: ↑21 и ↓0+25
Комментарии4

Оптимизация контекста для Claude Code на большом проекте (иногда и 50% экономия токенов)

Работаю над большим C++ проектом - реализация сетевого протокола. Использую Claude Code как основной инструмент. Со временем заметил: каждый новый чат начинается с того, что агент долго читает README.md, который разросся до 1000+ строк и 60 КБ.

Проблема

В CLAUDE.md была прописана команда читать README.md в начале каждого диалога, агенту нужно дать контекст проекта. Пока проект был небольшим это работало нормально. Но README рос вместе с проектом и в итоге стал содержать всё: архитектуру, логику DTLS, настройки веб-интерфейса, описание протокола, инструкции по сборке.

И как результат:

  • Агент тратит тысячи токенов на анализ файла до начала работы

  • Если задача касается только фронтенда, модель всё равно загружает детали реализации ядра протокола. Лишний контекст снижает точность ответов.

Решение

Вместо одного большого файла использовать иерархию маленьких, в отдельной папке claude-context/:

claude-context/
├── context-claude.md       # общая архитектура и навигация (~90 строк)
├── context-AC-claude.md    # Access Controller
├── context-WTP-claude.md   # WTP Agent
├── context-WEB-claude.md   # Web Interface
└── context-TESTS-claude.md # тесты

Главный файл context-claude.md содержит краткое описание проекта и таблицу-навигатор: какой файл читать для какой области. В дочерних файлах описана детализация по модулям, каждый 100-130 строк.

Инструкция в CLAUDE.md теперь выглядит так:

“Start each new conversation by reading claude-context/context-claude.md. For deeper context on specific areas, read the relevant file from that directory.”

Агент читает главный файл (90 строк), понимает область задачи, подгружает только нужный дочерний контекст.

Замер

Чтобы проверить эффект, я поставил Claude одну и ту же задачу в двух разных конфигурациях:

“Добавь тесты для WtpConfigController и WtpRadioController, проверь что если WTP address не строка, то возникает исключение std::runtime_error”

| Параметр             | README.md (60 КБ) | Иерархический контекст | Разница  |
| :------------------- | :---------------- | :--------------------- | :------- |
| Токены на сообщения  | 36.8k             | 17.6k                  | -53%     |
| Всего токенов        | 56.7k             | 37.6k                  | -34%     |
| Рост usage за задачу | +11%              | +6%                    | В 2 раза |
| Скорость анализа     | Заметная пауза    | Почти мгновенный старт |          |

Важный момент

README.md остался нетронутым - это документация для людей. Файлы в claude-context/ - отдельный артефакт, написанный под AI: плотно, без лирики, с ASCII-схемами и таблицами. Я старался не смешивать два разных назначения в одном файле.

При небольшом проекте в этом подходе смысла нет, накладные расходы на поддержку двух наборов документации не оправдаются.

Теги:
Всего голосов 7: ↑5 и ↓2+3
Комментарии8

🌲 Открываем регистрацию на Дебаг Кемп

Мы придумали формат, который давно хотели сами: выбираешься из города, два дня в сосновом лесу на Карельском перешейке — маршрут, костёр, мастер-классы по выживанию, нетворкинг без слайдов и питчей. Просто люди, с которыми интересно, и никакого Slack-а.

📅 6–7 июня 2026 (выходные) 👥 Всего 25 мест — маленький формат, это принципиально.

Цена растёт по мере приближения к дате. Оплатить можно частями через сплит → регистрация

Если вы 💎 практик сообщества — скидка 15% применяется при регистрации автоматически. Ещё не практик, но думаете? Сейчас самый разумный момент.

👀 Узнать больше · 📝 Регистрация

Вопросы — в чат, мы там живём.

Теги:
Всего голосов 4: ↑0 и ↓4-4
Комментарии0

Тема неопределённого поведения (UB) в языке C++ освещается и обсуждается многие годы, но это не значит, что она исчерпала себя. Это плата, которую программисты отдают за эффективные оптимизации кода, такие как удаление ряда проверок.

C++ - опасный инструмент, и не помешает лишний раз напомнить, как правильно держать его в руках. Причём с приходом инструментов вайб-кодинга ситуация, скорее всего, даже ухудшится, так как станет ещё сложнее удерживать неопределённое поведение под контролем. В докладе обсудили эту тему, заглянув в будущее.

Полезные ссылки из доклада:

Примечание. Был задан вопрос про безопасные компиляторы (ГОСТ Р 71206—2024). Подробнее с этой темой можно познакомиться здесь: Использование безопасной системы сборки программного обеспечения

Сделайте свой проект чистым с PVS-Studio. Месяц бесплатного использования по промокоду.

Теги:
Рейтинг0
Комментарии1

Переосмысление библиотеки LDL.

Я полностью пересмотрел концепцию библиотеки LDL.

Что такое LDL?
Это графическая библиотека с единым API для всех систем как старых, так и новых.

Раньше я писал её на C++98, что давало хорошую портабельность. Но сейчас я пересмотрел многие тезисы, которые декларировал на GitHub, чтобы наконец добраться до первого релиза.

Новая стратегия

Я решил выпускать релизы без реализации полного функционала (графика, звук, шрифты и т.д.) постепенно, итеративно.

  • Перешёл на C89 для максимальной переносимости. Это не только DOS или Windows 3.x, но и старые системы вроде Solaris, PlayStation 1 и другие.

  • Для первого релиза реализую минимальный базовый функционал: графику (OpenGL, Vulkan), окна и события. По возможностям аналог GLFW.

  • С каждым следующим релизом буду добавлять: 2D-рендер, звук, шрифты и прочее.

Лицензия и целевые платформы

  • Лицензия меняется на LGPLv3.

  • На старте поддерживаются Windows и Linux.

Качество и инструменты

При разработке использую:

  • AddressSanitizer (ASan)

  • UndefinedBehaviorSanitizer (UBSan)

  • Различные анализаторы кода

Я считаю, что такая стратегия полезнее, чем годами доводить библиотеку до версии 1.0 в офлайн-режиме.

Примеры и бэкенды

  • Добавлю десятки примеров с OpenGL 1.x, OpenGL 3.x и Vulkan.

  • Буду добавлять бэкенды для LDL: не только под ОС, но и поверх других графических библиотек — SDL, SFML, GLFW и т.д.

  • API остаётся единым для всех бэкендов.

Это позволит сразу добавить поддержку звука и шрифтов (через бэкенды), а в нативных версиях реализовывать их позже.

 2D-рендер без границ

Одной из главных задач LDL я вижу создание единого 2D-интерфейса, который стирает различия между поколениями графики.

Вам не нужно думать о том, что находится в системе: современная видеокарта с Vulkan, старый ускоритель с OpenGL 1.2 или вообще только центральный процессор (Software Rendering).

  • Единый интерфейс: Вы используете одни и те же команды для рисования пикселей, линий и спрайтов.

  • Адаптивность: LDL сам выберет наиболее эффективный способ вывода изображения. На современной системе это будет аппаратное ускорение, а на «железе» без видеокарты оптимизированный программный растеризатор.

  • Визуальная честность: Ваш визуальный стиль останется неизменным, на чем бы он ни запускался. Это дает возможность делать игры и приложения, которые выглядят и работают одинаково и на ретро-ноутбуке, и на современном мониторе.

Философия: Машина времени в вашем коде

Зачем тратить силы на поддержку систем, которые многие считают «трупами»?

1. Борьба с цифровым забвением

Современный софт живет 3–5 лет. Мы выбрасываем железо не потому, что оно сломалось, а потому, что софт стал слишком тяжелым и ленивым. LDL — это протест против «запланированного устаревания». Я хочу, чтобы код, написанный сегодня, мог дышать в железе любой эпохи.

2. Инженерный аскетизм

Когда у тебя гигабайты памяти, ты перестаешь ценить каждый байт. Написание библиотеки под C89 для слабого железа — это духовная практика для программиста. Это возвращение к искусству находить изящные решения в условиях жестких ограничений. Каждый сэкономленный такт процессора — это дань уважения инженерам прошлого.

3. Преемственность поколений

Мы стоим на плечах гигантов, но часто забываем их имена. LDL сохраняет возможность для диалога между эпохами. Это инструмент, который позволяет современному разработчику почувствовать «металл» старых машин, не теряя связи с современными технологиями вроде Vulkan.

Итог

LDL — это Little Directmedia Layer. Он маленький не потому, что слабый, а потому, что в нем нет ничего лишнего. Это попытка создать код, который будет принадлежать не конкретной версии ОС, а истории программирования в целом.

Один API. Один код. Тридцать лет компьютерной истории.

Теги:
Всего голосов 9: ↑8 и ↓1+8
Комментарии8

Всё, что нужно знать для начала работы в PVS-Studio

Статический анализатор PVS-Studio — это инструмент для поиска ошибок в коде на протяжении всего жизненного цикла проекта.
В новой статье разберём основные особенности анализатора PVS-Studio, сценарии и варианты анализа, а также узнаем всё, что нужно знать для начала работы с инструментом.

Теги:
Рейтинг0
Комментарии0

Сравнивал разные методы сделать call-once (а точнее инициализацию синглтона) - далее небольшие заметки.

Итак, нам нужно создать какой-то объект, пусть даже что-то нетривиальное (для определённости можно представлять что мы хотим прочитать данные из файла/эмбеддет ресурсов и разложить эти данные в хеш-мапу для будущих чтений). Рассмотрим следующие варианты:

  1. статичный глобальный объект, вычисление вызывается в конструкторе

  2. статичный глобальный объект + std::call_once

  3. статичный локальный объект в функции, вычисление вызывается в конструкторе

Чтобы в годболте видеть что и где вызывается - определяем тип имеющий только объявления методов (это заставит компилятор ставить явный call)

struct TSomeExternal {
    TSomeExternal(int);
    ~TSomeExternal();

    int DoCalc();
    char Data[60]; // просто чтобы было проще увидеть
};

Особенности первого варианта:

  • вычисление "до main"

  • неконтролируемый порядок инициализации глобальных ответов => нельзя иметь зависимости между такими объектами

  • вычисление только в рамках 1 потока

  • уникальный плюс: нет проверок "на горячем цикле" - при обращении к объекту вызывающая функция может считать что данные уже готовы (но именно этот плюс стоит нам второго пункта)

В годболте можно посмотреть на func1 - видно что она скомпилилась просто в забор поинтера, и call

TSomeExternal GlobalValue(7);
int func1() {
    int x = GlobalValue.DoCalc();
    return x + 14;
}

Также видно что были порождены - указание что нужно разместить объект

GlobalValue:
        .zero   60

А также _GLOBAL__sub_I_example.cpp - там видно, что вызывается конструктор, и регистрируется указатель на деструктор в пост-колбеках (__cxa_atexit)

Второй вариант (более правдоподобно было бы внести call_once внутрь глобального объекта, но для простоты сравнения сделал как ниже)

static std::once_flag flag;
static std::unique_ptr<TSomeExternal> ptr;
void init() {ptr = std::make_unique<TSomeExternal>(7);}

int func2() {
    std::call_once(flag, init);
    int x = ptr->DoCalc();
    return x + 14;
}
  • настоящая инициализация случается только в момент первого использования (ленивая инициализация). Это может быть как плюс (позволяет не тратиться на инициализацию если данные не будут использоваться), так и минус (первые запросы/итерации будут медленными)

  • разные инициализаторы данных вполне могут работать одновременно из разных потоков

  • имхо - дорогой основной цикл (очень много инструкций до TSomeExternal::DoCalc) - это всё подготовка вызова call pthread_once, который будет выполнен каждый раз

Третий вариант годболт

int func3() {
    static TSomeExternal prepared = {7};
    int x = prepared.DoCalc();
    return x + 14;
}
  • в рантайме есть проверка - но это проверка и так нужного нам указателя на null

push    rbx
movzx   eax, byte ptr [rip + guard variable for func3()::prepared]
test    al, al
je      .LBB0_1
  • если указатель не нулевой, то переход не будет выполнен и мы сразу переходим к

        lea     rdi, [rip + func3()::prepared]
        call    TSomeExternal::DoCalc()@PLT
  • в случае если указатель не готов, то подготовка случается под блокировкой (__cxa_guard_acquire)

  • конкретно в годболте не видно, но создаются слоты для guard variable for func3()::prepared и func3()::prepared (в этом смысле разницы со вторым вариантом мало)

Итого: ленивое вычисление, также возможна многопоточная инициализация, а также дешёвый success-path

Я бы назвал относительным неудобством третьего варианта, что указатель оказывается скрыт внутри функции, и надо сделать ещё доп действие чтобы использовать объект из нескольких функций. Пример такой обёртки Singleton.

Теги:
Всего голосов 1: ↑1 и ↓0+1
Комментарии1

NixOS: идея, до которой индустрия доросла только сейчас.

Кажется, NixOS наконец выходит из категории системы «для своих» и становится все заметнее в инженерной среде. Это закономерно: он очень точно попал в проблемы, с которыми команды массово столкнулись только в последние годы.

История началась в 2003 году, когда исследователь Элко Долстра и его коллеги в Утрехтском университете запустили проект Nix. Это исследовательский проект, который включал пакетный менеджер и собственный декларативный язык. Идея была сделать так, чтобы пакеты и зависимости собирались предсказуемо, не конфликтовали между собой и не превращали систему в хаос после очередного обновления. Чуть позже из этой логики вырос NixOS, где тот же подход применили уже ко всей операционной системе.

В этом и был главный поворот. Nix с самого начала смотрел на систему не как на набор вручную настроенных файлов и команд, а как на то, что можно описать целиком. Пакеты хранятся изолированно, разные версии могут спокойно жить рядом, а состояние машины задается через конфиг. За счет этого обновления становятся атомарными.

Это особенно интересно на фоне обычных Linux‑дистрибутивов. Там текущее состояние системы часто является результатом длинной цепочки действий: что‑то поставили, что‑то удалили, где‑то поправили конфиг, где‑то забыли. В NixOS логика другая: ты описываешь желаемое состояние, а система приводит машину именно к нему. Если новая конфигурация не взлетела, предыдущее состояние никуда не исчезает.

😏 Почему NixOS набирает популярность именно сейчас? Потому что индустрия наконец доросла до его сильных сторон. Чем больше у команды окружений, CI/CD, инфраструктуры как кода и цены ошибки, тем важнее воспроизводимость и предсказуемость. То, что раньше выглядело как нишевая экзотика, сегодня все чаще выглядит как очень здравый инженерный выбор.

Многие современные immutable‑системы по сути идут в ту же сторону, куда NixOS пошел еще много лет назад.

А если хочется не просто прочитать про Nix, а разобраться, как он работает на практике, приходите на наш открытый воркшоп.

📹 Открытый воркшоп в рамках ИнженеркаТех Плюс, 18 марта в 19:00 по МСК. Александр Сергеев из сообщества RULKC, Russian Linux Kernel Community, расскажет про Nix и функциональный подход к пакетам и сборке.

Если вы хотите получить полный контроль над окружением и наконец закрыть вопрос воспроизводимых сборок, этот воркшоп для вас. Разберем ключевые концепции Nix и наглядно покажем, чем он отличается от привычных систем управления пакетами.

Зарегистрироваться тут

Теги:
Всего голосов 6: ↑2 и ↓4-1
Комментарии0

🚀 Бесплатный курс для разработчиков: создаём свой язык программирования!

Приглашаем вас пройти наш курс и разобраться, как устроены языки программирования изнутри.

Изначально курс делали для прохождения внутри компании, но решили, что полезного материала так много, что хочется рассказать всему миру!

Этот курс подойдёт разработчикам, которые хотят выйти за рамки повседневной разработки и глубже понять фундаментальные принципы работы кода. Да, примеры будут на C++, но сам материал гораздо шире и будет полезен программистам с любым стеком. Независимо от того, пишете вы на C++, C#, Java или любом другом языке, понимание внутренней архитектуры языков сделает вас сильнее как инженера.

В рамках курса мы шаг за шагом разберём, как создаётся язык программирования:
— что такое лексер и парсер и какую роль они играют;
— как работает семантический анализ;
— как происходит вычисление и обработка кода;
— как все эти части объединяются в единую систему.

Вы увидите, как текст программы превращается в структуру, понятную машине, и поймёте, какие решения стоят за этим процессом.

Даже если вы не планируете создавать собственный язык программирования, курс поможет чуть глубже понять, как всё это устроено "под капотом", расширить технический кругозор и по-новому взглянуть на привычные инструменты. Это хороший способ систематизировать знания, освежить базовые концепции и просто обсудить интересные темы с коллегами.

Мы постарались сделать материал одновременно понятным и практико-ориентированным: минимум абстракции ради абстракции — максимум смысла.

Если вы хотите лучше понимать, что происходит "под капотом" программирования и прокачать инженерное мышление — этот курс для вас.

👉 Подробности и доступ к курсу по ссылке.

Присоединяйтесь и изучайте программирование глубже — бесплатно!

Теги:
Всего голосов 3: ↑2 и ↓1+1
Комментарии0

Прошу помощи. Не могу найти документацию на плату. Купил когда-то на а**-э*****сс, но к ней не было в комплекте вообще ничего. За время поисков удалось найти только два фрагмента схемы. Мб есть у кого такая....

Теги:
Всего голосов 2: ↑2 и ↓0+3
Комментарии12

Как далеко видит lookup в C++?

Хорошей практикой в C++ считается размещение функций рядом с типами, для которых они предназначены. Однако, чтобы такой подход работал корректно, важно понимать механизмы поиска имён и знать, где можно размещать функции, не нарушая правил языка.

Совсем недавно мы проверяли проект OpenCV и нашли там довольно интересную ошибку. Рассмотрели её подробнее и написали новую статью специально для тех, кто хочет разобраться с механизмом поиска имён в C++, в частности с поиском имён по аргументам.

Теги:
Всего голосов 3: ↑3 и ↓0+4
Комментарии0

Приветствую, Хабравчане!

Задумывались ли вы, насколько высок современный налог на железо в разработке ПО?

У меня в руках настоящий «старичок» из 2002-го: сокет 478, матплата GA-8IR2003, Celeron 1700 МГц (по силам как Pentium III на 1 ГГц, но с поддержкой SSE2), 2 Гб ОЗУ, GeForce 4 MX и верный HDD на 40 Гб.

Я хочу написать о нем статью, но не в стиле ностальгический обзор ретро-игр такого в сети полно. Моя цель вдохнуть в него жизнь и проверить, пригоден ли этот 23-летний дедушка для современной разработки.

На борт успешно встают Windows 7 и Debian 11, что открывает доступ к актуальному софту, IDE и библиотекам. Хочется понять: реально ли на таком непотребстве поднять бэкенд на C# или собрать что-то серьезное на C++?

Запасной вариант, если основному ПК не хватит инструкций.

В запасе ПК: Athlon x4 640, 8гб ОЗУ, ssd 256.

На нем, отключая ядра и понижая частоту можно добиться симуляции ПК начиная с 2000-ого по 2010 год. Думаю этот вариант будет предпочтительнее. Но начну конечно с celeron'а.

Что планирую потестить:

  1. C# под Linux: Запустить бэкенд и посмотреть, не «умрет» ли система.

  2. Базы данных: Погонять PostgreSQL 9.4 (она еще дружит с 32-битными процессорами).

  3. C++: Сравнить скорость сборки проекта с модулями и без них.

  4. Безумный челлендж: Попробовать собрать userver. В чате разработчиков сказали "вряд ли взлетит", а мне тем более интересно проверить.

  5. IDE: Какая версия Visual Studio оживет и можно ли в ней работать без боли.

Прошу совета у сообщества: накидайте идей! Какие бенчмарки прогнать? Какой софт или специфические проекты попробовать собрать, чтобы нащупать предел возможностей?

Будет интересно сделать вывод: пригоден ли древний ПК хоть для какой-то разработки сегодня, или «налог на железо» стал неподъемным. Жду ваши предложения!

Update: Поправил текст, ошибки и очепятки.

Теги:
Всего голосов 11: ↑10 и ↓1+12
Комментарии34

Тихий враг или молчаливый союзник: коротко о выравнивании в C++. Часть 2

Казалось бы, тайна выравнивания раскрыта. Вы победили невидимого врага — невыровненный доступ. Память под контролем, но производительность по-прежнему шепчет: "Есть ещё нюансы". Что? Нюансы? Какие? Пришло время посмотреть, что происходит, когда структуры начинают наследовать друг друга. Здесь всё становится... интереснее. Правила игры меняются.

Итак, путь ясен: мы погружаемся в мир наследования, чтобы услышать его диалог с памятью. Давайте сразу к делу. Приготовьтесь, правила только что усложнились. В статье поговорим о выравнивании, наследовании POD-структур и множественном наследовании.

Теги:
Всего голосов 3: ↑2 и ↓1+1
Комментарии0

Исключения — самый коварный механизм C++. Код компилируется, тесты проходят, но в продакшне система падает из-за partially constructed object или утечки в деструкторе. Результат — часовой дебагг и нестабильность.

Основная ошибка: путаница между Basic и Strong guarantee. «Валидный» объект ≠ «корректный». Вы нарушаете инварианты, даже не подозревая об этом.

На вебинаре «Exception Safety в C++: как писать код, который не ломается» развенчаем мифы об Exception Safety. Не теория, а разбор реальных антипаттернов из кодовых баз:

➕ Исключения в деструкторах и как RAII решает это.

➕ Где noexcept ломает гарантии, а где — критически важен.

➕ Move-семантика и её скрытые опасности.

‼️Вы получите: чёткую ментальную модель и чек-лист для код-ревью, который можно применять уже на следующий день.

📅 Дата: 5 февраля

⏰ Время: 17:00-18:00 (Мск)

👨‍🎓 Спикер: Козырев Дмитрий — специалист в области разработки на C++.

➡️ Регистрация ⬅️

Теги:
Всего голосов 4: ↑0 и ↓4-4
Комментарии0

Тихий враг или молчаливый союзник: коротко о выравнивании в C++

Представьте, ваша программа — образец чистого кода, прошедший ревью и покрытый тестами. Казалось бы, всё идеально. Но производительность не такая, на какую рассчитывали. Вы проверили всё, что знали. А что, если проблема в том, о чём вы могли не знать? Всё может упираться в выравнивание данных в памяти. Для многих этот механизм так и остаётся загадкой.

Предлагаю вам вместе попробовать разобраться в такой непростой теме в этой статье.

Теги:
Всего голосов 8: ↑8 и ↓0+9
Комментарии0

Что нового появилось в PVS-Studio в 2025 году

Новый 2026 год уже вовсю идёт, и мы решили оглянуться назад и вспомнить, что интересного появилось в PVS-Studio за богатый на изменения 2025 год:

  • десятки новых диагностических правил, включая проверки, ориентированные на безопасность;

  • обновления интеграций в популярные IDE, CI/CD-системы, ASOC и сборочные системы;

  • улучшения анализа;

  • расширенная поддержка стандартов.

Всё это и многое другое описано в новой статье в нашем блоге.

Теги:
Всего голосов 1: ↑1 и ↓0+1
Комментарии0

Надоело искать парные вкладки (.h/.cpp) в VS Code? Я навайбкодил расширение, которое их магнитит.

Привет! Меня всегда немного раздражала одна мелочь в VS Code.

Открываешь Source.cpp, хочешь посмотреть заголовок, а Source.h открыт где-то в конце списка вкладок или вообще затерялся среди десятка других файлов. Приходится глазами искать его или тянуться к дереву проекта.

Стандартные методы сортировки тут не помогают, поэтому я написал Tab Magnet.

Как это работает:
Вы просто кликаете на файл в Explorer-е. Tab Magnet проверяет, открыта ли его "пара" (например, .h для .c или .html для .component.ts), и если да - автоматически переносит её поближе, чтобы они стояли бок о бок.

Функционал:

  • Знает, что заголовки лучше держать справа, а тесты — рядом с кодом.

  • Поддерживает из коробки C/C++, C#, Web (JS/TS/HTML/CSS) и Angular.

  • Можно настроить свои правила (например, для Go тестов или специфичных структур папок).

vscode marketplace: Tab Magnet

GitHub: tab-magnet

Теги:
Всего голосов 10: ↑9 и ↓1+9
Комментарии9