Skip to content

Commit 591fce7

Browse files
authored
Cleaning up map writing (#1542)
* Cleaning up map writing * Update write.hpp * smarter nullable constexpr handling * Use auto&& for cleaner code * Update write.hpp * Update write.hpp * Update write.hpp * Update write.hpp
1 parent 2364172 commit 591fce7

File tree

2 files changed

+115
-76
lines changed

2 files changed

+115
-76
lines changed

include/glaze/concepts/container_concepts.hpp

+9-3
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,17 @@ namespace glz
2121
template <class T, class... U>
2222
concept is_any_of = (std::same_as<T, U> || ...);
2323

24-
template <typename T>
24+
template <class T>
2525
concept has_value_type = requires { typename T::value_type; };
2626

27-
template <typename T>
28-
concept has_element_type = requires { typename T::has_element_type; };
27+
template <class T>
28+
concept has_element_type = requires { typename T::element_type; };
29+
30+
template <class T>
31+
concept has_first_type = requires { typename T::first_type; };
32+
33+
template <class T>
34+
concept has_second_type = requires { typename T::second_type; };
2935

3036
template <class T>
3137
concept resizable = requires(T v) { v.resize(0); };

include/glaze/json/write.hpp

+106-73
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,45 @@ namespace glz
843843
template <class T>
844844
concept array_padding_known =
845845
requires { typename T::value_type; } && (required_padding<typename T::value_type>() > 0);
846+
847+
template <class Container>
848+
using iterator_pair_type = typename std::iterator_traits<decltype(std::begin(std::declval<Container&>()))>::value_type;
849+
850+
template <class Container, typename Iterator = iterator_pair_type<Container>>
851+
struct iterator_second_impl;
852+
853+
template <class Container, typename Iterator>
854+
requires has_value_type<Iterator>
855+
struct iterator_second_impl<Container, Iterator> {
856+
using type = typename Iterator::value_type;
857+
};
858+
859+
template <class Container, typename Iterator>
860+
requires (!has_value_type<Iterator> && has_second_type<Iterator>)
861+
struct iterator_second_impl<Container, Iterator> {
862+
using type = typename Iterator::second_type;
863+
};
864+
865+
template <class Container>
866+
using iterator_second_type = typename iterator_second_impl<Container>::type;
867+
868+
template <class Container, typename Iterator = iterator_pair_type<Container>>
869+
struct iterator_first_impl;
870+
871+
template <class Container, typename Iterator>
872+
requires has_value_type<Iterator>
873+
struct iterator_first_impl<Container, Iterator> {
874+
using type = typename Iterator::value_type;
875+
};
876+
877+
template <class Container, typename Iterator>
878+
requires (!has_value_type<Iterator> && has_first_type<Iterator>)
879+
struct iterator_first_impl<Container, Iterator> {
880+
using type = typename Iterator::first_type;
881+
};
882+
883+
template <class Container>
884+
using iterator_first_type = typename iterator_first_impl<Container>::type;
846885

847886
template <class T>
848887
requires(writable_array_t<T> || writable_map_t<T>)
@@ -1033,107 +1072,101 @@ namespace glz
10331072
}
10341073
}
10351074

