Skip to content

Commit

Permalink
Support for C++14 equivalent versions of find, contains and count whe…
Browse files Browse the repository at this point in the history
…n comparator is_transparent
  • Loading branch information
sjanel committed Jan 11, 2022
1 parent 5f2718e commit b3866b6
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 33 deletions.
50 changes: 44 additions & 6 deletions include/amc/flatset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
#include "type_traits.hpp"
#include "vector.hpp"

#ifdef AMC_CXX14
#include "istransparent.hpp"
#ifdef AMC_CXX17
#include <optional>
#endif

#endif
namespace amc {

/**
Expand Down Expand Up @@ -248,13 +250,35 @@ class FlatSet : private Compare {
return insert(hint, T(std::forward<Args &&>(args)...));
}

const_iterator find(const_reference v) const {
const_iterator lbIt = std::lower_bound(cbegin(), cend(), v, compRef());
return lbIt == cend() || compRef()(v, *lbIt) ? cend() : lbIt;
const_iterator find(const_reference k) const {
const_iterator lbIt = lower_bound(k);
return lbIt == cend() || compRef()(k, *lbIt) ? cend() : lbIt;
}

bool contains(const_reference k) const { return find(k) != end(); }

size_type count(const_reference k) const { return contains(k); }

#ifdef AMC_CXX14
template <class K, typename std::enable_if<!std::is_same<T, K>::value && has_is_transparent<Compare>::value,
bool>::type = true>
const_iterator find(const K &k) const {
const_iterator lbIt = lower_bound(k);
return lbIt == cend() || compRef()(k, *lbIt) ? cend() : lbIt;
}

bool contains(const_reference v) const { return find(v) != end(); }
size_type count(const_reference v) const { return contains(v) ? 1U : 0U; }
template <class K, typename std::enable_if<!std::is_same<T, K>::value && has_is_transparent<Compare>::value,
bool>::type = true>
bool contains(const K &k) const {
return find(k) != end();
}

template <class K, typename std::enable_if<!std::is_same<T, K>::value && has_is_transparent<Compare>::value,
bool>::type = true>
size_type count(const K &k) const {
return contains(k);
}
#endif

size_type erase(const_reference v) {
const_iterator it = find(v);
Expand Down Expand Up @@ -352,6 +376,20 @@ class FlatSet : private Compare {
const_iterator lower_bound(const_reference v) const { return std::lower_bound(begin(), end(), v, compRef()); }
const_iterator upper_bound(const_reference v) const { return std::upper_bound(begin(), end(), v, compRef()); }

#ifdef AMC_CXX14
template <class K, typename std::enable_if<!std::is_same<T, K>::value && has_is_transparent<Compare>::value,
bool>::type = true>
const_iterator lower_bound(const K &k) const {
return std::lower_bound(begin(), end(), k, compRef());
}

template <class K, typename std::enable_if<!std::is_same<T, K>::value && has_is_transparent<Compare>::value,
bool>::type = true>
const_iterator upper_bound(const K &k) const {
return std::upper_bound(begin(), end(), k, compRef());
}
#endif

#ifdef AMC_NONSTD_FEATURES
/// Get the underlying vector of elements of this FlatSet, by returning a newly move constructed vector.
vector_type steal_vector() {
Expand Down
19 changes: 19 additions & 0 deletions include/amc/hasreallocate.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <utility>

#include "isdetected.hpp"

namespace amc {
namespace vec {

/// Implement the member detection idiom to know if allocator provides 'reallocate' method.
template <class T>
using has_reallocate_t = decltype(std::declval<T>().reallocate(
std::declval<typename T::pointer>(), std::declval<typename T::size_type>(), std::declval<typename T::size_type>(),
std::declval<typename T::size_type>()));

template <class T>
using has_reallocate = is_detected<has_reallocate_t, T>;
} // namespace vec
} // namespace amc
15 changes: 15 additions & 0 deletions include/amc/istransparent.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <utility>

#include "isdetected.hpp"

namespace amc {

/// Implement the member detection idiom to know if T provides 'is_transparent' type
template <class T>
using has_is_transparent_t = typename T::is_transparent;

template <class T>
using has_is_transparent = is_detected<has_is_transparent_t, T>;
} // namespace amc
63 changes: 47 additions & 16 deletions include/amc/smallset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "allocator.hpp"
#include "config.hpp"
#include "fixedcapacityvector.hpp"
#include "istransparent.hpp"
#include "memory.hpp"
#include "type_traits.hpp"

Expand Down Expand Up @@ -124,6 +125,7 @@ class SmallSet {
static_assert(N <= 64, "N should be small as search has linear complexity for small sets");

static_assert(std::is_same<T, typename SetType::value_type>::value, "Set value type should be T");
static_assert(std::is_same<Compare, typename SetType::key_compare>::value, "Set compare type should be Compare");
static_assert(std::is_same<Alloc, typename SetType::allocator_type>::value, "Allocator should match set's");

public:
Expand Down Expand Up @@ -231,23 +233,27 @@ class SmallSet {
return *this;
}

const_iterator begin() const noexcept { return isSmall() ? iterator(_vec.begin()) : iterator(_set.begin()); }
const_iterator begin() const noexcept {
return isSmall() ? const_iterator(_vec.begin()) : const_iterator(_set.begin());
}
const_iterator cbegin() const noexcept { return begin(); }
const_iterator end() const noexcept { return isSmall() ? iterator(_vec.end()) : iterator(_set.end()); }
const_iterator end() const noexcept { return isSmall() ? const_iterator(_vec.end()) : const_iterator(_set.end()); }
const_iterator cend() const noexcept { return end(); }

const_reverse_iterator rbegin() const noexcept {
return isSmall() ? reverse_iterator(_vec.end()) : reverse_iterator(_set.end());
return isSmall() ? const_reverse_iterator(_vec.end()) : const_reverse_iterator(_set.end());
}
const_reverse_iterator crbegin() const noexcept { return rbegin(); }

const_reverse_iterator rend() const noexcept {
return isSmall() ? reverse_iterator(_vec.begin()) : reverse_iterator(_set.begin());
return isSmall() ? const_reverse_iterator(_vec.begin()) : const_reverse_iterator(_set.begin());
}
const_reverse_iterator crend() const noexcept { return rend(); }

bool empty() const noexcept { return isSmall() ? _vec.empty() : _set.empty(); }
bool empty() const noexcept { return _vec.empty() && _set.empty(); }

size_type size() const noexcept { return isSmall() ? _vec.size() : _set.size(); }

size_type max_size() const noexcept {
return std::max(static_cast<size_type>(_vec.max_size()), static_cast<size_type>(_set.max_size()));
}
Expand Down Expand Up @@ -316,7 +322,7 @@ class SmallSet {
return insert(T(std::forward<Args &&>(args)...));
}
miterator elIt = std::addressof(_vec.emplace_back(std::forward<Args &&>(args)...));
miterator it = std::find_if(_vec.begin(), elIt, FindFunctor(key_comp(), *elIt));
miterator it = std::find_if(_vec.begin(), elIt, FindFunctor<T>(key_comp(), *elIt));
bool isNew = it == elIt;
if (!isNew) {
_vec.pop_back();
Expand All @@ -334,14 +340,32 @@ class SmallSet {
return emplace(std::forward<Args &&>(args)...).first;
}

const_iterator find(const_reference v) const {
return isSmall() ? const_iterator(std::find_if(_vec.begin(), _vec.end(), FindFunctor(key_comp(), v)))
: const_iterator(_set.find(v));
const_iterator find(const_reference k) const {
return isSmall() ? const_iterator(find_small(k)) : const_iterator(_set.find(k));
}

template <class K, typename std::enable_if<!std::is_same<T, K>::value && has_is_transparent<Compare>::value,
bool>::type = true>
const_iterator find(const K &k) const {
return isSmall() ? const_iterator(find_small(k)) : const_iterator(_set.find(k));
}

bool contains(const_reference k) const { return isSmall() ? find_small(k) != _vec.end() : _set.count(k); }

template <class K, typename std::enable_if<!std::is_same<T, K>::value && has_is_transparent<Compare>::value,
bool>::type = true>
bool contains(const K &k) const {
return isSmall() ? find_small(k) != _vec.end() : _set.count(k);
}

bool contains(const_reference v) const { return find(v) != end(); }
size_type count(const_reference v) const { return contains(v); }

template <class K, typename std::enable_if<!std::is_same<T, K>::value && has_is_transparent<Compare>::value,
bool>::type = true>
size_type count(const K &k) const {
return contains(k);
}

size_type erase(const_reference v) {
if (!isSmall()) {
return _set.erase(v);
Expand Down Expand Up @@ -425,7 +449,7 @@ class SmallSet {
} else {
bool small = isSmall();
for (auto oit = o._vec.begin(); oit != o._vec.end();) {
FindFunctor fFunc(key_comp(), *oit);
FindFunctor<T> fFunc(key_comp(), *oit);
if (small) {
if (std::none_of(_vec.begin(), _vec.end(), fFunc)) {
if (isSmallContFull()) {
Expand Down Expand Up @@ -529,17 +553,24 @@ class SmallSet {
return _set.insert(std::forward<V>(v));
}

template <class K>
struct FindFunctor {
FindFunctor(Compare comp, const_reference v) : _comp(comp), _v(v) {}
FindFunctor(Compare &&comp, const K &k) : _comp(std::move(comp)), _k(k) {}

bool operator()(const_reference o) const { return !_comp(_v, o) && !_comp(o, _v); }
bool operator()(const_reference o) const { return !_comp(_k, o) && !_comp(o, _k); }

Compare _comp;
const_reference _v;
const K &_k;
};

miterator mfind_small(const_reference v) {
return std::find_if(_vec.begin(), _vec.end(), FindFunctor(key_comp(), v));
template <class K>
miterator mfind_small(const K &k) {
return std::find_if(_vec.begin(), _vec.end(), FindFunctor<K>(key_comp(), k));
}

template <class K>
typename VecType::const_iterator find_small(const K &k) const {
return std::find_if(_vec.begin(), _vec.end(), FindFunctor<K>(key_comp(), k));
}

void grow() {
Expand Down
11 changes: 1 addition & 10 deletions include/amc/smallvector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,12 @@
#include <stdexcept>

#include "allocator.hpp"
#include "isdetected.hpp"
#include "hasreallocate.hpp"
#include "vectorcommon.hpp"

namespace amc {
namespace vec {

/// Implement the member detection idiom to know if allocator provides 'reallocate' method.
template <class T>
using has_reallocate_t = decltype(std::declval<T>().reallocate(
std::declval<typename T::pointer>(), std::declval<typename T::size_type>(), std::declval<typename T::size_type>(),
std::declval<typename T::size_type>()));

template <class T>
using has_reallocate = is_detected<has_reallocate_t, T>;

template <class SizeType>
inline SizeType SafeNextCapacity(SizeType oldCapa, uintmax_t newSize, bool exact) {
if (exact) {
Expand Down
9 changes: 8 additions & 1 deletion test/amc_isdetected_test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "amc/allocator.hpp"
#include "amc/hasreallocate.hpp"
#include "amc/isdetected.hpp"
#include "amc/smallvector.hpp"
#include "amc/istransparent.hpp"

namespace amc {
static_assert(vec::has_reallocate<amc::allocator<int>>::value, "amc::allocator should provide reallocate method");
Expand All @@ -9,6 +10,7 @@ static_assert(!vec::has_reallocate<std::allocator<int>>::value, "std::allocator
struct Meow {
using pointer = const char *;
using size_type = int;
using is_transparent = std::true_type;

pointer reallocate(pointer);
pointer reallocate(pointer, size_type);
Expand All @@ -30,4 +32,9 @@ static_assert(is_detected_exact<Meow &, copy_assign_t, Meow>::value, "Copy assig

static_assert(!vec::has_reallocate<Meow>::value, "Meow should not provide reallocate method");
static_assert(vec::has_reallocate<Purr>::value, "Purr should provide reallocate method");

static_assert(has_is_transparent<Meow>::value, "Meow should have is_transparent type");
static_assert(has_is_transparent<std::less<>>::value, "std::less<> should have is_transparent type");
static_assert(!has_is_transparent<std::less<int>>::value, "std::less<T> should not have is_transparent type");
static_assert(!has_is_transparent<Purr>::value, "Purr should not have is_transparent type");
} // namespace amc
35 changes: 35 additions & 0 deletions test/sets_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,41 @@ TEST(FlatSetTest, MergeDifferentCompare) {
EXPECT_EQ(s2, RevSetType({19, 4, 2, -2}));
}

#ifdef AMC_CXX14
template <typename T>
class SetListEquivalentType : public ::testing::Test {
public:
using List = typename std::list<T>;
};
// clang-format off
typedef ::testing::Types<FlatSet<int, std::less<>>
#ifdef AMC_SMALLSET
,SmallSet<int, 2, std::less<>>
,SmallSet<int, 5, std::less<>>
#endif
>
SetsEquivalentTypes;
// clang-format on

TYPED_TEST_SUITE(SetListEquivalentType, SetsEquivalentTypes, );

TYPED_TEST(SetListEquivalentType, FindEquivalentType) {
TypeParam s{1, 2, 4};
EXPECT_EQ(s.find(3), s.end());
EXPECT_NE(s.find(1), s.end());
EXPECT_EQ(s.find(4.5), s.end());
EXPECT_NE(s.find(1.0), s.end());
}

TYPED_TEST(SetListEquivalentType, ContainsEquivalentType) {
TypeParam s{-3, 0, 6, 7};
EXPECT_TRUE(s.contains(false));
EXPECT_FALSE(s.contains(true));
EXPECT_TRUE(s.contains(7ULL));
EXPECT_FALSE(s.contains(static_cast<char>(4)));
}
#endif

#ifdef AMC_SMALLSET
TEST(SmallSetTest, MergeDifferentCompare) {
using SetType = SmallSet<char, 20, std::less<char>>;
Expand Down

0 comments on commit b3866b6

Please sign in to comment.