I play around with constexpr
and try to parse an ipv4 address into four digits, but it does not work. So i strip down the code to the point where it fails:
#include <cstdint>
#include <stdexcept>
#include <string_view>
#define WORKS 1
static constexpr uint8_t
getIpVal(const std::string_view &sv) noexcept {
size_t pos = 0;
auto len = sv.size();
unsigned long val = 0;
while (sv[pos] >= '0' && sv[pos] <= '9' && pos < len) {
int digit = sv[pos] - '0';
val *= 10;
val = digit;
if (val > UINT8_MAX) {
return 0;
// throw std::invalid_argument(sv.data());
}
pos;
}
if (pos < len) {
return 0;
// throw std::invalid_argument(sv.data());
}
return val;
}
static constexpr auto
operator""_ipv4(const char *ipv4Ptr, const size_t size) {
const std::string_view ipv4Str(ipv4Ptr, size);
const auto pos1 = ipv4Str.find('.');
if (pos1 == std::string_view::npos) {
throw std::invalid_argument(ipv4Ptr);
}
const auto str1 = ipv4Str.substr(0, pos1);
#if WORKS
return str1;
#else
return getIpVal(str1);
#endif
}
auto
test1() {
return "127.0.0.1"_ipv4;
}
auto
test2() {
return getIpVal("127");
}
I'm trying to compile this using Compiler Explorer: https://godbolt.org/z/aY3ETo6ba
As long as WORKS
is defined as 1
, everything seems to work fine:
.LC0:
.string "127.0.0.1"
test1():
mov eax, 3
mov edx, OFFSET FLAT:.LC0
ret
test2():
mov eax, 127
ret
But if I set it to zero, the compiler creates a complete code with a loop to evaluate the number. I do not understand why. The individual functions seem to work, but the combination fails. In my opinion it should just create two functions with numbers:
test1():
mov eax, 127
ret
test2():
mov eax, 127
ret
CodePudding user response:
Making a function constexpr
means that a function might get evaluated at compile time. Whether the function is actually evaluated at compile time depends on whether it's being called during the evaluation of a constant.
In your example, you're not calling either ""_ipv4
, or getIpVal
in the process of evaluating a constant. As a result, whether the functions return the answer directly, or call a loop, depends on the compiler doing optimizations. e.g. Clang optimizes the code to generate assembly that returns values directly. https://godbolt.org/z/zz3oj3Mor
If you want to guarantee that a function is called during constant evaluation, you can assign the result to a constexpr
variable, e.g.
constexpr auto n = getIpVal("127");
and now you'll get a compiler error message that:
while (sv[pos] >= '0' && sv[pos] <= '9' && pos < len)
is incorrect. This is because you're indexing into sv
before checking if pos
is a valid index. Normally this would be UB, but there's no UB at compile time, and you forced the compiler to evaluate the function at compile time, so you get a nice diagnostic.
Another way to guarantee that the function is called at compile time is to make it a consteval
function. However, such functions can only be called at compile time, so this won't work if you need to call this with non-constant arguments (e.g. arguments that you get from the user).