Reputation: 13298
Ok, I completely messed up this question, first for the typo and second because my oversimplified minimum example didn't have any problem at all. I was considering to delete the question but seeing that it have two answers with fairly good acceptance I will edit with the not oversimplified code. I'm sorry for all the mess1.
The following code creates an specializaton for std::hash
:
#include <bit>
#include <bitset>
struct test{ const char *const s; std::size_t n; };
template<>
struct std::hash<test>
{
constexpr std::size_t text_hash(const char *const text) const noexcept
{
auto result = ~std::size_t{};
auto shift = 0ul;
constexpr auto length = sizeof(result) * 8;
for (const char *c = text; *c; ++c)
{
result ^= (std::size_t(*c) << shift);
shift = ((shift + 8) % length);
}
return result;
}
constexpr std::size_t expand_number(std::size_t number) const noexcept
{
while (std::bit_width(number) < 64)
number *= 7ul;
return number;
}
constexpr std::size_t operator()(const test &t) const noexcept
{
return text_hash(t.s) ^ expand_number(t.n);
}
};
Used as follows, does not compile:
constexpr std::hash<test> HASH;
#define TEXT(T) HASH(test{T, __COUNTER__})
int main()
{
constexpr auto v = TEXT("Test");
return 0;
}
The error shown by the compiler (Compiler Explorer link) is:
<source>:41:24: in 'constexpr' expansion of 'HASH.std::hash<test>::operator()(test{((const char*)"Test"), 0})'
<source>:32:46: in 'constexpr' expansion of '((const std::hash<test>*)this)->std::hash<test>::expand_number(((std::size_t)t.test::n))'
<source>:25:9: error: 'constexpr' loop iteration count exceeds limit of 262144 (use '-fconstexpr-loop-limit=' to increase the limit)
25 | while (std::bit_width(number) < 64)
| ^~~~~
Non-constexpr
version works fine:
int main()
{
auto v = TEXT("Test");
return 0;
}
So why is this happening? How do I solve it?
1For anyone who wants to see the previous code, which compiles without any issue, here it is.
std::size_t A(std::size_t number) noexcept // Number shouldn't be 0
{
while (std::bit_width(number) < 64)
number *= 7ul;
return number;
}
constexpr std::size_t B(std::size_t number) noexcept // Number shouldn't be 0
{
while (std::bit_width(number) < 64)
number *= 7ul;
return number;
}
int main()
{
auto a = A(1);
constexpr auto b = B(1);
return 0;
}
Upvotes: 1
Views: 222
Reputation: 2040
__COUNTER__
has an initial value of 0, so clearly your loop never terminates.
https://godbolt.org/z/jc11bd636
Upvotes: 0
Reputation: 48021
If Ted Lyngmo's assertion about std::size_t being less than 64 for your implementation is correct, then clearly he's got the right answer.
But if you encounter the problem when compiling for a 64-bit target, it's possible the loop count that's exceeded is in the implementation of std::bit_width
rather than the loop in B
.
I peeked at an implementation of std::bit_width
:
consteval
, it uses intrinsic instructions.consteval
it falls back to a loop to compute the result.Perhaps there's a bug in the fallback implementation for the library you're using and it's that loop that's exceeding the maximum iterations. Since that is used only in a constexpr
context, it would match the behavior observed.
Upvotes: 2
Reputation: 117832
std::size_t
is 32 (or less than 64 bit) bit in your environment so std::bit_width(number)
can be at most 32 - and the condition in your loop will therefore always be true
:
while (std::bit_width(number) < 64) // always true if size_t has less than 64 bit
One adaptation that will not try to make it bigger than the number of bits in std::size_t
:
#include <limits>
constexpr std::size_t B(std::size_t number) noexcept
{
while (std::bit_width(number) < std::numeric_limits<std::size_t>::digits)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// the number of bits in a size_t
number *= 7ul;
return number;
}
Upvotes: 9