diff --git a/README.md b/README.md index 508a05a06..ffc33805d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ There are myriads of [JSON](http://json.org) libraries out there, and each may e Other aspects were not so important to us: -- **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). We use the following C++ data types: `std::string` for strings, `int` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. We know that there are more efficient ways to store the values, but we are happy enough right now. +- **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). We use the following C++ data types: `std::string` for strings, `int64_t` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. We know that there are more efficient ways to store the values, but we are happy enough right now. - **Speed**. We currently implement the parser as naive [recursive descent parser](http://en.wikipedia.org/wiki/Recursive_descent_parser) with hand coded string handling. It is fast enough, but a [LALR-parser](http://en.wikipedia.org/wiki/LALR_parser) with a decent regular expression processor should be even faster (but would consist of more files which makes the integration harder). diff --git a/header_only/json.h b/header_only/json.h index 14be12c37..6d2bfc1ca 100644 --- a/header_only/json.h +++ b/header_only/json.h @@ -74,7 +74,7 @@ class json /// a type for a Boolean using boolean_t = bool; /// a type for an integer number - using number_t = int; + using number_t = int64_t; /// a type for a floating point number using number_float_t = double; /// a type for list initialization @@ -127,10 +127,6 @@ class json json(const char*); /// create a Boolean object json(const bool) noexcept; - /// create a number object - json(const int) noexcept; - /// create a number object - json(const double) noexcept; /// create an array json(const array_t&); /// create an array (move) @@ -142,6 +138,18 @@ class json /// create from an initializer list (to an array or object) json(list_init_t); + /// create a number object (integer) + template::is_integer, T>::type = 0> + json(const T n) noexcept + : type_(value_type::number), value_(static_cast(n)) + {} + + /// create a number object (float) + template::value>::type> + json(const T n) noexcept + : type_(value_type::number_float), value_(static_cast(n)) + {} + /// copy constructor json(const json&); /// move constructor @@ -168,6 +176,7 @@ class json void replaceChar(std::string& str, char c, const std::string& replacement) const; /// escapes special characters to safely dump the string std::string escapeString(const std::string&) const; + public: /// explicit value conversion template @@ -177,7 +186,8 @@ class json operator std::string() const; /// implicit conversion to integer (only for numbers) operator int() const; - operator long() const; + /// implicit conversion to integer (only for numbers) + operator int64_t() const; /// implicit conversion to double (only for numbers) operator double() const; /// implicit conversion to Boolean (only for Booleans) @@ -216,42 +226,33 @@ class json /// explicit serialization std::string dump(int = -1) const noexcept; + /// add constructible objects to an array + template::value>::type = 0> + json & operator+=(const T& o) + { + push_back(json(o)); + return *this; + } + /// add an object/array to an array json& operator+=(const json&); - /// add a string to an array - json& operator+=(const std::string&); - /// add a null object to an array - json& operator+=(const std::nullptr_t); - /// add a string to an array - json& operator+=(const char*); - /// add a Boolean to an array - json& operator+=(bool); - /// add a number to an array - json& operator+=(int); - /// add a number to an array - json& operator+=(double); /// add a pair to an object json& operator+=(const object_t::value_type&); /// add a list of elements to array or list of pairs to object json& operator+=(list_init_t); + /// add constructible objects to an array + template::value>::type = 0> + void push_back(const T& o) + { + push_back(json(o)); + } + /// add an object/array to an array void push_back(const json&); /// add an object/array to an array (move) void push_back(json&&); - /// add a string to an array - void push_back(const std::string&); - /// add a null object to an array - void push_back(const std::nullptr_t); - /// add a string to an array - void push_back(const char*); - /// add a Boolean to an array - void push_back(bool); - /// add a number to an array - void push_back(int); - /// add a number to an array - void push_back(double); /// add a pair to an object void push_back(const object_t::value_type&); @@ -579,14 +580,6 @@ json::json(const bool b) noexcept : type_(value_type::boolean), value_(b) {} -json::json(const int i) noexcept - : type_(value_type::number), value_(i) -{} - -json::json(const double f) noexcept - : type_(value_type::number_float), value_(f) -{} - json::json(const array_t& a) : type_(value_type::array), value_(new array_t(a)) {} @@ -826,6 +819,24 @@ std::string json::get() const */ template<> int json::get() const +{ + switch (type_) + { + case (value_type::number): + return value_.number; + case (value_type::number_float): + return static_cast(value_.number_float); + default: + throw std::logic_error("cannot cast " + type_name() + " to JSON number"); + } +} + +/*! +@exception std::logic_error if the function is called for JSON objects whose + type is not number (int or float) +*/ +template<> +int64_t json::get() const { switch (type_) { @@ -920,6 +931,11 @@ json::operator int() const return get(); } +json::operator int64_t() const +{ + return get(); +} + json::operator double() const { return get(); @@ -1136,54 +1152,6 @@ json& json::operator+=(const json& o) return *this; } -json& json::operator+=(const std::string& s) -{ - push_back(json(s)); - return *this; -} - -json& json::operator+=(const char* s) -{ - push_back(json(s)); - return *this; -} - -json& json::operator+=(std::nullptr_t) -{ - push_back(json()); - return *this; -} - -json& json::operator+=(bool b) -{ - push_back(json(b)); - return *this; -} - -/*! -Adds a number (int) to the current object. This is done by wrapping the number -into a JSON and call push_back for this. - -@param i A number (int) to add to the array. -*/ -json& json::operator+=(int i) -{ - push_back(json(i)); - return *this; -} - -/*! -Adds a number (float) to the current object. This is done by wrapping the -number into a JSON and call push_back for this. - -@param f A number (float) to add to the array. -*/ -json& json::operator+=(double f) -{ - push_back(json(f)); - return *this; -} - /*! @todo comment me */ @@ -1271,48 +1239,6 @@ void json::push_back(json&& o) o.type_ = value_type::null; } -void json::push_back(const std::string& s) -{ - push_back(json(s)); -} - -void json::push_back(const char* s) -{ - push_back(json(s)); -} - -void json::push_back(std::nullptr_t) -{ - push_back(json()); -} - -void json::push_back(bool b) -{ - push_back(json(b)); -} - -/*! -Adds a number (int) to the current object. This is done by wrapping the number -into a JSON and call push_back for this. - -@param i A number (int) to add to the array. -*/ -void json::push_back(int i) -{ - push_back(json(i)); -} - -/*! -Adds a number (float) to the current object. This is done by wrapping the -number into a JSON and call push_back for this. - -@param f A number (float) to add to the array. -*/ -void json::push_back(double f) -{ - push_back(json(f)); -} - /*! @todo comment me */ @@ -2540,7 +2466,7 @@ json json::parser::parse() try { const auto float_val = std::stod(buffer_.substr(_firstpos_, pos_ - _firstpos_)); - const auto int_val = static_cast(float_val); + const auto int_val = static_cast(float_val); // check if conversion loses precision if (float_val == int_val) diff --git a/src/json.cc b/src/json.cc index 3ae9d15f3..b849c68fe 100644 --- a/src/json.cc +++ b/src/json.cc @@ -111,14 +111,6 @@ json::json(const bool b) noexcept : type_(value_type::boolean), value_(b) {} -json::json(const int i) noexcept - : type_(value_type::number), value_(i) -{} - -json::json(const double f) noexcept - : type_(value_type::number_float), value_(f) -{} - json::json(const array_t& a) : type_(value_type::array), value_(new array_t(a)) {} @@ -358,6 +350,24 @@ std::string json::get() const */ template<> int json::get() const +{ + switch (type_) + { + case (value_type::number): + return value_.number; + case (value_type::number_float): + return static_cast(value_.number_float); + default: + throw std::logic_error("cannot cast " + type_name() + " to JSON number"); + } +} + +/*! +@exception std::logic_error if the function is called for JSON objects whose + type is not number (int or float) +*/ +template<> +int64_t json::get() const { switch (type_) { @@ -452,6 +462,11 @@ json::operator int() const return get(); } +json::operator int64_t() const +{ + return get(); +} + json::operator double() const { return get(); @@ -668,54 +683,6 @@ json& json::operator+=(const json& o) return *this; } -json& json::operator+=(const std::string& s) -{ - push_back(json(s)); - return *this; -} - -json& json::operator+=(const char* s) -{ - push_back(json(s)); - return *this; -} - -json& json::operator+=(std::nullptr_t) -{ - push_back(json()); - return *this; -} - -json& json::operator+=(bool b) -{ - push_back(json(b)); - return *this; -} - -/*! -Adds a number (int) to the current object. This is done by wrapping the number -into a JSON and call push_back for this. - -@param i A number (int) to add to the array. -*/ -json& json::operator+=(int i) -{ - push_back(json(i)); - return *this; -} - -/*! -Adds a number (float) to the current object. This is done by wrapping the -number into a JSON and call push_back for this. - -@param f A number (float) to add to the array. -*/ -json& json::operator+=(double f) -{ - push_back(json(f)); - return *this; -} - /*! @todo comment me */ @@ -803,48 +770,6 @@ void json::push_back(json&& o) o.type_ = value_type::null; } -void json::push_back(const std::string& s) -{ - push_back(json(s)); -} - -void json::push_back(const char* s) -{ - push_back(json(s)); -} - -void json::push_back(std::nullptr_t) -{ - push_back(json()); -} - -void json::push_back(bool b) -{ - push_back(json(b)); -} - -/*! -Adds a number (int) to the current object. This is done by wrapping the number -into a JSON and call push_back for this. - -@param i A number (int) to add to the array. -*/ -void json::push_back(int i) -{ - push_back(json(i)); -} - -/*! -Adds a number (float) to the current object. This is done by wrapping the -number into a JSON and call push_back for this. - -@param f A number (float) to add to the array. -*/ -void json::push_back(double f) -{ - push_back(json(f)); -} - /*! @todo comment me */ @@ -2072,7 +1997,7 @@ json json::parser::parse() try { const auto float_val = std::stod(buffer_.substr(_firstpos_, pos_ - _firstpos_)); - const auto int_val = static_cast(float_val); + const auto int_val = static_cast(float_val); // check if conversion loses precision if (float_val == int_val) diff --git a/src/json.h b/src/json.h index f806fe06e..8c43391ab 100644 --- a/src/json.h +++ b/src/json.h @@ -74,7 +74,7 @@ class json /// a type for a Boolean using boolean_t = bool; /// a type for an integer number - using number_t = int; + using number_t = int64_t; /// a type for a floating point number using number_float_t = double; /// a type for list initialization @@ -127,10 +127,6 @@ class json json(const char*); /// create a Boolean object json(const bool) noexcept; - /// create a number object - json(const int) noexcept; - /// create a number object - json(const double) noexcept; /// create an array json(const array_t&); /// create an array (move) @@ -142,6 +138,18 @@ class json /// create from an initializer list (to an array or object) json(list_init_t); + /// create a number object (integer) + template::is_integer, T>::type = 0> + json(const T n) noexcept + : type_(value_type::number), value_(static_cast(n)) + {} + + /// create a number object (float) + template::value>::type> + json(const T n) noexcept + : type_(value_type::number_float), value_(static_cast(n)) + {} + /// copy constructor json(const json&); /// move constructor @@ -168,6 +176,7 @@ class json void replaceChar(std::string& str, char c, const std::string& replacement) const; /// escapes special characters to safely dump the string std::string escapeString(const std::string&) const; + public: /// explicit value conversion template @@ -177,6 +186,8 @@ class json operator std::string() const; /// implicit conversion to integer (only for numbers) operator int() const; + /// implicit conversion to integer (only for numbers) + operator int64_t() const; /// implicit conversion to double (only for numbers) operator double() const; /// implicit conversion to Boolean (only for Booleans) @@ -215,42 +226,33 @@ class json /// explicit serialization std::string dump(int = -1) const noexcept; + /// add constructible objects to an array + template::value>::type = 0> + json & operator+=(const T& o) + { + push_back(json(o)); + return *this; + } + /// add an object/array to an array json& operator+=(const json&); - /// add a string to an array - json& operator+=(const std::string&); - /// add a null object to an array - json& operator+=(const std::nullptr_t); - /// add a string to an array - json& operator+=(const char*); - /// add a Boolean to an array - json& operator+=(bool); - /// add a number to an array - json& operator+=(int); - /// add a number to an array - json& operator+=(double); /// add a pair to an object json& operator+=(const object_t::value_type&); /// add a list of elements to array or list of pairs to object json& operator+=(list_init_t); + /// add constructible objects to an array + template::value>::type = 0> + void push_back(const T& o) + { + push_back(json(o)); + } + /// add an object/array to an array void push_back(const json&); /// add an object/array to an array (move) void push_back(json&&); - /// add a string to an array - void push_back(const std::string&); - /// add a null object to an array - void push_back(const std::nullptr_t); - /// add a string to an array - void push_back(const char*); - /// add a Boolean to an array - void push_back(bool); - /// add a number to an array - void push_back(int); - /// add a number to an array - void push_back(double); /// add a pair to an object void push_back(const object_t::value_type&); diff --git a/test/json_unit.cc b/test/json_unit.cc index 57fc6cfcc..efb7e42f7 100644 --- a/test/json_unit.cc +++ b/test/json_unit.cc @@ -1094,6 +1094,14 @@ TEST_CASE("number (int)") json j2 = 42; int v2 = j2; CHECK(j2.get() == v2); + + json j3 = 2147483647; + int v3 = j3; + CHECK(j3.get() == v3); + + json j4 = 9223372036854775807; + int64_t v4 = j4; + CHECK(j4.get() == v4); } SECTION("Operators")