From 04b7004110a2a916d85c69a03c22c36f0a44990d Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sat, 20 Jun 2026 10:57:06 +0200 Subject: [PATCH] Add emplace() to etl::map and further containers (#1462) --- include/etl/flat_map.h | 157 ++++++++++++++ include/etl/flat_set.h | 20 +- include/etl/map.h | 282 ++++++++++++++++++++++++++ include/etl/multimap.h | 44 ++++ include/etl/multiset.h | 45 ++++ include/etl/reference_flat_map.h | 12 ++ include/etl/reference_flat_multimap.h | 12 ++ include/etl/reference_flat_multiset.h | 12 ++ include/etl/reference_flat_set.h | 12 ++ include/etl/set.h | 80 ++++++++ include/etl/unordered_map.h | 244 ++++++++++++++++++++++ include/etl/unordered_multimap.h | 82 +++++++- include/etl/unordered_multiset.h | 65 ++++++ include/etl/unordered_set.h | 96 +++++++++ test/test_flat_map.cpp | 60 ++++++ test/test_flat_multimap.cpp | 15 ++ test/test_flat_multiset.cpp | 8 + test/test_flat_set.cpp | 41 ++++ test/test_map.cpp | 160 +++++++++++++++ test/test_multimap.cpp | 34 ++++ test/test_multiset.cpp | 29 +++ test/test_reference_flat_map.cpp | 41 ++++ test/test_reference_flat_multimap.cpp | 33 +++ test/test_reference_flat_multiset.cpp | 30 +++ test/test_reference_flat_set.cpp | 30 +++ test/test_set.cpp | 70 +++++++ test/test_unordered_map.cpp | 67 ++++++ test/test_unordered_multimap.cpp | 14 ++ test/test_unordered_multiset.cpp | 15 ++ test/test_unordered_set.cpp | 57 ++++++ 30 files changed, 1861 insertions(+), 6 deletions(-) diff --git a/include/etl/flat_map.h b/include/etl/flat_map.h index a9188642..b7bc9f3d 100644 --- a/include/etl/flat_map.h +++ b/include/etl/flat_map.h @@ -579,6 +579,163 @@ namespace etl return result; } +#endif + +#if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT + //************************************************************************* + /// Inserts an element into the flat_map if the key does not exist. + /// The mapped value is constructed from args only if insertion takes place. + //************************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, Args&&... args) + { + iterator i_element = lower_bound(key); + + // Already exists? + if ((i_element != end()) && !compare(key, i_element->first)) + { + return ETL_OR_STD::pair(i_element, false); + } + + ETL_ASSERT(!full(), ETL_ERROR(flat_map_full)); + + // Create it. + value_type* pvalue = storage.allocate(); + ::new ((void*)etl::addressof(pvalue->first)) key_type(key); + ::new ((void*)etl::addressof(pvalue->second)) mapped_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + return refmap_t::insert_at(i_element, *pvalue); + } + + //************************************************************************* + /// Inserts an element into the flat_map if the key does not exist. + /// The key is moved into the container if insertion takes place. + //************************************************************************* + template + ETL_OR_STD::pair try_emplace(rvalue_key_reference key, Args&&... args) + { + iterator i_element = lower_bound(key); + + // Already exists? + if ((i_element != end()) && !compare(key, i_element->first)) + { + return ETL_OR_STD::pair(i_element, false); + } + + ETL_ASSERT(!full(), ETL_ERROR(flat_map_full)); + + // Create it. + value_type* pvalue = storage.allocate(); + ::new ((void*)etl::addressof(pvalue->first)) key_type(etl::move(key)); + ::new ((void*)etl::addressof(pvalue->second)) mapped_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + return refmap_t::insert_at(i_element, *pvalue); + } + +#else + + //************************************************************************* + /// Inserts an element into the flat_map if the key does not exist. + //************************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, const T1& value1) + { + iterator i_element = lower_bound(key); + + // Already exists? + if ((i_element != end()) && !compare(key, i_element->first)) + { + return ETL_OR_STD::pair(i_element, false); + } + + ETL_ASSERT(!full(), ETL_ERROR(flat_map_full)); + + // Create it. + value_type* pvalue = storage.allocate(); + ::new ((void*)etl::addressof(pvalue->first)) key_type(key); + ::new ((void*)etl::addressof(pvalue->second)) mapped_type(value1); + ETL_INCREMENT_DEBUG_COUNT; + + return refmap_t::insert_at(i_element, *pvalue); + } + + //************************************************************************* + /// Inserts an element into the flat_map if the key does not exist. + //************************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, const T1& value1, const T2& value2) + { + iterator i_element = lower_bound(key); + + // Already exists? + if ((i_element != end()) && !compare(key, i_element->first)) + { + return ETL_OR_STD::pair(i_element, false); + } + + ETL_ASSERT(!full(), ETL_ERROR(flat_map_full)); + + // Create it. + value_type* pvalue = storage.allocate(); + ::new ((void*)etl::addressof(pvalue->first)) key_type(key); + ::new ((void*)etl::addressof(pvalue->second)) mapped_type(value1, value2); + ETL_INCREMENT_DEBUG_COUNT; + + return refmap_t::insert_at(i_element, *pvalue); + } + + //************************************************************************* + /// Inserts an element into the flat_map if the key does not exist. + //************************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, const T1& value1, const T2& value2, const T3& value3) + { + iterator i_element = lower_bound(key); + + // Already exists? + if ((i_element != end()) && !compare(key, i_element->first)) + { + return ETL_OR_STD::pair(i_element, false); + } + + ETL_ASSERT(!full(), ETL_ERROR(flat_map_full)); + + // Create it. + value_type* pvalue = storage.allocate(); + ::new ((void*)etl::addressof(pvalue->first)) key_type(key); + ::new ((void*)etl::addressof(pvalue->second)) mapped_type(value1, value2, value3); + ETL_INCREMENT_DEBUG_COUNT; + + return refmap_t::insert_at(i_element, *pvalue); + } + + //************************************************************************* + /// Inserts an element into the flat_map if the key does not exist. + //************************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, const T1& value1, const T2& value2, const T3& value3, const T4& value4) + { + iterator i_element = lower_bound(key); + + // Already exists? + if ((i_element != end()) && !compare(key, i_element->first)) + { + return ETL_OR_STD::pair(i_element, false); + } + + ETL_ASSERT(!full(), ETL_ERROR(flat_map_full)); + + // Create it. + value_type* pvalue = storage.allocate(); + ::new ((void*)etl::addressof(pvalue->first)) key_type(key); + ::new ((void*)etl::addressof(pvalue->second)) mapped_type(value1, value2, value3, value4); + ETL_INCREMENT_DEBUG_COUNT; + + return refmap_t::insert_at(i_element, *pvalue); + } + #endif //********************************************************************* diff --git a/include/etl/flat_set.h b/include/etl/flat_set.h index c0d5ed07..daa5069e 100644 --- a/include/etl/flat_set.h +++ b/include/etl/flat_set.h @@ -339,7 +339,23 @@ namespace etl template ETL_OR_STD::pair emplace(Args&&... args) { - ETL_ASSERT(!full(), ETL_ERROR(flat_set_full)); + if (full()) + { + // A duplicate key does not require a free slot, so emplacing it is not + // a capacity failure. Construct a temporary to obtain its key and only + // emit flat_set_full if the key is not already present. This keeps + // emplace consistent with insert when the set is full. + value_type temp_value(etl::forward(args)...); + iterator position = find(temp_value); + + if (position == end()) + { + ETL_ASSERT_FAIL(ETL_ERROR(flat_set_full)); + return ETL_OR_STD::pair(end(), false); + } + + return ETL_OR_STD::pair(position, false); + } ETL_OR_STD::pair result; @@ -360,7 +376,7 @@ namespace etl // Destroy it. pvalue->~value_type(); storage.release(pvalue); - result = ETL_OR_STD::pair(end(), false); + result = ETL_OR_STD::pair(i_element, false); } return result; diff --git a/include/etl/map.h b/include/etl/map.h index 247e8fd2..77a3fa27 100644 --- a/include/etl/map.h +++ b/include/etl/map.h @@ -1319,6 +1319,188 @@ namespace etl } } +#if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT + //********************************************************************* + /// Emplaces a value to the map. + /// Constructs the value_type in place from the given arguments. + //********************************************************************* + template + ETL_OR_STD::pair emplace(Args&&... args) + { + ETL_ASSERT(!full(), ETL_ERROR(map_full)); + + // Get next available free node + Data_Node& node = allocate_data_node_from_args(etl::forward(args)...); + + // Obtain the inserted node (might be ETL_NULLPTR if node was a duplicate) + Node* inserted_node = insert_node(root_node, node); + bool inserted = inserted_node == &node; + + // Insert node into tree and return iterator to new node location in tree + return ETL_OR_STD::make_pair(iterator(*this, inserted_node), inserted); + } + + //********************************************************************* + /// Inserts an element into the map if the key does not exist. + /// The mapped value is constructed from args only if insertion takes place. + //********************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, Args&&... args) + { + // Check if key already exists + Node* found = find_node(root_node, key); + if (found) + { + return ETL_OR_STD::make_pair(iterator(*this, found), false); + } + + ETL_ASSERT(!full(), ETL_ERROR(map_full)); + + // Get next available free node + Data_Node& node = allocate_data_node_emplace(key, etl::forward(args)...); + + // Obtain the inserted node + Node* inserted_node = insert_node(root_node, node); + + // Insert node into tree and return iterator to new node location in tree + return ETL_OR_STD::make_pair(iterator(*this, inserted_node), true); + } + + //********************************************************************* + /// Inserts an element into the map if the key does not exist. + /// The key is moved into the container if insertion takes place. + //********************************************************************* + template + ETL_OR_STD::pair try_emplace(rvalue_key_reference key, Args&&... args) + { + // Check if key already exists + Node* found = find_node(root_node, key); + if (found) + { + return ETL_OR_STD::make_pair(iterator(*this, found), false); + } + + ETL_ASSERT(!full(), ETL_ERROR(map_full)); + + // Get next available free node + Data_Node& node = allocate_data_node_emplace(etl::move(key), etl::forward(args)...); + + // Obtain the inserted node + Node* inserted_node = insert_node(root_node, node); + + // Insert node into tree and return iterator to new node location in tree + return ETL_OR_STD::make_pair(iterator(*this, inserted_node), true); + } +#else + //********************************************************************* + /// Emplaces a value to the map. + //********************************************************************* + ETL_OR_STD::pair emplace(const value_type& value) + { + return insert(value); + } + + //********************************************************************* + /// Inserts an element into the map if the key does not exist. + //********************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, const T1& value1) + { + // Check if key already exists + Node* found = find_node(root_node, key); + if (found) + { + return ETL_OR_STD::make_pair(iterator(*this, found), false); + } + + ETL_ASSERT(!full(), ETL_ERROR(map_full)); + + // Get next available free node + Data_Node& node = allocate_data_node_emplace(key, value1); + + // Obtain the inserted node + Node* inserted_node = insert_node(root_node, node); + + // Insert node into tree and return iterator to new node location in tree + return ETL_OR_STD::make_pair(iterator(*this, inserted_node), true); + } + + //********************************************************************* + /// Inserts an element into the map if the key does not exist. + //********************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, const T1& value1, const T2& value2) + { + // Check if key already exists + Node* found = find_node(root_node, key); + if (found) + { + return ETL_OR_STD::make_pair(iterator(*this, found), false); + } + + ETL_ASSERT(!full(), ETL_ERROR(map_full)); + + // Get next available free node + Data_Node& node = allocate_data_node_emplace(key, value1, value2); + + // Obtain the inserted node + Node* inserted_node = insert_node(root_node, node); + + // Insert node into tree and return iterator to new node location in tree + return ETL_OR_STD::make_pair(iterator(*this, inserted_node), true); + } + + //********************************************************************* + /// Inserts an element into the map if the key does not exist. + //********************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, const T1& value1, const T2& value2, const T3& value3) + { + // Check if key already exists + Node* found = find_node(root_node, key); + if (found) + { + return ETL_OR_STD::make_pair(iterator(*this, found), false); + } + + ETL_ASSERT(!full(), ETL_ERROR(map_full)); + + // Get next available free node + Data_Node& node = allocate_data_node_emplace(key, value1, value2, value3); + + // Obtain the inserted node + Node* inserted_node = insert_node(root_node, node); + + // Insert node into tree and return iterator to new node location in tree + return ETL_OR_STD::make_pair(iterator(*this, inserted_node), true); + } + + //********************************************************************* + /// Inserts an element into the map if the key does not exist. + //********************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, const T1& value1, const T2& value2, const T3& value3, const T4& value4) + { + // Check if key already exists + Node* found = find_node(root_node, key); + if (found) + { + return ETL_OR_STD::make_pair(iterator(*this, found), false); + } + + ETL_ASSERT(!full(), ETL_ERROR(map_full)); + + // Get next available free node + Data_Node& node = allocate_data_node_emplace(key, value1, value2, value3, value4); + + // Obtain the inserted node + Node* inserted_node = insert_node(root_node, node); + + // Insert node into tree and return iterator to new node location in tree + return ETL_OR_STD::make_pair(iterator(*this, inserted_node), true); + } +#endif + //********************************************************************* /// Returns an iterator pointing to the first element in the container /// whose key is not considered to go before the key provided or end() @@ -1545,6 +1727,106 @@ namespace etl return *node; } + #if ETL_NOT_USING_STLPORT + //************************************************************************* + /// Allocate a Data_Node with the key and emplace args for the mapped value. + //************************************************************************* + template + Data_Node& allocate_data_node_emplace(const_key_reference key, Args&&... args) + { + Data_Node* node = allocate_data_node(); + + ::new ((void*)etl::addressof(node->value.first)) key_type(key); + ::new ((void*)etl::addressof(node->value.second)) mapped_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + return *node; + } + + //************************************************************************* + /// Allocate a Data_Node with a moved key and emplace args for the mapped value. + //************************************************************************* + template + Data_Node& allocate_data_node_emplace(rvalue_key_reference key, Args&&... args) + { + Data_Node* node = allocate_data_node(); + + ::new ((void*)etl::addressof(node->value.first)) key_type(etl::move(key)); + ::new ((void*)etl::addressof(node->value.second)) mapped_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + return *node; + } + + //************************************************************************* + /// Allocate a Data_Node by constructing the value_type from args. + //************************************************************************* + template + Data_Node& allocate_data_node_from_args(Args&&... args) + { + Data_Node* node = allocate_data_node(); + ::new (&node->value) value_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + return *node; + } + #endif + +#else + + //************************************************************************* + /// Allocate a Data_Node with the key and emplace args for the mapped value. + //************************************************************************* + template + Data_Node& allocate_data_node_emplace(const_key_reference key, const T1& value1) + { + Data_Node* node = allocate_data_node(); + + ::new ((void*)etl::addressof(node->value.first)) key_type(key); + ::new ((void*)etl::addressof(node->value.second)) mapped_type(value1); + ETL_INCREMENT_DEBUG_COUNT; + return *node; + } + + //************************************************************************* + /// Allocate a Data_Node with the key and emplace args for the mapped value. + //************************************************************************* + template + Data_Node& allocate_data_node_emplace(const_key_reference key, const T1& value1, const T2& value2) + { + Data_Node* node = allocate_data_node(); + + ::new ((void*)etl::addressof(node->value.first)) key_type(key); + ::new ((void*)etl::addressof(node->value.second)) mapped_type(value1, value2); + ETL_INCREMENT_DEBUG_COUNT; + return *node; + } + + //************************************************************************* + /// Allocate a Data_Node with the key and emplace args for the mapped value. + //************************************************************************* + template + Data_Node& allocate_data_node_emplace(const_key_reference key, const T1& value1, const T2& value2, const T3& value3) + { + Data_Node* node = allocate_data_node(); + + ::new ((void*)etl::addressof(node->value.first)) key_type(key); + ::new ((void*)etl::addressof(node->value.second)) mapped_type(value1, value2, value3); + ETL_INCREMENT_DEBUG_COUNT; + return *node; + } + + //************************************************************************* + /// Allocate a Data_Node with the key and emplace args for the mapped value. + //************************************************************************* + template + Data_Node& allocate_data_node_emplace(const_key_reference key, const T1& value1, const T2& value2, const T3& value3, const T4& value4) + { + Data_Node* node = allocate_data_node(); + + ::new ((void*)etl::addressof(node->value.first)) key_type(key); + ::new ((void*)etl::addressof(node->value.second)) mapped_type(value1, value2, value3, value4); + ETL_INCREMENT_DEBUG_COUNT; + return *node; + } + #endif //************************************************************************* diff --git a/include/etl/multimap.h b/include/etl/multimap.h index 0cf77189..32813e32 100644 --- a/include/etl/multimap.h +++ b/include/etl/multimap.h @@ -1368,6 +1368,35 @@ namespace etl } } + //********************************************************************* + /// Emplaces a value to the multimap. + //********************************************************************* + iterator emplace(const_reference value) + { + return insert(value); + } + +#if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT + //********************************************************************* + /// Emplaces a value to the multimap. + /// Constructs the value_type in place from the given arguments. + //********************************************************************* + template + iterator emplace(Args&&... args) + { + ETL_ASSERT(!full(), ETL_ERROR(multimap_full)); + + // Get next available free node + Data_Node& node = allocate_data_node_from_args(etl::forward(args)...); + + // Obtain the inserted node + Node* inserted_node = insert_node(root_node, node); + + // Return iterator to new node location in tree + return iterator(*this, inserted_node); + } +#endif + //********************************************************************* /// Returns an iterator pointing to the first element in the container /// whose key is not considered to go before the key provided or end() @@ -1567,6 +1596,21 @@ namespace etl ETL_INCREMENT_DEBUG_COUNT; return *node; } + + #if ETL_NOT_USING_STLPORT + //************************************************************************* + /// Allocate a Data_Node by constructing the value_type from args. + //************************************************************************* + template + Data_Node& allocate_data_node_from_args(Args&&... args) + { + Data_Node* node = allocate_data_node(); + ::new (&node->value) const value_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + return *node; + } + #endif + #endif //************************************************************************* diff --git a/include/etl/multiset.h b/include/etl/multiset.h index 8278822e..7ed9b200 100644 --- a/include/etl/multiset.h +++ b/include/etl/multiset.h @@ -1353,6 +1353,51 @@ namespace etl } } +#if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT + //********************************************************************* + /// Emplaces a value to the multiset. + //********************************************************************* + template + iterator emplace(Args&&... args) + { + ETL_ASSERT(!full(), ETL_ERROR(multiset_full)); + + // Construct the value + Data_Node* p_node = allocate_data_node(); + ::new ((void*)&p_node->value) value_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + // Obtain the inserted node + Node* inserted_node = insert_node(root_node, *p_node); + + return iterator(*this, inserted_node); + } +#else + //********************************************************************* + /// Emplaces a value to the multiset. + //********************************************************************* + iterator emplace(const_reference value) + { + return insert(value); + } + + //********************************************************************* + /// Emplaces a value to the multiset. + //********************************************************************* + template + iterator emplace(const T1& value1) + { + ETL_ASSERT(!full(), ETL_ERROR(multiset_full)); + + Data_Node* p_node = allocate_data_node(); + ::new ((void*)&p_node->value) value_type(value1); + ETL_INCREMENT_DEBUG_COUNT; + + Node* inserted_node = insert_node(root_node, *p_node); + return iterator(*this, inserted_node); + } +#endif + //********************************************************************* /// Returns an iterator pointing to the first element in the container /// whose key is not considered to go before the key provided or end() diff --git a/include/etl/reference_flat_map.h b/include/etl/reference_flat_map.h index 9df08ad9..b6bf0447 100644 --- a/include/etl/reference_flat_map.h +++ b/include/etl/reference_flat_map.h @@ -563,6 +563,18 @@ namespace etl return insert_at(i_element, value); } + //********************************************************************* + /// Emplaces a value to the reference_flat_map. + /// As the reference_flat_map stores references to externally owned + /// objects, the value is not constructed in place but inserted by + /// reference. + ///\param value The value to emplace. + //********************************************************************* + ETL_OR_STD::pair emplace(reference value) + { + return insert(value); + } + //********************************************************************* /// Inserts a value to the reference_flat_map. /// If asserts or exceptions are enabled, emits flat_map_full if the diff --git a/include/etl/reference_flat_multimap.h b/include/etl/reference_flat_multimap.h index 2616d5d9..57934215 100644 --- a/include/etl/reference_flat_multimap.h +++ b/include/etl/reference_flat_multimap.h @@ -485,6 +485,18 @@ namespace etl return insert_at(i_element, value); } + //********************************************************************* + /// Emplaces a value to the reference_flat_multimap. + /// As the reference_flat_multimap stores references to externally owned + /// objects, the value is not constructed in place but inserted by + /// reference. + ///\param value The value to emplace. + //********************************************************************* + ETL_OR_STD::pair emplace(value_type& value) + { + return insert(value); + } + //********************************************************************* /// Inserts a value to the flat_multi. /// If asserts or exceptions are enabled, emits flat_map_full if the diff --git a/include/etl/reference_flat_multiset.h b/include/etl/reference_flat_multiset.h index 026db485..8b2d0b41 100644 --- a/include/etl/reference_flat_multiset.h +++ b/include/etl/reference_flat_multiset.h @@ -478,6 +478,18 @@ namespace etl return result; } + //********************************************************************* + /// Emplaces a value to the reference_flat_multiset. + /// As the reference_flat_multiset stores references to externally owned + /// objects, the value is not constructed in place but inserted by + /// reference. + ///\param value The value to emplace. + //********************************************************************* + ETL_OR_STD::pair emplace(value_type& value) + { + return insert(value); + } + //********************************************************************* /// Inserts a value to the reference_flat_multiset. /// If asserts or exceptions are enabled, emits reference_flat_multiset_full diff --git a/include/etl/reference_flat_set.h b/include/etl/reference_flat_set.h index a9c91fe5..5da865aa 100644 --- a/include/etl/reference_flat_set.h +++ b/include/etl/reference_flat_set.h @@ -455,6 +455,18 @@ namespace etl return insert_at(i_element, value); } + //********************************************************************* + /// Emplaces a value to the reference_flat_set. + /// As the reference_flat_set stores references to externally owned + /// objects, the value is not constructed in place but inserted by + /// reference. + ///\param value The value to emplace. + //********************************************************************* + ETL_OR_STD::pair emplace(reference value) + { + return insert(value); + } + //********************************************************************* /// Inserts a value to the reference_flat_set. /// If asserts or exceptions are enabled, emits reference_flat_set_full if diff --git a/include/etl/set.h b/include/etl/set.h index 7453875a..cf5b1772 100644 --- a/include/etl/set.h +++ b/include/etl/set.h @@ -1267,6 +1267,86 @@ namespace etl } } +#if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT + //********************************************************************* + /// Emplaces a value to the set. + //********************************************************************* + template + ETL_OR_STD::pair emplace(Args&&... args) + { + if (full()) + { + // A duplicate key does not require a free node, so emplacing it is not + // a capacity failure. Construct a temporary to obtain its key and only + // emit set_full if the key is not already present. This keeps emplace + // consistent with insert when the set is full. + value_type temp_value(etl::forward(args)...); + iterator position = find(temp_value); + + if (position == end()) + { + ETL_ASSERT_FAIL(ETL_ERROR(set_full)); + return ETL_OR_STD::make_pair(end(), false); + } + + return ETL_OR_STD::make_pair(position, false); + } + + // Construct the value + Data_Node* p_node = allocate_data_node(); + ::new ((void*)&p_node->value) value_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + // Obtain the inserted node (might be ETL_NULLPTR if node was a duplicate) + Node* inserted_node = insert_node(root_node, *p_node); + bool inserted = inserted_node == p_node; + + return ETL_OR_STD::make_pair(iterator(*this, inserted_node), inserted); + } +#else + //********************************************************************* + /// Emplaces a value to the set. + //********************************************************************* + ETL_OR_STD::pair emplace(const_reference value) + { + return insert(value); + } + + //********************************************************************* + /// Emplaces a value to the set. + //********************************************************************* + template + ETL_OR_STD::pair emplace(const T1& value1) + { + if (full()) + { + // A duplicate key does not require a free node, so emplacing it is not + // a capacity failure. Construct a temporary to obtain its key and only + // emit set_full if the key is not already present. This keeps emplace + // consistent with insert when the set is full. + value_type temp_value(value1); + iterator position = find(temp_value); + + if (position == end()) + { + ETL_ASSERT_FAIL(ETL_ERROR(set_full)); + return ETL_OR_STD::make_pair(end(), false); + } + + return ETL_OR_STD::make_pair(position, false); + } + + Data_Node* p_node = allocate_data_node(); + ::new ((void*)&p_node->value) value_type(value1); + ETL_INCREMENT_DEBUG_COUNT; + + Node* inserted_node = insert_node(root_node, *p_node); + bool inserted = inserted_node == p_node; + + return ETL_OR_STD::make_pair(iterator(*this, inserted_node), inserted); + } +#endif + //********************************************************************* /// Returns an iterator pointing to the first element in the container /// whose key is not considered to go before the key provided or end() diff --git a/include/etl/unordered_map.h b/include/etl/unordered_map.h index 69531c7d..3459a801 100644 --- a/include/etl/unordered_map.h +++ b/include/etl/unordered_map.h @@ -1163,6 +1163,250 @@ namespace etl } } +#if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT + //********************************************************************* + /// Emplaces a value to the unordered_map. + /// Constructs the value_type in place from the given arguments. + //********************************************************************* + template + ETL_OR_STD::pair emplace(Args&&... args) + { + ETL_OR_STD::pair result(end(), false); + + ETL_ASSERT(!full(), ETL_ERROR(unordered_map_full)); + + // Construct the value in a temporary node to get the key for hashing. + node_t* node = allocate_data_node(); + node->clear(); + ::new ((void*)etl::addressof(node->key_value_pair)) value_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + const_key_reference key = node->key_value_pair.first; + + // Get the hash index. + size_t index = get_bucket_index(key); + + // Get the bucket & bucket iterator. + bucket_t* pbucket = pbuckets + index; + bucket_t& bucket = *pbucket; + + // The first one in the bucket? + if (bucket.empty()) + { + // Just add the pointer to the bucket; + bucket.insert_after(bucket.before_begin(), *node); + + adjust_first_last_markers_after_insert(pbucket); + + result.first = iterator((pbuckets + number_of_buckets), pbucket, pbucket->begin()); + result.second = true; + } + else + { + // Step though the bucket looking for a place to insert. + local_iterator inode_previous = bucket.before_begin(); + local_iterator inode = bucket.begin(); + + while (inode != bucket.end()) + { + // Do we already have this key? + if (key_equal_function(inode->key_value_pair.first, key)) + { + break; + } + + ++inode_previous; + ++inode; + } + + // Not already there? + if (inode == bucket.end()) + { + // Add the node to the end of the bucket; + bucket.insert_after(inode_previous, *node); + adjust_first_last_markers_after_insert(&bucket); + ++inode_previous; + + result.first = iterator((pbuckets + number_of_buckets), pbucket, inode_previous); + result.second = true; + } + else + { + // Duplicate found, destroy the node + node->key_value_pair.~value_type(); + pnodepool->release(node); + ETL_DECREMENT_DEBUG_COUNT; + } + } + + return result; + } + + //********************************************************************* + /// Inserts an element into the unordered_map if the key does not exist. + /// The mapped value is constructed from args only if insertion takes place. + //********************************************************************* + template + ETL_OR_STD::pair try_emplace(const_key_reference key, Args&&... args) + { + ETL_OR_STD::pair result(end(), false); + + // Get the hash index. + size_t index = get_bucket_index(key); + + // Get the bucket & bucket iterator. + bucket_t* pbucket = pbuckets + index; + bucket_t& bucket = *pbucket; + + // The first one in the bucket? + if (bucket.empty()) + { + ETL_ASSERT(!full(), ETL_ERROR(unordered_map_full)); + + // Get a new node. + node_t* node = allocate_data_node(); + node->clear(); + ::new ((void*)etl::addressof(node->key_value_pair.first)) key_type(key); + ::new ((void*)etl::addressof(node->key_value_pair.second)) mapped_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + // Just add the pointer to the bucket; + bucket.insert_after(bucket.before_begin(), *node); + + adjust_first_last_markers_after_insert(pbucket); + + result.first = iterator((pbuckets + number_of_buckets), pbucket, pbucket->begin()); + result.second = true; + } + else + { + // Step though the bucket looking for a place to insert. + local_iterator inode_previous = bucket.before_begin(); + local_iterator inode = bucket.begin(); + + while (inode != bucket.end()) + { + // Do we already have this key? + if (key_equal_function(inode->key_value_pair.first, key)) + { + // Found duplicate, return iterator to existing element + result.first = iterator((pbuckets + number_of_buckets), pbucket, inode); + return result; + } + + ++inode_previous; + ++inode; + } + + // Not already there, insert new element. + ETL_ASSERT(!full(), ETL_ERROR(unordered_map_full)); + + // Get a new node. + node_t* node = allocate_data_node(); + node->clear(); + ::new ((void*)etl::addressof(node->key_value_pair.first)) key_type(key); + ::new ((void*)etl::addressof(node->key_value_pair.second)) mapped_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + // Add the node to the end of the bucket; + bucket.insert_after(inode_previous, *node); + adjust_first_last_markers_after_insert(&bucket); + ++inode_previous; + + result.first = iterator((pbuckets + number_of_buckets), pbucket, inode_previous); + result.second = true; + } + + return result; + } + + //********************************************************************* + /// Inserts an element into the unordered_map if the key does not exist. + /// The key is moved into the container if insertion takes place. + //********************************************************************* + template + ETL_OR_STD::pair try_emplace(rvalue_key_reference key, Args&&... args) + { + ETL_OR_STD::pair result(end(), false); + + // Get the hash index. + size_t index = get_bucket_index(key); + + // Get the bucket & bucket iterator. + bucket_t* pbucket = pbuckets + index; + bucket_t& bucket = *pbucket; + + // The first one in the bucket? + if (bucket.empty()) + { + ETL_ASSERT(!full(), ETL_ERROR(unordered_map_full)); + + // Get a new node. + node_t* node = allocate_data_node(); + node->clear(); + ::new ((void*)etl::addressof(node->key_value_pair.first)) key_type(etl::move(key)); + ::new ((void*)etl::addressof(node->key_value_pair.second)) mapped_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + // Just add the pointer to the bucket; + bucket.insert_after(bucket.before_begin(), *node); + + adjust_first_last_markers_after_insert(pbucket); + + result.first = iterator((pbuckets + number_of_buckets), pbucket, pbucket->begin()); + result.second = true; + } + else + { + // Step though the bucket looking for a place to insert. + local_iterator inode_previous = bucket.before_begin(); + local_iterator inode = bucket.begin(); + + while (inode != bucket.end()) + { + // Do we already have this key? + if (key_equal_function(inode->key_value_pair.first, key)) + { + // Found duplicate, return iterator to existing element + result.first = iterator((pbuckets + number_of_buckets), pbucket, inode); + return result; + } + + ++inode_previous; + ++inode; + } + + // Not already there, insert new element. + ETL_ASSERT(!full(), ETL_ERROR(unordered_map_full)); + + // Get a new node. + node_t* node = allocate_data_node(); + node->clear(); + ::new ((void*)etl::addressof(node->key_value_pair.first)) key_type(etl::move(key)); + ::new ((void*)etl::addressof(node->key_value_pair.second)) mapped_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + // Add the node to the end of the bucket; + bucket.insert_after(inode_previous, *node); + adjust_first_last_markers_after_insert(&bucket); + ++inode_previous; + + result.first = iterator((pbuckets + number_of_buckets), pbucket, inode_previous); + result.second = true; + } + + return result; + } +#endif + + //********************************************************************* + /// Emplaces a value to the unordered_map. + //********************************************************************* + ETL_OR_STD::pair emplace(const_reference key_value_pair) + { + return insert(key_value_pair); + } + //********************************************************************* /// Erases an element. ///\param key The key to erase. diff --git a/include/etl/unordered_multimap.h b/include/etl/unordered_multimap.h index aa330abd..cbc1e512 100644 --- a/include/etl/unordered_multimap.h +++ b/include/etl/unordered_multimap.h @@ -687,10 +687,10 @@ namespace etl //********************************************************************* iterator insert(const_reference key_value_pair) { - iterator result = end(); - ETL_ASSERT(!full(), ETL_ERROR(unordered_multimap_full)); + iterator result = end(); + const_key_reference key = key_value_pair.first; // Get the hash index. @@ -759,10 +759,10 @@ namespace etl //********************************************************************* iterator insert(rvalue_reference key_value_pair) { - iterator result = end(); - ETL_ASSERT(!full(), ETL_ERROR(unordered_multimap_full)); + iterator result = end(); + const_key_reference key = key_value_pair.first; // Get the hash index. @@ -867,6 +867,80 @@ namespace etl } } +#if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT + //********************************************************************* + /// Emplaces a value to the unordered_multimap. + /// Constructs the value_type in place from the given arguments. + //********************************************************************* + template + iterator emplace(Args&&... args) + { + ETL_ASSERT(!full(), ETL_ERROR(unordered_multimap_full)); + + iterator result = end(); + + // Construct the value in a temporary node to get the key for hashing. + node_t* node = allocate_data_node(); + node->clear(); + ::new ((void*)etl::addressof(node->key_value_pair)) value_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + const_key_reference key = node->key_value_pair.first; + + // Get the hash index. + size_t index = get_bucket_index(key); + + // Get the bucket & bucket iterator. + bucket_t* pbucket = pbuckets + index; + bucket_t& bucket = *pbucket; + + // The first one in the bucket? + if (bucket.empty()) + { + // Just add the pointer to the bucket; + bucket.insert_after(bucket.before_begin(), *node); + adjust_first_last_markers_after_insert(pbucket); + + result = iterator((pbuckets + number_of_buckets), pbucket, pbucket->begin()); + } + else + { + // Step though the bucket looking for a place to insert. + local_iterator inode_previous = bucket.before_begin(); + local_iterator inode = bucket.begin(); + + while (inode != bucket.end()) + { + // Do we already have this key? + if (key_equal_function(inode->key_value_pair.first, key)) + { + break; + } + + ++inode_previous; + ++inode; + } + + // Add the node to the end of the bucket; + bucket.insert_after(inode_previous, *node); + adjust_first_last_markers_after_insert(&bucket); + ++inode_previous; + + result = iterator((pbuckets + number_of_buckets), pbucket, inode_previous); + } + + return result; + } +#endif + + //********************************************************************* + /// Emplaces a value to the unordered_multimap. + //********************************************************************* + iterator emplace(const_reference key_value_pair) + { + return insert(key_value_pair); + } + //********************************************************************* /// Erases an element. ///\param key The key to erase. diff --git a/include/etl/unordered_multiset.h b/include/etl/unordered_multiset.h index c8b6de7a..88354c3c 100644 --- a/include/etl/unordered_multiset.h +++ b/include/etl/unordered_multiset.h @@ -920,6 +920,71 @@ namespace etl } } +#if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT + //********************************************************************* + /// Emplaces a value to the unordered_multiset. + //********************************************************************* + template + iterator emplace(Args&&... args) + { + iterator result = end(); + + ETL_ASSERT(!full(), ETL_ERROR(unordered_multiset_full)); + + // Construct the value + node_t* node = allocate_data_node(); + node->clear(); + ::new (&node->key) value_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + key_parameter_t key = node->key; + + // Get the hash index. + size_t index = get_bucket_index(key); + + // Get the bucket & bucket iterator. + bucket_t* pbucket = pbuckets + index; + bucket_t& bucket = *pbucket; + + // The first one in the bucket? + if (bucket.empty()) + { + // Just add the pointer to the bucket; + bucket.insert_after(bucket.before_begin(), *node); + adjust_first_last_markers_after_insert(&bucket); + + result = iterator((pbuckets + number_of_buckets), pbucket, pbucket->begin()); + } + else + { + // Step though the bucket looking for a place to insert. + local_iterator inode_previous = bucket.before_begin(); + local_iterator inode = bucket.begin(); + + while (inode != bucket.end()) + { + // Do we already have this key? + if (key_equal_function(inode->key, key)) + { + break; + } + + ++inode_previous; + ++inode; + } + + // Add the node to the end of the bucket; + bucket.insert_after(inode_previous, *node); + adjust_first_last_markers_after_insert(&bucket); + ++inode_previous; + + result = iterator((pbuckets + number_of_buckets), pbucket, inode_previous); + } + + return result; + } +#endif + //********************************************************************* /// Erases an element. ///\param key The key to erase. diff --git a/include/etl/unordered_set.h b/include/etl/unordered_set.h index 66b1cfe6..46dd0e9b 100644 --- a/include/etl/unordered_set.h +++ b/include/etl/unordered_set.h @@ -980,6 +980,102 @@ namespace etl } } +#if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT + //********************************************************************* + /// Emplaces a value to the unordered_set. + //********************************************************************* + template + ETL_OR_STD::pair emplace(Args&&... args) + { + ETL_OR_STD::pair result(end(), false); + + if (full()) + { + // A duplicate key does not require a free node, so emplacing it is not + // a capacity failure. Construct a temporary to obtain its key and only + // emit unordered_set_full if the key is not already present. This keeps + // emplace consistent with insert when the unordered_set is full. + value_type temp_value(etl::forward(args)...); + iterator position = find(temp_value); + + if (position == end()) + { + ETL_ASSERT_FAIL(ETL_ERROR(unordered_set_full)); + return result; + } + + result.first = position; + result.second = false; + return result; + } + + // Construct the value in a temporary to get the key for hashing. + node_t* node = allocate_data_node(); + node->clear(); + ::new (&node->key) value_type(etl::forward(args)...); + ETL_INCREMENT_DEBUG_COUNT; + + key_parameter_t key = node->key; + + // Get the hash index. + size_t index = get_bucket_index(key); + + // Get the bucket & bucket iterator. + bucket_t* pbucket = pbuckets + index; + bucket_t& bucket = *pbucket; + + // The first one in the bucket? + if (bucket.empty()) + { + // Just add the pointer to the bucket; + bucket.insert_after(bucket.before_begin(), *node); + adjust_first_last_markers_after_insert(&bucket); + + result.first = iterator(pbuckets + number_of_buckets, pbucket, pbucket->begin()); + result.second = true; + } + else + { + // Step though the bucket looking for a place to insert. + local_iterator inode_previous = bucket.before_begin(); + local_iterator inode = bucket.begin(); + + while (inode != bucket.end()) + { + // Do we already have this key? + if (key_equal_function(inode->key, key)) + { + break; + } + + ++inode_previous; + ++inode; + } + + // Not already there? + if (inode == bucket.end()) + { + // Add the node to the end of the bucket; + bucket.insert_after(inode_previous, *node); + adjust_first_last_markers_after_insert(&bucket); + ++inode_previous; + + result.first = iterator(pbuckets + number_of_buckets, pbucket, inode_previous); + result.second = true; + } + else + { + // Duplicate found, destroy the node + node->key.~value_type(); + pnodepool->release(node); + ETL_DECREMENT_DEBUG_COUNT; + } + } + + return result; + } +#endif + //********************************************************************* /// Erases an element. ///\param key The key to erase. diff --git a/test/test_flat_map.cpp b/test/test_flat_map.cpp index cf91fce4..c88bedd9 100644 --- a/test/test_flat_map.cpp +++ b/test/test_flat_map.cpp @@ -981,6 +981,66 @@ namespace CHECK(!result.second); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_try_emplace_value) + { + Data1 data; + + // Insert with an lvalue key (const_key_reference overload). + int key0 = 0; + ETL_OR_STD::pair result1 = data.try_emplace(key0, "0"); + CHECK(result1.second == true); + CHECK(result1.first->first == 0); + CHECK(result1.first->second.a == "0"); + + // Insert with an rvalue key (rvalue_key_reference overload). + ETL_OR_STD::pair result2 = data.try_emplace(1, "1"); + CHECK(result2.second == true); + CHECK(result2.first->first == 1); + CHECK(result2.first->second.a == "1"); + + // Duplicate key: the mapped value must NOT be overwritten. + ETL_OR_STD::pair result3 = data.try_emplace(0, "99"); + CHECK(result3.second == false); + CHECK(result3.first->first == 0); + CHECK(result3.first->second.a == "0"); + + CHECK_EQUAL(2U, data.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_try_emplace_moved_value) + { + DataM data; + + MC m1("1"); + MC m2("2"); + + // Insert with a moved mapped value (lvalue key -> const_key_reference overload). + int key1 = 1; + ETL_OR_STD::pair result1 = data.try_emplace(key1, std::move(m1)); + CHECK(result1.second == true); + CHECK(!bool(m1)); // m1 was moved from + CHECK("1" == result1.first->second.value); + + // Duplicate key (rvalue key literal -> rvalue_key_reference overload): + // m2 must NOT be moved from. + ETL_OR_STD::pair result2 = data.try_emplace(1, std::move(m2)); + CHECK(result2.second == false); + CHECK(bool(m2)); // m2 was NOT moved + CHECK("1" == result2.first->second.value); + + CHECK_EQUAL(1U, data.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_try_emplace_excess) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + CHECK_THROW(data.try_emplace(10, N10), etl::flat_map_full); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_erase_key) { diff --git a/test/test_flat_multimap.cpp b/test/test_flat_multimap.cpp index 6d81d8ef..41962cc2 100644 --- a/test/test_flat_multimap.cpp +++ b/test/test_flat_multimap.cpp @@ -709,6 +709,21 @@ namespace CHECK(isEqual); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_moved_value) + { + DataM data; + + data.emplace(1, MC("1")); + data.emplace(2, MC("2")); + data.emplace(3, MC("3")); + + CHECK("1" == data.find(1)->second.value); + CHECK("2" == data.find(2)->second.value); + CHECK("3" == data.find(3)->second.value); + CHECK_EQUAL(3U, data.size()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_erase_key) { diff --git a/test/test_flat_multiset.cpp b/test/test_flat_multiset.cpp index b987e271..694bb802 100644 --- a/test/test_flat_multiset.cpp +++ b/test/test_flat_multiset.cpp @@ -728,6 +728,14 @@ namespace CHECK(isEqual); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_excess) + { + DataNDC data(initial_data.begin(), initial_data.end()); + + CHECK_THROW(data.emplace(N10), etl::flat_multiset_full); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_erase_key) { diff --git a/test/test_flat_set.cpp b/test/test_flat_set.cpp index ead57e42..1355f8c0 100644 --- a/test/test_flat_set.cpp +++ b/test/test_flat_set.cpp @@ -754,6 +754,47 @@ namespace CHECK_EQUAL(compare.size(), data.size()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_excess) + { + DataInt data(int_data.begin(), int_data.end()); + + CHECK_THROW(data.emplace(99), etl::flat_set_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_existing_value_when_full) + { + DataInt data; + ETL_OR_STD::pair data_result; + + for (size_t i = 0; i < SIZE; ++i) + { + data.emplace(static_cast(i)); + } + + CHECK(data.full()); + + // Emplacing a new key when the set is full should throw etl::flat_set_full. + CHECK_THROW(data.emplace(static_cast(SIZE)), etl::flat_set_full); + + // Emplacing an existing (duplicate) key when the set is full should not + // throw; it should return an iterator to the existing element, matching + // the behaviour of insert(). + for (size_t i = 0; i < SIZE; ++i) + { + data_result = data.emplace(static_cast(i)); + + CHECK(data_result.first != data.end()); + CHECK_EQUAL(static_cast(i), *data_result.first); + CHECK(data_result.second == false); + } + + // The set must be unchanged. + CHECK_EQUAL(SIZE, data.size()); + CHECK_TRUE(std::is_sorted(data.begin(), data.end())); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_erase_key) { diff --git a/test/test_map.cpp b/test/test_map.cpp index 56373eb5..9d8d6176 100644 --- a/test/test_map.cpp +++ b/test/test_map.cpp @@ -808,6 +808,166 @@ namespace CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::map_full); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + Compare_Data compare_data; + Data data; + + ETL_OR_STD::pair data_result = data.emplace(std::string("0"), 0); + compare_data.insert(ETL_OR_STD::make_pair(std::string("0"), 0)); + + // Check that the insertion was successful + CHECK(data_result.first->first == std::string("0")); + CHECK(data_result.first->second == 0); + CHECK(data_result.second == true); + + // Try adding a duplicate (should return iterator pointing to duplicate, inserted = false) + data_result = data.emplace(std::string("0"), 1); + + CHECK(data_result.first->first == std::string("0")); + CHECK(data_result.first->second == 0); + CHECK(data_result.second == false); + + // Add more elements + data.emplace(std::string("2"), 2); + compare_data.insert(ETL_OR_STD::make_pair(std::string("2"), 2)); + + data.emplace(std::string("1"), 1); + compare_data.insert(ETL_OR_STD::make_pair(std::string("1"), 1)); + + bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + CHECK_TRUE(std::is_sorted(data.begin(), data.end(), data.value_comp())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value_from_pair) + { + Compare_Data compare_data; + Data data; + + Data::value_type pair0(std::string("0"), 0); + Data::value_type pair1(std::string("1"), 1); + Data::value_type pair2(std::string("2"), 2); + + data.emplace(pair0); + compare_data.insert(pair0); + + data.emplace(pair1); + compare_data.insert(pair1); + + data.emplace(pair2); + compare_data.insert(pair2); + + bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + CHECK_TRUE(std::is_sorted(data.begin(), data.end(), data.value_comp())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_moved_value) + { + DataM data; + + ItemM d1(1); + ItemM d2(2); + ItemM d3(3); + + data.emplace(std::string("1"), etl::move(d1)); + data.emplace(std::string("2"), etl::move(d2)); + data.emplace(std::string("3"), etl::move(d3)); + data.emplace(std::string("4"), ItemM(4)); + + CHECK(!bool(d1)); + CHECK(!bool(d2)); + CHECK(!bool(d3)); + + CHECK(1 == data.at("1").value); + CHECK(2 == data.at("2").value); + CHECK(3 == data.at("3").value); + CHECK(4 == data.at("4").value); + + CHECK_TRUE(std::is_sorted(data.begin(), data.end(), data.value_comp())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_excess) + { + Data data(initial_data.begin(), initial_data.end()); + + CHECK_THROW(data.emplace(std::string("10"), 10), etl::map_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_try_emplace_value) + { + Data data; + + ETL_OR_STD::pair result1 = data.try_emplace(std::string("0"), 0); + CHECK(result1.first->first == std::string("0")); + CHECK(result1.first->second == 0); + CHECK(result1.second == true); + + // Try adding a duplicate (should not construct value, return existing) + ETL_OR_STD::pair result2 = data.try_emplace(std::string("0"), 99); + CHECK(result2.first->first == std::string("0")); + CHECK(result2.first->second == 0); + CHECK(result2.second == false); + + CHECK_EQUAL(1U, data.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_try_emplace_moved_value) + { + DataM data; + + ItemM d1(1); + ItemM d2(2); + + data.try_emplace(std::string("1"), etl::move(d1)); + CHECK(!bool(d1)); + CHECK(1 == data.at("1").value); + + // Duplicate key: d2 should NOT be moved from + data.try_emplace(std::string("1"), etl::move(d2)); + CHECK(bool(d2)); + CHECK(1 == data.at("1").value); + CHECK_EQUAL(1U, data.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_try_emplace_rvalue_key) + { + DataM data; + + std::string key1("1"); + std::string key2("2"); + std::string key1_dup("1"); + + auto result1 = data.try_emplace(etl::move(key1), ItemM(1)); + CHECK(result1.second == true); + CHECK(key1.empty()); // key was moved from + CHECK(1 == data.at("1").value); + + auto result2 = data.try_emplace(etl::move(key2), ItemM(2)); + CHECK(result2.second == true); + CHECK(key2.empty()); // key was moved from + CHECK(2 == data.at("2").value); + + // Duplicate key: key should NOT be moved from + auto result3 = data.try_emplace(etl::move(key1_dup), ItemM(99)); + CHECK(result3.second == false); + CHECK(!key1_dup.empty()); // key was NOT moved + CHECK(1 == data.at("1").value); + CHECK_EQUAL(2U, data.size()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_equal_range) { diff --git a/test/test_multimap.cpp b/test/test_multimap.cpp index 18ad279c..86f9af0f 100644 --- a/test/test_multimap.cpp +++ b/test/test_multimap.cpp @@ -649,6 +649,40 @@ namespace CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::multimap_full); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + Compare_Data compare_data; + Data data; + + data.emplace(std::string("0"), 0); + compare_data.insert(ETL_OR_STD::make_pair(std::string("0"), 0)); + + data.emplace(std::string("1"), 1); + compare_data.insert(ETL_OR_STD::make_pair(std::string("1"), 1)); + + data.emplace(std::string("0"), 2); + compare_data.insert(ETL_OR_STD::make_pair(std::string("0"), 2)); + + bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + CHECK(isEqual); + CHECK_EQUAL(3U, data.size()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_moved_value) + { + DataM data; + + data.emplace(std::string("1"), ItemM(1)); + data.emplace(std::string("2"), ItemM(2)); + data.emplace(std::string("3"), ItemM(3)); + + CHECK(1 == data.find("1")->second.value); + CHECK(2 == data.find("2")->second.value); + CHECK(3 == data.find("3")->second.value); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_equal_range) { diff --git a/test/test_multiset.cpp b/test/test_multiset.cpp index 6cf29f87..fcbae16a 100644 --- a/test/test_multiset.cpp +++ b/test/test_multiset.cpp @@ -602,6 +602,35 @@ namespace CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::multiset_full); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + Compare_Data compare_data; + Data data; + + data.emplace(0); + compare_data.insert(0); + + data.emplace(1); + compare_data.insert(1); + + data.emplace(0); + compare_data.insert(0); + + bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + CHECK(isEqual); + CHECK_EQUAL(3U, data.size()); + CHECK_TRUE(std::is_sorted(data.begin(), data.end(), data.key_comp())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_excess) + { + Data data(initial_data.begin(), initial_data.end()); + + CHECK_THROW(data.emplace(99), etl::multiset_full); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_moved_value) { diff --git a/test/test_reference_flat_map.cpp b/test/test_reference_flat_map.cpp index 8f22ddbe..27c94cc3 100644 --- a/test/test_reference_flat_map.cpp +++ b/test/test_reference_flat_map.cpp @@ -399,6 +399,47 @@ namespace CHECK(std::is_sorted(data.begin(), data.end())); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + Compare_DataNDC compare_data; + DataNDC data; + + ETL_OR_STD::pair result; + + DataNDC::value_type item(0, N0); + result = data.emplace(item); + compare_data.insert(item); + + bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + CHECK(result.second); + CHECK(*result.first == item); + + DataNDC::value_type item2(2, N2); + result = data.emplace(item2); + compare_data.insert(item2); + + isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + CHECK(result.second); + CHECK(*result.first == item2); + + DataNDC::value_type item1(1, N1); + result = data.emplace(item1); + compare_data.insert(item1); + + isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + CHECK(result.second); + CHECK(*result.first == item1); + + CHECK(std::is_sorted(data.begin(), data.end())); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_value_changed) { diff --git a/test/test_reference_flat_multimap.cpp b/test/test_reference_flat_multimap.cpp index 382b6f8b..a0ee3bf5 100644 --- a/test/test_reference_flat_multimap.cpp +++ b/test/test_reference_flat_multimap.cpp @@ -262,6 +262,39 @@ namespace CHECK(std::is_sorted(data.begin(), data.end())); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + Compare_DataNDC compare_data; + DataNDC data; + + DataNDC::value_type item0(0, N0); + data.emplace(item0); + compare_data.insert(item0); + + bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + DataNDC::value_type item2(2, N2); + data.emplace(item2); + compare_data.insert(item2); + + isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + DataNDC::value_type item1(1, N1); + data.emplace(item1); + compare_data.insert(item1); + + isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + CHECK(std::is_sorted(data.begin(), data.end())); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_value_multiple) { diff --git a/test/test_reference_flat_multiset.cpp b/test/test_reference_flat_multiset.cpp index b912c5e4..b007195d 100644 --- a/test/test_reference_flat_multiset.cpp +++ b/test/test_reference_flat_multiset.cpp @@ -245,6 +245,36 @@ namespace CHECK(std::is_sorted(data.begin(), data.end())); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + Compare_DataNDC compare_data; + DataNDC data; + + data.emplace(N0); + compare_data.insert(N0); + + bool isEqual = std::equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + data.emplace(N2); + compare_data.insert(N2); + + isEqual = std::equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + data.emplace(N1); + compare_data.insert(N1); + + isEqual = std::equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + CHECK(std::is_sorted(data.begin(), data.end())); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_value_multiple) { diff --git a/test/test_reference_flat_set.cpp b/test/test_reference_flat_set.cpp index a5a1d11b..c3a5ab6d 100644 --- a/test/test_reference_flat_set.cpp +++ b/test/test_reference_flat_set.cpp @@ -278,6 +278,36 @@ namespace CHECK(std::is_sorted(data.begin(), data.end())); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + Compare_DataNDC compare_data; + DataNDC data; + + data.emplace(N0); + compare_data.insert(N0); + + bool isEqual = std::equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + data.emplace(N2); + compare_data.insert(N2); + + isEqual = std::equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + data.emplace(N1); + compare_data.insert(N1); + + isEqual = std::equal(data.begin(), data.end(), compare_data.begin()); + + CHECK(isEqual); + + CHECK(std::is_sorted(data.begin(), data.end())); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_value_multiple) { diff --git a/test/test_set.cpp b/test/test_set.cpp index 2fb624d4..863325c0 100644 --- a/test/test_set.cpp +++ b/test/test_set.cpp @@ -558,6 +558,76 @@ namespace CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::set_full); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + Compare_Data compare_data; + Data data; + + auto data_result = data.emplace(0); + compare_data.insert(0); + + CHECK(data_result.first != data.end()); + CHECK(*data_result.first == 0); + CHECK(data_result.second == true); + + // Duplicate + data_result = data.emplace(0); + CHECK(*data_result.first == 0); + CHECK(data_result.second == false); + + data.emplace(2); + compare_data.insert(2); + + data.emplace(1); + compare_data.insert(1); + + bool isEqual = Check_Equal(data.begin(), data.end(), compare_data.begin()); + CHECK(isEqual); + CHECK_TRUE(std::is_sorted(data.begin(), data.end(), data.key_comp())); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_excess) + { + Data data(initial_data.begin(), initial_data.end()); + + CHECK_THROW(data.emplace(99), etl::set_full); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_existing_value_when_full) + { + Data data; + ETL_OR_STD::pair data_result; + + for (size_t i = 0; i < MAX_SIZE; ++i) + { + data.emplace(static_cast(i)); + } + + CHECK(data.full()); + + // Emplacing a new key when the set is full should throw etl::set_full. + CHECK_THROW(data.emplace(static_cast(MAX_SIZE)), etl::set_full); + + // Emplacing an existing (duplicate) key when the set is full should not + // throw; it should return an iterator to the existing element, matching + // the behaviour of insert(). + for (size_t i = 0; i < MAX_SIZE; ++i) + { + data_result = data.emplace(static_cast(i)); + + CHECK(data_result.first != data.end()); + CHECK_EQUAL(static_cast(i), *data_result.first); + CHECK(data_result.second == false); + } + + // The set must be unchanged. + CHECK_EQUAL(MAX_SIZE, data.size()); + CHECK_TRUE(std::is_sorted(data.begin(), data.end(), data.key_comp())); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_moved_value) { diff --git a/test/test_unordered_map.cpp b/test/test_unordered_map.cpp index fe47e6d4..793343eb 100644 --- a/test/test_unordered_map.cpp +++ b/test/test_unordered_map.cpp @@ -768,6 +768,73 @@ namespace CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::unordered_map_full); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + DataM data; + + data.emplace(std::string("1"), ItemM(1)); + data.emplace(std::string("2"), ItemM(2)); + data.emplace(std::string("3"), ItemM(3)); + + CHECK_EQUAL(3U, data.size()); + CHECK(1 == data.at("1").value); + CHECK(2 == data.at("2").value); + CHECK(3 == data.at("3").value); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_duplicate) + { + DataM data; + + auto result1 = data.emplace(std::string("1"), ItemM(1)); + auto result2 = data.emplace(std::string("1"), ItemM(99)); + + CHECK(result1.second == true); + CHECK(result2.second == false); + CHECK_EQUAL(1U, data.size()); + CHECK(1 == data.at("1").value); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_try_emplace_value) + { + DataM data; + + auto result1 = data.try_emplace(std::string("1"), ItemM(1)); + CHECK(result1.second == true); + CHECK_EQUAL(1U, data.size()); + CHECK(1 == data.at("1").value); + + // Duplicate key: value should NOT be constructed + auto result2 = data.try_emplace(std::string("1"), ItemM(99)); + CHECK(result2.second == false); + CHECK_EQUAL(1U, data.size()); + CHECK(1 == data.at("1").value); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_try_emplace_rvalue_key) + { + DataM data; + + std::string key1("1"); + std::string key1_dup("1"); + + auto result1 = data.try_emplace(etl::move(key1), ItemM(1)); + CHECK(result1.second == true); + CHECK(key1.empty()); // key was moved from + CHECK(1 == data.at("1").value); + + // Duplicate key: key should NOT be moved from + auto result2 = data.try_emplace(etl::move(key1_dup), ItemM(99)); + CHECK(result2.second == false); + CHECK(!key1_dup.empty()); // key was NOT moved + CHECK(1 == data.at("1").value); + CHECK_EQUAL(1U, data.size()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_moved_value) { diff --git a/test/test_unordered_multimap.cpp b/test/test_unordered_multimap.cpp index a48be17d..bf2c2aa6 100644 --- a/test/test_unordered_multimap.cpp +++ b/test/test_unordered_multimap.cpp @@ -565,6 +565,20 @@ namespace CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::unordered_multimap_full); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + DataM data; + + data.emplace(std::string("1"), ItemM(1)); + data.emplace(std::string("2"), ItemM(2)); + data.emplace(std::string("3"), ItemM(3)); + + CHECK_EQUAL(3U, data.size()); + CHECK(2 == data.find("2")->second.value); + CHECK(3 == data.find("3")->second.value); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_moved_value) { diff --git a/test/test_unordered_multiset.cpp b/test/test_unordered_multiset.cpp index 2a3944da..9a05aca8 100644 --- a/test/test_unordered_multiset.cpp +++ b/test/test_unordered_multiset.cpp @@ -563,6 +563,21 @@ namespace CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::unordered_multiset_full); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + DataM data; + + auto result1 = data.emplace(ItemM(1)); + auto result2 = data.emplace(ItemM(2)); + auto result3 = data.emplace(ItemM(1)); + + CHECK(result1 != data.end()); + CHECK(result2 != data.end()); + CHECK(result3 != data.end()); + CHECK_EQUAL(3U, data.size()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_moved_value) { diff --git a/test/test_unordered_set.cpp b/test/test_unordered_set.cpp index 60063aa2..da0831c8 100644 --- a/test/test_unordered_set.cpp +++ b/test/test_unordered_set.cpp @@ -159,6 +159,7 @@ namespace }; //*************************************************************************** +#include "etl/private/diagnostic_null_dereference_push.h" SUITE(test_unordered_set) { static const size_t SIZE = 10; @@ -544,6 +545,21 @@ namespace CHECK_THROW(data.insert(excess_data.begin(), excess_data.end()), etl::unordered_set_full); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_value) + { + DataM data; + + auto result1 = data.emplace(ItemM(1)); + auto result2 = data.emplace(ItemM(2)); + auto result3 = data.emplace(ItemM(3)); + + CHECK(result1.second == true); + CHECK(result2.second == true); + CHECK(result3.second == true); + CHECK_EQUAL(3U, data.size()); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_insert_moved_value) { @@ -599,6 +615,46 @@ namespace CHECK(data.size() == SIZE); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_emplace_existing_value_when_full) + { + DataNDC data; + + data.insert(N0); // Inserted + data.insert(N1); // Inserted + data.insert(N2); // Inserted + data.insert(N3); // Inserted + data.insert(N4); // Inserted + data.insert(N5); // Inserted + data.insert(N6); // Inserted + data.insert(N7); // Inserted + data.insert(N8); // Inserted + data.insert(N9); // Inserted + + CHECK(data.full()); + + // Emplacing a new key when the unordered_set is full should throw. + CHECK_THROW(data.emplace(N10), etl::unordered_set_full); + + // Emplacing an existing (duplicate) key when the unordered_set is full + // should not throw; it should return an iterator to the existing element, + // matching the behaviour of insert(). + ETL_OR_STD::pair result; + + CHECK_NO_THROW(result = data.emplace(N0)); + CHECK(result.first != data.end()); + CHECK(*result.first == N0); + CHECK(result.second == false); + + CHECK_NO_THROW(result = data.emplace(N9)); + CHECK(result.first != data.end()); + CHECK(*result.first == N9); + CHECK(result.second == false); + + // The unordered_set must be unchanged. + CHECK(data.size() == SIZE); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_erase_key) { @@ -1067,4 +1123,5 @@ namespace CHECK_FALSE(data.contains(not_inserted)); } } +#include "etl/private/diagnostic_pop.h" } // namespace