From e590604822b6a62fb8555fd0f6da3fc82da9bc20 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 28 Jul 2020 14:20:31 +0200 Subject: [PATCH] :bug: fix a bug due to missing overloads in ordered_map container --- include/nlohmann/ordered_map.hpp | 37 ++++++++- single_include/nlohmann/json.hpp | 37 ++++++++- test/CMakeLists.txt | 1 + test/src/unit-ordered_map.cpp | 125 +++++++++++++++++++++++++++++++ test/src/unit-regression.cpp | 19 +++++ 5 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 test/src/unit-ordered_map.cpp diff --git a/include/nlohmann/ordered_map.hpp b/include/nlohmann/ordered_map.hpp index 2a72fefd..5b88834f 100644 --- a/include/nlohmann/ordered_map.hpp +++ b/include/nlohmann/ordered_map.hpp @@ -30,7 +30,7 @@ template , ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) : Container{init, alloc} {} - std::pair emplace(key_type&& key, T&& t) + std::pair emplace(const key_type& key, T&& t) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -43,9 +43,40 @@ template , return {--this->end(), true}; } - T& operator[](Key&& key) + T& operator[](const Key& key) { - return emplace(std::move(key), T{}).first->second; + return emplace(key, T{}).first->second; + } + + const T& operator[](const Key& key) const + { + return at(key); + } + + T& at(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + throw std::out_of_range("key not found"); + } + + const T& at(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + throw std::out_of_range("key not found"); } size_type erase(const Key& key) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 6a0f47ab..dfdde16f 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -16420,7 +16420,7 @@ template , ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) : Container{init, alloc} {} - std::pair emplace(key_type&& key, T&& t) + std::pair emplace(const key_type& key, T&& t) { for (auto it = this->begin(); it != this->end(); ++it) { @@ -16433,9 +16433,40 @@ template , return {--this->end(), true}; } - T& operator[](Key&& key) + T& operator[](const Key& key) { - return emplace(std::move(key), T{}).first->second; + return emplace(key, T{}).first->second; + } + + const T& operator[](const Key& key) const + { + return at(key); + } + + T& at(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + throw std::out_of_range("key not found"); + } + + const T& at(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + throw std::out_of_range("key not found"); } size_type erase(const Key& key) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 76ba31d8..28834291 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -126,6 +126,7 @@ set(files src/unit-msgpack.cpp src/unit-noexcept.cpp src/unit-ordered_json.cpp + src/unit-ordered_map.cpp src/unit-pointer_access.cpp src/unit-readme.cpp src/unit-reference_access.cpp diff --git a/test/src/unit-ordered_map.cpp b/test/src/unit-ordered_map.cpp new file mode 100644 index 00000000..78871af0 --- /dev/null +++ b/test/src/unit-ordered_map.cpp @@ -0,0 +1,125 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ (test suite) +| | |__ | | | | | | version 3.9.0 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "doctest_compatibility.h" + +#include +using nlohmann::ordered_map; + + +TEST_CASE("ordered_map") +{ + SECTION("constructor") + { + SECTION("constructor from iterator range") + { + std::map m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}}; + ordered_map om(m.begin(), m.end()); + CHECK(om.size() == 3); + } + + SECTION("copy assignment") + { + std::map m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}}; + ordered_map om(m.begin(), m.end()); + const auto com = om; + CHECK(com.size() == 3); + } + } + + SECTION("at") + { + std::map m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}}; + ordered_map om(m.begin(), m.end()); + const auto com = om; + + SECTION("with Key&&") + { + CHECK(om.at(std::string("eins")) == std::string("one")); + CHECK(com.at(std::string("eins")) == std::string("one")); + CHECK_THROWS_AS(om.at(std::string("vier")), std::out_of_range); + CHECK_THROWS_AS(com.at(std::string("vier")), std::out_of_range); + } + + SECTION("with const Key&&") + { + const std::string eins = "eins"; + const std::string vier = "vier"; + CHECK(om.at(eins) == std::string("one")); + CHECK(com.at(eins) == std::string("one")); + CHECK_THROWS_AS(om.at(vier), std::out_of_range); + CHECK_THROWS_AS(com.at(vier), std::out_of_range); + } + + SECTION("with string literal") + { + CHECK(om.at("eins") == std::string("one")); + CHECK(com.at("eins") == std::string("one")); + CHECK_THROWS_AS(om.at("vier"), std::out_of_range); + CHECK_THROWS_AS(com.at("vier"), std::out_of_range); + } + } + + SECTION("operator[]") + { + std::map m {{"eins", "one"}, {"zwei", "two"}, {"drei", "three"}}; + ordered_map om(m.begin(), m.end()); + const auto com = om; + + SECTION("with Key&&") + { + CHECK(om[std::string("eins")] == std::string("one")); + CHECK(com[std::string("eins")] == std::string("one")); + + CHECK(om[std::string("vier")] == std::string("")); + CHECK(om.size() == 4); + } + + SECTION("with const Key&&") + { + const std::string eins = "eins"; + const std::string vier = "vier"; + + CHECK(om[eins] == std::string("one")); + CHECK(com[eins] == std::string("one")); + + CHECK(om[vier] == std::string("")); + CHECK(om.size() == 4); + } + + SECTION("with string literal") + { + CHECK(om["eins"] == std::string("one")); + CHECK(com["eins"] == std::string("one")); + + CHECK(om["vier"] == std::string("")); + CHECK(om.size() == 4); + } + } +} diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index 223466a4..0199a0fb 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -1976,6 +1976,25 @@ TEST_CASE("regression tests") json result = json::from_cbor(data, true, false); CHECK(result.is_discarded()); } + + SECTION("issue #2315 - json.update and vectordoes not work with ordered_json") + { + nlohmann::ordered_json jsonAnimals = {{"animal", "dog"}}; + nlohmann::ordered_json jsonCat = {{"animal", "cat"}}; + jsonAnimals.update(jsonCat); + CHECK(jsonAnimals["animal"] == "cat"); + + std::vector> intData = {std::make_pair("aaaa", 11), + std::make_pair("bbb", 222) + }; + nlohmann::ordered_json jsonObj; + for (const auto& data : intData) + { + jsonObj[data.first] = data.second; + } + CHECK(jsonObj["aaaa"] == 11); + CHECK(jsonObj["bbb"] == 222); + } } #if !defined(JSON_NOEXCEPTION)