Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/glaze/api/type_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,11 @@ namespace glz
static constexpr std::string_view name =
join_v<name_v<Ret>, chars<" (">, name_v<Obj>, chars<"::*)(">, name_v<Args>..., chars<")">>;
};

template <class Ret, class Obj, class... Args>
struct meta<Ret (Obj::*)(Args...) volatile>
{
static constexpr std::string_view name =
join_v<type_name<Ret>, chars<" (">, name_v<Obj>, chars<"::*)(">, name_v<Args>..., chars<") volatile">>;
};
}
176 changes: 167 additions & 9 deletions include/glaze/reflection/get_name.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,141 @@ namespace glz
#elif defined(_MSC_VER)
#endif

consteval std::string_view trim_ascii_space(std::string_view str)
{
const auto first = str.find_first_not_of(" \t");
if (first == std::string_view::npos) {
return {};
}
const auto last = str.find_last_not_of(" \t");
return str.substr(first, last - first + 1);
}

consteval bool has_wrapping_parens(std::string_view str)
{
if (str.size() < 2 || str.front() != '(' || str.back() != ')') {
return false;
}

size_t depth{};
for (size_t i = 0; i < str.size(); ++i) {
const auto c = str[i];
if (c == '(') {
++depth;
}
else if (c == ')') {
if (depth == 0) {
return false;
}
--depth;
if (depth == 0) {
return i + 1 == str.size();
}
}
}

return false;
}

consteval std::string_view strip_unmatched_trailing_parens(std::string_view str)
{
size_t open_count{};
size_t close_count{};
for (const auto c : str) {
if (c == '(') {
++open_count;
}
else if (c == ')') {
++close_count;
}
}

while (close_count > open_count && !str.empty() && str.back() == ')') {
str.remove_suffix(1);
--close_count;
}

return str;
}

consteval std::string_view normalize_extracted_name(std::string_view str)
{
str = trim_ascii_space(str);

while (has_wrapping_parens(str)) {
str.remove_prefix(1);
str.remove_suffix(1);
str = trim_ascii_space(str);
}

str = strip_unmatched_trailing_parens(str);

if (const auto scope_pos = str.rfind("::"); scope_pos != std::string_view::npos) {
return str.substr(scope_pos + 2);
}
return str;
}

#if defined(_MSC_VER) && !defined(__clang__)
consteval std::string_view extract_template_argument(std::string_view str)
{
const auto arg_start = str.rfind('<');
if (arg_start != std::string_view::npos) {
str.remove_prefix(arg_start + 1);
}
const auto arg_end = str.rfind('>');
if (arg_end != std::string_view::npos) {
str = str.substr(0, arg_end);
}
return trim_ascii_space(str);
}

consteval std::string_view strip_leading_decl_keywords(std::string_view str)
{
str = trim_ascii_space(str);
while (true) {
if (str.starts_with("enum ")) {
str.remove_prefix(5);
}
else if (str.starts_with("struct ")) {
str.remove_prefix(7);
}
else if (str.starts_with("class ")) {
str.remove_prefix(6);
}
else {
break;
}
str = trim_ascii_space(str);
}
return str;
}
#endif

#if !defined(_MSC_VER) || defined(__clang__)
consteval std::string_view parse_name_from_pretty_function(std::string_view str)
{
const auto amp_pos = str.find("&");
if (amp_pos != std::string_view::npos) {
size_t prefix = amp_pos;
while (prefix > 0 && str[prefix - 1] == ' ') {
--prefix;
}
if (prefix > 0 && str[prefix - 1] == '(') {
str.remove_prefix(prefix - 1);
}
else {
str.remove_prefix(amp_pos + 1);
}
}
const auto tail_pos = str.find(pretty_function_tail);
if (tail_pos != std::string_view::npos) {
str = str.substr(0, tail_pos);
}
return normalize_extracted_name(str);
}
#endif

template <auto P>
requires(std::is_member_pointer_v<decltype(P)>)
consteval std::string_view get_name()
Expand All @@ -299,10 +434,32 @@ namespace glz
#else
// TODO: Use std::source_location when deprecating clang 14
// std::string_view str = std::source_location::current().function_name();
return parse_name_from_pretty_function(GLZ_PRETTY_FUNCTION);
#endif
}

template <auto P>
requires(std::is_pointer_v<decltype(P)> && std::is_object_v<std::remove_pointer_t<decltype(P)>>)
consteval std::string_view get_name()
{
#if defined(_MSC_VER) && !defined(__clang__)
// Example: `... glz::get_name<&T::static_value>(void)`
std::string_view str = GLZ_PRETTY_FUNCTION;
str = str.substr(str.find("&") + 1);
str = str.substr(0, str.find(pretty_function_tail));
return str.substr(str.rfind("::") + 2);
const auto arg_start = str.rfind('<');
if (arg_start != std::string_view::npos) {
str.remove_prefix(arg_start + 1);
}
const auto arg_end = str.rfind('>');
if (arg_end != std::string_view::npos) {
str = str.substr(0, arg_end);
}
const auto amp_pos = str.find('&');
if (amp_pos != std::string_view::npos) {
str.remove_prefix(amp_pos + 1);
}
return normalize_extracted_name(str);
#else
return parse_name_from_pretty_function(GLZ_PRETTY_FUNCTION);
#endif
}

