Skip to content

Commit

Permalink
Add clamp/erase/erase_if algorithms to bsl (#3743)
Browse files Browse the repository at this point in the history
* Add bsl::clamp for pre-c++17 systems

* add bsl::erase/erase_if for the sequence containers (vector, deque, list)

* fix typo & include <functional>

* add 'bsl::' to uses of iterator_traits

* fix another dumb SunOS typo

* fix YET another dumb SunOS typo

* Add missing 'inline'

* add erase_if to map/unordered_map

* Add 'erase_if' to all the sets

* Regen 03 files

* Reowrk AlgorithmUtil - change name, put it into namespace bslstl

* Reformat some loops; NFCI

* Add a class banner and a few 'inline's

* One more inline
  • Loading branch information
Marshall Clow authored and GitHub Enterprise committed Jun 30, 2022
1 parent 1592340 commit d200f0f
Show file tree
Hide file tree
Showing 37 changed files with 1,534 additions and 23 deletions.
87 changes: 85 additions & 2 deletions groups/bsl/bslstl/bslstl_algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,40 @@ BSLS_IDENT("$Id: $")
// 'bsl_algorithm.h' directly.

#include <bslscm_version.h>
#include <bsls_assert.h>
#include <bsls_keyword.h>
#include <bsls_libraryfeatures.h>

#include <bslstl_iterator.h> // iterator tags
#include <bslstl_pair.h>

#include <algorithm>
#include <functional> // less

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bsls_nativestd.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace bslstl {

struct AlgorithmUtil {
// Provide a namespace for implementing helper routines for algorithm
// implementations.

// CLASS FUNCTIONS
template <class CONTAINER, class PREDICATE>
static
typename CONTAINER::size_type
containerEraseIf(CONTAINER& container, PREDICATE predicate);
// Erase all the elements in the specified container 'container' that
// satisfy the specified predicate 'predicate'. Return the number of
// elements erased.
};

} // close package namespace
} // close enterprise namespace

