228 lines
7.7 KiB
C++
228 lines
7.7 KiB
C++
/*
|
|
* Copyright (c) 2023, Alibaba Group Holding Limited;
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* Author's Email: metabeyond@outlook.com
|
|
* Author's Github: https://github.com/refvalue/
|
|
* Description: this source file contains code for parsing function names from
|
|
* their signatures, especially optimized for the MSVC implementation.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#if __has_include(<span>)
|
|
#include <span>
|
|
|
|
#include "meta_string.hpp"
|
|
#include "string_finder.hpp"
|
|
#endif
|
|
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
namespace refvalue {
|
|
namespace detail {
|
|
#if __has_include(<span>)
|
|
template <meta_string Signature>
|
|
struct parse_qualified_function_name {
|
|
#if defined(_MSC_VER) && defined(_WIN64)
|
|
static constexpr std::array calling_conventions{"__cdecl", "__vectorcall"};
|
|
#elif defined(_MSC_VER) && defined(_WIN32)
|
|
static constexpr std::array calling_conventions{
|
|
"__cdecl", "__stdcall", "__fastcall", "__vectorcall", "__thiscall"};
|
|
#elif defined(__clang__) || defined(__GNUC__)
|
|
static constexpr std::array<const char*, 0> calling_conventions{};
|
|
#else
|
|
#error "Unsupported platform."
|
|
#endif
|
|
|
|
static constexpr std::string_view view{Signature};
|
|
static constexpr auto depths = [] {
|
|
constexpr std::string_view left_tokens{"<(["};
|
|
constexpr std::string_view right_tokens{">)]"};
|
|
std::array<std::size_t, view.size()> result{};
|
|
|
|
for (std::size_t i = 0, j = 0; i < view.size(); i++) {
|
|
if (left_tokens.find(view[i]) != std::string_view::npos) {
|
|
j++;
|
|
}
|
|
|
|
if (right_tokens.find(view[i]) != std::string_view::npos && j != 0) {
|
|
j--;
|
|
}
|
|
|
|
result[i] = j;
|
|
}
|
|
|
|
return result;
|
|
}();
|
|
|
|
template <find_mode_type Mode>
|
|
static constexpr std::size_t find_significant_index(
|
|
std::string_view keyword, std::size_t depth = 0,
|
|
std::size_t index = string_finder_traits<Mode>::default_index) {
|
|
for (auto i = uniform_find_string<Mode>(view, keyword, index);
|
|
i < view.size(); i = uniform_find_string<Mode>(
|
|
view, keyword, skip_keyword<Mode>(i, keyword))) {
|
|
if (depths[i] == depth) {
|
|
return skip_keyword<Mode>(i, keyword);
|
|
}
|
|
}
|
|
|
|
return std::string_view::npos;
|
|
}
|
|
|
|
static constexpr auto value = [] {
|
|
// When using MSVC, Finds the start index of the function name past the
|
|
// top-level calling convention token (e.g. __cdecl/__vectorcall for x64,
|
|
// __stdcall/__cdecl/__fastcall/__vectorcall/__thiscall for x86).
|
|
// When using GCC or Clang, returns zero immediately.
|
|
constexpr auto start_index = []() -> std::size_t {
|
|
// Workaround for failing compilation on x86-64 Clang assertions trunk.
|
|
// std::array<T, 0>::begin() and std::array<T, 0>::end() are not
|
|
// odr-usable.
|
|
if constexpr (!calling_conventions.empty()) {
|
|
for (auto&& item : calling_conventions) {
|
|
if (auto index =
|
|
find_significant_index<find_mode_type::full_match>(item);
|
|
index != std::string_view::npos) {
|
|
return index + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}();
|
|
|
|
// Finds the end index of the function name before the top-level left
|
|
// "<" or "(" indicating the start of the template or function arguments.
|
|
constexpr auto function_name_start_index = (std::max)(
|
|
start_index,
|
|
find_significant_index<find_mode_type::full_match_reverse>("::"));
|
|
|
|
constexpr auto end_index = find_significant_index<find_mode_type::any_of>(
|
|
"<(", 1,
|
|
function_name_start_index != std::string_view::npos
|
|
? function_name_start_index
|
|
: start_index);
|
|
|
|
constexpr auto final_size = end_index != std::string_view::npos
|
|
? (end_index - 1 - start_index)
|
|
: (view.size() - start_index);
|
|
|
|
return meta_string{std::span<const char, final_size>{
|
|
view.data() + start_index, final_size}};
|
|
}();
|
|
};
|
|
|
|
template <meta_string Signature>
|
|
inline constexpr auto&& parse_qualified_function_name_v =
|
|
parse_qualified_function_name<Signature>::value;
|
|
#endif
|
|
|
|
template <auto Func>
|
|
constexpr auto qualified_name_of_impl() noexcept {
|
|
#ifdef _MSC_VER
|
|
constexpr std::size_t suffix_size{16};
|
|
constexpr std::string_view keyword{
|
|
"refvalue::detail::qualified_name_of_impl<"};
|
|
constexpr std::string_view signature{__FUNCSIG__};
|
|
constexpr std::string_view anonymous_namespace{"`anonymous-namespace'::"};
|
|
#elif defined(__clang__)
|
|
constexpr std::size_t suffix_size{1};
|
|
constexpr std::string_view keyword{"[Func = "};
|
|
constexpr std::string_view signature{__PRETTY_FUNCTION__};
|
|
constexpr std::string_view anonymous_namespace{"(anonymous namespace)::"};
|
|
#elif defined(__GNUC__)
|
|
constexpr std::size_t suffix_size{1};
|
|
constexpr std::string_view keyword{"Func = "};
|
|
constexpr std::string_view signature{__PRETTY_FUNCTION__};
|
|
constexpr std::string_view anonymous_namespace{"{anonymous}::"};
|
|
#else
|
|
#error "Unsupported compiler."
|
|
#endif
|
|
// Skips the possible '&' token for GCC and Clang.
|
|
constexpr auto prefix_size = signature.find(keyword) + keyword.size();
|
|
constexpr auto additional_size = signature[prefix_size] == '&' ? 1 : 0;
|
|
constexpr auto intermediate = signature.substr(
|
|
prefix_size + additional_size,
|
|
signature.size() - prefix_size - additional_size - suffix_size);
|
|
|
|
constexpr std::string_view result = intermediate;
|
|
constexpr size_t rpos = result.rfind(anonymous_namespace);
|
|
if constexpr (rpos != std::string_view::npos) {
|
|
constexpr std::string_view str =
|
|
result.substr(rpos + anonymous_namespace.size());
|
|
constexpr size_t right = str.find('(');
|
|
if constexpr (right != std::string_view::npos) {
|
|
return str.substr(0, right);
|
|
}
|
|
else {
|
|
return str;
|
|
}
|
|
}
|
|
else {
|
|
constexpr size_t left = result.find("l ") + 2;
|
|
constexpr size_t right = result.find('(');
|
|
if constexpr (left != std::string_view::npos) {
|
|
if constexpr (right != std::string_view::npos) {
|
|
return result.substr(left, right - left);
|
|
}
|
|
else {
|
|
return result;
|
|
}
|
|
}
|
|
else {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
} // namespace detail
|
|
|
|
template <auto Func>
|
|
struct qualified_name_of {
|
|
static constexpr auto value = detail::qualified_name_of_impl<Func>();
|
|
};
|
|
|
|
template <auto Func>
|
|
inline constexpr auto&& qualified_name_of_v = qualified_name_of<Func>::value;
|
|
|
|
#if __has_include(<span>)
|
|
template <auto Func>
|
|
struct name_of {
|
|
static constexpr auto value = [] {
|
|
// a::b::c::function_name<nested1::nested2::nested3::class_name<...>>
|
|
constexpr std::string_view qualified_name{qualified_name_of_v<Func>};
|
|
constexpr auto template_argument_start_token_index =
|
|
qualified_name.find_first_of('<');
|
|
constexpr auto scope_token_index =
|
|
qualified_name.rfind("::", template_argument_start_token_index);
|
|
constexpr auto class_name_index =
|
|
scope_token_index != std::string_view::npos ? scope_token_index + 2 : 0;
|
|
constexpr auto result = qualified_name.substr(class_name_index);
|
|
|
|
return meta_string{
|
|
std::span<const char, result.size()>{result.data(), result.size()}};
|
|
}();
|
|
};
|
|
|
|
template <auto Func>
|
|
inline constexpr auto&& name_of_v = name_of<Func>::value;
|
|
#endif
|
|
} // namespace refvalue
|