frozen для C++: полное руководство с примерами

Онлайн-тренажер Python для начинающих

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

Начать курс

Библиотека frozen для C++: Полное руководство по compile-time контейнерам

Библиотека frozen — это header-only библиотека для C++17 и новее, предоставляющая контейнеры (map, set, string), которые полностью инициализируются на этапе компиляции (compile-time). Она позволяет создавать неизменяемые (immutable) структуры данных, которые не требуют динамического выделения памяти во время выполнения. Это идеальное решение для встраиваемых систем, игровых движков, высоконагруженных серверов и любого кода, где критична производительность и предсказуемость времени выполнения.

Основная идея frozen заключается в том, что вы определяете контейнер как constexpr или constinit, и вся работа по сортировке, хешированию и построению структуры данных происходит во время компиляции. В runtime вы получаете готовый, оптимизированный контейнер, который работает с минимальными накладными расходами. Например, frozen::map использует бинарный поиск по отсортированному массиву, а frozen::unordered_map — совершенное хеширование (perfect hashing), что гарантирует O(1) доступ без коллизий.

Для разработчиков на C++ это означает: отсутствие динамической памяти (нет new/delete), отсутствие исключений (noexcept), полная потокобезопасность (так как данные только для чтения) и возможность размещения контейнеров в flash-памяти микроконтроллеров. Библиотека активно используется в проектах Facebook (ныне Meta), в компиляторах и в ядрах операционных систем.

Установка

Библиотека frozen является header-only, поэтому установка сводится к копированию заголовочных файлов в ваш проект. Самый простой способ — использовать Conan или CMake FetchContent. Ниже приведены точные команды для различных менеджеров пакетов.

Установка через Conan (рекомендуется)

conan install frozen/1.1.1@ -r conancenter
# Затем добавьте в CMakeLists.txt:
# find_package(frozen REQUIRED)
# target_link_libraries(your_target frozen::frozen)

Установка через vcpkg

vcpkg install frozen
# После установки используйте:
# find_package(frozen CONFIG REQUIRED)
# target_link_libraries(your_target frozen::frozen)

Установка через CMake FetchContent (без менеджера пакетов)

include(FetchContent)
FetchContent_Declare(
  frozen
  GIT_REPOSITORY https://github.com/serge-sans-paille/frozen.git
  GIT_TAG v1.1.1
)
FetchContent_MakeAvailable(frozen)
target_link_libraries(your_target frozen::frozen)

Ручная установка (header-only)

git clone https://github.com/serge-sans-paille/frozen.git
# Скопируйте папку frozen/include/frozen в ваш проект
# Добавьте путь к include в компилятор:
# g++ -I/path/to/frozen/include your_file.cpp -std=c++17

Быстрый старт: минимальный рабочий пример

Создайте файл example.cpp со следующим содержимым:

#include <iostream>
#include <frozen/unordered_map.h>
#include <frozen/string.h>

int main() {
    // Создаем compile-time map с совершенным хешированием
    constexpr frozen::unordered_map<frozen::string, int, 3> fruit_calories = {
        {"apple", 95},
        {"banana", 105},
        {"orange", 62}
    };

    // Поиск в runtime — O(1), без коллизий
    std::cout << "Калорийность банана: " << fruit_calories.at("banana") << "\n";

    // Проверка наличия ключа
    if (auto it = fruit_calories.find("grape"); it != fruit_calories.end()) {
        std::cout << "Виноград найден: " << it->second << "\n";
    } else {
        std::cout << "Виноград не найден\n";
    }

    // Итерация по всем элементам
    for (auto& [fruit, calories] : fruit_calories) {
        std::cout << fruit.data() << ": " << calories << " ккал\n";
    }

    return 0;
}

Скомпилируйте и запустите:

g++ -std=c++17 -O2 example.cpp -o example
./example

Ожидаемый вывод:

Калорийность банана: 105
Виноград не найден
apple: 95 ккал
banana: 105 ккал
orange: 62 ккал

Основные методы, функции и классы

Библиотека frozen предоставляет несколько ключевых контейнеров. Все они объявлены в пространстве имен frozen и требуют указания размера на этапе компиляции (как шаблонный параметр). Рассмотрим 8 основных компонентов.

1. frozen::map (упорядоченный map)

Сигнатура:

template<class Key, class Value, std::size_t N>
struct map {
    constexpr map(std::initializer_list<std::pair<Key, Value>> init);
    constexpr const Value& at(const Key& key) const;
    constexpr const_iterator find(const Key& key) const;
    constexpr bool count(const Key& key) const;
    constexpr std::size_t size() const noexcept;
    // ... итераторы begin(), end()
};

Что делает: Создает отсортированный массив пар ключ-значение. Поиск выполняется бинарным поиском за O(log N). Все данные известны на этапе компиляции.

Пример:

#include <frozen/map.h>
#include <iostream>

int main() {
    constexpr frozen::map<int, const char*, 4> status_codes = {
        {200, "OK"},
        {404, "Not Found"},
        {500, "Internal Server Error"},
        {302, "Found"}
    };

    // Поиск по ключу
    std::cout << "Код 404: " << status_codes.at(404) << "\n"; // Not Found

    // Проверка существования
    if (status_codes.count(418)) {
        std::cout << "Код 418 существует\n";
    } else {
        std::cout << "Код 418 не найден\n";
    }

    // Итерация (в порядке сортировки ключей)
    for (auto& [code, msg] : status_codes) {
        std::cout << code << ": " << msg << "\n";
    }
    // Вывод: 200: OK, 302: Found, 404: Not Found, 500: Internal Server Error
    return 0;
}

