Skip to content

Instantly share code, notes, and snippets.

@schaumb
Created April 22, 2022 18:48
Show Gist options
  • Save schaumb/8c7d1db493ba6ca1821bae584cec93d3 to your computer and use it in GitHub Desktop.
Save schaumb/8c7d1db493ba6ca1821bae584cec93d3 to your computer and use it in GitHub Desktop.
Pretty function processor.
#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