Превращение строкового литерала в constexpr CRC32 внутри функции - PullRequest
2 голосов
/ 18 апреля 2020

У меня есть API, который должен быть максимально простым. В то же время строки некоторых функций «хорошо известны», поэтому всегда являются строковыми литералами и поэтому могут быть преобразованы в значения CRC32 во время компиляции, например, так:

function(crc32("text"), ...);

Контрольная сумма происходит в время компиляции с использованием constexpr.

Я хочу упростить этот API, чтобы люди, с которыми я работаю, не знали этих бессмысленных деталей. Я хочу, чтобы контрольная сумма во время компиляции происходила внутри функции, например:

function("text", ...);

Выполнение контрольной суммы внутри встроенной функции не работает, поскольку аргумент функции больше не является constexpr. Следующий код не удастся скомпилировать:

inline void function (const char* text, ...) {
    constexpr uint32_t hash = crc32(text); // does not work
}

Это для эмулируемой среды, поэтому важно, чтобы контрольная сумма происходила во время компиляции. Есть несколько функций, которые работают таким образом, и все они выиграют от упрощения API.

Какие варианты у меня есть, чтобы скрыть тот факт, что вы должны помнить, чтобы использовать вызов к crc32?

Используя идею rustyx, я сделал это:

struct AutoCRC {
    constexpr AutoCRC(const char* str) : hash{crc32(str)} {}
    constexpr AutoCRC(const uint32_t h) : hash{h} {}
    const uint32_t hash;
};

Но это не сработало. По сравнению с использованием constexpr CRC32 ha sh непосредственно это почти удвоило размер двоичного файла: 1488 -> 2696.

Ответы [ 2 ]

0 голосов
/ 19 апреля 2020

Кажется, что нет другого выбора, кроме как использовать макрос в этом случае:

#define FUNCTION(text, ...) nspc::function(crc32(text), __VA_ARGS__)

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

Макрос решает мою проблему.

0 голосов
/ 18 апреля 2020

Обратите внимание, что даже в случае function(crc32("text"), ...) контекст вызова crc32("text") не constexpr, и поэтому он не гарантированно произойдет во время компиляции. Для такой гарантии вам нужно consteval.

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

Например:

#include <string_view>

constexpr uint32_t checksum(std::string_view sv) noexcept {
    uint32_t sum = 0;
    for (unsigned char c : sv)
        sum += c;
    return sum;
}

struct StrWithSum {
    const char* str;
    const uint32_t sum;
    constexpr StrWithSum(const char* p) : str(p), sum(checksum(p)) {}
};

uint32_t do_something(StrWithSum str) {
    return str.sum + 2;
}

int main() {
    return do_something("ABC");
    static_assert(StrWithSum("ABC").sum == 198, "sum");
}

Мы можем увидеть , что функция do_something компилируется до операции + 2 как в G CC, так и в clang:

_Z12do_something10StrWithSum: # do_something
  lea eax, [rsi + 2]
  ret

И всей программе до

main: # @main
  mov eax, 200
  ret

(к сожалению, не в VC ++).

...