https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66639

            Bug ID: 66639
           Summary: Feature request C++: mark __func__ , __FUNCTION__ &
                    __PRETTY_FUNCTION__ as constexpr
           Product: gcc
           Version: 5.1.1
            Status: UNCONFIRMED
          Severity: enhancement
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: simon at gleissner dot de
  Target Milestone: ---

Currently, __func__ , __FUNCTION__ and __PRETTY_FUNCTION__ are internally
declared as static constant arrays:

    static const char __func__[] = "function-name";

If compiled as C++11/14, it might be possible to declare them as 'constexpr',
to fully enable them for constexpr functions.

I have written an exception wrapper to precompile the error text (as C++14, see
below). Basically, the following code concatenate strings during compile time,
not at run time. The text is thrown during a std::system_error as the
'what'-argument.

The interesting part is in the THROW_SYSTEM_ERROR macro, where i use
__PRETTY_FUNCTION__. The code works and the output is (as expected)

    Exception in 'int main()' by 'testfunc()': Invalid argument (system:22)

When we look in the compiled code (-O3 or -Os), a precompiled struct object
with the precompiled string "in 'int main()' by 'testfunc()'" is pushed onto
the stack byte for byte:

        call    __cxa_allocate_exception
        movq    %rax, %rbx
        movb    $105, (%rsp)
        movb    $110, 1(%rsp)
        movb    $32, 2(%rsp)
        movb    $39, 3(%rsp)
        movb    $105, 4(%rsp)
        movb    $110, 5(%rsp)
        movb    $116, 6(%rsp)
        movb    $32, 7(%rsp)
        movb    $109, 8(%rsp)
        movb    $97, 9(%rsp)
        movb    $105, 10(%rsp)
        movb    $110, 11(%rsp)
        movb    $40, 12(%rsp)
        movb    $41, 13(%rsp)
        movb    $39, 14(%rsp)
        movb    $32, 15(%rsp)
        movb    $98, 16(%rsp)
        movb    $121, 17(%rsp)
        movb    $32, 18(%rsp)
        movb    $39, 19(%rsp)
        movb    $116, 20(%rsp)
        movb    $101, 21(%rsp)
        movb    $115, 22(%rsp)
        movb    $116, 23(%rsp)
        movb    $102, 24(%rsp)
        movb    $117, 25(%rsp)
        movb    $110, 26(%rsp)
        movb    $99, 27(%rsp)
        movb    $40, 28(%rsp)
        movb    $41, 29(%rsp)
        movb    $39, 30(%rsp)
        movb    $0, 31(%rsp)
        call    _ZNSt3_V215system_categoryEv

This is not really what i have expected, but because i use a temporary object,
it is understandable.

However, when i use a static constexpr object for the precompiled text (enable
the disabled macro in the source), i get the error messages

    ../src/Tests.cpp:112:111: error: the value of ‘__PRETTY_FUNCTION__’ is not
usable in a constant expression
    ../src/Tests.cpp:112:65: note: ‘__PRETTY_FUNCTION__’ was not declared
‘constexpr


As __PRETTY_FUNCTION__ can already be used in constexpr functions at compile
time, i don't think that this change might be very big.



//============================================================================
// Name        : Tests.cpp
// Author      : Simon Gleissner
//============================================================================

#include <iostream>
#include <system_error>
#include <cerrno>
#include <cstring>

namespace MyLib {

template<typename... CHARS>
struct Message
{
        constexpr Message(CHARS... chars)
                : str {chars...,'\0'}
        {}

        const char str[sizeof...(CHARS)+1];
};

// we can not specialize a function template parially,
// therefore we have to wrap it statically inside a class
// (which can partially be specialized)
template<unsigned COUNTDOWN_A, unsigned COUNTDOWN_B, unsigned COUNTDOWN_C>
struct CharIndexer
{
        template<unsigned SIZE_A, unsigned SIZE_B, unsigned SIZE_C, typename...
CHARS>
        static constexpr auto CreateChars(
                        const char (&str_a)[SIZE_A],
                        const char (&str_b)[SIZE_B],
                        const char (&str_c)[SIZE_C],
                        CHARS... chars)
        {
                return CharIndexer<COUNTDOWN_A-1, COUNTDOWN_B,
COUNTDOWN_C>::CreateChars(str_a, str_b, str_c, chars...,
str_a[SIZE_A-COUNTDOWN_A]);
        }
};

template<unsigned COUNTDOWN_B, unsigned COUNTDOWN_C>
struct CharIndexer<1, COUNTDOWN_B, COUNTDOWN_C>
{
        template<unsigned SIZE_A, unsigned SIZE_B, unsigned SIZE_C, typename...
CHARS>
        static constexpr auto CreateChars(
                        const char (&str_a)[SIZE_A],
                        const char (&str_b)[SIZE_B],
                        const char (&str_c)[SIZE_C],
                        CHARS... chars)
        {
                return CharIndexer<1, COUNTDOWN_B-1,
COUNTDOWN_C>::CreateChars(str_a, str_b, str_c, chars...,
str_b[SIZE_B-COUNTDOWN_B]);
        }
};

template<unsigned COUNTDOWN_C>
struct CharIndexer<1, 1, COUNTDOWN_C>
{
        template<unsigned SIZE_A, unsigned SIZE_B, unsigned SIZE_C, typename...
CHARS>
        static constexpr auto CreateChars(
                        const char (&str_a)[SIZE_A],
                        const char (&str_b)[SIZE_B],
                        const char (&str_c)[SIZE_C],
                        CHARS... chars)
        {
                return CharIndexer<1, 1, COUNTDOWN_C-1>::CreateChars(str_a,
str_b, str_c, chars..., str_c[SIZE_C-COUNTDOWN_C]);
        }
};

template<>
struct CharIndexer<1, 1, 1>
{
        template<unsigned SIZE_A, unsigned SIZE_B, unsigned SIZE_C, typename...
CHARS>
        static constexpr auto CreateChars(
                        const char (&str_a)[SIZE_A],
                        const char (&str_b)[SIZE_B],
                        const char (&str_c)[SIZE_C],
                        CHARS... chars)
        {
                return Message<CHARS...>(chars...);
        }
};

template<unsigned A, unsigned B, unsigned C>
constexpr auto concatenate(const char (&a)[A], const char (&b)[B], const char
(&c)[C])
{
        return CharIndexer<A,B,C>::CreateChars(a,b,c);
}

} // namespace MyLib

#if 0
#define THROW_SYSTEM_ERROR(ERROR_NO, CAUSE)                     \
do {                                                            \
        constexpr static auto what = MyLib::concatenate(        \
                "in '", __PRETTY_FUNCTION__, "' by '" CAUSE "'");       \
        throw std::system_error(ERROR_NO,                       \
                        std::system_category(), what.str);      \
} while(0)
#endif


#define THROW_SYSTEM_ERROR(ERROR_NO, CAUSE)                             \
        throw std::system_error(ERROR_NO, std::system_category(),       \
                MyLib::concatenate("in '", __PRETTY_FUNCTION__, "' by '" CAUSE
"'").str)


int main()
{
        try
        {
                THROW_SYSTEM_ERROR(EINVAL, "testfunc()");
        }
        catch(const std::system_error& exception)
        {
                std::cerr << "Exception " << exception.what() << " (" <<
exception.code() << ")" << std::endl;
        }

        return 0;
}

Reply via email to