0

Can't get this to work and my brain is melting. The code looks like it should work. I want the function to only work at compile-time and not generate any runtime code or a callable function.

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 );
nowi
  • 437
  • 4
  • 13

4 Answers4

0

The <char...>-overload of operator"" is only usable for numbers, not for strings:

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
}

Therefore, you have to use the operator""_(char const *, std::size_t)-literal operator:

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;
}

Since c++14 loops are allowed in constexpr-functions so these kind of string-literal operators are usable at compile time.

MadScientist
  • 3,390
  • 15
  • 19
  • Unfortunately, it must be handled by a template parameter. It cannot be using the normal argument list – nowi Apr 02 '20 at 06:27
  • Why? This overload of `operator""` does work in `constexpr`-contexts. – MadScientist Apr 02 '20 at 06:31
  • But it generates other code which is a problem for me. – nowi Apr 02 '20 at 06:32
  • 1
    *"is easier to handle"*. It is in fact the only standard way. – Jarod42 Apr 02 '20 at 06:58
  • Right, I always forget that the ``-Version only applies to numbers and not to strings. – MadScientist Apr 02 '20 at 07:08
  • There's no such type as `uint_32t`. It should be `uint32_t`. – Ruslan Apr 02 '20 at 07:12
  • @nowi "*But it generates other code which is a problem for me*" - you are going to have to be more specific about your troubles if you want to get help to solve it. – Remy Lebeau Apr 02 '20 at 07:18
  • @RemyLebeau sorry, to clarify: i need it to return a different type depending on the parameters. this returns a crc32 of the string for a class parameter – nowi Apr 02 '20 at 07:56
  • Right now it is simply not possible to return a type of a string-literal-operator that depends on the content of the string. This is only possible for the ``-overload but unfortunately this one is not usable according to the standard. – MadScientist Apr 02 '20 at 08:51
0

You have 2 issues.

There are no template version for string. gcc has extension for that

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

So you need to use other overload

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

Second issue if you miss the else part of 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... );
}

else you recursive function is still instantiated, even when sizeof...( lstr ) == 0.

But with constexpr std::uint_32t operator""_crc(char const * str, std::size_t len) overload, you won't have if constexpr issue anyway

Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

What about C++20 ?

In CppReference i read that, starting from C++20,

If the overload set includes a string literal operator template with a non-type template parameter for which str is a well-formed template argument, then the user-defined literal expression is treated as a function call operator "" X<str>(),

So I suppose that if the string argument can be used to construct a str object for a template value parameter...

I imagine something as follows

#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 );
 }

Obviously you have to modify getCrc() to compute an effective CRC.

Unfortunately this, at the moment, seems to works with g++ but not with clang++.

max66
  • 65,235
  • 10
  • 71
  • 111
0

Based on this comment...

@RemyLebeau sorry, to clarify: i need it to return a different type depending on the parameters. this returns a crc32 of the string for a class parameter

As noted in that comment thread, there is no way to do that in a standard compliant way (through the C++17).

If you can use either clang or gcc, then you can use the gcc extension as both support it. I often forget about MSVC unless it is explicitly mentioned - I've not really used a windows-based OS since they went from Windows 3 to Windows ME.

However, since your crc function is constexpr, you can map the crc value to a unique type.

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>{};

You will get a unique type for each unique value returned from "some string"_crc.

It's not as nice, but you may not get much closer to what you want with string literals using your compiler.

Before you commit to using a 32-bit CRC, look here to see the chance of getting a collision (e.g., producing the same hash with different strings) and make sure you are comfortable with it.

Jody Hagins
  • 27,943
  • 6
  • 58
  • 87