Обновить
128K+

C++ *

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

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

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

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

Посмотреть запись можно тут:

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

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

Делимся записью вебинара "Применение ЗОСРВ "Нейтрино" и PVS-Studio для разработки ПО согласно требованиям МЭК 61508"! 🔥

На вебинаре разобрали подходы к разработке функционально безопасного ПО для ЗОСРВ «Нейтрино» в соответствии с требованиями МЭК 61508 и показали, какую роль в этом процессе играют инструменты статического анализа.

В первой части поговорили о принципах разработки ПО для «Нейтрино», рассмотрели требования МЭК 61508 к инструментальным средствам, и продемонстрировали работу PVS-Studio в комплекте разработчика «Нейтрино».

Во второй части подробнее остановились на статическом анализе как инструменте для обеспечения безопасности. Разобрали требования МЭК 61508 и МЭК 26262, их связь со стандартами MISRA и рассмотрели, как PVS-Studio помогает контролировать качество и соответствие кода требованиям стандартов.

В завершение рассказали о новой мажорной версии PVS-Studio 8.0: рассмотрели ключевые изменения и нововведения релиза и обозначили дальнейшие направления развития инструмента, включая функциональную безопасность.

Посмотреть можно тут:
- VK Video
- Rutube
- YouTube
- Наш сайт

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

Представлен открытый проект AI File Sorter. Решение раскладывает файлы по папкам и меняет названия. Приложение анализирует содержимое картинок и документов: test123.jpg превращается в фото_в_деревне.jpg, а файл PDF получает имя по тексту внутри. Перед сортировкой можно проверить и поправить предложения, а последний запуск с изменениями можно откатить. Проект работает на Windows, macOS и Linux, при этом бесплатно, если использовать локальную ИИ-модель.

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

Зачем мы интегрируем свой анализатор в такое количество инструментов?

Мы разрабатываем и продаём статический анализатор PVS-Studio. В самом продукте есть множество всякого интересного: утилиты командной строки, диагностические правила разных категорий, вспомогательные утилиты и тому подобное.

За не самым красивым оборотом “тому подобное” мы обычно скрываем наши многочисленные интеграции со сторонними продуктами (плагины, расширения, сценарии работы). Вы можете спросить: “А зачем скрывать?” Смотрите сами: первой нашей интеграцией был плагин для Visual Studio 2005, вышедший 18 лет назад. Сегодня же анализатор интегрируется с более чем тремя десятками других инструментов для разработки: плагины для IDE, игровые движки, платформы контроля качества кода, сборочные системы, платформы CI/CD и т. п.

Поддерживать всё это в актуальном и рабочем состоянии — большая работа, и в новой статье расскажем, зачем мы вообще этим занимаемся.

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

Представлен открытый проект Game Optimizer, который оптимизирует работу CPU под игры в Windows (распределяет потоки и кэш, чтобы разделить нагрузку).

На примере Overwatch у тестеров получилось увеличить FPS вдвое с 210 до 450 кадров. Утилита грамотно распределяет ресурсы, как работает:

  • Снижает нагрузку на ваш ПК, которую создают фоновые приложения и утилизируют ресурсы CPU, в которых нуждается игра. Особенно актуально, если у процессора есть кэш нескольких уровней (L1, L2, L3).

  • CPU Game Optimizer создаёт маски для игр, которые разделяют ресурсы.

  • Маски можно создавать самому или использовать пресеты.

Проект хорошо сочетается с такими процессорами:

  • Intel Core Ultra 9 285K, Ultra 7 265K, Ultra 5 245K, Core i9-14900K, i7-14700K, i5-14600K, i5-14400F, Core i9-13900K, i7-13700K, i5-13600K, Core i9-12900K, i7-12700K, i5-12600K.

  • AMD Ryzen 9 9950×3D• Ryzen 9 9900×3D, Ryzen 9 7950×3D, 7900×3D, Ryzen 9 9950X, 9900X, PRO 9965, PRO 9955, PRO 9945, Ryzen 9 9950×3D2 Dual Edition, Ryzen 9 7950X, 7900X, 7900, PRO 7945, Ryzen 9 5950X, 5900X, 5900XT, 5900, PRO 5945, Ryzen 9 3950X, 3900X, Ryzen 7 3700X, 2700X.

Даёт небольшой прирост с этими процессорами:

  • Intel i5-12400, i3-12100, i3-13100, i3-14100, i9-9900K, i7-9700K, i5-9600K, i7-2600K.

  • AMD Ryzen 7 9700X, 9700F and Ryzen 5 9600X, 9600, 9500F, Ryzen 7 7700X, 7700, Ryzen 5 7600X, 7600, 7500F, Ryzen 7 5800X, 5800XT, 5700X and Ryzen 5 5600X, 5600, Ryzen 7 8700G, Ryzen 5 8600G, monolithic APU: 9800×3D, 9850×3D, 7800×3D, 5800×3D, 5700×3D, 5600×3D.

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

