json: store numbers natively as uint/sint/real

allows more accurate representations and better error checking.
This commit is contained in:
Danny Robson 2016-05-12 17:41:31 +10:00
parent 1f432c13b7
commit 8142944139
5 changed files with 313 additions and 106 deletions

View File

@ -183,22 +183,23 @@ json::flat::parse (const boost::filesystem::path &path)
std::ostream& std::ostream&
json::flat::operator<< (std::ostream &os, json::flat::type t) json::flat::operator<< (std::ostream &os, json::flat::type t)
{ {
using T = json::flat::type;
switch (t) { switch (t) {
case json::flat::type::STRING: os << "STRING"; break; case T::STRING: return os << "STRING";
case json::flat::type::NUL: os << "NUL"; break; case T::NUL: return os << "NUL";
case json::flat::type::BOOLEAN: os << "BOOLEAN"; break; case T::BOOLEAN: return os << "BOOLEAN";
case json::flat::type::INTEGER: os << "INTEGER"; break; case T::INTEGER: return os << "INTEGER";
case json::flat::type::REAL: os << "REAL"; break; case T::REAL: return os << "REAL";
case json::flat::type::OBJECT_BEGIN: os << "OBJECT_BEGIN"; break; case T::OBJECT_BEGIN: return os << "OBJECT_BEGIN";
case json::flat::type::OBJECT_END: os << "OBJECT_END"; break; case T::OBJECT_END: return os << "OBJECT_END";
case json::flat::type::ARRAY_BEGIN: os << "ARRAY_BEGIN"; break; case T::ARRAY_BEGIN: return os << "ARRAY_BEGIN";
case json::flat::type::ARRAY_END: os << "ARRAY_END"; break; case T::ARRAY_END: return os << "ARRAY_END";
default: case T::UNKNOWN: ;
unreachable (); // fall out
} }
unreachable ();
return os;
} }

View File

