Skip to content

Commit

Permalink
Merge pull request #184 from dariomt/master
Browse files Browse the repository at this point in the history
Implementation of get_ref()
  • Loading branch information
nlohmann committed Jan 20, 2016
2 parents 72e33ee + db3c7fd commit 663ad13
Show file tree
Hide file tree
Showing 4 changed files with 330 additions and 0 deletions.
26 changes: 26 additions & 0 deletions doc/examples/get_ref.cpp
Original file line number Diff line number Diff line change
@@ -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';
}
}
59 changes: 59 additions & 0 deletions src/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2416,6 +2416,19 @@ class basic_json
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:

/// @name value access
Expand Down Expand Up @@ -2563,6 +2576,52 @@ class basic_json
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)
Expand Down
59 changes: 59 additions & 0 deletions src/json.hpp.re2c
Original file line number Diff line number Diff line change
Expand Up @@ -2416,6 +2416,19 @@ class basic_json
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:

/// @name value access
Expand Down Expand Up @@ -2563,6 +2576,52 @@ class basic_json
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)

Expand Down
186 changes: 186 additions & 0 deletions test/unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2604,6 +2604,22 @@ TEST_CASE("pointer access")
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")
{
using test_type = json::array_t;
Expand Down Expand Up @@ -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")
{
SECTION("array")
Expand Down

0 comments on commit 663ad13

Please sign in to comment.