namespace bsl {

// Import selected symbols into bsl namespace
Expand Down Expand Up @@ -243,6 +265,22 @@ namespace bsl {
# endif // !BSLS_LIBRARYFEATURES_STDCPP_MSVC
#endif // !BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY

#ifndef BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY
template<class TYPE, class COMPARE>
BSLS_KEYWORD_CONSTEXPR_CPP14
const TYPE&
clamp(const TYPE& value, const TYPE& low, const TYPE& high, COMPARE comp);
// Return the specified 'value' adjusted so that it is in the range
// ['low', 'high'), using the specified comparison predicate 'comp'.

template<class TYPE>
BSLS_KEYWORD_CONSTEXPR_CPP14
const TYPE&
clamp(const TYPE& value, const TYPE& low, const TYPE& high);
// Return the specified 'value' adjusted so that it is in the range
// ['low', 'high').
#endif

#ifndef BSLS_LIBRARYFEATURES_HAS_CPP17_SEARCH_OVERLOAD
template<class FORWARD_ITERATOR, class SEARCHER>
BSLS_KEYWORD_CONSTEXPR_CPP14
Expand All @@ -253,19 +291,43 @@ namespace bsl {
// first occurrence of the pattern sought by the specified 'searcher'
// if found, and 'last' otherwise. See [alg.search].
#endif // BSLS_LIBRARYFEATURES_HAS_CPP17_SEARCH_OVERLOAD

} // close namespace bsl

// ============================================================================
// INLINE DEFINITIONS
// ============================================================================

// --------------------
// struct AlgorithmUtil
// --------------------

template <class CONTAINER, class PREDICATE>
inline
typename CONTAINER::size_type
BloombergLP::bslstl::AlgorithmUtil::containerEraseIf(CONTAINER& container,
PREDICATE predicate)
{
typename CONTAINER::size_type oldSize = container.size();
for (typename CONTAINER::iterator it = container.begin();
it != container.end();) {
if (predicate(*it)) {
it = container.erase(it);
}
else {
++it;
}
}
return oldSize - container.size();
}

#ifdef BSLSTL_ITERATOR_PROVIDE_SUN_CPP98_FIXES
template <class INPUT_ITERATOR, class TYPE>
inline
typename bsl::iterator_traits<INPUT_ITERATOR>::difference_type
bsl::count(INPUT_ITERATOR first, INPUT_ITERATOR last, const TYPE& value)
{
typename iterator_traits<INPUT_ITERATOR>::difference_type ret = 0;
typename bsl::iterator_traits<INPUT_ITERATOR>::difference_type ret = 0;
std::count(first, last, value, ret);
return ret;
}
Expand All @@ -275,7 +337,7 @@ inline
typename bsl::iterator_traits<INPUT_ITERATOR>::difference_type
bsl::count_if(INPUT_ITERATOR first, INPUT_ITERATOR last, PREDICATE pred)
{
typename iterator_traits<INPUT_ITERATOR>::difference_type ret = 0;
typename bsl::iterator_traits<INPUT_ITERATOR>::difference_type ret = 0;
std::count_if(first, last, pred, ret);
return ret;
}
Expand Down Expand Up @@ -338,6 +400,26 @@ bsl::copy_if(INPUT_ITERATOR first,
#endif // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY


#ifndef BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY
template<class TYPE, class COMPARE>
BSLS_KEYWORD_CONSTEXPR_CPP14
inline const TYPE&
bsl::clamp(const TYPE& value, const TYPE& low, const TYPE& high, COMPARE comp)
{
BSLS_ASSERT(!comp(high, low));
return comp(value, low) ? low : comp(high, value) ? high : value;
}

template<class TYPE>
BSLS_KEYWORD_CONSTEXPR_CPP14
inline const TYPE&
bsl::clamp(const TYPE& value, const TYPE& low, const TYPE& high)
{
return bsl::clamp(value, low, high, std::less<TYPE>());
}
#endif // BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY


#ifndef BSLS_LIBRARYFEATURES_HAS_CPP17_SEARCH_OVERLOAD
template<class FORWARD_ITERATOR, class SEARCHER>
inline
Expand All @@ -351,6 +433,7 @@ bsl::search(FORWARD_ITERATOR first,
}
#endif // BSLS_LIBRARYFEATURES_HAS_CPP17_SEARCH_OVERLOAD


#endif // INCLUDED_BSLSTL_ALGORITHM

// ----------------------------------------------------------------------------
Expand Down
27 changes: 27 additions & 0 deletions groups/bsl/bslstl/bslstl_algorithm.t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
// [ 1] bool any_of (InputIter first, InputIter last, PREDICATE pred);
// [ 1] bool none_of(InputIter first, InputIter last, PREDICATE pred);
// [ 2] BREATHING TEST
// [ 3] bsl::clamp();
// ----------------------------------------------------------------------------

// ============================================================================
Expand Down Expand Up @@ -340,6 +341,32 @@ int main(int argc, char *argv[])
printf("TEST " __FILE__ " CASE %d\n", test);

switch (test) { case 0:
case 3: {
// --------------------------------------------------------------------
// TESTING C++17 <BSL_ALGORITHM.H> ADDITIONS
//
// Concerns:
//: 1 The call 'bsl::clamp' exists and return expected values for
//: simple cases.
//
// Plan:
//: 1 Call each version of the algorithm with simple inputs and verify
//: that the result is correct.
//
// Testing:
// bsl::clamp();
// --------------------------------------------------------------------

if (verbose) printf("\nTESTING C++17 <BSL_ALGORITHM.H> ADDITIONS"
"\n=========================================\n");

#ifndef BSLS_LIBRARYFEATURES_HAS_CPP17_BASELINE_LIBRARY
ASSERT(3 == bsl::clamp(3, 1, 10));
ASSERT(3 == bsl::clamp(1, 3, 10));
ASSERT(3 == bsl::clamp(3, 10, 1, std::greater<int>()));
ASSERT(3 == bsl::clamp(1, 10, 3, std::greater<int>()));
#endif
} break;
case 2: {
// --------------------------------------------------------------------
// BREATHING TEST
Expand Down
2 changes: 2 additions & 0 deletions groups/bsl/bslstl/bslstl_deque.0.t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@
//
// FREE FUNCTIONS
// [20] void swap(deque& a, deque& b);
// [33] size_t erase(deque<T,A>&, const U&);
// [33] size_t erase_if(deque<T,A>&, PREDICATE);
// ----------------------------------------------------------------------------
// [22] CONCERN: 'std::length_error' is used properly.
//
Expand Down
101 changes: 101 additions & 0 deletions groups/bsl/bslstl/bslstl_deque.3.t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,26 @@ size_t numNotMovedInto(const CONTAINER& X,
return numNotMoved;
}

// ============
// class EqPred
// ============

template <class TYPE>
struct EqPred
// A predicate for testing 'erase_if'; it takes a value at construction
// and uses it for comparisons later.
{
TYPE d_ch;
EqPred(TYPE ch) : d_ch(ch) {}

bool operator() (TYPE ch) const
// return 'true' if the specified 'ch' is equal to the stored value,
// and 'false' otherwise.
{
return d_ch == ch;
}
};

// ============================================================================
// TEST DRIVER TEMPLATE
// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -228,6 +248,9 @@ struct TestDriver3 : TestSupport<TYPE, ALLOC> {

// TEST CASES

static void testCase33();
// test 'bsl::erase' and 'bsl::erase_if' with 'bsl::deque'.

static void testCase31();
// Test 'noexcept' specifications

Expand Down Expand Up @@ -339,6 +362,56 @@ class StdBslmaTestDriver3 : public StdBslmaTestDriverHelper<TestDriver3, TYPE>
// TEST CASES
// ----------

template <class TYPE, class ALLOC>
void TestDriver3<TYPE, ALLOC>::testCase33()
// test 'bsl::erase' and 'bsl::erase_if' with 'bsl::deque'.
{
static const struct {
int d_line; // source line number
const char *d_initial_p; // initial values
char d_element; // value to remove
const char *d_results_p; // expected result value
} DATA[] = {
//line initial element results
//---- ------------------- ------- -------------------
{ L_, "", 'A', "" },
{ L_, "A", 'A', "" },
{ L_, "A", 'B', "A" },
{ L_, "B", 'A', "B" },
{ L_, "AB", 'A', "B" },
{ L_, "BA", 'A', "B" },
{ L_, "BC", 'D', "BC" },
{ L_, "ABC", 'C', "AB" },
{ L_, "CBADEABCDAB", 'B', "CADEACDA" },
{ L_, "CBADEABCDABCDEA", 'E', "CBADABCDABCDA" },
{ L_, "ZZZZZZZZZZZZZZZZ", 'Z', "" }
};
enum { NUM_DATA = sizeof DATA / sizeof *DATA };

for (size_t i = 0; i < NUM_DATA; ++i)
{
int LINE = DATA[i].d_line;
const char *initial = DATA[i].d_initial_p;
size_t initialLen = strlen(initial);
const char *results = DATA[i].d_results_p;
size_t resultsLen = strlen(results);

Obj v1(initial, initial + initialLen);
Obj v2(initial, initial + initialLen);
Obj vres(results, results + resultsLen);
size_t ret1 = bsl::erase (v1, DATA[i].d_element);
size_t ret2 = bsl::erase_if(v2, EqPred<TYPE>(DATA[i].d_element));

// Are the modified containers correct?
ASSERTV(LINE, v1 == vres);
ASSERTV(LINE, v2 == vres);

// Are the return values correct?
ASSERTV(LINE, ret1 == initialLen - resultsLen);
ASSERTV(LINE, ret2 == initialLen - resultsLen);
}
}

template <class TYPE, class ALLOC>
void TestDriver3<TYPE,ALLOC>::testCase31()
{
Expand Down Expand Up @@ -4660,6 +4733,34 @@ int main(int argc, char *argv[])
printf("TEST " __FILE__ " CASE %d\n", test);

switch (test) { case 0: // Zero is always the leading case.
case 33: {
// --------------------------------------------------------------------
// TESTING FREE FUNCTIONS 'BSL::ERASE' AND 'BSL::ERASE_IF'
//
// Concerns:
//: 1 The free functions exist, and are callable with a deque.
//
// Plan:
//: 1 Fill a deque with known values, then attempt to erase some of
//: the values using 'bsl::erase' and 'bsl::erase_if'. Verify that
//: the resultant deque is the right size, contains the correct
//: values, and that the value returned from the functions is
//: correct.
//
// Testing:
// size_t erase(deque<T,A>&, const U&);
// size_t erase_if(deque<T,A>&, PREDICATE);
// --------------------------------------------------------------------

if (verbose)
printf(
"\nTESTING FREE FUNCTIONS 'BSL::ERASE' AND 'BSL::ERASE_IF'"
"\n=======================================================\n");

TestDriver3<char>::testCase33();
TestDriver3<int>::testCase33();
TestDriver3<long>::testCase33();
} break;
case 32: {
//---------------------------------------------------------------------
// TESTING CLASS TEMPLATE DEDUCTION GUIDES (AT COMPILE TIME)
Expand Down
31 changes: 31 additions & 0 deletions groups/bsl/bslstl/bslstl_deque.h
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ BSLS_IDENT("$Id: $")

#include <bslscm_version.h>

#include <bslstl_algorithm.h>
#include <bslstl_iterator.h>
#include <bslstl_iteratorutil.h>
#include <bslstl_randomaccessiterator.h>
Expand Down Expand Up @@ -1530,6 +1531,18 @@ bool operator>=(const deque<VALUE_TYPE, ALLOCATOR>& lhs,
// 'value_type'. Note that this operator returns '!(lhs < rhs)'.

// FREE FUNCTIONS
template <class VALUE_TYPE, class ALLOCATOR, class OTHER_TYPE>
typename deque<VALUE_TYPE, ALLOCATOR>::size_type
erase(deque<VALUE_TYPE, ALLOCATOR>& deq, const OTHER_TYPE& value);
// Erase all the elements in the specified deque 'deq' that compare equal
// to the specified 'value'. Return the number of elements erased.

template <class VALUE_TYPE, class ALLOCATOR, class PREDICATE>
typename deque<VALUE_TYPE, ALLOCATOR>::size_type
erase_if(deque<VALUE_TYPE, ALLOCATOR>& deq, PREDICATE predicate);
// Erase all the elements in the specified deque 'deq' that satisfy the
// specified predicate 'predicate'. Return the number of elements erased.

template <class VALUE_TYPE, class ALLOCATOR>
void swap(deque<VALUE_TYPE, ALLOCATOR>& a, deque<VALUE_TYPE, ALLOCATOR>& b)
BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(false);
Expand Down Expand Up @@ -3892,6 +3905,24 @@ bool operator>=(const deque<VALUE_TYPE, ALLOCATOR>& lhs,
}

// FREE FUNCTIONS
template <class VALUE_TYPE, class ALLOCATOR, class OTHER_TYPE>
inline typename deque<VALUE_TYPE, ALLOCATOR>::size_type
erase(deque<VALUE_TYPE, ALLOCATOR>& deq, const OTHER_TYPE& value)
{
typename deque<VALUE_TYPE, ALLOCATOR>::size_type oldSize = deq.size();
deq.erase(bsl::remove(deq.begin(), deq.end(), value), deq.end());
return oldSize - deq.size();
}

template <class VALUE_TYPE, class ALLOCATOR, class PREDICATE>
inline typename deque<VALUE_TYPE, ALLOCATOR>::size_type
erase_if(deque<VALUE_TYPE, ALLOCATOR>& deq, PREDICATE predicate)
{
typename deque<VALUE_TYPE, ALLOCATOR>::size_type oldSize = deq.size();
deq.erase(bsl::remove_if(deq.begin(), deq.end(), predicate), deq.end());
return oldSize - deq.size();
}

template <class VALUE_TYPE, class ALLOCATOR>
inline
void swap(deque<VALUE_TYPE, ALLOCATOR>& a, deque<VALUE_TYPE, ALLOCATOR>& b)
Expand Down
Loading

0 comments on commit d200f0f

Please sign in to comment.