json: store numbers natively as uint/sint/real
allows more accurate representations and better error checking.
This commit is contained in:
parent
1f432c13b7
commit
8142944139
@ -183,22 +183,23 @@ json::flat::parse (const boost::filesystem::path &path)
|
||||
std::ostream&
|
||||
json::flat::operator<< (std::ostream &os, json::flat::type t)
|
||||
{
|
||||
using T = json::flat::type;
|
||||
|
||||
switch (t) {
|
||||
case json::flat::type::STRING: os << "STRING"; break;
|
||||
case json::flat::type::NUL: os << "NUL"; break;
|
||||
case json::flat::type::BOOLEAN: os << "BOOLEAN"; break;
|
||||
case json::flat::type::INTEGER: os << "INTEGER"; break;
|
||||
case json::flat::type::REAL: os << "REAL"; break;
|
||||
case T::STRING: return os << "STRING";
|
||||
case T::NUL: return os << "NUL";
|
||||
case T::BOOLEAN: return os << "BOOLEAN";
|
||||
case T::INTEGER: return os << "INTEGER";
|
||||
case T::REAL: return os << "REAL";
|
||||
|
||||
case json::flat::type::OBJECT_BEGIN: os << "OBJECT_BEGIN"; break;
|
||||
case json::flat::type::OBJECT_END: os << "OBJECT_END"; break;
|
||||
case json::flat::type::ARRAY_BEGIN: os << "ARRAY_BEGIN"; break;
|
||||
case json::flat::type::ARRAY_END: os << "ARRAY_END"; break;
|
||||
case T::OBJECT_BEGIN: return os << "OBJECT_BEGIN";
|
||||
case T::OBJECT_END: return os << "OBJECT_END";
|
||||
case T::ARRAY_BEGIN: return os << "ARRAY_BEGIN";
|
||||
case T::ARRAY_END: return os << "ARRAY_END";
|
||||
|
||||
default:
|
||||
unreachable ();
|
||||
case T::UNKNOWN: ;
|
||||
// fall out
|
||||
}
|
||||
|
||||
return os;
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,7 @@ validate (json::tree::string &node,
|
||||
// check length is less than a maximum
|
||||
auto maxLength = schema.find ("maxLength");
|
||||
if (maxLength != schema.cend ()) {
|
||||
auto cmp = maxLength->second->as_number ().native ();
|
||||
auto cmp = maxLength->second->as_number ().uint ();
|
||||
if (!util::is_integer (cmp))
|
||||
throw length_error ("maxLength");
|
||||
|
||||
@ -185,7 +185,7 @@ validate (json::tree::string &node,
|
||||
// check length is greater than a maximum
|
||||
auto minLength = schema.find ("minLength");
|
||||
if (minLength != schema.cend ()) {
|
||||
auto cmp = minLength->second->as_number ().native ();
|
||||
auto cmp = minLength->second->as_number ().uint ();
|
||||
if (!util::is_integer (cmp))
|
||||
throw length_error ("minLength");
|
||||
|
||||
@ -206,31 +206,42 @@ validate (json::tree::string &node,
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
static void
|
||||
validate (json::tree::number &node,
|
||||
const json::tree::object &schema)
|
||||
{
|
||||
const auto &val = node.native ();
|
||||
validate_number (T val, const json::tree::object &schema) {
|
||||
using R = json::tree::number::repr_t;
|
||||
|
||||
// check strictly positive integer multiple
|
||||
auto mult = schema.find ("multipleOf");
|
||||
if (mult != schema.cend ()) {
|
||||
auto div = mult->second->as_number ().native ();
|
||||
const auto &div = mult->second->as_number ();
|
||||
|
||||
if (val <= 0 || util::almost_equal (val, div))
|
||||
throw json::schema_error ("multipleOf");
|
||||
switch (div.repr ()) {
|
||||
case R::REAL: if (util::exactly_zero (std::fmod (val, div.real ()))) throw json::schema_error ("multipleOf"); break;
|
||||
case R::SINT: if (util::exactly_zero (std::fmod (val, div.sint ()))) throw json::schema_error ("multipleOf"); break;
|
||||
case R::UINT: if (util::exactly_zero (std::fmod (val, div.uint ()))) throw json::schema_error ("multipleOf"); break;
|
||||
}
|
||||
}
|
||||
|
||||
// check maximum holds. exclusive requires max condition.
|
||||
auto max = schema.find ("maximum");
|
||||
auto exclusiveMax = schema.find ("exclusiveMaximum");
|
||||
if (max != schema.end ()) {
|
||||
auto cmp = max->second->as_number ().native ();
|
||||
const auto &cmp = max->second->as_number ();
|
||||
|
||||
if (exclusiveMax != schema.end () && exclusiveMax->second->as_boolean () && val >= cmp)
|
||||
throw json::schema_error ("exclusiveMax");
|
||||
else if (val > cmp)
|
||||
throw json::schema_error ("maximum");
|
||||
if (exclusiveMax != schema.end () && exclusiveMax->second->as_boolean ()) {
|
||||
switch (cmp.repr ()) {
|
||||
case R::REAL: if (val >= T(cmp.real ())) throw json::schema_error ("exclusiveMax"); break;
|
||||
case R::SINT: if (val >= T(cmp.uint ())) throw json::schema_error ("exclusiveMax"); break;
|
||||
case R::UINT: if (val >= T(cmp.sint ())) throw json::schema_error ("exclusiveMax"); break;
|
||||
}
|
||||
} else {
|
||||
switch (cmp.repr ()) {
|
||||
case R::REAL: if (val > T(cmp.real ())) throw json::schema_error ("maximum"); break;
|
||||
case R::SINT: if (val > T(cmp.sint ())) throw json::schema_error ("maximum"); break;
|
||||
case R::UINT: if (val > T(cmp.uint ())) throw json::schema_error ("maximum"); break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (exclusiveMax != schema.cend ())
|
||||
throw json::schema_error ("exclusiveMax");
|
||||
@ -240,16 +251,42 @@ validate (json::tree::number &node,
|
||||
auto min = schema.find ("minimum");
|
||||
auto exclusiveMin = schema.find ("exclusiveMinimum");
|
||||
if (min != schema.end ()) {
|
||||
auto cmp = min->second->as_number ().native ();
|
||||
const auto &cmp = min->second->as_number ();
|
||||
|
||||
if (exclusiveMin != schema.end () && exclusiveMin->second->as_boolean () && val <= cmp)
|
||||
throw json::schema_error ("exclusiveMin");
|
||||
else if (val < cmp)
|
||||
throw json::schema_error ("minimum");
|
||||
if (exclusiveMin != schema.end () && exclusiveMin->second->as_boolean ()) {
|
||||
switch (cmp.repr ()) {
|
||||
case R::REAL: if (val <= T(cmp.real ())) throw json::schema_error ("exclusiveMin"); break;
|
||||
case R::SINT: if (val <= T(cmp.sint ())) throw json::schema_error ("exclusiveMin"); break;
|
||||
case R::UINT: if (val <= T(cmp.uint ())) throw json::schema_error ("exclusiveMin"); break;
|
||||
}
|
||||
} else {
|
||||
switch (cmp.repr ()) {
|
||||
case R::REAL: if (val < T(cmp.real ())) throw json::schema_error ("minimum"); break;
|
||||
case R::SINT: if (val < T(cmp.sint ())) throw json::schema_error ("minimum"); break;
|
||||
case R::UINT: if (val < T(cmp.uint ())) throw json::schema_error ("minimum"); break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (exclusiveMin != schema.cend ())
|
||||
throw json::schema_error ("exclusiveMin");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
static void
|
||||
validate (json::tree::number &node,
|
||||
const json::tree::object &schema)
|
||||
{
|
||||
using N = json::tree::number;
|
||||
using R = N::repr_t;
|
||||
|
||||
switch (node.repr ()) {
|
||||
case R::REAL: validate_number<N::real_t> (node.real (), schema); break;
|
||||
case R::SINT: validate_number<N::sint_t> (node.sint (), schema); break;
|
||||
case R::UINT: validate_number<N::uint_t> (node.uint (), schema); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -304,7 +341,7 @@ validate (json::tree::node &node,
|
||||
if (type != schema.cend ()) {
|
||||
// check against a single named type
|
||||
if (type->second->is_string ()) {
|
||||
auto a = type->second->as_string ();
|
||||
const auto &a = type->second->as_string ();
|
||||
auto b = to_string (node.type ());
|
||||
|
||||
if (a != b)
|
||||
|
243
json/tree.cpp
243
json/tree.cpp
@ -49,21 +49,29 @@ using json::tree::null;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
namespace util {
|
||||
template <>
|
||||
bool
|
||||
is_integer (const json::tree::number &node)
|
||||
{
|
||||
return is_integer (node.native ());
|
||||
using R = json::tree::number::repr_t;
|
||||
|
||||
switch (node.repr ()) {
|
||||
case R::REAL:
|
||||
return is_integer (node.real ());
|
||||
|
||||
case R::SINT:
|
||||
case R::UINT:
|
||||
return true;
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
bool
|
||||
is_integer (const json::tree::node &node)
|
||||
{
|
||||
return node.is_number () &&
|
||||
is_integer (node.as_number ());
|
||||
return node.is_number () && is_integer (node.as_number ());
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,43 +135,67 @@ parse (std::vector<json::flat::item>::const_iterator first,
|
||||
CHECK (first != last);
|
||||
CHECK (output.get () == nullptr);
|
||||
|
||||
using T = json::flat::type;
|
||||
|
||||
switch (first->tag) {
|
||||
case json::flat::type::NUL:
|
||||
case T::NUL:
|
||||
output.reset (new json::tree::null ());
|
||||
return first + 1;
|
||||
|
||||
case json::flat::type::BOOLEAN:
|
||||
case T::BOOLEAN:
|
||||
CHECK (*first->first == 't' || *first->first == 'f');
|
||||
output.reset (new json::tree::boolean (*first->first == 't'));
|
||||
return first + 1;
|
||||
|
||||
case json::flat::type::STRING:
|
||||
case T::STRING:
|
||||
CHECK_NEQ (first->first, first->last);
|
||||
output.reset (new json::tree::string (first->first + 1, first->last - 1));
|
||||
return first + 1;
|
||||
|
||||
case json::flat::type::INTEGER:
|
||||
case json::flat::type::REAL:
|
||||
case T::INTEGER:
|
||||
if (first->first[0] == '-') {
|
||||
char *end;
|
||||
intmax_t v = strtoll (first->first, &end, 10);
|
||||
|
||||
if (end == first->first || end > first->last)
|
||||
throw json::parse_error ("invalid signed integer");
|
||||
output.reset (new json::tree::number (v));
|
||||
} else {
|
||||
char *end;
|
||||
uintmax_t v = strtoull (first->first, &end, 10);
|
||||
|
||||
if (end == first->first || end > first->last)
|
||||
throw json::parse_error ("invalid unsigned integer");
|
||||
output.reset (new json::tree::number (v));
|
||||
}
|
||||
|
||||
return first + 1;
|
||||
|
||||
case T::REAL:
|
||||
output.reset (new json::tree::number (std::atof (first->first)));
|
||||
return first + 1;
|
||||
|
||||
case json::flat::type::ARRAY_BEGIN: {
|
||||
case T::ARRAY_BEGIN: {
|
||||
auto value = std::make_unique<json::tree::array> ();
|
||||
auto cursor = ::parse (first + 1, last, *value);
|
||||
output = std::move (value);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
case json::flat::type::OBJECT_BEGIN: {
|
||||
case T::OBJECT_BEGIN: {
|
||||
auto value = std::make_unique<json::tree::object> ();
|
||||
auto cursor = ::parse (first + 1, last, *value);
|
||||
output = std::move (value);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
default:
|
||||
case T::UNKNOWN:
|
||||
case T::OBJECT_END:
|
||||
case T::ARRAY_END:
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
@ -304,7 +336,7 @@ json::tree::node::as_bool (void) const
|
||||
float
|
||||
json::tree::node::as_float (void) const
|
||||
{
|
||||
return static_cast<float> (as_number ().native ());
|
||||
return static_cast<float> (as_number ().real ());
|
||||
}
|
||||
|
||||
|
||||
@ -312,20 +344,23 @@ json::tree::node::as_float (void) const
|
||||
double
|
||||
json::tree::node::as_double (void) const
|
||||
{
|
||||
return as_number ().native ();
|
||||
return as_number ().real ();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
size_t
|
||||
uintmax_t
|
||||
json::tree::node::as_uint (void) const
|
||||
{
|
||||
auto val = as_number ().native ();
|
||||
if (!util::is_integer (val))
|
||||
throw json::type_error ("cast fractional value to uint");
|
||||
return as_number ().uint ();
|
||||
}
|
||||
|
||||
// TODO: use trunc_cast
|
||||
return static_cast<size_t> (val);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
intmax_t
|
||||
json::tree::node::as_sint (void) const
|
||||
{
|
||||
return as_number ().sint ();
|
||||
}
|
||||
|
||||
|
||||
@ -344,45 +379,60 @@ namespace json { namespace tree {
|
||||
{
|
||||
return as_bool ();
|
||||
}
|
||||
} }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace json { namespace tree {
|
||||
template <>
|
||||
float json::tree::node::as (void) const
|
||||
{
|
||||
return as_float ();
|
||||
}
|
||||
} }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace json { namespace tree {
|
||||
template <>
|
||||
double
|
||||
json::tree::node::as (void) const
|
||||
{
|
||||
return as_double ();
|
||||
}
|
||||
} }
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#define AS_INTEGRAL(T) \
|
||||
namespace json { namespace tree { \
|
||||
template <> \
|
||||
T \
|
||||
node::as (void) const \
|
||||
{ \
|
||||
return static_cast<T> (as_double ()); \
|
||||
} \
|
||||
} }
|
||||
template <>
|
||||
uint32_t
|
||||
json::tree::node::as (void) const
|
||||
{
|
||||
return static_cast<uint32_t> (as_uint ());
|
||||
}
|
||||
|
||||
AS_INTEGRAL(uint8_t)
|
||||
AS_INTEGRAL(uint16_t)
|
||||
AS_INTEGRAL(uint32_t)
|
||||
AS_INTEGRAL(uint64_t)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
uint64_t
|
||||
json::tree::node::as (void) const
|
||||
{
|
||||
return static_cast<uint64_t> (as_uint ());
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
int32_t
|
||||
json::tree::node::as (void) const
|
||||
{
|
||||
return static_cast<int32_t> (as_sint ());
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
template <>
|
||||
int64_t
|
||||
json::tree::node::as (void) const
|
||||
{
|
||||
return static_cast<int64_t> (as_sint ());
|
||||
}
|
||||
} }
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -411,7 +461,7 @@ json::tree::node::operator[] (const std::string &key)&
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
json::tree::node&
|
||||
json::tree::node::operator[] (unsigned int idx)&
|
||||
json::tree::node::operator[] (size_t idx)&
|
||||
{ return as_array()[idx]; }
|
||||
|
||||
|
||||
@ -423,7 +473,7 @@ json::tree::node::operator[] (const std::string &key) const&
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const json::tree::node&
|
||||
json::tree::node::operator[] (unsigned int idx) const&
|
||||
json::tree::node::operator[] (size_t idx) const&
|
||||
{ return as_array()[idx]; }
|
||||
|
||||
|
||||
@ -634,7 +684,7 @@ json::tree::array::size (void) const
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
json::tree::node&
|
||||
json::tree::array::operator[] (unsigned int idx)&
|
||||
json::tree::array::operator[] (size_t idx)&
|
||||
{
|
||||
return *m_values[idx];
|
||||
}
|
||||
@ -642,7 +692,7 @@ json::tree::array::operator[] (unsigned int idx)&
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
const json::tree::node&
|
||||
json::tree::array::operator[] (unsigned int idx) const&
|
||||
json::tree::array::operator[] (size_t idx) const&
|
||||
{
|
||||
return *m_values[idx];
|
||||
}
|
||||
@ -761,7 +811,13 @@ json::tree::string::operator== (const std::string &rhs) const
|
||||
std::unique_ptr<json::tree::node>
|
||||
json::tree::number::clone (void) const
|
||||
{
|
||||
return std::make_unique<json::tree::number> (m_value);
|
||||
switch (m_repr) {
|
||||
case REAL: return std::make_unique<json::tree::number> (m_value.r);
|
||||
case SINT: return std::make_unique<json::tree::number> (m_value.s);
|
||||
case UINT: return std::make_unique<json::tree::number> (m_value.u);
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
@ -769,15 +825,86 @@ json::tree::number::clone (void) const
|
||||
std::ostream&
|
||||
json::tree::number::write (std::ostream &os) const
|
||||
{
|
||||
os << std::setprecision (std::numeric_limits<double>::digits10) << m_value;
|
||||
return os;
|
||||
auto old = int (os.precision ());
|
||||
|
||||
switch (m_repr) {
|
||||
case REAL: return os << std::numeric_limits<real_t>::digits10 << m_value.r << std::setprecision (old);
|
||||
case SINT: return os << std::numeric_limits<sint_t>::digits10 << m_value.s << std::setprecision (old);
|
||||
case UINT: return os << std::numeric_limits<uint_t>::digits10 << m_value.u << std::setprecision (old);
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool
|
||||
json::tree::number::operator ==(const json::tree::number &rhs) const
|
||||
{ return util::almost_equal (rhs.m_value, m_value); }
|
||||
json::tree::number::operator ==(const json::tree::number &rhs) const {
|
||||
if (repr () != rhs.repr ())
|
||||
return false;
|
||||
|
||||
switch (repr ()) {
|
||||
case REAL: return util::almost_equal (real (), rhs.real ());
|
||||
case SINT: return util::almost_equal (sint (), rhs.sint ());
|
||||
case UINT: return util::almost_equal (uint (), rhs.uint ());
|
||||
}
|
||||
|
||||
unreachable ();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
json::tree::number::real_t
|
||||
json::tree::number::real (void) const
|
||||
{
|
||||
if (m_repr != REAL)
|
||||
throw json::type_error ("number is not a real");
|
||||
|
||||
return m_value.r;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
json::tree::number::sint_t
|
||||
json::tree::number::sint (void) const
|
||||
{
|
||||
if (m_repr != SINT)
|
||||
throw json::type_error ("number is not a sint");
|
||||
|
||||
return m_value.s;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
json::tree::number::uint_t
|
||||
json::tree::number::uint (void) const
|
||||
{
|
||||
if (m_repr != UINT)
|
||||
throw json::type_error ("number is not a uint");
|
||||
|
||||
return m_value.u;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
json::tree::number::operator json::tree::number::real_t (void) const
|
||||
{
|
||||
return real ();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
json::tree::number::operator json::tree::number::sint_t (void) const
|
||||
{
|
||||
return sint ();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
json::tree::number::operator json::tree::number::uint_t (void) const
|
||||
{
|
||||
return uint ();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -854,14 +981,26 @@ namespace json { namespace tree {
|
||||
|
||||
template <>
|
||||
std::unique_ptr<node>
|
||||
io<int>::serialise (const int &i) {
|
||||
return std::unique_ptr<node> (new number (i));
|
||||
io<int32_t>::serialise (const int32_t &i) {
|
||||
return std::unique_ptr<node> (new number (intmax_t {i}));
|
||||
}
|
||||
|
||||
template <>
|
||||
std::unique_ptr<node>
|
||||
io<size_t>::serialise (const size_t &i) {
|
||||
return std::unique_ptr<node> (new number (i));
|
||||
io<int64_t>::serialise (const int64_t &i) {
|
||||
return std::unique_ptr<node> (new number (intmax_t {i}));
|
||||
}
|
||||
|
||||
template <>
|
||||
std::unique_ptr<node>
|
||||
io<uint32_t>::serialise (const uint32_t &i) {
|
||||
return std::unique_ptr<node> (new number (uintmax_t {i}));
|
||||
}
|
||||
|
||||
template <>
|
||||
std::unique_ptr<node>
|
||||
io<uint64_t>::serialise (const uint64_t &i) {
|
||||
return std::unique_ptr<node> (new number (uintmax_t {i}));
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -52,6 +52,7 @@ namespace json { namespace tree {
|
||||
/// Abstract base for all JSON values
|
||||
class node {
|
||||
public:
|
||||
node (const node&) = delete;
|
||||
virtual ~node () { ; }
|
||||
virtual std::unique_ptr<node> clone (void) const = 0;
|
||||
|
||||
@ -75,7 +76,8 @@ namespace json { namespace tree {
|
||||
virtual bool as_bool (void) const;
|
||||
virtual float as_float (void) const;
|
||||
virtual double as_double (void) const;
|
||||
virtual size_t as_uint (void) const;
|
||||
virtual intmax_t as_sint (void) const;
|
||||
virtual uintmax_t as_uint (void) const;
|
||||
virtual const char* as_chars (void) const&;
|
||||
|
||||
template <typename T>
|
||||
@ -103,24 +105,26 @@ namespace json { namespace tree {
|
||||
virtual bool operator!=(const char *rhs) const { return !(*this == rhs); }
|
||||
|
||||
virtual node& operator[] (const std::string&)&;
|
||||
virtual node& operator[] (unsigned int)&;
|
||||
virtual node& operator[] (size_t)&;
|
||||
virtual const node& operator[] (const std::string&) const&;
|
||||
virtual const node& operator[] (unsigned int) const&;
|
||||
virtual const node& operator[] (size_t) const&;
|
||||
|
||||
virtual std::ostream& write (std::ostream &os) const = 0;
|
||||
|
||||
protected:
|
||||
node () = default;
|
||||
};
|
||||
|
||||
|
||||
/// Represents a JSON object, and contains its children.
|
||||
class object final : public node {
|
||||
protected:
|
||||
typedef std::map<std::string, std::unique_ptr<node>> value_store;
|
||||
private:
|
||||
using value_store = std::map<std::string, std::unique_ptr<node>>;
|
||||
|
||||
public:
|
||||
typedef value_store::iterator iterator;
|
||||
typedef value_store::const_iterator const_iterator;
|
||||
|
||||
protected:
|
||||
value_store m_values;
|
||||
|
||||
public:
|
||||
virtual ~object ();
|
||||
@ -136,8 +140,8 @@ namespace json { namespace tree {
|
||||
{ return rhs == *this; }
|
||||
|
||||
virtual void insert (const std::string &key, std::unique_ptr<node>&& value);
|
||||
virtual const node& operator[](const std::string &key) const& override;
|
||||
virtual node& operator[](const std::string &key)& override;
|
||||
virtual const node& operator[] (const std::string &key) const& override;
|
||||
virtual node& operator[] (const std::string &key)& override;
|
||||
virtual bool has (const std::string&) const;
|
||||
|
||||
virtual const_iterator find (const std::string&) const;
|
||||
@ -153,6 +157,9 @@ namespace json { namespace tree {
|
||||
virtual void erase (const std::string &key);
|
||||
|
||||
virtual std::ostream& write (std::ostream &os) const override;
|
||||
|
||||
private:
|
||||
value_store m_values;
|
||||
};
|
||||
|
||||
|
||||
@ -182,8 +189,8 @@ namespace json { namespace tree {
|
||||
virtual bool operator==(const node &rhs) const override;
|
||||
|
||||
virtual size_t size (void) const;
|
||||
virtual node& operator [](unsigned int idx)& override;
|
||||
virtual const node& operator [](unsigned int idx) const& override;
|
||||
virtual node& operator[] (size_t idx)& override;
|
||||
virtual const node& operator[] (size_t idx) const& override;
|
||||
|
||||
virtual iterator begin (void);
|
||||
virtual iterator end (void);
|
||||
@ -234,13 +241,21 @@ namespace json { namespace tree {
|
||||
|
||||
/// Represents a JSON integer/float literal.
|
||||
class number final : public node {
|
||||
protected:
|
||||
double m_value;
|
||||
|
||||
public:
|
||||
explicit number (double _value): m_value (_value) { ; }
|
||||
explicit number (int _value): m_value (_value) { ; }
|
||||
explicit number (size_t _value): m_value (_value) { ; }
|
||||
enum repr_t {
|
||||
REAL,
|
||||
SINT,
|
||||
UINT
|
||||
};
|
||||
|
||||
using real_t = double;
|
||||
using sint_t = intmax_t;
|
||||
using uint_t = uintmax_t;
|
||||
|
||||
|
||||
explicit number (real_t _value): m_repr (REAL) { m_value.r = _value; }
|
||||
explicit number (sint_t _value): m_repr (SINT) { m_value.s = _value; }
|
||||
explicit number (uint_t _value): m_repr (UINT) { m_value.u = _value; }
|
||||
virtual std::unique_ptr<node> clone (void) const override;
|
||||
|
||||
virtual const number& as_number (void) const& override { return *this; }
|
||||
@ -248,15 +263,30 @@ namespace json { namespace tree {
|
||||
virtual bool is_number (void) const override { return true; }
|
||||
|
||||
virtual type_t type (void) const override { return NUMBER; }
|
||||
virtual repr_t repr (void) const { return m_repr; }
|
||||
|
||||
virtual bool operator==(const number &rhs) const override;
|
||||
virtual bool operator==(const node &rhs) const override
|
||||
{ return rhs == *this; }
|
||||
|
||||
operator double(void) const { return m_value; }
|
||||
double native (void) const { return m_value; }
|
||||
operator real_t (void) const;
|
||||
operator sint_t (void) const;
|
||||
operator uint_t (void) const;
|
||||
|
||||
real_t real (void) const;
|
||||
sint_t sint (void) const;
|
||||
uint_t uint (void) const;
|
||||
|
||||
virtual std::ostream& write (std::ostream &os) const override;
|
||||
|
||||
private:
|
||||
union {
|
||||
real_t r;
|
||||
sint_t s;
|
||||
uint_t u;
|
||||
} m_value;
|
||||
|
||||
repr_t m_repr;
|
||||
};
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@ main (void)
|
||||
tap.expect (!ref["integer"].is_string (), "integer not is_string");
|
||||
tap.expect (
|
||||
util::exactly_equal (
|
||||
(unsigned)ref["integer"].as_number ().native (),
|
||||
(unsigned)ref["integer"].as_number ().as_uint (),
|
||||
1u
|
||||
),
|
||||
"integer value equality"
|
||||
@ -102,7 +102,7 @@ main (void)
|
||||
tap.expect (!ref["double"].is_string (), "double not is_string");
|
||||
tap.expect (
|
||||
util::exactly_equal (
|
||||
ref["double"].as_number ().native (),
|
||||
ref["double"].as_number ().as<double> (),
|
||||
3.14
|
||||
),
|
||||
"double value equality"
|
||||
|
Loading…
Reference in New Issue
Block a user