Constant parameter of an expression string in C ++ 11 constexpr function

I'm trying to make a constexpr function that converts a UUID string like "f6ece560-cc3b-459a-87f1-22331582216e" to a class something like this:

 class UUID { public: explicit UUID(uint8_t bytes[]); // Must be 16 byte array. 

This is what I have so far:

 // Compile time hex conversion of a single character into a nibble (half-byte). constexpr uint8_t hexToNibble(char a) { // Does not work: // static_assert(a >= '0' && a <= '9' || a >= 'a' && a <= 'f' || a >= 'A' && a <= 'F', "Invalid hex character"); return a >= '0' && a <= '9' ? (a - '0') : a >= 'a' && a <= 'f' ? (a - 'a' + 10) : a >= 'A' && a <= 'F' ? (a - 'A' + 10) : 0; } // Compile time hex conversion of two characters into a byte. constexpr uint8_t hexToByte(char a, char b) { return (hexToNibble(a) << 4) + hexToNibble(b); } // Compile time string length. constexpr int strlenConst(const char* str) { return *str ? 1 + strlenConst(str + 1) : 0; } // Convert a UUID string to an array of bytes. // Example: "f6ece560-cc3b-459a-87f1-22331582216e". constexpr std::array<uint8_t, 16> UUIDFromString(const char* str) { // This does not work: // static_assert(strlenConst(str) == 36, "Invalid GUID length"); return std::array<uint8_t, 16>{ hexToByte(str[0], str[1]), hexToByte(str[2], str[3]), hexToByte(str[4], str[5]), hexToByte(str[6], str[7]), hexToByte(str[9], str[10]), hexToByte(str[11], str[12]), hexToByte(str[14], str[15]), hexToByte(str[16], str[17]), hexToByte(str[19], str[20]), hexToByte(str[21], str[22]), hexToByte(str[24], str[25]), hexToByte(str[26], str[27]), hexToByte(str[28], str[29]), hexToByte(str[30], str[31]), hexToByte(str[32], str[33]), hexToByte(str[34], str[35]), }; } #define MAKE_UUID(var, str) \ static_assert(strlenConst(str) == 36, "Invalid GUID length for " #var); \ const UUID var(UUIDFromString(str).data()); // Works but doesn't check string length. const UUID UUID_1(UUIDFromString("f6ece560-cc3b-459a-87f1-22331582216e").data()); // Checks string length but uses an ugly macro. MAKE_UUID(UUID_2, "f6ece560-cc3b-459a-87f1-22331582216e") 

As you can see, there is a problem - it seems that it is impossible to have functional parameters that are constant expressions in the constexpr function, so you cannot execute static_assert by parameters, even if the passed value in was a constant expression.

So, I used the macro to check the length of the string and refused to check the characters.

Is there any way around this? Also, how can I easily make sure that this function is actually evaluated at compile time?

Edit : is this not the same as C ++ 11 - static_assert in constexpr function? - or at least the same answers' t work - see comments below.

Change 2 . Shafik's good answer works for a size problem, but not for checking hexadecimal characters. As far as I can tell, this is not possible - even if you use it ...

 // Compile time hex conversion of a single character into a nibble (half-byte). template<char a> constexpr uint8_t hexToNibble() { static_assert(a >= '0' && a <= '9' || a >= 'a' && a <= 'f' || a >= 'A' && a <= 'F', "Invalid hex character"); return a >= '0' && a <= '9' ? (a - '0') : a >= 'a' && a <= 'f' ? (a - 'a' + 10) : a >= 'A' && a <= 'F' ? (a - 'A' + 10) : 0; } // Compile time hex conversion of two characters into a byte. template<char a, char b> constexpr uint8_t hexToByte() { return (hexToNibble<a>() << 4) + hexToNibble<b>(); } 

This will not work:

 // Convert a UUID string to an array of bytes. // Example: "f6ece560-cc3b-459a-87f1-22331582216e". template <size_t N> constexpr std::array<uint8_t, 16> UUIDFromString(const char (&str)[N]) { // Note you have to include the null byte. static_assert(N == 37, "Invalid GUID length."); return std::array<uint8_t, 16>{ hexToByte<str[0], str[1]>(), hexToByte<str[2], str[3]>(), 

Because str[0] not a constant expression.

+5
source share
2 answers

How does AndyG point to a C ++ 11 question - static_assert in constexpr function? tells us the way to do this is to use non-type template arguments that should be available at compile time.

The problem with this solution is that the OP uses string literals that cannot be associated with arguments other than type :

In particular, this means that string literals, addresses of array elements, and addresses of non-static members cannot be used as template arguments for instantiating templates for which non-standard template parameters are pointers to objects.

One work for this does not directly use a string literal, but uses a property important to the problem, which is the length of the array, for example:

 template <size_t N> constexpr std::array<uint8_t, 16> UUIDFromString( const char (&str)[N]) { static_assert(N == 36, "Invalid GUID length"); //.... } 
+4
source

If you want static_assert s, you have no choice, except for the template parameter, the corresponding arguments.

It has been noted that you cannot bind a string literal to a template other than the type parameter, which is legally true, but you can do it with a little rounding.

Here's a sketch:

 #include <cstddef> #include <array> constexpr char const f6ece560_cc3b_459a_87f1_22331582216e[] = "f6ece560-cc3b-459a-87f1-22331582216e"; constexpr int strlenConst(const char* str) { return *str ? 1 + strlenConst(str + 1) : 0; } template<char const * Hex> struct UUID { static constexpr char const * hex = Hex; static constexpr std::size_t len = strlenConst(hex); static_assert(len == 36,"Invalid GUID length"); template<char Ch> static constexpr uint8_t hexToNibble() { static_assert((Ch >= '0' && Ch <= '9') || (Ch >= 'a' && Ch <= 'f') || (Ch >= 'A' && Ch <= 'F'), "Invalid hex character"); return Ch >= '0' && Ch <= '9' ? (Ch - '0') : Ch >= 'a' && Ch <= 'f' ? (Ch - 'a' + 10) : Ch >= 'A' && Ch <= 'F' ? (Ch - 'A' + 10) : 0; } template<char First, char Second> static constexpr uint8_t hexToByte() { return (hexToNibble<First>() << 4) + hexToNibble<Second>(); } static constexpr std::array<uint8_t, 16> get() { return std::array<uint8_t, 16>{{ hexToByte<hex[0], hex[1]>(), hexToByte<hex[2], hex[3]>(), hexToByte<hex[4], hex[5]>(), hexToByte<hex[6], hex[7]>(), hexToByte<hex[9], hex[10]>(), hexToByte<hex[11], hex[12]>(), hexToByte<hex[14], hex[15]>(), hexToByte<hex[16], hex[17]>(), hexToByte<hex[19], hex[20]>(), hexToByte<hex[21], hex[22]>(), hexToByte<hex[24], hex[25]>(), hexToByte<hex[26], hex[27]>(), hexToByte<hex[28], hex[29]>(), hexToByte<hex[30], hex[31]>(), hexToByte<hex[32], hex[33]>(), hexToByte<hex[34], hex[35]>() }}; } }; #include <iostream> using namespace std; int main() { using some_uuid_t = UUID<f6ece560_cc3b_459a_87f1_22331582216e>; cout << some_uuid_t::hex << endl; auto uuid = some_uuid_t::get(); for (auto const & byte : uuid) { cout << int(byte); } cout << endl; return 0; } 

(gcc 4.9.2 / clang 3.5.2 -std = C ++ 11 -Wall -pedantic)

+2
source

Source: https://habr.com/ru/post/1215746/


All Articles