2. frozen::unordered_map (хеш-таблица с совершенным хешированием)

Сигнатура:

template<class Key, class Value, std::size_t N>
struct unordered_map {
    constexpr unordered_map(std::initializer_list<std::pair<Key, Value>> init);
    constexpr const Value& at(const Key& key) const;
    constexpr const_iterator find(const Key& key) const;
    constexpr bool count(const Key& key) const;
    constexpr std::size_t size() const noexcept;
    // ... итераторы
};

Что делает: Использует совершенное хеширование (perfect hashing), гарантирующее O(1) поиск без коллизий. Хеш-функция вычисляется на этапе компиляции. Идеально для lookup-таблиц.

Пример:

#include <frozen/unordered_map.h>
#include <frozen/string.h>
#include <iostream>

int main() {
    constexpr frozen::unordered_map<frozen::string, int, 5> month_days = {
        {"jan", 31}, {"feb", 28}, {"mar", 31},
        {"apr", 30}, {"may", 31}
    };

    // Быстрый доступ O(1)
    std::cout << "Дней в марте: " << month_days.at("mar") << "\n";

    // Использование find
    auto it = month_days.find("jun");
    if (it == month_days.end()) {
        std::cout << "Июнь не найден\n";
    }
    return 0;
}

3. frozen::set (упорядоченное множество)

Сигнатура:

template<class Key, std::size_t N>
struct set {
    constexpr set(std::initializer_list<Key> init);
    constexpr const_iterator find(const Key& key) const;
    constexpr bool count(const Key& key) const;
    constexpr bool contains(const Key& key) const; // C++20
    constexpr std::size_t size() const noexcept;
};

Что делает: Отсортированный массив уникальных ключей. Поиск — бинарный O(log N). Полезен для проверки принадлежности к множеству.

Пример:

#include <frozen/set.h>
#include <iostream>

int main() {
    constexpr frozen::set<int, 6> primes = {2, 3, 5, 7, 11, 13};

    std::cout << "Проверка простых чисел:\n";
    for (int i = 1; i <= 15; ++i) {
        if (primes.count(i)) {
            std::cout << i << " — простое\n";
        }
    }
    return 0;
}

4. frozen::unordered_set (множество с совершенным хешированием)

Сигнатура:

template<class Key, std::size_t N>
struct unordered_set {
    constexpr unordered_set(std::initializer_list<Key> init);
    constexpr const_iterator find(const Key& key) const;
    constexpr bool count(const Key& key) const;
    constexpr std::size_t size() const noexcept;
};

Что делает: Аналог unordered_map, но хранит только ключи. Поиск O(1). Используется для быстрой проверки наличия элемента.

Пример:

#include <frozen/unordered_set.h>
#include <frozen/string.h>
#include <iostream>

int main() {
    constexpr frozen::unordered_set<frozen::string, 4> keywords = {
        "if", "else", "while", "for"
    };

    frozen::string test = "while";
    if (keywords.count(test)) {
        std::cout << test.data() << " — ключевое слово C++\n";
    }
    return 0;
}

5. frozen::string (compile-time строка)

Сигнатура:

template<std::size_t N>
struct string {
    constexpr string(const char (&str)[N + 1]); // из литерала
    constexpr const char* data() const noexcept;
    constexpr std::size_t size() const noexcept;
    constexpr char operator[](std::size_t i) const;
    constexpr bool operator==(const string& other) const;
    // ... сравнения, хеширование
};

Что делает: Обертка над C-строкой фиксированной длины. Позволяет использовать строковые литералы в compile-time контекстах. Поддерживает сравнение и хеширование.

Пример:

#include <frozen/string.h>
#include <iostream>

int main() {
    constexpr frozen::string<5> hello = "hello";
    constexpr frozen::string<5> world = "world";

    // Сравнение строк
    constexpr bool same = (hello == world);
    std::cout << "Строки равны: " << std::boolalpha << same << "\n";

    // Доступ по индексу
    std::cout << "Первый символ: " << hello[0] << "\n"; // 'h'

    // Получение C-строки
    std::cout << "Данные: " << hello.data() << "\n";
    return 0;
}

6. frozen::bitset (битовое множество фиксированного размера)

Сигнатура:

template<std::size_t N>
struct bitset {
    constexpr bitset() = default;
    constexpr bitset(std::initializer_list<std::size_t> bits);
    constexpr bool test(std::size_t pos) const;
    constexpr bitset& set(std::size_t pos);
    constexpr bitset& reset(std::size_t pos);
    constexpr std::size_t count() const noexcept;
    // ... операторы &, |, ^, ~
};

Что делает: Аналог std::bitset, но с возможностью инициализации через constexpr и с дополнительными удобствами. Хранит N бит в массиве целых чисел.

Пример:

#include <frozen/bitset.h>
#include <iostream>

int main() {
    constexpr frozen::bitset<8> flags = {1, 3, 5}; // биты 1, 3, 5 установлены

    std::cout << "Бит 3: " << flags.test(3) << "\n"; // true
    std::cout << "Бит 0: " << flags.test(0) << "\n";                    

Рекомендаций