Инициализация ресурсов в многопоточном приложении — классическая головная боль.
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;
std::call_once сам блокирует конкурентные вызовыИспользуете std::call_once в продакшене? Какие кейсы у вас?
Библиотека C/C++ разработчика
#буст
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6😁1
Надоело писать 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🎉2❤1🌚1
Представьте: вы профилируете код и видите, что 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
Писал итераторы с кучей 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