Created
April 22, 2022 18:48
-
-
Save schaumb/8c7d1db493ba6ca1821bae584cec93d3 to your computer and use it in GitHub Desktop.
Pretty function processor.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <type_traits> | |
#include <string_view> | |
#include <iostream> | |
#include <atomic> | |
#include <utility> | |
#include <tuple> | |
#include <cassert> | |
template<char... Chars> | |
struct str { | |
template<typename CharType = char> | |
constexpr static char data[sizeof...(Chars) + 1] {Chars..., '\n'}; | |
template<typename CharType = char> | |
constexpr operator std::basic_string_view<CharType>() const { | |
return {data<CharType>, sizeof...(Chars)}; | |
} | |
}; | |
template<typename T> | |
constexpr static bool is_str_v = false; | |
template<char... Chars> | |
constexpr static bool is_str_v<str<Chars...>> = true; | |
constexpr std::string_view remove_prefix(std::string_view from, std::string_view what) { | |
if (from.rfind(what, 0) != 0) | |
assert(false); | |
from.remove_prefix(what.size()); | |
return from; | |
} | |
constexpr std::string_view remove_prefix_if(std::string_view from, std::string_view what) { | |
if (from.rfind(what, 0) != 0) | |
return from; | |
from.remove_prefix(what.size()); | |
return from; | |
} | |
constexpr std::string_view remove_suffix(std::string_view from, std::string_view what) { | |
auto where = from.size() - what.size() - 1; | |
if (from.find(what, where) != where) | |
assert(false); | |
from.remove_suffix(what.size() + 1); | |
return from; | |
} | |
constexpr std::pair<std::string_view, std::string_view> split_by_first_if(std::string_view from, std::string_view what) { | |
if (auto where = from.find(what); where != std::string_view::npos) { | |
std::string_view pre = from; | |
pre.remove_suffix(pre.size() - where); | |
from.remove_prefix(where + what.size()); | |
return {pre, from}; | |
} | |
return {from, {}}; | |
} | |
constexpr std::pair<std::string_view, std::string_view> split_by_last_if(std::string_view from, std::string_view what) { | |
if (auto where = from.rfind(what); where != std::string_view::npos) { | |
std::string_view pre = from; | |
pre.remove_suffix(pre.size() - where); | |
from.remove_prefix(where + what.size()); | |
return {pre, from}; | |
} | |
return {{}, from}; | |
} | |
constexpr bool is_lambda(std::string_view wholeName) { | |
#if defined(__clang__) | |
auto [pre, post] = split_by_last_if(wholeName, "::"); | |
// format: "(lambda at filename:line:char)", where '(' can be part of filename, but ':' is not. | |
return post.rfind("(lambda at ", 0) == 0 && *post.rbegin() == ')' | |
&& post.find(':') > post.rfind('('); // This condition filters function pointers/references which returns lambda. | |
#elif defined(__GNUC__) | |
// at the end <lambda( ... )>, where ... can be anything, with balanced parenthesis | |
if (wholeName.size() < 10 || wholeName[wholeName.size()-1] != '>' || wholeName[wholeName.size()-2] != ')') | |
return false; | |
std::size_t at = wholeName.size() - 3; | |
for (std::size_t braces{1}; braces; --at) | |
braces += wholeName[at = wholeName.find_last_of("()", at)] == '(' ? -1 : 1; | |
return at >= 6 && wholeName.substr(at-6, 7) == "<lambda"; | |
#elif defined(_MSC_VER) | |
auto [pre, post] = split_by_last_if(wholeName, "::"); | |
// format: "class NAMESPACES::<lambda_idchars>" | |
if (pre.empty()) { | |
return wholeName.rfind("class <lambda_", 0) == 0 && *wholeName.rbegin() == '>'; | |
} else { | |
return wholeName.rfind("class ", 0) == 0 && post.rfind("<lambda_", 0) == 0 && *wholeName.rbegin() == '>'; | |
} | |
#endif | |
} | |
template <typename T> | |
constexpr inline auto n() noexcept { | |
# if defined(__clang__) | |
constexpr std::string_view name{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__)}; | |
constexpr std::string_view n1 = remove_prefix(name, "auto n() [T = "); | |
constexpr std::string_view n2 = remove_suffix(n1, "]"); | |
constexpr std::string_view final_name = n2; | |
# elif defined(__GNUC__) | |
constexpr std::string_view name{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__)}; | |
constexpr std::string_view n1 = remove_prefix(name, "constexpr auto n() [with T = "); | |
constexpr std::string_view n2 = remove_suffix(n1, "]"); | |
constexpr std::string_view final_name = n2; | |
# elif defined(_MSC_VER) | |
constexpr std::string_view name{__FUNCSIG__, sizeof(__FUNCSIG__)}; | |
constexpr std::string_view n1 = remove_prefix(name, "auto __cdecl n<"); | |
constexpr std::string_view n2 = remove_suffix(n1, ">(void) noexcept"); | |
constexpr std::string_view final_name = n2; | |
# endif | |
return final_name; | |
} | |
template<typename T, std::size_t ... Ix> | |
constexpr inline auto name_getter(std::index_sequence<Ix...>) noexcept { | |
constexpr std::string_view name = n<T>(); | |
return str<name[Ix]...>{}; | |
} | |
template<typename T> | |
using FullName = decltype(name_getter<T>(std::make_index_sequence<n<T>().size()>{})); | |
template<typename T> | |
constexpr std::string_view full_name = FullName<T>{}; | |
enum class ClassType { | |
#if !defined(_MSC_VER) | |
user_defined, | |
class_ = user_defined, | |
struct_ = user_defined, | |
enum_ = user_defined, | |
#else | |
class_, | |
struct_, | |
enum_, | |
#endif | |
lambda, | |
integral, | |
floating_point, | |
null_pointer, | |
member_pointer, | |
pointer, | |
array, | |
function, | |
void_, | |
}; | |
template<typename T> | |
constexpr static std::enable_if_t<!is_str_v<T>, ClassType> getType() { | |
if constexpr (std::is_enum_v<T>) { | |
return ClassType::enum_; | |
} else if constexpr (std::is_integral_v<T>) { | |
return ClassType::integral; | |
} else if constexpr (std::is_floating_point_v<T>) { | |
return ClassType::floating_point; | |
} else if constexpr (std::is_same_v<std::remove_const_t<T>, std::nullptr_t>) { | |
return ClassType::null_pointer; | |
} else if constexpr (std::is_member_pointer_v<T>) { | |
return ClassType::member_pointer; | |
} else if constexpr (std::is_pointer_v<T>) { | |
return ClassType::pointer; | |
} else if constexpr (std::is_array_v<T>) { | |
return ClassType::array; | |
} else if constexpr (std::is_function_v<std::remove_reference_t<T>>) { | |
return ClassType::function; | |
} else if constexpr (std::is_void_v<std::remove_const_t<T>>) { | |
return ClassType::void_; | |
} else if constexpr (constexpr std::string_view name = full_name<std::remove_cv_t<std::remove_reference_t<T>>>; is_lambda(name)) { | |
return ClassType::lambda; | |
} else if (name.rfind("struct ", 0) == 0) { | |
return ClassType::struct_; | |
} else { | |
return ClassType::class_; | |
} | |
} | |
template<typename T> | |
constexpr static std::enable_if_t<is_str_v<T>, ClassType> getType() { | |
} | |
template<typename Info, ClassType type = getType<Info>(), typename = void> | |
struct ClassInfo; | |
template<typename Info, ClassType type> | |
struct ClassInfo<Info, type, std::enable_if_t<!is_str_v<Info>>> : ClassInfo<FullName<Info>, type, void> {}; | |
template<char ... Args, ClassType type> | |
struct ClassInfo<str<Args...>, type, std::enable_if_t<type == ClassType::class_ ||type == ClassType::struct_ || type == ClassType::enum_>> { | |
constexpr operator std::string_view() const { | |
constexpr std::string_view name = str<Args...>{}; | |
constexpr std::string_view n0 = remove_prefix_if(name, "const "); | |
constexpr std::string_view n01 = remove_prefix_if(n0, "volatile "); | |
constexpr std::string_view n02 = remove_prefix_if(n01, "const "); | |
constexpr std::string_view n1 = remove_prefix_if(n02, "struct "); | |
constexpr std::string_view n2 = remove_prefix_if(n1, "class "); | |
constexpr std::string_view n3 = remove_prefix_if(n2, "enum "); | |
auto [ns, nons_name] = split_by_last_if(n3, "::"); // split namespace | |
auto [final_name, templates] = split_by_first_if(nons_name, "<"); // split template arguments | |
return final_name; | |
} | |
}; | |
template<char ... Args> | |
struct ClassInfo<str<Args...>, ClassType::pointer, void> { | |
constexpr operator std::string_view() const { | |
return "*"; | |
} | |
}; | |
template<char ... Args> | |
struct ClassInfo<str<Args...>, ClassType::member_pointer, void> { | |
constexpr operator std::string_view() const { | |
return "::*"; | |
} | |
}; | |
template<char ... Args> | |
struct ClassInfo<str<Args...>, ClassType::lambda, void> { | |
constexpr operator std::string_view() const { | |
return "lambda"; | |
} | |
}; | |
template<char ... Args> | |
struct ClassInfo<str<Args...>, ClassType::function, void> { | |
constexpr operator std::string_view() const { | |
return "function"; | |
} | |
}; | |
template<char ... Args, ClassType type> | |
struct ClassInfo<str<Args...>, type, std::enable_if_t<type == ClassType::integral>> { | |
constexpr operator std::string_view() const { | |
constexpr std::string_view name = str<Args...>{}; | |
constexpr std::string_view n0 = remove_prefix_if(name, "const "); | |
constexpr std::string_view n01 = remove_prefix_if(n0, "volatile "); | |
constexpr std::string_view n02 = remove_prefix_if(n01, "const "); | |
return n02; | |
} | |
}; | |
template<typename T> | |
constexpr std::string_view name = ClassInfo<T>{}; | |
template<typename T> | |
constexpr std::string_view getName() { | |
return name<T>; | |
} | |
struct A; | |
class B; | |
enum C {}; | |
enum class D; | |
#define ASSERTS(CL, WHAT) \ | |
static_assert(getName<CL>() == #CL) | |
#define ASSERTS_N(CL, N, WHAT) \ | |
static_assert(getName<CL>() == N) | |
template<typename T> | |
struct I; | |
template<typename T> | |
class J; | |
#include <array> | |
#include <vector> | |
namespace ASD::ASD { | |
auto L = []{}; | |
auto L2 = []{}; | |
decltype(L) (*Cica) (decltype(L2)); | |
} | |
struct A { | |
int b; | |
}; | |
int main() { | |
ASSERTS(A, struct_); | |
ASSERTS(B, class_); | |
ASSERTS(C, enum_); | |
ASSERTS(D, enum_); | |
ASSERTS_N(struct E, "E", struct_); | |
ASSERTS_N(class F, "F", class_); | |
enum G{}; | |
ASSERTS(G, enum_); | |
enum class H{}; | |
ASSERTS(H, enum_); | |
ASSERTS_N(I<int>, "I", struct_); | |
ASSERTS_N(J<int>, "J", class_); | |
std::cout << full_name<const volatile decltype(::ASD::ASD::L)&> << "\n"; | |
std::cout << name<const volatile decltype(::ASD::ASD::L)&> << "\n"; | |
std::cout << full_name<const volatile decltype(::ASD::ASD::L2)> << "\n"; | |
std::cout << name<const volatile decltype(::ASD::ASD::L2)> << "\n"; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment