Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do these two pieces of code using constexpr, __PRETTY_FUNCTION__ and char * have different results?

I have this code where if you comment out the line commented "But this doesn't work?!" it compiles just fine, but if you don't, the compiler generates an error.

At least, gcc 8.2 generates an error.

But, they seem identical to me. What's the problem? Is this legal code at all?

template <int x>
struct test_template {
    static int size() { return x; }
};

constexpr int ce_strlen(char const *s)
{
    int i = 0;
    while (s[i]) ++i;
    return i;
}

int joe()
{
    constexpr int plen = ce_strlen(__PRETTY_FUNCTION__); // This works
    test_template<plen> a; // This declaration is valid.
    test_template<ce_strlen(__PRETTY_FUNCTION__)> b; // But this doesn't work?!
    return a.size() + b.size();
}

I ran into this while trying to come up with a way to create profile tags for an intrusive profiling system at compile time. I succeeded, but my final code does not involve using ce_strlen.

like image 461
Omnifarious Avatar asked Sep 24 '18 02:09

Omnifarious


People also ask

What is __ Pretty_function __ C++?

The identifier __PRETTY_FUNCTION__ holds the name of the function pretty printed in a language specific fashion. These names are always the same in a C function, but in a C++ function they may be different. For example, this program: extern "C" { extern int printf (char *, ...

What is the point of constexpr?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.

How do I know if a function is constexpr?

The easiest way to check whether a function (e.g., foo ) is constexpr is to assign its return value to a constexpr as below: constexpr auto i = foo(); if the returned value is not constexpr compilation will fail.

Should I use constexpr everywhere?

Yes. I believe putting such const ness is always a good practice wherever you can. For example in your class if a given method is not modifying any member then you always tend to put a const keyword in the end.


1 Answers

Indeed this is a bug in GCC as discussed in the comments, but I thought I'd throw in some additional insight as to the nature of this bug. In the GCC NEWS file there is this line:

  • __FUNCTION__ and __PRETTY_FUNCTION__ are now treated as variables by the parser; previously they were treated as string constants. So code like printf (__FUNCTION__ ": foo") must be rewritten to printf ("%s: foo", __FUNCTION__). This is necessary for templates.

But __PRETTY_FUNCTION__ isn't really a variable, it's a special case treated in the parser as we see in constexpr.c:

    case DECL_EXPR:
      {
    tree decl = DECL_EXPR_DECL (body);
    if (TREE_CODE (decl) == USING_DECL
        /* Accept __func__, __FUNCTION__, and __PRETTY_FUNCTION__.  */
        || DECL_ARTIFICIAL (decl))
      return NULL_TREE;
    return error_mark_node;
}

If it really was a variable, we'd expect it to pass the same test cases as these:

constexpr const char* s2 = "TEST";
constexpr const char* s3 = s2;
test_template<ce_strlen("TEST")> c;
test_template<ce_strlen(s2)> d;
test_template<ce_strlen(s3)> e;
like image 103
user10405994 Avatar answered Oct 19 '22 12:10

user10405994