@ -174,7 +174,7 @@ validate (json::tree::string &node,
// check length is less than a maximum // check length is less than a maximum
auto maxLength = schema.find ("maxLength"); auto maxLength = schema.find ("maxLength");
if (maxLength != schema.cend ()) { if (maxLength != schema.cend ()) {
auto cmp = maxLength->second->as_number ().native (); auto cmp = maxLength->second->as_number ().uint ();
if (!util::is_integer (cmp)) if (!util::is_integer (cmp))
throw length_error ("maxLength"); throw length_error ("maxLength");
@ -185,7 +185,7 @@ validate (json::tree::string &node,
// check length is greater than a maximum // check length is greater than a maximum
auto minLength = schema.find ("minLength"); auto minLength = schema.find ("minLength");
if (minLength != schema.cend ()) { if (minLength != schema.cend ()) {
auto cmp = minLength->second->as_number ().native (); auto cmp = minLength->second->as_number ().uint ();
if (!util::is_integer (cmp)) if (!util::is_integer (cmp))
throw length_error ("minLength"); throw length_error ("minLength");
@ -206,31 +206,42 @@ validate (json::tree::string &node,
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <typename T>
static void static void
validate (json::tree::number &node, validate_number (T val, const json::tree::object &schema) {
const json::tree::object &schema) using R = json::tree::number::repr_t;
{
const auto &val = node.native ();
// check strictly positive integer multiple // check strictly positive integer multiple
auto mult = schema.find ("multipleOf"); auto mult = schema.find ("multipleOf");
if (mult != schema.cend ()) { 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)) switch (div.repr ()) {
throw json::schema_error ("multipleOf"); 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. // check maximum holds. exclusive requires max condition.
auto max = schema.find ("maximum"); auto max = schema.find ("maximum");
auto exclusiveMax = schema.find ("exclusiveMaximum"); auto exclusiveMax = schema.find ("exclusiveMaximum");
if (max != schema.end ()) { 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) if (exclusiveMax != schema.end () && exclusiveMax->second->as_boolean ()) {
throw json::schema_error ("exclusiveMax"); switch (cmp.repr ()) {
else if (val > cmp) case R::REAL: if (val >= T(cmp.real ())) throw json::schema_error ("exclusiveMax"); break;
throw json::schema_error ("maximum"); 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 { } else {
if (exclusiveMax != schema.cend ()) if (exclusiveMax != schema.cend ())
throw json::schema_error ("exclusiveMax"); throw json::schema_error ("exclusiveMax");
@ -240,16 +251,42 @@ validate (json::tree::number &node,
auto min = schema.find ("minimum"); auto min = schema.find ("minimum");
auto exclusiveMin = schema.find ("exclusiveMinimum"); auto exclusiveMin = schema.find ("exclusiveMinimum");
if (min != schema.end ()) { 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) if (exclusiveMin != schema.end () && exclusiveMin->second->as_boolean ()) {
throw json::schema_error ("exclusiveMin"); switch (cmp.repr ()) {
else if (val < cmp) case R::REAL: if (val <= T(cmp.real ())) throw json::schema_error ("exclusiveMin"); break;
throw json::schema_error ("minimum"); 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 { } else {
if (exclusiveMin != schema.cend ()) if (exclusiveMin != schema.cend ())
throw json::schema_error ("exclusiveMin"); 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 ()) { if (type != schema.cend ()) {
// check against a single named type // check against a single named type
if (type->second->is_string ()) { if (type->second->is_string ()) {
auto a = type->second->as_string (); const auto &a = type->second->as_string ();
auto b = to_string (node.type ()); auto b = to_string (node.type ());
if (a != b) if (a != b)

View File

@ -49,21 +49,29 @@ using json::tree::null;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
namespace util { namespace util {
template <>
bool bool
is_integer (const json::tree::number &node) 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 bool
is_integer (const json::tree::node &node) is_integer (const json::tree::node &node)
{ {
return node.is_number () && return node.is_number () && is_integer (node.as_number ());
is_integer (node.as_number ());
} }
} }
@ -127,43 +135,67 @@ parse (std::vector<json::flat::item>::const_iterator first,
CHECK (first != last); CHECK (first != last);
CHECK (output.get () == nullptr); CHECK (output.get () == nullptr);
using T = json::flat::type;
switch (first->tag) { switch (first->tag) {
case json::flat::type::NUL: case T::NUL:
output.reset (new json::tree::null ()); output.reset (new json::tree::null ());
return first + 1; return first + 1;
case json::flat::type::BOOLEAN: case T::BOOLEAN:
CHECK (*first->first == 't' || *first->first == 'f'); CHECK (*first->first == 't' || *first->first == 'f');
output.reset (new json::tree::boolean (*first->first == 't')); output.reset (new json::tree::boolean (*first->first == 't'));
return first + 1; return first + 1;
case json::flat::type::STRING: case T::STRING:
CHECK_NEQ (first->first, first->last); CHECK_NEQ (first->first, first->last);
output.reset (new json::tree::string (first->first + 1, first->last - 1)); output.reset (new json::tree::string (first->first + 1, first->last - 1));
return first + 1; return first + 1;
case json::flat::type::INTEGER: case T::INTEGER:
case json::flat::type::REAL: 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))); output.reset (new json::tree::number (std::atof (first->first)));
return first + 1; return first + 1;
case json::flat::type::ARRAY_BEGIN: { case T::ARRAY_BEGIN: {
auto value = std::make_unique<json::tree::array> (); auto value = std::make_unique<json::tree::array> ();
auto cursor = ::parse (first + 1, last, *value); auto cursor = ::parse (first + 1, last, *value);
output = std::move (value); output = std::move (value);
return cursor; return cursor;
} }
case json::flat::type::OBJECT_BEGIN: { case T::OBJECT_BEGIN: {
auto value = std::make_unique<json::tree::object> (); auto value = std::make_unique<json::tree::object> ();
auto cursor = ::parse (first + 1, last, *value); auto cursor = ::parse (first + 1, last, *value);
output = std::move (value); output = std::move (value);
return cursor; return cursor;
} }
default: case T::UNKNOWN:
case T::OBJECT_END:
case T::ARRAY_END:
unreachable (); unreachable ();
} }
unreachable ();
} }
@ -304,7 +336,7 @@ json::tree::node::as_bool (void) const
float float
json::tree::node::as_float (void) const 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 double
json::tree::node::as_double (void) const 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 json::tree::node::as_uint (void) const
{ {
auto val = as_number ().native (); return as_number ().uint ();
if (!util::is_integer (val)) }
throw json::type_error ("cast fractional value to 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 (); return as_bool ();
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
namespace json { namespace tree {
template <> template <>
float json::tree::node::as (void) const float json::tree::node::as (void) const
{ {
return as_float (); return as_float ();
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
namespace json { namespace tree {
template <> template <>
double double
json::tree::node::as (void) const json::tree::node::as (void) const
{ {
return as_double (); return as_double ();
} }
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#define AS_INTEGRAL(T) \ template <>
namespace json { namespace tree { \ uint32_t
template <> \ json::tree::node::as (void) const
T \ {
node::as (void) const \ return static_cast<uint32_t> (as_uint ());
{ \ }
return static_cast<T> (as_double ()); \
} \
} }
AS_INTEGRAL(uint8_t)
AS_INTEGRAL(uint16_t) //-----------------------------------------------------------------------------
AS_INTEGRAL(uint32_t) template <>
AS_INTEGRAL(uint64_t) 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&
json::tree::node::operator[] (unsigned int idx)& json::tree::node::operator[] (size_t idx)&
{ return as_array()[idx]; } { return as_array()[idx]; }
@ -423,7 +473,7 @@ json::tree::node::operator[] (const std::string &key) const&
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const json::tree::node& const json::tree::node&
json::tree::node::operator[] (unsigned int idx) const& json::tree::node::operator[] (size_t idx) const&
{ return as_array()[idx]; } { return as_array()[idx]; }
@ -634,7 +684,7 @@ json::tree::array::size (void) const
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
json::tree::node& json::tree::node&
json::tree::array::operator[] (unsigned int idx)& json::tree::array::operator[] (size_t idx)&
{ {
return *m_values[idx]; return *m_values[idx];
} }
@ -642,7 +692,7 @@ json::tree::array::operator[] (unsigned int idx)&
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const json::tree::node& const json::tree::node&
json::tree::array::operator[] (unsigned int idx) const& json::tree::array::operator[] (size_t idx) const&
{ {
return *m_values[idx]; return *m_values[idx];
} }
@ -761,7 +811,13 @@ json::tree::string::operator== (const std::string &rhs) const
std::unique_ptr<json::tree::node> std::unique_ptr<json::tree::node>
json::tree::number::clone (void) const 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& std::ostream&
json::tree::number::write (std::ostream &os) const json::tree::number::write (std::ostream &os) const
{ {
os << std::setprecision (std::numeric_limits<double>::digits10) << m_value; auto old = int (os.precision ());
return os;
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 bool
json::tree::number::operator ==(const json::tree::number &rhs) const json::tree::number::operator ==(const json::tree::number &rhs) const {
{ return util::almost_equal (rhs.m_value, m_value); } 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 <> template <>
std::unique_ptr<node> std::unique_ptr<node>
io<int>::serialise (const int &i) { io<int32_t>::serialise (const int32_t &i) {
return std::unique_ptr<node> (new number (i)); return std::unique_ptr<node> (new number (intmax_t {i}));
} }
template <> template <>
std::unique_ptr<node> std::unique_ptr<node>
io<size_t>::serialise (const size_t &i) { io<int64_t>::serialise (const int64_t &i) {
return std::unique_ptr<node> (new number (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 <> template <>

View File

@ -52,6 +52,7 @@ namespace json { namespace tree {
/// Abstract base for all JSON values /// Abstract base for all JSON values
class node { class node {
public: public:
node (const node&) = delete;
virtual ~node () { ; } virtual ~node () { ; }
virtual std::unique_ptr<node> clone (void) const = 0; virtual std::unique_ptr<node> clone (void) const = 0;
@ -75,7 +76,8 @@ namespace json { namespace tree {
virtual bool as_bool (void) const; virtual bool as_bool (void) const;
virtual float as_float (void) const; virtual float as_float (void) const;
virtual double as_double (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&; virtual const char* as_chars (void) const&;
template <typename T> template <typename T>
@ -103,24 +105,26 @@ namespace json { namespace tree {
virtual bool operator!=(const char *rhs) const { return !(*this == rhs); } virtual bool operator!=(const char *rhs) const { return !(*this == rhs); }
virtual node& operator[] (const std::string&)&; 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[] (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; virtual std::ostream& write (std::ostream &os) const = 0;
protected:
node () = default;
}; };
/// Represents a JSON object, and contains its children. /// Represents a JSON object, and contains its children.
class object final : public node { class object final : public node {
protected: private:
typedef std::map<std::string, std::unique_ptr<node>> value_store; using value_store = std::map<std::string, std::unique_ptr<node>>;
public: public:
typedef value_store::iterator iterator; typedef value_store::iterator iterator;
typedef value_store::const_iterator const_iterator; typedef value_store::const_iterator const_iterator;
protected:
value_store m_values;
public: public:
virtual ~object (); virtual ~object ();
@ -136,8 +140,8 @@ namespace json { namespace tree {
{ return rhs == *this; } { return rhs == *this; }
virtual void insert (const std::string &key, std::unique_ptr<node>&& value); virtual void insert (const std::string &key, std::unique_ptr<node>&& value);
virtual const node& operator[](const std::string &key) const& override; virtual const node& operator[] (const std::string &key) const& override;
virtual node& operator[](const std::string &key)& override; virtual node& operator[] (const std::string &key)& override;
virtual bool has (const std::string&) const; virtual bool has (const std::string&) const;
virtual const_iterator find (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 void erase (const std::string &key);
virtual std::ostream& write (std::ostream &os) const override; 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 bool operator==(const node &rhs) const override;
virtual size_t size (void) const; virtual size_t size (void) const;
virtual node& operator [](unsigned int idx)& override; virtual node& operator[] (size_t idx)& override;
virtual const node& operator [](unsigned int idx) const& override; virtual const node& operator[] (size_t idx) const& override;
virtual iterator begin (void); virtual iterator begin (void);
virtual iterator end (void); virtual iterator end (void);
@ -234,13 +241,21 @@ namespace json { namespace tree {
/// Represents a JSON integer/float literal. /// Represents a JSON integer/float literal.
class number final : public node { class number final : public node {
protected:
double m_value;
public: public:
explicit number (double _value): m_value (_value) { ; } enum repr_t {
explicit number (int _value): m_value (_value) { ; } REAL,
explicit number (size_t _value): m_value (_value) { ; } 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 std::unique_ptr<node> clone (void) const override;
virtual const number& as_number (void) const& override { return *this; } 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 bool is_number (void) const override { return true; }
virtual type_t type (void) const override { return NUMBER; } 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 number &rhs) const override;
virtual bool operator==(const node &rhs) const override virtual bool operator==(const node &rhs) const override
{ return rhs == *this; } { return rhs == *this; }
operator double(void) const { return m_value; } operator real_t (void) const;
double native (void) const { return m_value; } 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; 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;
}; };

View File

@ -54,7 +54,7 @@ main (void)
tap.expect (!ref["integer"].is_string (), "integer not is_string"); tap.expect (!ref["integer"].is_string (), "integer not is_string");
tap.expect ( tap.expect (
util::exactly_equal ( util::exactly_equal (
(unsigned)ref["integer"].as_number ().native (), (unsigned)ref["integer"].as_number ().as_uint (),
1u 1u
), ),
"integer value equality" "integer value equality"
@ -102,7 +102,7 @@ main (void)
tap.expect (!ref["double"].is_string (), "double not is_string"); tap.expect (!ref["double"].is_string (), "double not is_string");
tap.expect ( tap.expect (
util::exactly_equal ( util::exactly_equal (
ref["double"].as_number ().native (), ref["double"].as_number ().as<double> (),
3.14 3.14
), ),
"double value equality" "double value equality"