Skip to content

Commit 4d21e0e

Browse files
committed
append_arrays compile time option
1 parent 2d41866 commit 4d21e0e

File tree

5 files changed

+117
-29
lines changed

5 files changed

+117
-29
lines changed

docs/wrappers.md

+35
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Glaze provides a number of wrappers that indicate at compile time how a value sh
55
## Available Wrappers
66

77
```c++
8+
glz::append_arrays<&T::x> // When reading into an array that is appendable, the new data will be appended rather than overwrite
89
glz::bools_as_numbers<&T::x> // Read and write booleans as numbers
910
glz::quoted_num<&T::x> // Read and write numbers as strings
1011
glz::quoted<&T::x> // Read a value as a string and unescape, to avoid the user having to parse twice
@@ -29,6 +30,40 @@ glz::manage<&T::x, &T::read_x, &T::write_x> // Calls read_x() after reading x an
2930

3031
`glz::opts` is the compile time options struct passed to most of Glaze functions to configure read/write behavior. Often wrappers are associated with compile time options and can also be set via `glz::opts`. For example, the `glz::quoted_num` wrapper is associated with the `quoted_num` boolean in `glz::opts`.
3132

33+
## append_arrays
34+
35+
When reading into an array that is appendable, the new data will be appended rather than overwrite
36+
37+
Associated option: `glz::opts{.append_arrays = true};`
38+
39+
```c++
40+
struct append_obj
41+
{
42+
std::vector<std::string> names{};
43+
std::vector<std::array<int, 2>> arrays{};
44+
};
45+
46+
template <>
47+
struct glz::meta<append_obj>
48+
{
49+
using T = append_obj;
50+
static constexpr auto value = object("names", append_arrays<&T::names>, "arrays", append_arrays<&T::arrays>);
51+
};
52+
```
53+
54+
In use:
55+
56+
```c++
57+
append_obj obj{};
58+
expect(not glz::read_json(obj, R"({"names":["Bob"],"arrays":[[0,0]]})"));
59+
expect(obj.names == std::vector<std::string>{"Bob"});
60+
expect(obj.arrays == std::vector<std::array<int, 2>>{{0,0}});
61+
62+
expect(not glz::read_json(obj, R"({"names":["Liz"],"arrays":[[1,1]]})"));
63+
expect(obj.names == std::vector<std::string>{"Bob", "Liz"});
64+
expect(obj.arrays == std::vector<std::array<int, 2>>{{0,0},{1,1}});
65+
```
66+
3267
## bools_as_numbers
3368
3469
Read and write booleans as numbers

include/glaze/core/opts.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ namespace glz
6363
char indentation_char = ' '; // Prettified JSON indentation char
6464
uint8_t indentation_width = 3; // Prettified JSON indentation size
6565
bool_t new_lines_in_arrays = true; // Whether prettified arrays should have new lines for each element
66+
bool_t append_arrays = false; // When reading into an array the data will be appended if the type supports it
6667
bool_t shrink_to_fit = false; // Shrinks dynamic containers to new size to save memory
6768
bool_t write_type_info = true; // Write type info for meta objects in variants
6869
bool_t error_on_missing_keys = false; // Require all non nullable keys to be present in the object. Use

include/glaze/core/wrappers.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ namespace glz
6161
return [](auto&& v) { return custom_t{v, From, To}; };
6262
}
6363
}
64+
65+
// When reading into an array that is appendable, the new data will be appended rather than overwrite
66+
template <auto MemPtr>
67+
constexpr auto append_arrays = detail::opts_wrapper<MemPtr, &opts::append_arrays>();
6468

6569
// Read and write booleans as numbers
6670
template <auto MemPtr>

include/glaze/json/read.hpp

+33-29
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,7 @@ namespace glz
12721272
if (*it == ']') {
12731273
GLZ_SUB_LEVEL;
12741274
++it;
1275-
if constexpr (resizable<T>) {
1275+
if constexpr (resizable<T> && not Opts.append_arrays) {
12761276
value.clear();
12771277

12781278
if constexpr (Opts.shrink_to_fit) {
@@ -1284,42 +1284,46 @@ namespace glz
12841284

12851285
const size_t ws_size = size_t(it - ws_start);
12861286

1287-
const auto n = value.size();
1287+
static constexpr bool should_append = resizable<T> && Opts.append_arrays;
1288+
if constexpr (not should_append)
1289+
{
1290+
const auto n = value.size();
12881291

1289-
auto value_it = value.begin();
1292+
auto value_it = value.begin();
12901293

1291-
for (size_t i = 0; i < n; ++i) {
1292-
read<JSON>::op<ws_handled<Opts>()>(*value_it++, ctx, it, end);
1293-
if (bool(ctx.error)) [[unlikely]]
1294-
return;
1295-
GLZ_SKIP_WS();
1296-
if (*it == ',') {
1297-
++it;
1294+
for (size_t i = 0; i < n; ++i) {
1295+
read<JSON>::op<ws_handled<Opts>()>(*value_it++, ctx, it, end);
1296+
if (bool(ctx.error)) [[unlikely]]
1297+
return;
1298+
GLZ_SKIP_WS();
1299+
if (*it == ',') {
1300+
++it;
12981301

1299-
if constexpr (!Opts.minified) {
1300-
if (ws_size && ws_size < size_t(end - it)) {
1301-
skip_matching_ws(ws_start, it, ws_size);
1302+
if constexpr (!Opts.minified) {
1303+
if (ws_size && ws_size < size_t(end - it)) {
1304+
skip_matching_ws(ws_start, it, ws_size);
1305+
}
13021306
}
1303-
}
13041307

1305-
GLZ_SKIP_WS();
1306-
}
1307-
else if (*it == ']') {
1308-
GLZ_SUB_LEVEL;
1309-
++it;
1310-
if constexpr (erasable<T>) {
1311-
value.erase(value_it,
1312-
value.end()); // use erase rather than resize for non-default constructible elements
1308+
GLZ_SKIP_WS();
1309+
}
1310+
else if (*it == ']') {
1311+
GLZ_SUB_LEVEL;
1312+
++it;
1313+
if constexpr (erasable<T>) {
1314+
value.erase(value_it,
1315+
value.end()); // use erase rather than resize for non-default constructible elements
13131316

1314-
if constexpr (Opts.shrink_to_fit) {
1315-
value.shrink_to_fit();
1317+
if constexpr (Opts.shrink_to_fit) {
1318+
value.shrink_to_fit();
1319+
}
13161320
}
1321+
return;
1322+
}
1323+
else [[unlikely]] {
1324+
ctx.error = error_code::expected_bracket;
1325+
return;
13171326
}
1318-
return;
1319-
}
1320-
else [[unlikely]] {
1321-
ctx.error = error_code::expected_bracket;
1322-
return;
13231327
}
13241328
}
13251329

tests/json_test/json_test.cpp

+44
Original file line numberDiff line numberDiff line change
@@ -10141,6 +10141,50 @@ suite meta_keys_for_struct = [] {
1014110141
};
1014210142
};
1014310143

10144+
struct append_obj
10145+
{
10146+
std::vector<std::string> names{};
10147+
std::vector<std::array<int, 2>> arrays{};
10148+
};
10149+
10150+
template <>
10151+
struct glz::meta<append_obj>
10152+
{
10153+
using T = append_obj;
10154+
static constexpr auto value = object("names", append_arrays<&T::names>, "arrays", append_arrays<&T::arrays>);
10155+
};
10156+
10157+
suite append_arrays_tests = [] {
10158+
"append_arrays vector"_test = [] {
10159+
std::vector<int> v{};
10160+
constexpr glz::opts append_opts{.append_arrays = true};
10161+
expect(not glz::read<append_opts>(v, "[1,2,3]"));
10162+
expect(v == std::vector<int>{1,2,3});
10163+
expect(not glz::read<append_opts>(v, "[4,5,6]"));
10164+
expect(v == std::vector<int>{1,2,3,4,5,6});
10165+
};
10166+
10167+
"append_arrays deque"_test = [] {
10168+
std::deque<int> v{};
10169+
constexpr glz::opts append_opts{.append_arrays = true};
10170+
expect(not glz::read<append_opts>(v, "[1,2,3]"));
10171+
expect(v == std::deque<int>{1,2,3});
10172+
expect(not glz::read<append_opts>(v, "[4,5,6]"));
10173+
expect(v == std::deque<int>{1,2,3,4,5,6});
10174+
};
10175+
10176+
"append_arrays append_obj"_test = [] {
10177+
append_obj obj{};
10178+
expect(not glz::read_json(obj, R"({"names":["Bob"],"arrays":[[0,0]]})"));
10179+
expect(obj.names == std::vector<std::string>{"Bob"});
10180+
expect(obj.arrays == std::vector<std::array<int, 2>>{{0,0}});
10181+
10182+
expect(not glz::read_json(obj, R"({"names":["Liz"],"arrays":[[1,1]]})"));
10183+
expect(obj.names == std::vector<std::string>{"Bob", "Liz"});
10184+
expect(obj.arrays == std::vector<std::array<int, 2>>{{0,0},{1,1}});
10185+
};
10186+
};
10187+
1014410188
int main()
1014510189
{
1014610190
trace.end("json_test");

0 commit comments

Comments
 (0)