Вопрос или проблема
У меня есть 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
и метапрограммирования. Далее я подробнее опишу, как это сделать, а также объясню, какое решение позволит вам использовать эти соответствия как на этапе компиляции, так и во время выполнения программы.
Решение
-
Структура для хранения ключей и значений:
Создадим обертку, которая будет хранить пары "строка-CRC". Мы будем использоватьstd::array
для хранения этих пар фиксированного размера. -
Создание
constexpr
функции для вычисления CRC:
Ваша функцияgetCrc32
будет вычислять CRC и добавлять его вconstexpr
массив, что позволит нам в дальнейшем использовать его в другихconstexpr
контекстах. -
Использование шаблонов для работы с строками:
Мы воспользуемся параметрами шаблона для передачи строковых литералов в функцию и получения результата.
Пример кода
#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);
}
}
Пояснение кода
-
Функция
getCrc32
:
Эта функция принимает строку и возвращает её контрольную сумму CRC-32. Она использует алгоритм CRC с побитовой манипуляцией для вычисления контрольной суммы. -
Структура
CrcMap
:
Эта структура хранит массив пар CRC и строк. Методfind
ищет строку по заданной контрольной сумме. -
Создание
constexpr
массиваcrc_entries
:
В этом массиве мы инициализируем пары "CRC – строка", используя вычисленные контрольные суммы из функцииgetCrc32
. -
Использование в
main
:
Мы вызываем методfind
для поиска строки по её контрольной сумме, полученной изgetCrc32
. Если строка найдена, она выводится на экран.
Заключение
Такое решение позволяет вам как на этапе компиляции, так и на этапе выполнения получать доступ к соответствиям между контрольными суммами и строками. Вы сможете легко расширять этот подход, добавляя новые строки и их контрольные суммы в массив. Программирование в C++ 17 с использованием constexpr
открывает множество возможностей для оптимизации и улучшения производительности вашего кода.