C++11 constexpr 函数中的常量表达式字符串参数
Constant expression string parameter in C++11 constexpr function
我正在尝试创建一个 constexpr
函数,将 "f6ece560-cc3b-459a-87f1-22331582216e"
之类的 UUID 字符串转换为 class 这样的字符串:
class UUID {
public:
explicit UUID(uint8_t bytes[]); // Must be 16 byte array.
这是我目前得到的:
// 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")
如您所见,存在一个问题 - 似乎不可能在 constexpr 函数中包含作为常量表达式的函数参数,因此您不能对参数执行 static_assert
s,即使传入的值是一个常量表达式。
所以我求助于宏来检查字符串长度,并放弃检查字符。
有办法解决这个问题吗?另外,我怎样才能轻松地确保这个函数在编译时得到实际评估?
编辑:这与 C++11 - static_assert within constexpr function? 不同 - 或者至少相同的答案不起作用 - 请参阅下面的评论。
编辑 2:Shafik 的出色回答适用于大小问题,但不适用于检查十六进制字符。据我所知这是不可能的——即使你使用这个...
// 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>();
}
这行不通:
// 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]>(),
因为 str[0]
不是常量表达式。
正如 AndyG the question C++11 - static_assert within constexpr function? 告诉我们的一种方法是使用必须在编译时可用的非类型模板参数。
此解决方案的问题在于 OP 使用的字符串文字 can not be bound to non-type arguments:
In particular, this implies that string literals, addresses of array elements, and addresses of non-static members cannot be used as template arguments to instantiate templates whose corresponding non-type template parameters are pointers to objects.
一个变通方法不是直接使用字符串文字,而是使用对数组长度问题很重要的 属性,如下所示:
template <size_t N>
constexpr std::array<uint8_t, 16> UUIDFromString( const char (&str)[N])
{
static_assert(N == 36, "Invalid GUID length");
//....
}
如果你想要 static_assert
s,你别无选择,只能模板参数化
相关论点。
有人指出您不能将字符串文字绑定到非类型模板
参数,这在法律上是正确的,但你可以做,稍微绕一下。
这是一个草图:
#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)
我正在尝试创建一个 constexpr
函数,将 "f6ece560-cc3b-459a-87f1-22331582216e"
之类的 UUID 字符串转换为 class 这样的字符串:
class UUID {
public:
explicit UUID(uint8_t bytes[]); // Must be 16 byte array.
这是我目前得到的:
// 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")
如您所见,存在一个问题 - 似乎不可能在 constexpr 函数中包含作为常量表达式的函数参数,因此您不能对参数执行 static_assert
s,即使传入的值是一个常量表达式。
所以我求助于宏来检查字符串长度,并放弃检查字符。
有办法解决这个问题吗?另外,我怎样才能轻松地确保这个函数在编译时得到实际评估?
编辑:这与 C++11 - static_assert within constexpr function? 不同 - 或者至少相同的答案不起作用 - 请参阅下面的评论。
编辑 2:Shafik 的出色回答适用于大小问题,但不适用于检查十六进制字符。据我所知这是不可能的——即使你使用这个...
// 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>();
}
这行不通:
// 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]>(),
因为 str[0]
不是常量表达式。
正如 AndyG
此解决方案的问题在于 OP 使用的字符串文字 can not be bound to non-type arguments:
In particular, this implies that string literals, addresses of array elements, and addresses of non-static members cannot be used as template arguments to instantiate templates whose corresponding non-type template parameters are pointers to objects.
一个变通方法不是直接使用字符串文字,而是使用对数组长度问题很重要的 属性,如下所示:
template <size_t N>
constexpr std::array<uint8_t, 16> UUIDFromString( const char (&str)[N])
{
static_assert(N == 36, "Invalid GUID length");
//....
}
如果你想要 static_assert
s,你别无选择,只能模板参数化
相关论点。
有人指出您不能将字符串文字绑定到非类型模板 参数,这在法律上是正确的,但你可以做,稍微绕一下。
这是一个草图:
#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)