Expand All @@ -311,9 +468,8 @@ namespace glz
consteval auto get_name()
{
#if defined(_MSC_VER) && !defined(__clang__)
std::string_view str = GLZ_PRETTY_FUNCTION;
str = str.substr(str.rfind("::") + 2);
return str.substr(0, str.find('>'));
std::string_view str = extract_template_argument(GLZ_PRETTY_FUNCTION);
return normalize_extracted_name(strip_leading_decl_keywords(str));
#else
std::string_view str = GLZ_PRETTY_FUNCTION;
str = str.substr(str.rfind("::") + 2);
Expand All @@ -326,9 +482,11 @@ namespace glz
consteval auto get_name()
{
#if defined(_MSC_VER) && !defined(__clang__)
std::string_view str = GLZ_PRETTY_FUNCTION;
str = str.substr(str.rfind("= ") + 2);
return str.substr(0, str.find('>'));
std::string_view str = extract_template_argument(GLZ_PRETTY_FUNCTION);
if (const auto eq_pos = str.rfind("= "); eq_pos != std::string_view::npos) {
str.remove_prefix(eq_pos + 2);
}
return normalize_extracted_name(strip_leading_decl_keywords(str));
#else
std::string_view str = GLZ_PRETTY_FUNCTION;
str = str.substr(str.rfind("= ") + 2);
Expand Down
28 changes: 28 additions & 0 deletions tests/json_test/json_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7028,6 +7028,20 @@ struct glz::meta<cx_values>
static constexpr auto value{glz::object("info", &T::info, "index", &T::index, "value", &T::value)};
};

struct cx_values_implicit_static_key
{
static constexpr std::string_view other_type{"http://www.github.io"};
std::string type{"http://www.github.io"};
int status{};
};

template <>
struct glz::meta<cx_values_implicit_static_key>
{
using T = cx_values_implicit_static_key;
static constexpr auto value{glz::object(&T::other_type, &T::type, &T::status)};
};

struct direct_cx_value_conversion
{
static constexpr std::uint64_t const_v{42};
Expand Down Expand Up @@ -7140,6 +7154,20 @@ suite constexpr_values_test = [] {
expect(obj.value == "special");
};

"constexpr_values_implicit_static_key"_test = [] {
cx_values_implicit_static_key obj{};
obj.status = 100;
std::string s{};
expect(not glz::write_json(obj, s));
expect(s == R"({"other_type":"http://www.github.io","type":"http://www.github.io","status":100})") << s;

s = R"({"other_type":"something_else","type":"updated","status":200})";
expect(!glz::read_json(obj, s));
expect(cx_values_implicit_static_key::other_type == "http://www.github.io");
expect(obj.type == "updated");
expect(obj.status == 200);
};

using const_only_variant =
std::variant<direct_cx_value_conversion_different_value, direct_cx_value_conversion,
string_direct_cx_value_conversion, string_two_direct_cx_value_conversion,
Expand Down
4 changes: 4 additions & 0 deletions tests/json_test/json_variant_support_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,15 @@ suite tagged_variant_tests = [] {
expect(parsed_var == var);
};

#if !defined(_MSC_VER)
"tagged_variant_schema_tests"_test = [] {
auto s = glz::write_json_schema<tagged_variant>().value_or("error");
expect(
s ==
R"({"type":["object"],"$defs":{"int32_t":{"type":["integer"],"minimum":-2147483648,"maximum":2147483647},"std::map<std::string,int32_t>":{"type":["object"],"additionalProperties":{"$ref":"#/$defs/int32_t"}},"std::string":{"type":["string"]}},"oneOf":[{"type":["object"],"properties":{"action":{"const":"PUT"},"data":{"$ref":"#/$defs/std::map<std::string,int32_t>"}},"additionalProperties":false,"required":["action"],"title":"PUT"},{"type":["object"],"properties":{"action":{"const":"DELETE"},"data":{"$ref":"#/$defs/std::string"}},"additionalProperties":false,"required":["action"],"title":"DELETE"}],"title":"std::variant<put_action, delete_action>"})")
<< s;
};
#endif

"array_variant_tests"_test = [] {
// Test array based variant (experimental, not meant for external usage since api might change)
Expand Down Expand Up @@ -196,13 +198,15 @@ suite tagged_variant_tests = [] {
expect(s == R"({"num":["int8_t",-5]})");
};

#if !defined(_MSC_VER)
"shared_ptr variant schema"_test = [] {
const auto schema = glz::write_json_schema<std::shared_ptr<tagged_variant2>>().value_or("error");
expect(
schema ==
R"({"type":["object","null"],"$defs":{"int32_t":{"type":["integer"],"minimum":-2147483648,"maximum":2147483647},"std::map<std::string,int32_t>":{"type":["object"],"additionalProperties":{"$ref":"#/$defs/int32_t"}},"std::string":{"type":["string"]}},"oneOf":[{"type":["object"],"properties":{"data":{"$ref":"#/$defs/std::map<std::string,int32_t>"},"type":{"const":"put_action"}},"additionalProperties":false,"required":["type"],"title":"put_action"},{"type":["object"],"properties":{"data":{"$ref":"#/$defs/std::string"},"type":{"const":"delete_action"}},"additionalProperties":false,"required":["type"],"title":"delete_action"},{"type":["null"],"title":"std::monostate","const":null}],"title":"std::shared_ptr<std::variant<put_action, delete_action, std::monostate>>"})")
<< schema;
};
#endif
};

struct variant_obj
Expand Down
4 changes: 4 additions & 0 deletions tests/reflection/reflection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ struct test_type
suite reflect_test_type = [] {
static_assert(glz::reflect<test_type>::size == 2);
static_assert(glz::reflect<test_type>::keys[0] == "int1");
static_assert(glz::normalize_extracted_name("callable_reflection_regression::operator()") ==
std::string_view{"operator()"});
static_assert(glz::normalize_extracted_name("cx_values_implicit_static_key::other_type)") ==
std::string_view{"other_type"});

"for_each_field"_test = [] {
test_type var{42, 43};
Expand Down
Loading