Только во время компиляции пользовательский литерал CRC32 - PullRequest
0 голосов
/ 02 апреля 2020

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

template< char... str >
constexpr uint32_t operator "" _crc( )
{
    constexpr auto lambda = [ ]( auto l,
                                 uint32_t crc,
                                 auto first,
                                 auto ... lstr )
    {
        if constexpr ( sizeof...( lstr ) == 0 )
            return ~crc;

        return l( l,
                  uCRCTable[ ( crc ^ first ) & 0xFF ] ^ crc >> 8,
                  lstr... );
    };
    return lambda( lambda,
                   uCRCTable[ 0 ],
                   str... );
}

static_assert( 0xC213271D == "stack overflow"_crc );

Ответы [ 4 ]

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

На основании этого комментария ...

@ RemyLebeau извините, чтобы уточнить: мне нужно, чтобы он возвращал другой тип в зависимости от параметров. это возвращает crc32 строки для параметра класса

. Как отмечалось в этой ветке комментариев, нет способа сделать это стандартным образом (через C ++ 17).

Если вы можете использовать либо clang, либо g cc, то вы можете использовать расширение g cc, поскольку оба поддерживают его. Я часто забываю о MSV C, если это явно не упомянуто - я на самом деле не использовал ОС на windows, поскольку они перешли с Windows 3 на Windows ME.

Однако, так как ваша функция cr c является constexpr, вы можете сопоставить значение cr c с уникальным типом.

constexpr std::uint32_t
operator""_crc(char const * str, std::size_t len)
{
    // implementation to compute the value
}

template <auto Val>
struct CRC
: std::integral_constant<decltype(Val), Val>
{ };

// example...
constexpr auto zzz = CRC<"zzz"_crc>{};

Вы получите уникальный тип для каждого уникального значения, возвращаемого из "некоторой строки" _cr c.

Это не так приятно, но вы не можете приблизиться к тому, что вы хотите, с помощью строковых литералов с помощью вашего компилятора.

Прежде чем вы решите использовать 32-битный CR C, посмотрите здесь , чтобы увидеть вероятность получения столкновения (например, создание одного и того же га sh с разными строками) и убедитесь, что вы чувствуете себя комфортно с ним.

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

У вас 2 проблемы.

Нет версии шаблона для строки. g cc имеет расширение для этого

template<typename Char, Char... str >
constexpr uint32_t operator "" _crc()

Так что вам нужно использовать другую перегрузку

constexpr std::uint_32t operator""_crc(char const * str, std::size_t len)

Вторая проблема, если вы пропустите else часть if constexpr:

template <typename Char, Char... str >
constexpr uint32_t operator "" _crc( )
{
    constexpr auto lambda = [ ]( auto l,
                                 uint32_t crc,
                                 auto first,
                                 auto ... lstr )
    {
        if constexpr ( sizeof...( lstr ) == 0 )
            return ~crc;
        else
            return l( l,
                  uCRCTable[ ( crc ^ first ) & 0xFF ] ^ crc >> 8,
                  lstr... );
    };
    return lambda( lambda,
                   uCRCTable[ 0 ],
                   str... );
}

иначе ваша рекурсивная функция все еще создается, даже когда sizeof...( lstr ) == 0.

Но с перегрузкой constexpr std::uint_32t operator""_crc(char const * str, std::size_t len) вы не будете есть if constexpr проблема в любом случае

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

Как насчет C ++ 20?

В CppReference Я прочитал это, начиная с C ++ 20,

Если в набор перегрузки входит шаблон строкового литерального оператора с нетипичным параметром шаблона, для которого str является правильно сформированным аргументом шаблона, тогда пользовательское литеральное выражение обрабатывается как вызов функции operator "" X<str>(),

Поэтому я предполагаю, что если строковый аргумент можно использовать для создания объекта str для параметра значения шаблона ...

Я представляю что-то следующим образом

#include <cstdint>

struct Crc
 {
   std::uint32_t  val;

   static constexpr std::uint32_t getCrc (char const * str)
    { return 123u; }

   constexpr Crc (char const * str) : val{getCrc(str)}
    { }
 };

template <Crc crcVal>
constexpr std::uint32_t operator "" _crc ()
 { return crcVal.val; }

int main ()
 {
   static_assert( 123u == "hello world"_crc );
 }

Очевидно, что вы должны изменить getCrc() для вычисления эффективного CR C.

К сожалению, в данный момент это, похоже, работает с g ++, но не с clang ++.

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

Перегрузка <char...> в operator"" может использоваться только для чисел, а не для строк:

template <char ...cs>
constexpr int operator""_boom()
{
  return sizeof...(cs);
}

void test()
{
  static_assert(1234_boom == 4, "");
  static_assert("abcd"_boom == 4, ""); // does not compile
}

Поэтому вы должны использовать буквенный оператор operator""_(char const *, std::size_t):

constexpr std::uint32_t operator""_crc(char const * str, std::size_t len)
{
  char const * end = str + len;
  std::uint32_t hash = uCRCTable[0];
  for (; str < end; ++str)
  {
    hash = uCRCTable[(hash ^ *str) & 0xff] ^ hash >> 8;
  }
  return ~hash;
}

Поскольку циклы c ++ 14 разрешены в функциях constexpr, поэтому строковые литеральные операторы можно использовать во время компиляции.

...