1036-
template <auto Opts>
1075+
template <auto Opts, class B>
10371076
requires(writable_map_t<T> || (map_like_array && Opts.concatenate == true))
1038-
static void op(auto&& value, is_context auto&& ctx, auto&&... args)
1077+
static void op(auto&& value, is_context auto&& ctx, B&& b, auto&& ix)
10391078
{
10401079
if constexpr (not has_opening_handled(Opts)) {
1041-
dump<'{'>(args...);
1080+
dump<'{'>(b, ix);
10421081
}
10431082

10441083
if (!empty_range(value)) {
10451084
if constexpr (!has_opening_handled(Opts)) {
10461085
if constexpr (Opts.prettify) {
10471086
ctx.indentation_level += Opts.indentation_width;
1048-
dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, args...);
1049-
}
1050-
}
1051-
1052-
auto write_first_entry = [&ctx, &args...](auto&& it) {
1053-
if constexpr (requires {
1054-
it->first;
1055-
it->second;
1056-
}) {
1057-
// Allow non-const access, unlike ranges
1058-
if (skip_member<Opts>(it->second)) {
1059-
return true;
1060-
}
1061-
write_pair_content<Opts>(it->first, it->second, ctx, args...);
1062-
return false;
1063-
}
1064-
else {
1065-
const auto& [key, entry_val] = *it;
1066-
if (skip_member<Opts>(entry_val)) {
1067-
return true;
1087+
if constexpr (vector_like<B>) {
1088+
if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size()) [[unlikely]] {
1089+
b.resize(2 * k);
1090+
}
10681091
}
1069-
write_pair_content<Opts>(key, entry_val, ctx, args...);
1070-
return false;
1092+
std::memcpy(&b[ix], "\n", 1);
1093+
++ix;
1094+
std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1095+
ix += ctx.indentation_level;
10711096
}
1072-
};
1073-
1074-
auto it = std::begin(value);
1075-
[[maybe_unused]] bool starting = write_first_entry(it);
1076-
for (++it; it != std::end(value); ++it) {
1077-
// I couldn't find an easy way around this code duplication
1078-
// Ranges need to be decomposed with const auto& [...],
1079-
// but we don't want to const qualify our maps for the sake of reflection writing
1080-
// we need to be able to populate the tuple of pointers when writing with reflection
1081-
if constexpr (requires {
1082-
it->first;
1083-
it->second;
1084-
}) {
1085-
if (skip_member<Opts>(it->second)) {
1086-
continue;
1087-
}
1097+
}
1098+
1099+
using val_t = iterator_second_type<T>; // the type of value in each [key, value] pair
1100+
1101+
if constexpr (not always_skipped<val_t>)
1102+
{
1103+
if constexpr (null_t<val_t> && Opts.skip_null_members)
1104+
{
1105+
auto write_first_entry = [&](auto&& it) {
1106+
auto&& [key, entry_val] = *it;
1107+
if (skip_member<Opts>(entry_val)) {
1108+
return true;
1109+
}
1110+
write_pair_content<Opts>(key, entry_val, ctx, b, ix);
1111+
return false;
1112+
};
1113+
1114+
auto it = std::begin(value);
1115+
bool first = write_first_entry(it);
1116+
++it;
1117+
for (const auto end = std::end(value); it != end; ++it) {
1118+
auto&& [key, entry_val] = *it;
1119+
if (skip_member<Opts>(entry_val)) {
1120+
continue;
1121+
}
10881122

1089-
// When Opts.skip_null_members, *any* entry may be skipped, meaning separator dumping must be
1090-
// conditional for every entry. Avoid this branch when not skipping null members.
1091-
// Alternatively, write separator after each entry except last but then branch is permanent
1092-
if constexpr (Opts.skip_null_members) {
1093-
if (!starting) {
1094-
write_object_entry_separator<Opts>(ctx, args...);
1123+
// When Opts.skip_null_members, *any* entry may be skipped, meaning separator dumping must be
1124+
// conditional for every entry.
1125+
// Alternatively, write separator after each entry except last but then branch is permanent
1126+
if (not first) {
1127+
write_object_entry_separator<Opts>(ctx, b, ix);
10951128
}
1096-
}
1097-
else {
1098-
write_object_entry_separator<Opts>(ctx, args...);
1099-
}
11001129

1101-
write_pair_content<Opts>(it->first, it->second, ctx, args...);
1102-
}
1103-
else {
1104-
const auto& [key, entry_val] = *it;
1105-
if (skip_member<Opts>(entry_val)) {
1106-
continue;
1107-
}
1130+
write_pair_content<Opts>(key, entry_val, ctx, b, ix);
11081131

1109-
// When Opts.skip_null_members, *any* entry may be skipped, meaning separator dumping must be
1110-
// conditional for every entry. Avoid this branch when not skipping null members.
1111-
// Alternatively, write separator after each entry except last but then branch is permanent
1112-
if constexpr (Opts.skip_null_members) {
1113-
if (!starting) {
1114-
write_object_entry_separator<Opts>(ctx, args...);
1115-
}
1132+
first = false;
11161133
}
1117-
else {
1118-
write_object_entry_separator<Opts>(ctx, args...);
1134+
}
1135+
else {
1136+
auto write_first_entry = [&](auto&& it) {
1137+
auto&& [key, entry_val] = *it;
1138+
write_pair_content<Opts>(key, entry_val, ctx, b, ix);
1139+
};
1140+
1141+
auto it = std::begin(value);
1142+
write_first_entry(it);
1143+
++it;
1144+
for (const auto end = std::end(value); it != end; ++it) {
1145+
auto&& [key, entry_val] = *it;
1146+
write_object_entry_separator<Opts>(ctx, b, ix);
1147+
write_pair_content<Opts>(key, entry_val, ctx, b, ix);
11191148
}
1120-
1121-
write_pair_content<Opts>(key, entry_val, ctx, args...);
11221149
}
1123-
1124-
starting = false;
11251150
}
11261151

11271152
if constexpr (!has_closing_handled(Opts)) {
11281153
if constexpr (Opts.prettify) {
11291154
ctx.indentation_level -= Opts.indentation_width;
1130-
dump_newline_indent<Opts.indentation_char>(ctx.indentation_level, args...);
1155+
if constexpr (vector_like<B>) {
1156+
if (const auto k = ix + ctx.indentation_level + write_padding_bytes; k > b.size()) [[unlikely]] {
1157+
b.resize(2 * k);
1158+
}
1159+
}
1160+
std::memcpy(&b[ix], "\n", 1);
1161+
++ix;
1162+
std::memset(&b[ix], Opts.indentation_char, ctx.indentation_level);
1163+
ix += ctx.indentation_level;
11311164
}
11321165
}
11331166
}
11341167

11351168
if constexpr (!has_closing_handled(Opts)) {
1136-
dump<'}'>(args...);
1169+
dump<'}'>(b, ix);
11371170
}
11381171
}
11391172
};

0 commit comments

Comments
 (0)