I'm looking for feedback on:
Its API/interface: since I'm using modern C++, I want the API to be as expressive and easy to write/read as possible. I also want it to be terse.
Its implementation: I'm trying to make the library's performance acceptable, but not looking to make the "fastest" library. It would be great to get feedback on how to improve the performance. I'm also making use of move semantics and since I'm not 100% sure of my perfect forwarding functions' correctness I'm also looking for feedback on them.
The full source code is available here. The JSON library is a module of a bigger "utility" library called SSVUtils.
Here's a quick rundown of the JSON module's files:
Json/Json.hpp
: "Main" module header file. The only file to be included by the user.Json/Common/Common.hpp
: Typedefs and aliases.Json/Common/VecMap.hpp
: Simple map-like container implementation that uses a sorted vector as the underlying storage.Json/Io/*
: JSON reader/writer implementations.Json/Num/Num.hpp*
: Data structure intended for the storage of number. Uses an union. Can store in three different representations:IntS
(signed integer),IntU
(unsigned integer),Real
(double).Json/Num/NumHelper.hpp
: Helperstruct
to get/setNum
instances from C++ numeric types.Json/Val/Val.hpp
: The "core" of the JSON library. A "value" object that uses an union to store one of these types:
Obj
: Key/value JSON map.Arr
: JSON array.Str
: Generic string - implemented withstd::string
.Num
: Numeric type.Bln
: Boolean type;Nll
: Null type;
Json/Val/Internal/Cnv.hpp
: defines conversions between C++ types and JSON structures. You can think of these conversions as serializations/de-serializations. The user can define its own conversions. Conversions do not create new values - they either fill an existing C++ object taken by non-const reference from aJson::Val
, or they fill aJson::Val
from an existing C++ object.Json/Val/Internal/AsHelper.hpp
: defines copy-conversions between C++ types and JSON structures. Usually just creates a new C++ object, fills it using itsCnv
converter, then returns it.Json/Val/Internal/Chk.hpp
: runtime checkers to determine whether a JSON value is storing a specific C++ type.Json/Val/Internal/ValItrHelper.hpp
: iteration helper struct that helps with iterating over JSON values.Json/Val/Internal/CnvFuncs.hpp
: helper functions to automatically convert from/to a JSON value depending on its type.
Syntax usage example:
// Simple struct that we're going to serialize/deserialize
struct Person
{
std::string name, surname;
int age;
};
// Macro that opens/closes conversion namespace
SSVJ_CNV_NAMESPACE()
{
// Macro that defines a converter for Person
// `mV` is the name of the variable that will refer to the JSON value
// `mX` is the name of the variable that will refer to the Person value
SSVJ_CNV(Person, mV, mX)
{
// Converting a Person to JSON simply writes/reads a JSON array
// containing its name, surname and age
ssvj::cnvArr(mV, mX.name, mX.surname, mX.age);
}
SSVJ_CNV_END()
}
SSVJ_CNV_NAMESPACE_END()
int main()
{
// Initialize a JSON array
ssvj::Val people{ssvj::Arr{}};
// Emplace C++ objects in the JSON array
people.emplace<Person>("John", "Doe", 35);
people.emplace<Person>("Bill", "Gates", 75);
// Get copies of the C++ objects from the JSON array
auto johnDoe(people[0].as<Person>());
auto billGates(people[1].as<Person>());
// Modify the copy and put it back into the JSON array
billGates.age = 50;
people[1] = billGates;
assert(people[1][0].as<std::string>() == "Bill");
assert(people[1][1].as<std::string>() == "Gates");
assert(people[1][2].as<int>() == 50);
// Iterate every item of the array as a `Person`
for(const auto& p : people.forArrAs<Person>())
{
std::cout << "Name: " << p.name << "\n"
<< "Surname: " << p.surname << "\n"
<< "Age: " << p.age << "\n\n";
}
// Write and read from file
people.writeToFile("/tmp/people.json");
auto people2(ssvj::Val::fromFile("/tmp/people.json"));
assert(people == people2);
}
- What do you think about the user syntax? Can it be improved?
- Could the implementation be improved? Are move semantics being used correctly?
- Can the conversion of types be better both in its implementation and syntax?