This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c++/66639] New: Feature request C++: mark __func__ , __FUNCTION__ & __PRETTY_FUNCTION__ as constexpr
- From: "simon at gleissner dot de" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Tue, 23 Jun 2015 13:08:36 +0000
- Subject: [Bug c++/66639] New: Feature request C++: mark __func__ , __FUNCTION__ & __PRETTY_FUNCTION__ as constexpr
- Auto-submitted: auto-generated
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;
}