Библиотека C/C++ разработчика | cpp, boost, qt
19.7K subscribers
1.93K photos
58 videos
16 files
4.16K links
Все самое полезное для плюсовика и сишника в одном канале.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/d6cd2932

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a5bac324c8ba6dcaa1ad17
Download Telegram
✏️ Потокобезопасная инициализация через std::call_once

Инициализация ресурсов в многопоточном приложении — классическая головная боль. std::call_once решает эту проблему элегантно: гарантирует вызов функции ровно один раз, даже если несколько потоков пытаются сделать это одновременно. Забудьте про ручные мьютексы и double-checked locking с их подводными камнями.

#include <mutex>
#include <memory>
#include <iostream>

class DatabaseConnection {
public:
static DatabaseConnection& getInstance() {
// std::call_once гарантирует однократный вызов лямбды
// даже при конкурентном доступе из разных потоков
std::call_once(initFlag, []() {
instance.reset(new DatabaseConnection());
std::cout << "Database initialized\n";
});
return *instance;
}

void query(const std::string& sql) {
std::cout << "Executing: " << sql << "\n";
}

// Запрещаем копирование и перемещение
DatabaseConnection(const DatabaseConnection&) = delete;
DatabaseConnection& operator=(const DatabaseConnection&) = delete;

private:
DatabaseConnection() {
// Тяжелая инициализация: подключение к БД,
// загрузка конфигурации и т.д.
}

static std::once_flag initFlag; // Флаг для call_once
static std::unique_ptr<DatabaseConnection> instance;
};

// Определение статических членов
std::once_flag DatabaseConnection::initFlag;
std::unique_ptr<DatabaseConnection> DatabaseConnection::instance;



‼️ Ключевые особенности:

Потокобезопасность из коробки — не нужен ручной mutex, std::call_once сам блокирует конкурентные вызовы
Без накладных расходов после первого вызова — последующие вызовы проверяют только атомарный флаг
Исключения не ломают логику — если инициализация бросила exception, флаг не устанавливается и следующий поток попробует снова
Работает с любыми callable — лямбды, функции, функторы, std::bind


Используете std::call_once в продакшене? Какие кейсы у вас?

Библиотека C/C++ разработчика

#буст
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6😁1
🍴 Callback chain (цепочка обработчиков)

Надоело писать if-else лесенки для обработки событий? Лямбды превращают это в изящную цепочку.


Паттерн Chain of Responsibility через лямбды позволяет регистрировать обработчики, которые выполняются последовательно, пока кто-то не обработает событие:

#include <vector>
#include <functional>
#include <algorithm>

template<typename Event>
class CallbackChain {
using Handler = std::function<bool(const Event&)>;
std::vector<Handler> handlers_;

public:
// Добавить обработчик в конец цепочки
void add_handler(Handler handler) {
handlers_.push_back(std::move(handler));
}

// Добавить обработчик в начало (высокий приоритет)
void add_handler_front(Handler handler) {
handlers_.insert(handlers_.begin(), std::move(handler));
}

// Обработать событие (возвращает true, если кто-то обработал)
bool handle(const Event& event) const {
for (const auto& handler : handlers_) {
if (handler(event)) {
return true; // Обработчик вернул true - останавливаемся
}
}
return false; // Никто не обработал
}

// Уведомить всех обработчиков (не останавливаясь)
void notify_all(const Event& event) const {
for (const auto& handler : handlers_) {
handler(event); // Игнорируем возвращаемое значение
}
}

// Очистить все обработчики
void clear() { handlers_.clear(); }

size_t size() const { return handlers_.size(); }
};


Использование для обработки HTTP-запросов:

struct HttpRequest {
std::string path;
std::string method;
std::map<std::string, std::string> params;
};

CallbackChain<HttpRequest> router;

// Регистрируем обработчики
router.add_handler([](const HttpRequest& req) {
if (req.path == "/api/users" && req.method == "GET") {
handle_get_users();
return true; // Обработали
}
return false; // Не наш запрос
});

router.add_handler([](const HttpRequest& req) {
if (req.path.starts_with("/api/")) {
return handle_api_request(req);
}
return false;
});

// В главном цикле
void process_request(const HttpRequest& req) {
if (!router.handle(req)) {
send_404_error();
}
}


Библиотека C/C++ разработчика

#шаблонный_код
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7👏2🎉21🌚1
🔥 std::flat_map в C++23 — быстрее std::map в 3-5 раз?

Представьте: вы профилируете код и видите, что std::map тормозит. Cache misses, фрагментация памяти, медленный поиск.


C++23 представляет std::flat_map и std::flat_set — адаптеры контейнеров, которые хранят элементы в непрерывной памяти. Вместо узлов дерева — два плоских массива (ключи + значения).

#include <flat_map>

std::flat_map<int, std::string> cache{
{1, "one"}, {2, "two"}, {3, "three"}
};

// Все элементы рядом в памяти — процессор доволен
auto it = cache.find(2); // Бинарный поиск по упорядоченному массиву


❗️ Главные преимущества:

• Меньше cache misses → быстрее на реальных данных
• Меньше аллокаций памяти
• Лучше для read-heavy сценариев

✏️ Когда использовать? Если у вас много поисков и мало изменений — flat_map будет более производительным чем классический map.

Библиотека C/C++ разработчика

#константная_правильность
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🥰4
🍒 std::generator — ленивые последовательности в стандарте

Писал итераторы с кучей boilerplate для ленивого чтения? Или тащил ranges::views для простых генераторов?


C++23 добавляет std::generator — coroutine-based ленивые последовательности. Пишешь как обычную функцию, получаешь итератор. Идеально для парсеров, ленивого чтения файлов, бесконечных последовательностей.

#include <generator>
#include <print>
#include <fstream>
#include <string>
#include <optional>

// Простой генератор чисел Фибоначчи
std::generator<uint64_t> fibonacci(size_t count) {
uint64_t a = 0, b = 1;

for (size_t i = 0; i < count; ++i) {
co_yield a;
auto next = a + b;
a = b;
b = next;
}
}

// Ленивое чтение строк из файла
std::generator<std::string> read_lines(const std::string& filename) {
std::ifstream file(filename);
std::string line;

while (std::getline(file, line)) {
co_yield line;
}
}

// Генератор простых чисел (бесконечный)
std::generator<uint64_t> primes() {
co_yield 2;

std::vector<uint64_t> found_primes;
uint64_t candidate = 3;

while (true) {
bool is_prime = true;
for (auto p : found_primes) {
if (p * p > candidate) break;
if (candidate % p == 0) {
is_prime = false;
break;
}
}

if (is_prime) {
found_primes.push_back(candidate);
co_yield candidate;
}

candidate += 2;
}
}

// Генератор с трансформацией
std::generator<int> squares(int n) {
for (int i = 0; i < n; ++i) {
co_yield i * i;
}
}

void demo() {
// Первые 10 чисел Фибоначчи
std::print("Fibonacci: ");
for (auto num : fibonacci(10)) {
std::print("{} ", num);
}
std::println("");

// Первые 20 простых
std::print("Primes: ");
size_t count = 0;
for (auto prime : primes()) {
std::print("{} ", prime);
if (++count >= 20) break;
}
std::println("");

// Квадраты
std::print("Squares: ");
for (auto sq : squares(5)) {
std::print("{} ", sq);
}
std::println("");
}


❗️ Используй generator вместо ручного написания итераторов для ленивых последовательностей. Память выделяется только под state coroutine. Можно делать бесконечные последовательности без риска.

Библиотека C/C++ разработчика

#под_капотом
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍1