Skip to content

Commit

Permalink
C++11 constexpr type name parsing for diagnostics
Browse files Browse the repository at this point in the history
  • Loading branch information
zajo committed Aug 23, 2024
1 parent b026a00 commit 35460a8
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 66 deletions.
5 changes: 3 additions & 2 deletions include/boost/leaf/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <iosfwd>
#include <cerrno>
#include <cstring>

#if BOOST_LEAF_CFG_STD_STRING
# include <string>
Expand Down Expand Up @@ -57,7 +58,7 @@ struct BOOST_LEAF_SYMBOL_VISIBLE e_errno
template <class CharT, class Traits>
friend std::ostream & operator<<(std::basic_ostream<CharT, Traits> & os, e_errno const & err)
{
return print_type_str<e_errno>(os) << err.value << ", \"" << std::strerror(err.value) << '"';
return os << parse_name<e_errno>() << ": " << err.value << ", \"" << std::strerror(err.value) << '"';
}
};

Expand Down Expand Up @@ -101,7 +102,7 @@ namespace windows
*--z = 0;
if( z[-1] == '\r' )
*--z = 0;
return print_type_str<e_LastError>(os) << err.value << ", \"" << (LPCSTR)mb.p << '"';
return os << parse_name<e_LastError>() << ": " << err.value << ", \"" << (LPCSTR)mb.p << '"';
}
return os;
}
Expand Down
10 changes: 10 additions & 0 deletions include/boost/leaf/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,16 @@

////////////////////////////////////////

#ifndef BOOST_LEAF_PRETTY_FUNCTION
# if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)
# define BOOST_LEAF_PRETTY_FUNCTION __FUNCSIG__
# else
# define BOOST_LEAF_PRETTY_FUNCTION __PRETTY_FUNCTION__
# endif
#endif

////////////////////////////////////////

// Configure BOOST_LEAF_NO_EXCEPTIONS, unless already #defined
#ifndef BOOST_LEAF_NO_EXCEPTIONS

Expand Down
174 changes: 115 additions & 59 deletions include/boost/leaf/detail/demangle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,81 +18,137 @@
#include <boost/leaf/config.hpp>

#include <iosfwd>
#include <cstring>

namespace boost { namespace leaf {

namespace leaf_detail
{
template <int N>
BOOST_LEAF_CONSTEXPR inline int check_prefix(char const * t, char const (&prefix)[N]) noexcept
template <int S1, int S2, int I, bool = S1 >= S2>
struct cpp11_prefix
{
return std::strncmp(t, prefix, sizeof(prefix)-1) == 0 ? sizeof(prefix) - 1 : 0;
constexpr static bool check(char const (&)[S1], char const (&)[S2]) noexcept
{
return false;
}
};
template <int S1, int S2, int I>
struct cpp11_prefix<S1, S2, I, true>
{
constexpr static bool check(char const (&str)[S1], char const (&prefix)[S2]) noexcept
{
return str[I] == prefix[I] && cpp11_prefix<S1, S2, I - 1>::check(str, prefix);
}
};
template <int S1, int S2>
struct cpp11_prefix<S1, S2, 0, true>
{
constexpr static bool check(char const (&str)[S1], char const (&prefix)[S2]) noexcept
{
return str[0] == prefix[0];
}
};
template <int S1, int S2>
constexpr int check_prefix(char const (&str)[S1], char const (&prefix)[S2]) noexcept
{
return cpp11_prefix<S1, S2, S2 - 2>::check(str, prefix) ? S2 - 1 : 0;
}
}

template <class Name>
inline char const * type_str()
{
#if defined(__clang__)
BOOST_LEAF_ASSERT(leaf_detail::check_prefix(__PRETTY_FUNCTION__, "const char *boost::leaf::type_str() [Name = ") == 44);
return __PRETTY_FUNCTION__ + 44;
#elif defined(__GNUC__)
BOOST_LEAF_ASSERT(leaf_detail::check_prefix(__PRETTY_FUNCTION__, "const char* boost::leaf::type_str() [with Name = ") == 49);
return __PRETTY_FUNCTION__ + 49;
#elif defined _MSC_VER
if( char const * p = std::strstr(__FUNCSIG__, "boost::leaf::type_str<") )
return p + 22;
else
return __FUNCSIG__;
#else
if( int clang_style = leaf_detail::check_prefix(__PRETTY_FUNCTION__, "const char *boost::leaf::type_str() [Name = ") )
return __PRETTY_FUNCTION__ + clang_style;
else if( int gcc_style = leaf_detail::check_prefix(__PRETTY_FUNCTION__, "const char* boost::leaf::type_str() [with Name = ") )
return __PRETTY_FUNCTION__ + gcc_style;
else
return __PRETTY_FUNCTION__;
#endif
}
////////////////////////////////////////

template <class Name, class CharT, class Traits>
inline std::ostream & print_type_str(std::basic_ostream<CharT, Traits> & os)
{
if( char const * t = type_str<Name>() )
{
char const * end = std::strchr(t, 0);
#if defined(__clang__) || defined(__GNUC__)
BOOST_LEAF_ASSERT(end != t);
BOOST_LEAF_ASSERT(end[-1] == ']');
return os.write(t, end - t - 1) << ": ";
#elif defined(_MSC_VER)
BOOST_LEAF_ASSERT(end - t > 7);
BOOST_LEAF_ASSERT(std::memcmp(end - 7, ">(void)", 7) == 0);
if( *t == 's' )
template <int S1, int S2, int I1, int I2, bool = S1 >= S2>
struct cpp11_suffix
{
constexpr static bool check(char const (&)[S1], char const (&)[S2]) noexcept
{
BOOST_LEAF_ASSERT(std::memcmp(t + 1, "truct ", 6) == 0);
t += 7;
return false;
}
else if( *t == 'c' )
};
template <int S1, int S2, int I1, int I2>
struct cpp11_suffix<S1, S2, I1, I2, true>
{
constexpr static bool check(char const (&str)[S1], char const (&suffix)[S2]) noexcept
{
BOOST_LEAF_ASSERT(std::memcmp(t + 1, "lass ", 5) == 0);
t += 6;
return str[I1] == suffix[I2] && cpp11_suffix<S1, S2, I1 - 1, I2 - 1>::check(str, suffix);
}
else if( *t == 'e' )
};
template <int S1, int S2, int I1>
struct cpp11_suffix<S1, S2, I1, 0, true>
{
constexpr static bool check(char const (&str)[S1], char const (&suffix)[S2]) noexcept
{
BOOST_LEAF_ASSERT(std::memcmp(t + 1, "num ", 4) == 0);
t += 5;
return str[I1] == suffix[0];
}
return os.write(t, end - t - 7) << ": ";
#else
if( end != t && end[-1] == ']')
return os.write(t, end - t - 1) << ": ";
else
return os << t << ": ";
#endif
};
template <int S1, int S2>
constexpr int check_suffix(char const (&str)[S1], char const (&suffix)[S2]) noexcept
{
return cpp11_suffix<S1, S2, S1 - 2, S2 - 2>::check(str, suffix) ? S1 - S2 : 0;
}
}

struct parsed_name
{
char const * name;
int len;

parsed_name(char const * name, int len) noexcept:
name(name),
len(len)
{
}

template <int S>
parsed_name(char const(&name)[S]) noexcept:
name(name),
len(S-1)
{
}

bool parse_success() const noexcept
{
return name[len] != 0;
}

template <class CharT, class Traits>
friend std::ostream & operator<<(std::basic_ostream<CharT, Traits> & os, parsed_name const & pn)
{
return os.write(pn.name, pn.len);
}
};

template <class Name>
parsed_name parse_name()
{
// Workaround for older gcc compilers where __PRETTY_FUNCTION__ is not constexpr:
// instead of constexpr int x = N, we evaluate int const x = sizeof(char[1 + N]) - 1.
#define BOOST_LEAF_PARSE_PF(prefix, suffix) \
{ \
if( int const s = leaf_detail::check_suffix(BOOST_LEAF_PRETTY_FUNCTION, suffix) ) \
if( int const p = leaf_detail::check_prefix(BOOST_LEAF_PRETTY_FUNCTION, prefix) ) \
return parsed_name(BOOST_LEAF_PRETTY_FUNCTION + sizeof(char[1 + p]) - 1, sizeof(char[1 + s - p]) - 1); \
}
else
return os << "no name: ";
// clang style:
BOOST_LEAF_PARSE_PF( "parsed_name boost::leaf::parse_name() [Name = ", "]");
// old clang style:
BOOST_LEAF_PARSE_PF( "boost::leaf::parsed_name boost::leaf::parse_name() [Name = ", "]");
// gcc style:
BOOST_LEAF_PARSE_PF( "boost::leaf::parsed_name boost::leaf::parse_name() [with Name = ", "]");
// msvc style, __cdecl, struct/class/enum:
BOOST_LEAF_PARSE_PF( "struct boost::leaf::parsed_name __cdecl boost::leaf::parse_name<struct ", ">(void)");
BOOST_LEAF_PARSE_PF( "struct boost::leaf::parsed_name __cdecl boost::leaf::parse_name<class ", ">(void)");
BOOST_LEAF_PARSE_PF( "struct boost::leaf::parsed_name __cdecl boost::leaf::parse_name<enum ", ">(void)");
// msvc style, __stdcall, struct/class/enum:
BOOST_LEAF_PARSE_PF( "struct boost::leaf::parsed_name __stdcall boost::leaf::parse_name<struct ", ">(void)");
BOOST_LEAF_PARSE_PF( "struct boost::leaf::parsed_name __stdcall boost::leaf::parse_name<class ", ">(void)");
BOOST_LEAF_PARSE_PF( "struct boost::leaf::parsed_name __stdcall boost::leaf::parse_name<enum ", ">(void)");
// msvc style, __fastcall, struct/class/enum:
BOOST_LEAF_PARSE_PF( "struct boost::leaf::parsed_name __fastcall boost::leaf::parse_name<struct ", ">(void)");
BOOST_LEAF_PARSE_PF( "struct boost::leaf::parsed_name __fastcall boost::leaf::parse_name<class ", ">(void)");
BOOST_LEAF_PARSE_PF( "struct boost::leaf::parsed_name __fastcall boost::leaf::parse_name<enum ", ">(void)");
#undef BOOST_LEAF_PARSE_PF

// Unable to parse, return as-is. Note, parsing is done at compile-time.
return parsed_name(BOOST_LEAF_PRETTY_FUNCTION);
}

} }
Expand Down
8 changes: 4 additions & 4 deletions include/boost/leaf/detail/print.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ namespace leaf_detail
template <class CharT, class Traits>
static void print( std::basic_ostream<CharT, Traits> & os, Wrapper const & x )
{
print_type_str<Wrapper>(os) << x.value;
os << parse_name<Wrapper>() << ": " << x.value;
}
};

Expand All @@ -82,7 +82,7 @@ namespace leaf_detail
template <class CharT, class Traits>
static void print( std::basic_ostream<CharT, Traits> & os, Wrapper const & ex )
{
print_type_str<Wrapper>(os) << "std::exception::what(): " << ex.what();
os << parse_name<Wrapper>() << ": std::exception::what(): " << ex.what();
}
};

Expand All @@ -94,7 +94,7 @@ namespace leaf_detail
template <class CharT, class Traits>
static void print( std::basic_ostream<CharT, Traits> & os, Wrapper const & )
{
print_type_str<Wrapper>(os) << "{not printable}";
os << parse_name<Wrapper>() << ": {not printable}";
}
};

Expand All @@ -106,7 +106,7 @@ namespace leaf_detail
template <class CharT, class Traits>
static void print( std::basic_ostream<CharT, Traits> & os, Wrapper const & w )
{
print_type_str<Wrapper>(os) << static_cast<typename std::underlying_type<Wrapper>::type>(w);
os << parse_name<Wrapper>() << ": " << static_cast<typename std::underlying_type<Wrapper>::type>(w);
}
};

Expand Down
2 changes: 1 addition & 1 deletion include/boost/leaf/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ struct BOOST_LEAF_SYMBOL_VISIBLE e_source_location
template <class CharT, class Traits>
friend std::ostream & operator<<( std::basic_ostream<CharT, Traits> & os, e_source_location const & x )
{
return leaf::print_type_str<e_source_location>(os) << x.file << '(' << x.line << ") in function " << x.function;
return os << leaf::parse_name<e_source_location>() << x.file << '(' << x.line << ") in function " << x.function;
}
};

Expand Down
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ if option_enable_unit_tests
'on_error_preload_nested_success_exception_test',
'on_error_preload_nested_success_result_test',
'optional_test',
'parse_name_test',
'print_test',
'result_bad_result_test',
'result_implicit_conversion_test',
Expand Down
1 change: 1 addition & 0 deletions test/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ run on_error_preload_nested_new_error_result_test.cpp ;
run on_error_preload_nested_success_exception_test.cpp ;
run on_error_preload_nested_success_result_test.cpp ;
run optional_test.cpp ;
run parse_name_test.cpp ;
run print_test.cpp ;
run result_bad_result_test.cpp ;
run result_implicit_conversion_test.cpp ;
Expand Down
79 changes: 79 additions & 0 deletions test/parse_name_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2018-2024 Emil Dotchevski and Reverge Studios, Inc.

// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifdef BOOST_LEAF_TEST_SINGLE_HEADER
# include "leaf.hpp"
#else
# include <boost/leaf/detail/demangle.hpp>
#endif

#if BOOST_LEAF_CFG_STD_STRING

namespace leaf = boost::leaf;

#include "lightweight_test.hpp"

namespace leaf_test
{
class class_ { };
struct struct_ { };
enum enum_ { };
template <int> class class_template1 { };
template <int> struct struct_template1 { };
template <class> class class_template2 { };
template <class> struct struct_template2 { };
}

class class_ { };
struct struct_ { };
enum enum_ { };
template <int> class class_template1 { };
template <int> struct struct_template1 { };
template <class> class class_template2 { };
template <class> struct struct_template2 { };

namespace
{
template <class Name>
void test_name(std::string const & correct)
{
leaf::parsed_name pn = leaf::parse_name<Name>();
BOOST_TEST(pn.parse_success());
BOOST_TEST_EQ(std::string(pn.name, pn.len), correct);
}
}

int main()
{
test_name<leaf_test::class_>("leaf_test::class_");
test_name<leaf_test::struct_>("leaf_test::struct_");
test_name<leaf_test::enum_>("leaf_test::enum_");
test_name<leaf_test::class_template1<42>>("leaf_test::class_template1<42>");
test_name<leaf_test::struct_template1<42>>("leaf_test::struct_template1<42>");
test_name<leaf_test::class_template2<int>>("leaf_test::class_template2<int>");
test_name<leaf_test::struct_template2<int>>("leaf_test::struct_template2<int>");

test_name<class_>("class_");
test_name<struct_>("struct_");
test_name<enum_>("enum_");
test_name<class_template1<42>>("class_template1<42>");
test_name<struct_template1<42>>("struct_template1<42>");
test_name<class_template2<int>>("class_template2<int>");
test_name<struct_template2<int>>("struct_template2<int>");

return boost::report_errors();
}

#else

#include <iostream>

int main()
{
std::cout << "Unit test not applicable." << std::endl;
return 0;
}

#endif

0 comments on commit 35460a8

Please sign in to comment.