IMO we should not have a multi-dozens-KLOC library for something that should be almost invisible. And I don't think that being invisible is the purpose of this library.
I would just specify your case as a data item:
ARRAY_ITEM("foo", foo, INT, 3)
And that's really all, specification-wise. Here ARRAY_ITEM is a simple macro that calculates the offset of the array within the struct (need to give the struct a name!), so that results maybe in { .srcfield="foo", .typekind=TYPE_ARRAY, .arraylength=3, .basetype=INT, offset=offsetof(MyStruct, foo) } or something like that. One can compute the length of the C array at compile time and verify that the lengths match, so there will be absolutely no danger of making a wrong data item.
The corresponding code that handles the data items of kind ARRAY_ITEM would be something like this:
case TYPE_ARRAY: {
char *store = add_offset(&myStruct, spec->offset);
int elemSize = typeKindToElementSize[spec->typekind];
JsonArray *jsonArray = get_json_array(jsonObject, spec->srcfield);
for (int i = 0; i < spec->arraylength; i++) {
JsonValue *jsonValue = get_json_array_element(jsonArray, i);
/* This makes sure the dynamic type of jsonValue matches, then
does the appropriate conversion and stores the thing. We also
want to check errors, either now or at the end. */
store_json_value(jsonValue, spec->basetype, store + (i * elemSize));
}
}
You'll need <10, maybe 5, of these cases, and basically it's all code that you will test once and then it just works without surprises. This is less than a day of work initially, will have very low maintenance, will work exactly with the type representations you choose and be easy to change. The other aspect is you're now free from a library that you don't understand, will give you Modern C++ headaches, and will be slow to compile.
I'm likely to write a new version of this for every new project, to avoid dependencies and to allow for change.
Also, ugh. macros. what happens if you talk to 5 different network protocols all with more-or-less-JSON-like semantics - do you now have ARRAY_ITEM_JSON, ARRAY_ITEM_BSON, ARRAY_ITEM_CBOR, ARRAY_ITEM_YAML ? What when the format changes so that `ARRAY_ITEM("foo", foo, INT, 3)` now wants floats ? woohoo, magic truncation instead of a compile error.
As they say : thanks but no thanks, I'll stay with `for (auto& [key, value] : o.items())` which ensures that everyone including the fresh-out-of-school student can understand what happens without needing to read obtuse macro definitions
I say let data be data and write code where you need code. How does the library handle this in one step? How long does it take you to find out?
> Also, ugh. macros.
Data macros are the best. You don't have to debug them at runtime, they let you get rid of a lot of boilerplate such as compile-time computations (e.g. offsetof(), sizeof())
> do you now have ARRAY_ITEM_JSON, ARRAY_ITEM_BSON, ARRAY_ITEM_CBOR, ARRAY_ITEM_YAML
I'm really sorry if you have to do that. I never had, and likely never will. But I'd probably just write the example I gave, in 5 flavours, in 5 separate implementation files. The alternative, dealing with 5 oversized libraries that approach this thing in a totally different way, is not appealing to me at all.
> What when the format changes so that `ARRAY_ITEM("foo", foo, INT, 3)` now wants floats ? woohoo, magic truncation instead of a compile error.
No, runtime error message about a wrong type in a JSON payload. Or if you mean this: the type of the C array's elements changed from int to float. Then just check the type of the array elements against the specified type (INT) which should expect a specific C type. There are easy ways to get a representation of the array element type in C++ as well as an in C (the macro can do it automatically).
I would just specify your case as a data item:
And that's really all, specification-wise. Here ARRAY_ITEM is a simple macro that calculates the offset of the array within the struct (need to give the struct a name!), so that results maybe in { .srcfield="foo", .typekind=TYPE_ARRAY, .arraylength=3, .basetype=INT, offset=offsetof(MyStruct, foo) } or something like that. One can compute the length of the C array at compile time and verify that the lengths match, so there will be absolutely no danger of making a wrong data item.The corresponding code that handles the data items of kind ARRAY_ITEM would be something like this:
You'll need <10, maybe 5, of these cases, and basically it's all code that you will test once and then it just works without surprises. This is less than a day of work initially, will have very low maintenance, will work exactly with the type representations you choose and be easy to change. The other aspect is you're now free from a library that you don't understand, will give you Modern C++ headaches, and will be slow to compile.I'm likely to write a new version of this for every new project, to avoid dependencies and to allow for change.