Merge pull request #184 from dariomt/master

Implementation of get_ref()
This commit is contained in:
Niels 2016-01-20 20:58:44 +01:00
commit 663ad13fc3
4 changed files with 330 additions and 0 deletions

26
doc/examples/get_ref.cpp Normal file
View file

@ -0,0 +1,26 @@
#include <json.hpp>
using namespace nlohmann;
int main()
{
// create a JSON number
json value = 17;
// explicitly getting references
auto r1 = value.get_ref<const json::number_integer_t&>();
auto r2 = value.get_ref<json::number_integer_t&>();
// print the values
std::cout << r1 << ' ' << r2 << '\n';
// incompatible type throws exception
try
{
auto r3 = value.get_ref<json::number_float_t&>();
}
catch(std::domain_error& ex)
{
std::cout << ex.what() << '\n';
}
}

View file

@ -2416,6 +2416,19 @@ class basic_json
return is_number_float() ? &m_value.number_float : nullptr; return is_number_float() ? &m_value.number_float : nullptr;
} }
/// helper function to implement get_ref without code duplication
/// for const and non-const overloads
/// ThisType will be deduced as 'basic_jason' or 'const basic_json'
template<typename ReferenceType, typename ThisType>
static ReferenceType get_ref_impl(ThisType& obj)
{
using PointerType = typename std::add_pointer<ReferenceType>::type;
// delegate the call to get_ptr<>()
auto ptr = obj.template get_ptr<PointerType>();
if (ptr) return *ptr;
throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + obj.type_name());
}
public: public:
/// @name value access /// @name value access
@ -2563,6 +2576,52 @@ class basic_json
return get_impl_ptr(static_cast<const PointerType>(nullptr)); return get_impl_ptr(static_cast<const PointerType>(nullptr));
} }
/*!
@brief get a reference value (implicit)
Implict reference access to the internally stored JSON value. No copies are
made.
@warning Writing data to the referee of the result yields an undefined
state.
@tparam ReferenceType reference type; must be a reference to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref
number_float_t.
@return reference to the internally stored JSON value if the requested reference
type @a ReferenceType fits to the JSON value; throws std::domain_error otherwise
@throw std::domain_error in case passed type @a ReferenceType is incompatible
with the stored JSON value
@complexity Constant.
*/
template<typename ReferenceType, typename
std::enable_if<
std::is_reference<ReferenceType>::value
, int>::type = 0>
ReferenceType get_ref()
{
// delegate call to get_ref_impl
return get_ref_impl<ReferenceType>(*this);
}
/*!
@brief get a reference value (implicit)
@copydoc get_ref()
*/
template<typename ReferenceType, typename
std::enable_if<
std::is_reference<ReferenceType>::value
and std::is_const< typename std::remove_reference<ReferenceType>::type >::value
, int>::type = 0>
ReferenceType get_ref() const
{
// delegate call to get_ref_impl
return get_ref_impl<ReferenceType>(*this);
}
/*! /*!
@brief get a value (implicit) @brief get a value (implicit)

View file

@ -2416,6 +2416,19 @@ class basic_json
return is_number_float() ? &m_value.number_float : nullptr; return is_number_float() ? &m_value.number_float : nullptr;
} }
/// helper function to implement get_ref without code duplication
/// for const and non-const overloads
/// ThisType will be deduced as 'basic_jason' or 'const basic_json'
template<typename ReferenceType, typename ThisType>
static ReferenceType get_ref_impl(ThisType& obj)
{
using PointerType = typename std::add_pointer<ReferenceType>::type;
// delegate the call to get_ptr<>()
auto ptr = obj.template get_ptr<PointerType>();
if (ptr) return *ptr;
throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + obj.type_name());
}
public: public:
/// @name value access /// @name value access
@ -2563,6 +2576,52 @@ class basic_json
return get_impl_ptr(static_cast<const PointerType>(nullptr)); return get_impl_ptr(static_cast<const PointerType>(nullptr));
} }
/*!
@brief get a reference value (implicit)
Implict reference access to the internally stored JSON value. No copies are
made.
@warning Writing data to the referee of the result yields an undefined
state.
@tparam ReferenceType reference type; must be a reference to @ref array_t, @ref
object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref
number_float_t.
@return reference to the internally stored JSON value if the requested reference
type @a ReferenceType fits to the JSON value; throws std::domain_error otherwise
@throw std::domain_error in case passed type @a ReferenceType is incompatible
with the stored JSON value
@complexity Constant.
*/
template<typename ReferenceType, typename
std::enable_if<
std::is_reference<ReferenceType>::value
, int>::type = 0>
ReferenceType get_ref()
{
// delegate call to get_ref_impl
return get_ref_impl<ReferenceType>(*this);
}
/*!
@brief get a reference value (implicit)
@copydoc get_ref()
*/
template<typename ReferenceType, typename
std::enable_if<
std::is_reference<ReferenceType>::value
and std::is_const< typename std::remove_reference<ReferenceType>::type >::value
, int>::type = 0>
ReferenceType get_ref() const
{
// delegate call to get_ref_impl
return get_ref_impl<ReferenceType>(*this);
}
/*! /*!
@brief get a value (implicit) @brief get a value (implicit)

View file

@ -2604,6 +2604,22 @@ TEST_CASE("pointer access")
CHECK(value.get_ptr<json::number_float_t*>() == nullptr); CHECK(value.get_ptr<json::number_float_t*>() == nullptr);
} }
SECTION("pointer access to const object_t")
{
using test_type = json::object_t;
const json value = {{"one", 1}, {"two", 2}};
// this should not compile
// test_type* p1 = value.get_ptr<test_type*>();
// check if pointers are returned correctly
const test_type* p2 = value.get_ptr<const test_type*>();
CHECK(*p2 == value.get<test_type>());
const test_type* const p3 = value.get_ptr<const test_type* const>();
CHECK(p2 == p3);
}
SECTION("pointer access to array_t") SECTION("pointer access to array_t")
{ {
using test_type = json::array_t; using test_type = json::array_t;
@ -2740,6 +2756,176 @@ TEST_CASE("pointer access")
} }
} }
TEST_CASE("reference access")
{
// create a JSON value with different types
json json_types =
{
{"boolean", true},
{
"number", {
{"integer", 42},
{"floating-point", 17.23}
}
},
{"string", "Hello, world!"},
{"array", {1, 2, 3, 4, 5}},
{"null", nullptr}
};
SECTION("reference access to object_t")
{
using test_type = json::object_t;
json value = {{"one", 1}, {"two", 2}};
// check if references are returned correctly
test_type& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_NOTHROW(value.get_ref<json::object_t&>());
CHECK_THROWS(value.get_ref<json::array_t&>());
CHECK_THROWS(value.get_ref<json::string_t&>());
CHECK_THROWS(value.get_ref<json::boolean_t&>());
CHECK_THROWS(value.get_ref<json::number_integer_t&>());
CHECK_THROWS(value.get_ref<json::number_float_t&>());
}
SECTION("const reference access to const object_t")
{
using test_type = json::object_t;
const json value = {{"one", 1}, {"two", 2}};
// this should not compile
// test_type& p1 = value.get_ref<test_type&>();
// check if references are returned correctly
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
}
SECTION("reference access to array_t")
{
using test_type = json::array_t;
json value = {1, 2, 3, 4};
// check if references are returned correctly
test_type& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS(value.get_ref<json::object_t&>());
CHECK_NOTHROW(value.get_ref<json::array_t&>());
CHECK_THROWS(value.get_ref<json::string_t&>());
CHECK_THROWS(value.get_ref<json::boolean_t&>());
CHECK_THROWS(value.get_ref<json::number_integer_t&>());
CHECK_THROWS(value.get_ref<json::number_float_t&>());
}
SECTION("reference access to string_t")
{
using test_type = json::string_t;
json value = "hello";
// check if references are returned correctly
test_type& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS(value.get_ref<json::object_t&>());
CHECK_THROWS(value.get_ref<json::array_t&>());
CHECK_NOTHROW(value.get_ref<json::string_t&>());
CHECK_THROWS(value.get_ref<json::boolean_t&>());
CHECK_THROWS(value.get_ref<json::number_integer_t&>());
CHECK_THROWS(value.get_ref<json::number_float_t&>());
}
SECTION("reference access to boolean_t")
{
using test_type = json::boolean_t;
json value = false;
// check if references are returned correctly
test_type& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS(value.get_ref<json::object_t&>());
CHECK_THROWS(value.get_ref<json::array_t&>());
CHECK_THROWS(value.get_ref<json::string_t&>());
CHECK_NOTHROW(value.get_ref<json::boolean_t&>());
CHECK_THROWS(value.get_ref<json::number_integer_t&>());
CHECK_THROWS(value.get_ref<json::number_float_t&>());
}
SECTION("reference access to number_integer_t")
{
using test_type = json::number_integer_t;
json value = 23;
// check if references are returned correctly
test_type& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS(value.get_ref<json::object_t&>());
CHECK_THROWS(value.get_ref<json::array_t&>());
CHECK_THROWS(value.get_ref<json::string_t&>());
CHECK_THROWS(value.get_ref<json::boolean_t&>());
CHECK_NOTHROW(value.get_ref<json::number_integer_t&>());
CHECK_THROWS(value.get_ref<json::number_float_t&>());
}
SECTION("reference access to number_float_t")
{
using test_type = json::number_float_t;
json value = 42.23;
// check if references are returned correctly
test_type& p1 = value.get_ref<test_type&>();
CHECK(&p1 == value.get_ptr<test_type*>());
CHECK(p1 == value.get<test_type>());
const test_type& p2 = value.get_ref<const test_type&>();
CHECK(&p2 == value.get_ptr<const test_type*>());
CHECK(p2 == value.get<test_type>());
// check if mismatching references throw correctly
CHECK_THROWS(value.get_ref<json::object_t&>());
CHECK_THROWS(value.get_ref<json::array_t&>());
CHECK_THROWS(value.get_ref<json::string_t&>());
CHECK_THROWS(value.get_ref<json::boolean_t&>());
CHECK_THROWS(value.get_ref<json::number_integer_t&>());
CHECK_NOTHROW(value.get_ref<json::number_float_t&>());
}
}
TEST_CASE("element access") TEST_CASE("element access")
{ {
SECTION("array") SECTION("array")