Создание constexpr карты, заполненной вызовами constexpr функции в C++ 17

Вопрос или проблема

У меня есть constexpr функция, которая вычисляет CRC во время компиляции. Мне нужно создать отображение между CRC, которые генерируются этой функцией, и строковыми входными данными, и использовать их во время выполнения (даже лучше, если это также возможно использовать во время компиляции).

Процесс создания отображения должен происходить внутри этой constexpr функции, чтобы она автоматически регистрировала все сгенерированные CRC. Также это не обязательно должно быть отображение. Достаточно пар ключ-значение, которые можно было бы искать.

Кстати, я использую C++ 17.

Это сигнатура моей функции:

template <size_t length>
static constexpr unsigned int getCrc32(const char (&str)[length])
{
        //вычислить CRC во время компиляции

        // То, что мне нужно, должно происходить здесь: сохранить вычисленный
        // CRC и строку для последующего доступа
}

Я пробовал разные способы с шаблонами и другими вещами, но я либо получаю компилирующийся код с неопределенным поведением, либо неизменяемую constexpr стену.

Ответ или решение

Создание constexpr карты с соответствиями между CRC и строковыми вводами в C++ 17

Вам необходимо создать constexpr структуру, которая позволит вам сопоставлять заранее вычисленные контрольные суммы CRC с соответствующими строковыми значениями. Это можно реализовать с помощью std::array и метапрограммирования. Далее я подробнее опишу, как это сделать, а также объясню, какое решение позволит вам использовать эти соответствия как на этапе компиляции, так и во время выполнения программы.

Решение

  1. Структура для хранения ключей и значений:
    Создадим обертку, которая будет хранить пары "строка-CRC". Мы будем использовать std::array для хранения этих пар фиксированного размера.

  2. Создание constexpr функции для вычисления CRC:
    Ваша функция getCrc32 будет вычислять CRC и добавлять его в constexpr массив, что позволит нам в дальнейшем использовать его в других constexpr контекстах.

  3. Использование шаблонов для работы с строками:
    Мы воспользуемся параметрами шаблона для передачи строковых литералов в функцию и получения результата.

Пример кода

#include <array>
#include <utility>
#include <cstddef>

template <size_t length>
constexpr unsigned int getCrc32(const char (&str)[length]) {
    unsigned int crc = 0xffffffff; // Начальное значение CRC

    for (size_t i = 0; i < length - 1; ++i) { // length - 1 для игнорирования нулевого символа
        crc ^= static_cast<unsigned char>(str[i]);
        for (int j = 0; j < 8; ++j) {
            crc = (crc >> 1) ^ (0xedb88320u & static_cast<unsigned int>(-(crc & 1)));
        }
    }
    return crc;
}

template <typename T, size_t N>
struct CrcMap {
    std::array<std::pair<unsigned int, T>, N> entries;

    constexpr CrcMap(const std::array<std::pair<unsigned int, T>, N>& entryArray) 
        : entries(entryArray) {}

    constexpr const T* find(unsigned int crc) const {
        for (const auto& entry : entries) {
            if (entry.first == crc) {
                return entry.second.c_str();
            }
        }
        return nullptr; // Если не найдено
    }
};

// Переменные для хранения значений CRC и строк
constexpr auto crc_entries = std::array{
    std::make_pair(getCrc32("example1"), "example1"),
    std::make_pair(getCrc32("example2"), "example2"),
    std::make_pair(getCrc32("example3"), "example3")
};

constexpr auto crc_map = CrcMap<const char*, 3>(crc_entries);

// Пример использования
constexpr const char* lookup_example = crc_map.find(getCrc32("example1"));

int main() {
    if (lookup_example) {
        // Вывод: example1
        printf("%s\n", lookup_example);
    }
}

Пояснение кода

  1. Функция getCrc32:
    Эта функция принимает строку и возвращает её контрольную сумму CRC-32. Она использует алгоритм CRC с побитовой манипуляцией для вычисления контрольной суммы.

  2. Структура CrcMap:
    Эта структура хранит массив пар CRC и строк. Метод find ищет строку по заданной контрольной сумме.

  3. Создание constexpr массива crc_entries:
    В этом массиве мы инициализируем пары "CRC – строка", используя вычисленные контрольные суммы из функции getCrc32.

  4. Использование в main:
    Мы вызываем метод find для поиска строки по её контрольной сумме, полученной из getCrc32. Если строка найдена, она выводится на экран.

Заключение

Такое решение позволяет вам как на этапе компиляции, так и на этапе выполнения получать доступ к соответствиям между контрольными суммами и строками. Вы сможете легко расширять этот подход, добавляя новые строки и их контрольные суммы в массив. Программирование в C++ 17 с использованием constexpr открывает множество возможностей для оптимизации и улучшения производительности вашего кода.

Оцените материал
Добавить комментарий

Капча загружается...