Собираем маленький scope guard!
Иногда нужно выполнить действие при выходе из функции: закрыть временный режим, вернуть флаг обратно, удалить временный файл или откатить локальное состояние.
Писать cleanup руками в каждом return неудобно:
Если return станет больше, легко забыть один из вызовов disable().
Сделаем небольшой scope guard:
В деструкторе он выполнит переданную функцию:
Запретим копирование, чтобы cleanup не вызвался дважды:
Поля класса:
Теперь очистка привязана к области видимости:
disable вызовется и при обычном завершении, и при раннем return.
Тот же подход удобен для временных файлов:
И для восстановления старого значения:
Scope guard это простой способ сделать cleanup надёжным. Код меньше зависит от того, сколько return появится внутри функции.
📣 C++ Ready | #практика
Иногда нужно выполнить действие при выходе из функции: закрыть временный режим, вернуть флаг обратно, удалить временный файл или откатить локальное состояние.
Писать cleanup руками в каждом return неудобно:
void process() {
enable();
if (!check_input()) {
disable();
return;
}
run_job();
disable();
}Если return станет больше, легко забыть один из вызовов disable().
Сделаем небольшой scope guard:
#include <functional>
#include <utility>
class ScopeExit {
public:
explicit ScopeExit(std::function<void()> fn)
: fn_(std::move(fn)) {}
В деструкторе он выполнит переданную функцию:
~ScopeExit() {
if (fn_) {
fn_();
}
}Запретим копирование, чтобы cleanup не вызвался дважды:
ScopeExit(const ScopeExit&) = delete;
ScopeExit& operator=(const ScopeExit&) = delete;
Поля класса:
private:
std::function<void()> fn_;
};
Теперь очистка привязана к области видимости:
void process() {
enable();
ScopeExit guard([] {
disable();
});
if (!check_input()) {
return;
}
run_job();
}disable вызовется и при обычном завершении, и при раннем return.
Тот же подход удобен для временных файлов:
ScopeExit cleanup([&] {
std::filesystem::remove(path);
});И для восстановления старого значения:
bool old = config.debug;
config.debug = true;
ScopeExit restore([&] {
config.debug = old;
});
Scope guard это простой способ сделать cleanup надёжным. Код меньше зависит от того, сколько return появится внутри функции.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥3
Интересная статья о том, как устроить свою SDL-подобную библиотеку на C++!
В этой статье:
• Как создать окно, renderer и базовый цикл обработки событий
• Как разделять платформозависимый код и общие интерфейсы
• Почему в графических библиотеках важны абстракции, совместимость и управление ресурсами
📣 C++ Ready | #статья
В этой статье:
• Как создать окно, renderer и базовый цикл обработки событий
• Как разделять платформозависимый код и общие интерфейсы
• Почему в графических библиотеках важны абстракции, совместимость и управление ресурсами
Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9🔥5👍3
Шпаргалка по special member functions в C++!
Например, если в классе объявить деструктор, компилятор уже не будет автоматически объявлять move constructor и move assignment. А если объявить move constructor, copy-операции могут стать deleted.
На картинке таблица правил генерации специальных методов класса: default constructor, destructor, copy constructor, copy assignment, move constructor и move assignment в зависимости от того, что уже объявил разработчик.
Сохрани, чтобы не потерять!
📣 C++ Ready | #ресурс
Например, если в классе объявить деструктор, компилятор уже не будет автоматически объявлять move constructor и move assignment. А если объявить move constructor, copy-операции могут стать deleted.
На картинке таблица правил генерации специальных методов класса: default constructor, destructor, copy constructor, copy assignment, move constructor и move assignment в зависимости от того, что уже объявил разработчик.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤5👍5
Собираем типобезопасные флаги через enum class!
Иногда нужно хранить набор прав или режимов: чтение, запись, выполнение, логирование, debug-режимы. Часто для этого используют обычные int-флаги, но с ними легко случайно смешать разные значения.
Начнём с enum class:
Сам по себе enum class не умеет складываться через
Теперь можно собрать набор прав:
Чтобы проверять наличие флага, сделаем маленькую функцию:
Использование получается читаемым:
Можно добавить и удаление флага:
Например, забрать право записи:
Такой подход удобен для прав доступа, настроек движка, режимов логирования, feature flags и любых случаев, где несколько булевых значений логически относятся к одному набору.
📣 C++ Ready | #практика
Иногда нужно хранить набор прав или режимов: чтение, запись, выполнение, логирование, debug-режимы. Часто для этого используют обычные int-флаги, но с ними легко случайно смешать разные значения.
Начнём с enum class:
enum class Permission : unsigned {
Read = 1 << 0,
Write = 1 << 1,
Exec = 1 << 2
};Сам по себе enum class не умеет складываться через
|, поэтому добавим оператор:Permission operator|(Permission a, Permission b) {
return static_cast<Permission>(
static_cast<unsigned>(a) | static_cast<unsigned>(b)
);
}Теперь можно собрать набор прав:
auto user = Permission::Read | Permission::Write;
Чтобы проверять наличие флага, сделаем маленькую функцию:
bool has(Permission value, Permission flag) {
return (
static_cast<unsigned>(value) &
static_cast<unsigned>(flag)
) != 0;
}Использование получается читаемым:
if (has(user, Permission::Write)) {
std::cout << "Можно изменять данные\n";
}Можно добавить и удаление флага:
Permission remove(Permission value, Permission flag) {
return static_cast<Permission>(
static_cast<unsigned>(value) & ~static_cast<unsigned>(flag)
);
}Например, забрать право записи:
user = remove(user, Permission::Write);
Такой подход удобен для прав доступа, настроек движка, режимов логирования, feature flags и любых случаев, где несколько булевых значений логически относятся к одному набору.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍7🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Этот репозиторий помогает писать более безопасный, понятный и удобный для поддержки код. Здесь объясняется, как правильно использовать инструменты разработки, оформлять проекты, избегать распространённых ошибок и учитывать производительность, переносимость и многопоточность.
Оставляю ссылочку: GitHub
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍10🔥4
Интересная статья о том, как начать делать игру на C++ с помощью SFML!
В этой статье:
• Как создать базовое окно и подключить SFML к проекту
• Как отрисовать персонажа и подготовить первые игровые объекты
• С чего начинается простая 2D-игра на C++ без тяжёлого движка
📣 C++ Ready | #статья
В этой статье:
• Как создать базовое окно и подключить SFML к проекту
• Как отрисовать персонажа и подготовить первые игровые объекты
• С чего начинается простая 2D-игра на C++ без тяжёлого движка
Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥7❤3😁1
Когда в C++ помогает std::exchange?
Иногда нужно забрать старое значение переменной и сразу записать новое. Часто это делают в две строки:
Работает, но намерение размазано, сначала читаем, потом отдельно меняем.
В C++ есть std::exchange:
Он возвращает старое значение, а в переменную кладёт новое.
Это удобно для флагов:
Теперь сразу понятно, мы выключили running и сохранили, был ли он включён раньше.
Ещё пример, забрать накопленный буфер:
Метод возвращает старый buffer, а внутри объекта оставляет пустую строку.
📣 C++ Ready | #совет
Иногда нужно забрать старое значение переменной и сразу записать новое. Часто это делают в две строки:
auto old = state;
state = State::Closed;
Работает, но намерение размазано, сначала читаем, потом отдельно меняем.
В C++ есть std::exchange:
auto old = std::exchange(state, State::Closed);
Он возвращает старое значение, а в переменную кладёт новое.
Это удобно для флагов:
bool wasRunning = std::exchange(running, false);
Теперь сразу понятно, мы выключили running и сохранили, был ли он включён раньше.
Ещё пример, забрать накопленный буфер:
std::string takeBuffer() {
return std::exchange(buffer, {});
}Метод возвращает старый buffer, а внутри объекта оставляет пустую строку.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍4🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
Этот репозиторий представляет собой подробное руководство по современным практикам разработки на C++. В нём собраны рекомендации по организации проектов, стилю кода, оптимизации производительности, тестированию, работе с инструментами анализа и другим аспектам профессиональной разработки.
Оставляю ссылочку: GitHub
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍8🔥5
Шпаргалка по random distributions в C++!
Например, std::uniform_int_distribution подходит для случайных целых чисел в диапазоне, а std::normal_distribution для значений с нормальным распределением.
На картинке распределения из <random>: uniform, bernoulli, binomial, geometric, normal, lognormal, poisson, exponential, gamma, weibull и другие варианты с формулами и графиками.
Сохрани, чтобы не потерять!
📣 C++ Ready | #ресурс
Например, std::uniform_int_distribution подходит для случайных целых чисел в диапазоне, а std::normal_distribution для значений с нормальным распределением.
На картинке распределения из <random>: uniform, bernoulli, binomial, geometric, normal, lognormal, poisson, exponential, gamma, weibull и другие варианты с формулами и графиками.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤11🔥5🤝1
Собираем проверку расширений файлов на C++!
Иногда нужно быстро отфильтровать файлы: взять только
Подключим нужные заголовки:
Функция будет принимать путь и расширение:
Обойдём папку рекурсивно:
Проверим расширение:
Теперь можно найти все JSON-файлы:
Если папки нет,
std::filesystem позволяет писать маленькие файловые утилиты без платформенного кода. Такой фильтр легко расширить под поиск логов, конфигов, бэкапов или временных файлов.
📣 C++ Ready | #практика
Иногда нужно быстро отфильтровать файлы: взять только
.log, .json, .txt или изображения. Сделаем небольшую функцию на std::filesystem, которая проходит по папке и возвращает файлы с нужным расширением.Подключим нужные заголовки:
#include <filesystem>
#include <string>
#include <vector>
namespace fs = std::filesystem;
Функция будет принимать путь и расширение:
std::vector<fs::path> find_files(
const fs::path& root,
std::string_view extension
) {
std::vector<fs::path> result;
Обойдём папку рекурсивно:
for (const auto& entry : fs::recursive_directory_iterator(root)) {
if (!entry.is_regular_file()) {
continue;
}Проверим расширение:
if (entry.path().extension() == extension) {
result.push_back(entry.path());
}
}
return result;
}Теперь можно найти все JSON-файлы:
auto files = find_files("configs", ".json");
for (const auto& file : files) {
std::cout << file << '\n';
}Если папки нет,
recursive_directory_iterator может бросить исключение. Для утилит это нормально обработать явно:try {
auto files = find_files("configs", ".json");
} catch (const fs::filesystem_error& error) {
std::cerr << error.what() << '\n';
}std::filesystem позволяет писать маленькие файловые утилиты без платформенного кода. Такой фильтр легко расширить под поиск логов, конфигов, бэкапов или временных файлов.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍5🔥2
Разберём std::filesystem: 7 операций для работы с файлами и папками в C++!
std::filesystem помогает писать файловые утилиты без платформенного кода: собирать пути, проверять файлы, создавать папки, обходить директории и аккуратно чистить временные данные. Эта шпора пригодится для CLI-инструментов, логов, конфигов, сборочных скриптов и backend-задач на C++.
📣 C++ Ready | #шпора
std::filesystem помогает писать файловые утилиты без платформенного кода: собирать пути, проверять файлы, создавать папки, обходить директории и аккуратно чистить временные данные. Эта шпора пригодится для CLI-инструментов, логов, конфигов, сборочных скриптов и backend-задач на C++.
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12❤2👍2
Media is too big
VIEW IN TELEGRAM
hacking C++ визуальный сайт с современными материалами по C++!
На сайте собраны гайды, короткие статьи, рецепты, cheat sheets, инфографика и подборки материалов по современному C++: стандартная библиотека, алгоритмы, ranges, контейнеры, templates, move semantics, memory management, random, diagnostics, best practices и инструменты. Это не одна страница и не сухая документация: можно проваливаться в разделы, листать примеры, открывать инфографику и быстро повторять конкретные темы перед практикой или разбором чужого кода.
📣 C++ Ready | #ресурс
На сайте собраны гайды, короткие статьи, рецепты, cheat sheets, инфографика и подборки материалов по современному C++: стандартная библиотека, алгоритмы, ranges, контейнеры, templates, move semantics, memory management, random, diagnostics, best practices и инструменты. Это не одна страница и не сухая документация: можно проваливаться в разделы, листать примеры, открывать инфографику и быстро повторять конкретные темы перед практикой или разбором чужого кода.
Оставляю ссылочку: hackingcpp.com
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍4🔥3
Зачем в C++ использовать std::optional?
Иногда функция должна показать, что результат может отсутствовать.
Часто для этого возвращают специальное значение:
Проблема в том, что
Код вызова тоже становится неочевидным:
В C++17 для таких случаев есть
Теперь отсутствие результата выражено прямо в типе:
А само значение можно получить после проверки:
Такой подход хорошо подходит для поиска, парсинга, чтения конфига и любых операций, где “ничего не найдено” нормальный сценарий, а не ошибка программы.
📣 C++ Ready | #совет
Иногда функция должна показать, что результат может отсутствовать.
Часто для этого возвращают специальное значение:
int find_user_id(std::string_view name) {
if (name.empty()) {
return -1;
}
return 42;
}Проблема в том, что
-1 это договорённость, которую легко забыть.Код вызова тоже становится неочевидным:
int id = find_user_id(name);
if (id == -1) {
std::cout << "not found\n";
}
В C++17 для таких случаев есть
std::optional:std::optional<int> find_user_id(std::string_view name) {
if (name.empty()) {
return std::nullopt;
}
return 42;
}Теперь отсутствие результата выражено прямо в типе:
auto id = find_user_id(name);
if (!id) {
std::cout << "not found\n";
return;
}
А само значение можно получить после проверки:
std::cout << *id << '\n';
Такой подход хорошо подходит для поиска, парсинга, чтения конфига и любых операций, где “ничего не найдено” нормальный сценарий, а не ошибка программы.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9🔥6👍3
Сильная статья о том, как написать свой компилятор на C++ с использованием LLVM!
В этой статье:
• Как компилятор проходит путь от исходного кода до промежуточного представления
• Зачем нужны лексер, парсер, семантика и LLVM IR
• Как устроить ошибки, типы и генерацию кода в собственном языке
📣 C++ Ready | #статья
В этой статье:
• Как компилятор проходит путь от исходного кода до промежуточного представления
• Зачем нужны лексер, парсер, семантика и LLVM IR
• Как устроить ошибки, типы и генерацию кода в собственном языке
Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7❤5🔥4
Когда в C++ лучше использовать std::span?
Иногда функция должна работать с массивом, vector или частью данных, но владеть этими данными ей не нужно.
Часто в таком случае передают vector:
Проблема в том, что функция теперь привязана именно к std::vector.
Если у тебя есть обычный массив, придётся писать перегрузку или создавать vector:
В C++20 для таких случаев есть std::span:
Теперь функция принимает разные источники данных:
Можно передать и часть диапазона:
std::span не владеет памятью. Он просто смотрит на уже существующие данные.
Поэтому нельзя возвращать span на локальный массив:
После выхода из функции массив исчезнет, а span останется висячей ссылкой.
📣 C++ Ready | #совет
Иногда функция должна работать с массивом, vector или частью данных, но владеть этими данными ей не нужно.
Часто в таком случае передают vector:
int sum(const std::vector<int>& values) {
int result = 0;
for (int value : values) {
result += value;
}
return result;
}Проблема в том, что функция теперь привязана именно к std::vector.
Если у тебя есть обычный массив, придётся писать перегрузку или создавать vector:
int data[] = {1, 2, 3};
// sum(data) не подойдётВ C++20 для таких случаев есть std::span:
int sum(std::span<const int> values) {
int result = 0;
for (int value : values) {
result += value;
}
return result;
}Теперь функция принимает разные источники данных:
std::vector<int> a{1, 2, 3};
std::array<int, 3> b{4, 5, 6};
int c[] = {7, 8, 9};
sum(a);
sum(b);
sum(c);Можно передать и часть диапазона:
sum(std::span(a).subspan(1, 2));
std::span не владеет памятью. Он просто смотрит на уже существующие данные.
Поэтому нельзя возвращать span на локальный массив:
std::span<int> bad() {
int values[] = {1, 2, 3};
return values;
}После выхода из функции массив исчезнет, а span останется висячей ссылкой.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤5🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Полезный ресурс для разработчиков на C и C++. На сайте собрана подробная документация по языкам и стандартной библиотеке: типы данных, функции, классы, шаблоны, контейнеры, алгоритмы, работа с памятью, потоки, многопоточность и новые возможности стандартов C++11, C++14, C++17, C++20 и C++23.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍8🔥4