Skip to content

Commit 3ece9b1

Browse files
committed
smarter nullable constexpr handling
1 parent a550525 commit 3ece9b1

File tree

2 files changed

+135
-67
lines changed

2 files changed

+135
-67
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

+126-64
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,27 @@ 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 IteratorValue = iterator_pair_type<Container>>
851+
struct iterator_value_impl;
852+
853+
template <class Container, typename IteratorValue>
854+
requires has_value_type<IteratorValue>
855+
struct iterator_value_impl<Container, IteratorValue> {
856+
using type = typename IteratorValue::value_type;
857+
};
858+
859+
template <class Container, typename IteratorValue>
860+
requires (!has_value_type<IteratorValue> && has_second_type<IteratorValue>)
861+
struct iterator_value_impl<Container, IteratorValue> {
862+
using type = typename IteratorValue::second_type;
863+
};
864+
865+
template <class Container>
866+
using iterator_value_type = typename iterator_value_impl<Container>::type;
846867

847868
template <class T>
848869
requires(writable_array_t<T> || writable_map_t<T>)
@@ -1056,80 +1077,121 @@ namespace glz
10561077
ix += ctx.indentation_level;
10571078
}
10581079
}
1080+
1081+
using val_t = iterator_value_type<T>; // the type of value in each [key, value] pair
1082+
1083+
if constexpr (not always_skipped<val_t>)
1084+
{
1085+
if constexpr (null_t<val_t>)
1086+
{
1087+
auto write_first_entry = [&ctx, &b, &ix](auto&& it) {
1088+
if constexpr (requires {
1089+
it->first;
1090+
it->second;
1091+
}) {
1092+
// Allow non-const access, unlike ranges
1093+
if (skip_member<Opts>(it->second)) {
1094+
return true;
1095+
}
1096+
write_pair_content<Opts>(it->first, it->second, ctx, b, ix);
1097+
return false;
1098+
}
1099+
else {
1100+
const auto& [key, entry_val] = *it;
1101+
if (skip_member<Opts>(entry_val)) {
1102+
return true;
1103+
}
1104+
write_pair_content<Opts>(key, entry_val, ctx, b, ix);
1105+
return false;
1106+
}
1107+
};
1108+
1109+
auto it = std::begin(value);
1110+
[[maybe_unused]] bool starting = write_first_entry(it);
1111+
++it;
1112+
for (const auto end = std::end(value); it != end; ++it) {
1113+
// I couldn't find an easy way around this code duplication
1114+
// Ranges need to be decomposed with const auto& [...],
1115+
// but we don't want to const qualify our maps for the sake of reflection writing
1116+
// we need to be able to populate the tuple of pointers when writing with reflection
1117+
if constexpr (requires {
1118+
it->first;
1119+
it->second;
1120+
}) {
1121+
if (skip_member<Opts>(it->second)) {
1122+
continue;
1123+
}
10591124

1060-
auto write_first_entry = [&ctx, &b, &ix](auto&& it) {
1061-
if constexpr (requires {
1062-
it->first;
1063-
it->second;
1064-
}) {
1065-
// Allow non-const access, unlike ranges
1066-
if (skip_member<Opts>(it->second)) {
1067-
return true;
1068-
}
1069-
write_pair_content<Opts>(it->first, it->second, ctx, b, ix);
1070-
return false;
1071-
}
1072-
else {
1073-
const auto& [key, entry_val] = *it;
1074-
if (skip_member<Opts>(entry_val)) {
1075-
return true;
1076-
}
1077-
write_pair_content<Opts>(key, entry_val, ctx, b, ix);
1078-
return false;
1079-
}
1080-
};
1081-
1082-
auto it = std::begin(value);
1083-
[[maybe_unused]] bool starting = write_first_entry(it);
1084-
for (++it; it != std::end(value); ++it) {
1085-
// I couldn't find an easy way around this code duplication
1086-
// Ranges need to be decomposed with const auto& [...],
1087-
// but we don't want to const qualify our maps for the sake of reflection writing
1088-
// we need to be able to populate the tuple of pointers when writing with reflection
1089-
if constexpr (requires {
1090-
it->first;
1091-
it->second;
1092-
}) {
1093-
if (skip_member<Opts>(it->second)) {
1094-
continue;
1095-
}
1125+
// When Opts.skip_null_members, *any* entry may be skipped, meaning separator dumping must be
1126+
// conditional for every entry. Avoid this branch when not skipping null members.
1127+
// Alternatively, write separator after each entry except last but then branch is permanent
1128+
if constexpr (Opts.skip_null_members) {
1129+
if (!starting) {
1130+
write_object_entry_separator<Opts>(ctx, b, ix);
1131+
}
1132+
}
1133+
else {
1134+
write_object_entry_separator<Opts>(ctx, b, ix);
1135+
}
10961136

1097-
// When Opts.skip_null_members, *any* entry may be skipped, meaning separator dumping must be
1098-
// conditional for every entry. Avoid this branch when not skipping null members.
1099-
// Alternatively, write separator after each entry except last but then branch is permanent
1100-
if constexpr (Opts.skip_null_members) {
1101-
if (!starting) {
1102-
write_object_entry_separator<Opts>(ctx, b, ix);
1137+
write_pair_content<Opts>(it->first, it->second, ctx, b, ix);
11031138
}
1104-
}
1105-
else {
1106-
write_object_entry_separator<Opts>(ctx, b, ix);
1107-
}
1139+
else {
1140+
const auto& [key, entry_val] = *it;
1141+
if (skip_member<Opts>(entry_val)) {
1142+
continue;
1143+
}
11081144

1109-
write_pair_content<Opts>(it->first, it->second, ctx, b, ix);
1145+
// When Opts.skip_null_members, *any* entry may be skipped, meaning separator dumping must be
1146+
// conditional for every entry. Avoid this branch when not skipping null members.
1147+
// Alternatively, write separator after each entry except last but then branch is permanent
1148+
if constexpr (Opts.skip_null_members) {
1149+
if (!starting) {
1150+
write_object_entry_separator<Opts>(ctx, b, ix);
1151+
}
1152+
}
1153+
else {
1154+
write_object_entry_separator<Opts>(ctx, b, ix);
1155+
}
1156+
1157+
write_pair_content<Opts>(key, entry_val, ctx, b, ix);
1158+
}
1159+
1160+
starting = false;
1161+
}
11101162
}
11111163
else {
1112-
const auto& [key, entry_val] = *it;
1113-
if (skip_member<Opts>(entry_val)) {
1114-
continue;
1115-
}
1116-
1117-
// When Opts.skip_null_members, *any* entry may be skipped, meaning separator dumping must be
1118-
// conditional for every entry. Avoid this branch when not skipping null members.
1119-
// Alternatively, write separator after each entry except last but then branch is permanent
1120-
if constexpr (Opts.skip_null_members) {
1121-
if (!starting) {
1164+
auto write_first_entry = [&ctx, &b, &ix](auto&& it) {
1165+
if constexpr (requires {
1166+
it->first;
1167+
it->second;
1168+
}) {
1169+
write_pair_content<Opts>(it->first, it->second, ctx, b, ix);
1170+
}
1171+
else {
1172+
const auto& [key, entry_val] = *it;
1173+
write_pair_content<Opts>(key, entry_val, ctx, b, ix);
1174+
}
1175+
};
1176+
1177+
auto it = std::begin(value);
1178+
write_first_entry(it);
1179+
++it;
1180+
for (const auto end = std::end(value); it != end; ++it) {
1181+
if constexpr (requires {
1182+
it->first;
1183+
it->second;
1184+
}) {
11221185
write_object_entry_separator<Opts>(ctx, b, ix);
1186+
write_pair_content<Opts>(it->first, it->second, ctx, b, ix);
1187+
}
1188+
else {
1189+
const auto& [key, entry_val] = *it;
1190+
write_object_entry_separator<Opts>(ctx, b, ix);
1191+
write_pair_content<Opts>(key, entry_val, ctx, b, ix);
11231192
}
11241193
}
1125-
else {
1126-
write_object_entry_separator<Opts>(ctx, b, ix);
1127-
}
1128-
1129-
write_pair_content<Opts>(key, entry_val, ctx, b, ix);
11301194
}
1131-
1132-
starting = false;
11331195
}
11341196

11351197
if constexpr (!has_closing_handled(Opts)) {

0 commit comments

Comments
 (0)