Reputation: 1222
Please refer to this snippet:
#include <type_traits>
#include <string_view>
constexpr std::size_t strlen(char const* s) {
std::size_t n = 0;
while (*s++ != '\0')
++n;
return n;
}
template <std::size_t>
struct X {};
int main() {
constexpr auto pf = __PRETTY_FUNCTION__; // gcc ok; clang ok; (1)
static_assert(std::string_view(__PRETTY_FUNCTION__) == std::string_view("int main()")); // gcc ok; clang ok; (2)
X<strlen(__PRETTY_FUNCTION__)> x; // gcc not ok; clang ok; (3)
}
Clang 8 compiles it, but GCC 8.3 dos not. See on godbolt. GCC fails on line (3)
although lines (1)
and (2)
are ok.
If I am able to declare pf
in line (1)
and use __PRETTY_FUNCTION__
in static_assert
it means that the expression __PRETTY_FUNCTION__
is core constant expression. And if I'm not able do declare X<strlen(__PRETTY_FUNCTION__)> x
it means that strlen(__PRETTY_FUNCTION__)
is not an integral constant expression.
So why strlen(__PRETTY_FUNCTION__)
is not an integral constant expression? Is it implied by the standard, or is it a GCC bug?
Upvotes: 14
Views: 2644
Reputation: 1222
I'm still concerned about the line (2)
, so I figured out something that doesn't fit into a comment.
I modified the snippet a little bit, please refer to this:
#include <type_traits>
#include <string_view>
constexpr std::size_t strlen(char const* s) {
std::size_t n = 0;
while (*s++ != '\0')
++n;
return n;
}
template <std::size_t>
struct X {};
static char const PF[] = "int main()";
int main() {
constexpr auto pf = std::string_view(__PRETTY_FUNCTION__); // gcc ok; clang ok; (1)
static_assert(pf == std::string_view("int main()")); // gcc ok; clang ok; (2)
X<strlen(__PRETTY_FUNCTION__)> x; // gcc not ok; clang ok; (3)
static_assert(pf[0] == 'i'); // not ok; (4)
X<pf.size()> x1; // ok; (5)
X<strlen(pf.data())> x2; // not ok; (6)
static_assert(__builtin_constant_p(pf.data()) == 0); // ok (7)
static_assert(__builtin_strlen(pf.data()) == 10); // ok (8)
static_assert(__builtin_memcmp(pf.data(), "int main()", __builtin_strlen(pf.data())) == 0); // ok (9)
static_assert(std::char_traits<char>::compare(pf.data(), "int main()", std::char_traits<char>::length(pf.data())) == 0); // ok (10)
static_assert(std::char_traits<char>::length(PF) == 10); // not ok (11)
static_assert(__builtin_strlen(PF) == 10); // not ok (12)
}
As I understand, static_assert
would fail to prove the value of (2)
if it didn't believe
the expression was constexpr (as in case of line (4)
). But despite the faulty lines (2)
and (4)
the line (5)
seems OK to GCC. So, I peeked in std::char_traits<char>
. The traits use __builtin_constant_p
to dispatch between the __builtin_*
implementations and the ones like my strlen
. The line (7)
states "the constexprness of the __PRETTY_FUNCTION__
can't be proven" (see gcc doc), but despite of that the expression __builtin_memcmp(__PRETTY_FUNCTION__)
can be evaluated at compile time (see line (8)
).
Taking onto consideration fails in (11)
and (12)
, one can conclude that in some cases __PRETTY_FUNCTION__
acts as if it was declared static constexpr char const []
and in the other cases as static char const []
. In other words if __PRETTY_FUNCTION__
has a type then it is not consistent during the all steps of compilation (I refer to GCC 8.3).
Upvotes: 0
Reputation: 3614
__PRETTY_FUNCTION__
is not standard. As such a compiler can implement it in different places (while parsing, while building the AST or while linking).
If it's supposed to be implemented while parsing, then it can be a constant expression (I guess that's what clang is doing). However, if it's implemented while linking (that is, the compiler emits a symbol for it and the linker will resolve it), it can't be a constant expression.
I think GCC use the latter case.
Please notice that you can take a sizeof() of these in that case, since it's a const char[]
if you need compile-time constant string's length computation.
So replace expression 3 by:
X<sizeof(__PRETTY_FUNCTION__) - 1> x;
and it'll compile fine on both compiler.
EDIT: As NathanOliver pointed out, it seems that GCC consider the signature of __PRETTY_FUNCTION__
as static const char[]
while clang/visual studio consider it as static constexpr const char[]
. This is a painful nuisance in GCC (not a bug, since it's not standard) and they seems to have fixed it in the >8.0.0 version.
In expression (1) and expression (2), __PRETTY_FUNCTION__
is decayed to a const char*
(the pointer are constant, but nothing can be said about the data). For me, expression (2) might not prove anything, since there is no guarantee the pointers should be equal on both side of the equality, even if they points to the "same" content. string_view
constructor expects const char*
, thus anything other than __PRETTY_FUNCTION__
that could decay to const char*
would pass expression (2).
Upvotes: 6