diff --git a/examples/example_json_map.cpp b/examples/example_json_map.cpp index 4ce1884c3..043035146 100644 --- a/examples/example_json_map.cpp +++ b/examples/example_json_map.cpp @@ -2,8 +2,6 @@ #define CROW_JSON_USE_MAP #include "crow.h" - - int main() { crow::SimpleApp app; @@ -19,6 +17,22 @@ CROW_ROUTE(app, "/json") return x; }); +CROW_ROUTE(app, "/json-initializer-list-constructor") +([] { + return crow::json::wvalue({ + {"first", "Hello world!"}, /* stores a char const* hence a json::type::String */ + {"second", std::string("How are you today?")}, /* stores a std::string hence a json::type::String. */ + {"third", 54}, /* stores an int (as 54 is an int literal) hence a std::int64_t. */ + {"fourth", 54l}, /* stores a long (as 54l is a long literal) hence a std::int64_t. */ + {"fifth", 54u}, /* stores an unsigned int (as 54u is a unsigned int literal) hence a std::uint64_t. */ + {"sixth", 54ul}, /* stores an unsigned long (as 54ul is an unsigned long literal) hence a std::uint64_t. */ + {"seventh", 2.f}, /* stores a float (as 2.f is a float literal) hence a double. */ + {"eighth", 2.}, /* stores a double (as 2. is a double literal) hence a double. */ + {"ninth", nullptr}, /* stores a std::nullptr hence json::type::Null . */ + {"tenth", true} /* stores a bool hence json::type::True . */ + }); +}); + // enables all log app.loglevel(crow::LogLevel::Debug); diff --git a/include/crow/json.h b/include/crow/json.h index d5699ebe5..04a6209ae 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -1224,15 +1224,30 @@ namespace crow class wvalue : public returnable { friend class crow::mustache::template_t; + + public: + using object_type = +#ifdef CROW_JSON_USE_MAP + std::map; +#else + std::unordered_map; +#endif + public: type t() const { return t_; } private: type t_{type::Null}; ///< The type of the value. num_type nt{num_type::Null}; ///< The specific type of the number if \ref t_ is a number. - union { + union number { double d; int64_t si; - uint64_t ui {}; + uint64_t ui; + + public: + constexpr number() noexcept : ui() {} /* default constructor initializes unsigned integer. */ + constexpr number(std::uint64_t value) noexcept : ui(value) {} + constexpr number(std::int64_t value) noexcept : si(value) {} + constexpr number(double value) noexcept : d(value) {} } num; ///< Value if type is a number. std::string s; ///< Value if type is a string. std::unique_ptr> l; ///< Value if type is a list. @@ -1243,9 +1258,35 @@ namespace crow #endif public: - wvalue() : returnable("application/json") {} + wvalue(std::nullptr_t) : returnable("application/json"), t_(type::Null) {} + + wvalue(bool value) : returnable("application/json"), t_(value ? type::True : type::False) {} + + wvalue(std::uint8_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast(value)) {} + wvalue(std::uint16_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast(value)) {} + wvalue(std::uint32_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast(value)) {} + wvalue(std::uint64_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Unsigned_integer), num(static_cast(value)) {} + + wvalue(std::int8_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast(value)) {} + wvalue(std::int16_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast(value)) {} + wvalue(std::int32_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast(value)) {} + wvalue(std::int64_t value) : returnable("application/json"), t_(type::Number), nt(num_type::Signed_integer), num(static_cast(value)) {} + + wvalue(float value) : returnable("application/json"), t_(type::Number), nt(num_type::Floating_point), num(static_cast(value)) {} + wvalue(double value) : returnable("application/json"), t_(type::Number), nt(num_type::Floating_point), num(static_cast(value)) {} + + wvalue(char const* value) : returnable("application/json"), t_(type::String), s(value) {} + + wvalue(std::string const& value) : returnable("application/json"), t_(type::String), s(value) {} + wvalue(std::string&& value) : returnable("application/json"), t_(type::String), s(std::move(value)) {} + + wvalue(std::initializer_list> initializer_list) : returnable("application/json"), t_(type::Object), o(new object_type(initializer_list)) {} + + wvalue(object_type const& value) : returnable("application/json"), t_(type::Object), o(new object_type(value)) {} + wvalue(object_type&& value) : returnable("application/json"), t_(type::Object), o(new object_type(std::move(value))) {} + wvalue(std::vector& r) : returnable("application/json") { t_ = type::List; @@ -1508,6 +1549,42 @@ namespace crow return *this; } + wvalue& operator=(std::initializer_list> initializer_list) + { + if (t_ != type::Object) { + reset(); + t_ = type::Object; + o = std::unique_ptr(new object_type(initializer_list)); + } else { + (*o) = initializer_list; + } + return *this; + } + + wvalue& operator=(object_type const& value) + { + if (t_ != type::Object) { + reset(); + t_ = type::Object; + o = std::unique_ptr(new object_type(value)); + } else { + (*o) = value; + } + return *this; + } + + wvalue& operator=(object_type&& value) + { + if (t_ != type::Object) { + reset(); + t_ = type::Object; + o = std::unique_ptr(new object_type(std::move(value))); + } else { + (*o) = std::move(value); + } + return *this; + } + wvalue& operator[](unsigned index) { if (t_ != type::List) diff --git a/tests/unittest.cpp b/tests/unittest.cpp index 22d2e038d..ebb15c854 100644 --- a/tests/unittest.cpp +++ b/tests/unittest.cpp @@ -746,6 +746,261 @@ TEST_CASE("json_copy_r_to_w_to_w_to_r") CHECK("other" == x["obj"]["other"].key()); } +TEST_CASE("json::wvalue::wvalue(bool)") { + CHECK(json::wvalue(true).t() == json::type::True); + CHECK(json::wvalue(false).t() == json::type::False); +} + +TEST_CASE("json::wvalue::wvalue(std::uint8_t)") { + std::uint8_t i = 42; + json::wvalue value = i; + + CHECK(value.t() == json::type::Number); + CHECK(value.dump() == "42"); +} + +TEST_CASE("json::wvalue::wvalue(std::uint16_t)") { + std::uint16_t i = 42; + json::wvalue value = i; + + CHECK(value.t() == json::type::Number); + CHECK(value.dump() == "42"); +} + +TEST_CASE("json::wvalue::wvalue(std::uint32_t)") { + std::uint32_t i = 42; + json::wvalue value = i; + + CHECK(value.t() == json::type::Number); + CHECK(value.dump() == "42"); +} + +TEST_CASE("json::wvalue::wvalue(std::uint64_t)") { + std::uint64_t i = 42; + json::wvalue value = i; + + CHECK(value.t() == json::type::Number); + CHECK(value.dump() == "42"); +} + +TEST_CASE("json::wvalue::wvalue(std::int8_t)") { + std::int8_t i = -42; + json::wvalue value = i; + + CHECK(value.t() == json::type::Number); + CHECK(value.dump() == "-42"); +} + +TEST_CASE("json::wvalue::wvalue(std::int16_t)") { + std::int16_t i = -42; + json::wvalue value = i; + + CHECK(value.t() == json::type::Number); + CHECK(value.dump() == "-42"); +} + +TEST_CASE("json::wvalue::wvalue(std::int32_t)") { + std::int32_t i = -42; + json::wvalue value = i; + + CHECK(value.t() == json::type::Number); + CHECK(value.dump() == "-42"); +} + +TEST_CASE("json::wvalue::wvalue(std::int64_t)") { + std::int64_t i = -42; + json::wvalue value = i; + + CHECK(value.t() == json::type::Number); + CHECK(value.dump() == "-42"); +} + +TEST_CASE("json::wvalue::wvalue(float)") { + float f = 4.2; + json::wvalue value = f; + + CHECK(value.t() == json::type::Number); + CHECK(value.dump() == "4.2"); +} + +TEST_CASE("json::wvalue::wvalue(double)") { + double d = 4.2; + json::wvalue value = d; + + CHECK(value.t() == json::type::Number); + CHECK(value.dump() == "4.2"); +} + +TEST_CASE("json::wvalue::wvalue(char const*)") { + char const* str = "Hello world!"; + json::wvalue value = str; + + CHECK(value.t() == json::type::String); + CHECK(value.dump() == "\"Hello world!\""); +} + +TEST_CASE("json::wvalue::wvalue(std::string const&)") { + std::string str = "Hello world!"; + json::wvalue value = str; + + CHECK(value.t() == json::type::String); + CHECK(value.dump() == "\"Hello world!\""); +} + +TEST_CASE("json::wvalue::wvalue(std::string&&)") { + std::string str = "Hello world!"; + json::wvalue value = std::move(str); + + CHECK(value.t() == json::type::String); + CHECK(value.dump() == "\"Hello world!\""); +} + +TEST_CASE("json::wvalue::wvalue(std::initializer_list>)") { + json::wvalue integer, number, truth, lie, null; + integer = 2147483647; + number = 23.43; + truth = true; + lie = false; + + /* initializer-list constructor. */ + json::wvalue value({ + {"integer", integer}, + {"number", number}, + {"truth", truth}, + {"lie", lie}, + {"null", null} + }); + + CHECK(value["integer"].dump() == integer.dump()); + CHECK(value["number"].dump() == number.dump()); + CHECK(value["truth"].dump() == truth.dump()); + CHECK(value["lie"].dump() == lie.dump()); + CHECK(value["null"].dump() == null.dump()); +} + +TEST_CASE("json::wvalue::wvalue(std::[unordered_]map const&)") { + json::wvalue integer, number, truth, lie, null; + integer = 2147483647; + number = 23.43; + truth = true; + lie = false; + + json::wvalue::object_type map({ + {"integer", integer}, + {"number", number}, + {"truth", truth}, + {"lie", lie}, + {"null", null} + }); + + json::wvalue value(map); /* copy-constructor. */ + + CHECK(value["integer"].dump() == integer.dump()); + CHECK(value["number"].dump() == number.dump()); + CHECK(value["truth"].dump() == truth.dump()); + CHECK(value["lie"].dump() == lie.dump()); + CHECK(value["null"].dump() == null.dump()); +} + +TEST_CASE("json::wvalue::wvalue(std::[unordered_]map&&)") { + json::wvalue integer, number, truth, lie, null; + integer = 2147483647; + number = 23.43; + truth = true; + lie = false; + + json::wvalue::object_type map = {{ + {"integer", integer}, + {"number", number}, + {"truth", truth}, + {"lie", lie}, + {"null", null} + }}; + + json::wvalue value(std::move(map)); /* move constructor. */ + // json::wvalue value = std::move(map); /* move constructor. */ + + CHECK(value["integer"].dump() == integer.dump()); + CHECK(value["number"].dump() == number.dump()); + CHECK(value["truth"].dump() == truth.dump()); + CHECK(value["lie"].dump() == lie.dump()); + CHECK(value["null"].dump() == null.dump()); +} + +TEST_CASE("json::wvalue::operator=(std::initializer_list>)") { + json::wvalue integer, number, truth, lie, null; + integer = 2147483647; + number = 23.43; + truth = true; + lie = false; + + json::wvalue value; + /* initializer-list assignment. */ + value = { + {"integer", integer}, + {"number", number}, + {"truth", truth}, + {"lie", lie}, + {"null", null} + }; + + CHECK(value["integer"].dump() == integer.dump()); + CHECK(value["number"].dump() == number.dump()); + CHECK(value["truth"].dump() == truth.dump()); + CHECK(value["lie"].dump() == lie.dump()); + CHECK(value["null"].dump() == null.dump()); +} + +TEST_CASE("json::wvalue::operator=(std::[unordered_]map const&)") { + json::wvalue integer, number, truth, lie, null; + integer = 2147483647; + number = 23.43; + truth = true; + lie = false; + + json::wvalue::object_type map({ + {"integer", integer}, + {"number", number}, + {"truth", truth}, + {"lie", lie}, + {"null", null} + }); + + json::wvalue value; + value = map; /* copy assignment. */ + + CHECK(value["integer"].dump() == integer.dump()); + CHECK(value["number"].dump() == number.dump()); + CHECK(value["truth"].dump() == truth.dump()); + CHECK(value["lie"].dump() == lie.dump()); + CHECK(value["null"].dump() == null.dump()); +} + +TEST_CASE("json::wvalue::operator=(std::[unordered_]map&&)") { + json::wvalue integer, number, truth, lie, null; + integer = 2147483647; + number = 23.43; + truth = true; + lie = false; + + json::wvalue::object_type map({ + {"integer", integer}, + {"number", number}, + {"truth", truth}, + {"lie", lie}, + {"null", null} + }); + + json::wvalue value; + value = std::move(map); /* move assignment. */ + + CHECK(value["integer"].dump() == integer.dump()); + CHECK(value["number"].dump() == number.dump()); + CHECK(value["truth"].dump() == truth.dump()); + CHECK(value["lie"].dump() == lie.dump()); + CHECK(value["null"].dump() == null.dump()); +} + TEST_CASE("json_vector") {//TODO probably make constructors for the same values as = operator json::wvalue a;