Обновить
256K+

C++ *

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

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

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

Не знаю, то ли я туповат, то ли невезучий, то ли кругом сказочники. Пытаюсь исследовать, какие новые интересные виды ошибок может находить ИИ в С++ коде, о которых не знает 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 отдаёт указатели наружу. Не баг.

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

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

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

Теги:
+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.

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

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

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

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

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

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

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

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

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

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

Теги:
+3
Комментарии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 года подряд защищать свое предложение, отвечая на все мелкие придирки различных подкомитетов?

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

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

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

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

Теги:
+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 обычно означает, что флаги линкера не подхватились.

Теги:
+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