nicolai
nicolai

Reputation: 1222

__PRETTY_FUNCTION__ in constant expression

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

Answers (2)

nicolai
nicolai

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

xryl669
xryl669

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

Related Questions