Рефакторинг 22 000 строк C++: Как победить класс-монстр в Vulkan-движке [Анонс стрима]

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

Меня зовут Shagu, и последние несколько месяцев я в одиночку пишу Shuttle Engine — экспериментальный 3D-рендерер и редактор сцен на C++20 и Vulkan 1.3/1.4. Проект ориентирован на современные графические технологии: GPU-driven pipeline (Indexed Indirect Drawing, Compute-пассы для подготовки геометрии), Bindless Descriptors, Buffer Device Address (BDA) и PBR/IBL освещение.

Но, как это часто бывает в инди-разработке, за быстрым прогрессом и красивой картинкой скрывается технический долг.

История одной боли: Эволюция монолита

Изначально весь проект начинался как прототип, и вся инициализация Vulkan, окон и отрисовки находилась… в одной гигантской функции main(). Когда масштабы стали критическими, я перенес этот код в класс Application.

Но теперь и Application превратился в классический «Класс-Бог» (God Class). В одном месте у меня намешано всё:

  • Инициализация устройств и очередей Vulkan;

  • Создание Swapchain и управление кадрами;

  • Запись барьеров (pipelineBarrier2) прямо внутри кадра рендеринга;

  • Редакторский интерфейс ImGui и логика загрузки ассетов.

Добавление любого нового пасса рендеринга (например, теней или SSAO) превращается в ручное дописывание сотен строк кода в этот монолит и риск сломать синхронизацию Vulkan. Пора это исправить.

Что будем делать на стриме?

В эту пятницу, 5 сентября в 19:00 по МСК (UTC+3), я проведу свой первый LIVE-стрим, который будет полностью посвящен фундаментальному архитектурному рефакторингу Shuttle Engine.

Мы превратим класс Application в легкий и понятный оркестратор (Mediator), разбив его на три независимых модуля:

  1. Application Module (PAL): Полностью изолируем работу с ОС (Win32/SDL), событиями ввода и созданием нативных окон.

  2. Engine Module (Vulkan Runtime): Перенесем туда всю работу с графическим API, VMA, RenderGraph и сценой.

  3. UI Module (RmlUi + ImGui): Выделим интерфейс в отдельный слой.

Этот рефакторинг — критически важный шаг, который подготовит фундамент для следующих тем: асинхронного многопоточного рендеринга и перехода на полноценную архитектуру RenderGraph.

Детали трансляции:

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

P.S. Проект разрабатывается независимо. Если вы хотите поддержать создание Shuttle Engine и помочь автору в обустройстве новой рабочей базы в это непростое время, вы можете сделать это на моей странице поддержки: https://boosty.to/shagunov. Любой вклад очень помогает продолжать работу над движком!

До встречи на стриме!

Теги:
+6
Комментарии5

Простой способ вычислить размер структуры/класса

Возьмем такой объект:

struct Object {
    bool b;     // std::alignment_of<bool>::value == 1
    double d;   // std::alignment_of<double>::value == 8
    int i;      // std::alignment_of<int>::value == 4
    char c;     // std::alignment_of<char>::value == 1
};

В комментарии к каждому типу данных указано требование к его выравниванию в памяти процесса на примере архитектуры x86_64 (спасибо Coder007 за комментарий). Не путать с размером типа данных. Начнем с первого поля структуры с типом bool – поместим его по нулевому адресу на импровизированной линейке памяти где каждая клетка занимает один байт (рис. 1).

У следующего поля типа double значение выравнивания 8, т.е. его начало должно располагаться на кратном 8 адресе памяти (рис. 2).

Для int с выравниваем 4 ближайший кратный 4 адрес это 16 (рис. 3).

А для char по той же схеме поставим его по адресу 20 (рис. 4).

Но это не все. Если допустить, что размер структуры 21 байт, то следующая такая же структура Object будет располагаться следом - по адресу 21. Для ее поля типа bool допустимо находиться на 21-й позиции, однако тогда поле типа double будет уже 29-й, которая не кратна 8, т.е. нарушится правило выравнивания (рис. 5).

Для данного примера наиболее простой способ разместить в памяти все объекты и при этом учесть их выравнивание это добавить в конец структуры 3 байта (рис. 6).

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

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

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

В честь выхода 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

Теги:
+6
Комментарии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 с поддержкой новых языков.

Теги:
Всего голосов 2: ↑2 и ↓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.

Теги:
Всего голосов 2: ↑2 и ↓0+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