Skip to content

Commit

Permalink
Add comparisons and concept checks (jbcoe#29)
Browse files Browse the repository at this point in the history
Concept checks and comparisons ended up coupled as <=> needs concept checks.

For CI, use AppleClang 14 and Xcode 14 to get concept support
  • Loading branch information
jbcoe authored Sep 24, 2023
1 parent f2767b1 commit c20885a
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 19 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/cmake.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ jobs:
compiler: { type: VISUAL, version: 16, cc: "cl", cxx: "cl" },
}
- {
name: "MacOS Apple Clang 13",
os: macos-11,
name: "MacOS Apple Clang 14",
os: macos-13,
compiler:
{
type: APPLE_CLANG,
version: "13.0",
version: "14.0",
cc: "clang",
cxx: "clang++",
std: 20,
Expand All @@ -65,11 +65,11 @@ jobs:
with:
version: ${{ matrix.settings.compiler.version }}
platform: x64
- name: Select Xcode 13.0
if: matrix.config.compiler.type == 'APPLE_CLANG' && matrix.config.compiler.version == '13.0'
- name: Select Xcode 14.0
if: matrix.config.compiler.type == 'APPLE_CLANG' && matrix.config.compiler.version == '14.0'
shell: bash
run: |
sudo xcode-select -s "/Applications/Xcode_13.0.app"
sudo xcode-select -s "/Applications/Xcode_14.0.app"
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
Expand Down
48 changes: 44 additions & 4 deletions indirect.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define XYZ_INDIRECT_H

#include <cassert>
#include <compare>
#include <concepts>
#include <memory>
#include <utility>

Expand All @@ -41,7 +43,9 @@ class indirect {
using value_type = T;
using allocator_type = A;

indirect() {
indirect()
requires std::default_initializable<T>
{
T* mem = allocator_traits::allocate(alloc_, 1);
try {
allocator_traits::construct(alloc_, mem);
Expand All @@ -53,7 +57,9 @@ class indirect {
}

template <class... Ts>
indirect(std::in_place_t, Ts&&... ts) {
indirect(std::in_place_t, Ts&&... ts)
requires std::constructible_from<T, Ts&&...>
{
T* mem = allocator_traits::allocate(alloc_, 1);
try {
allocator_traits::construct(alloc_, mem, std::forward<Ts>(ts)...);
Expand All @@ -66,6 +72,7 @@ class indirect {

template <class... Ts>
indirect(std::allocator_arg_t, const A& alloc, std::in_place_t, Ts&&... ts)
requires std::constructible_from<T, Ts&&...>
: alloc_(alloc) {
T* mem = allocator_traits::allocate(alloc_, 1);
try {
Expand All @@ -77,7 +84,9 @@ class indirect {
}
}

indirect(const indirect& other) : alloc_(other.alloc_) {
indirect(const indirect& other)
requires std::copy_constructible<T>
: alloc_(other.alloc_) {
assert(other.p_ != nullptr);
T* mem = allocator_traits::allocate(alloc_, 1);
try {
Expand All @@ -98,7 +107,9 @@ class indirect {

~indirect() { reset(); }

indirect& operator=(const indirect& other) {
indirect& operator=(const indirect& other)
requires std::copy_constructible<T>
{
assert(other.p_ != nullptr);
indirect tmp(other);
swap(tmp);
Expand Down Expand Up @@ -149,6 +160,30 @@ class indirect {
swap(lhs.p_, rhs.p_);
}

friend bool operator==(const indirect& lhs, const indirect& rhs) noexcept
requires std::equality_comparable<T>
{
assert(lhs.p_ != nullptr);
assert(rhs.p_ != nullptr);
return *lhs == *rhs;
}

friend bool operator!=(const indirect& lhs, const indirect& rhs) noexcept
requires std::equality_comparable<T>
{
assert(lhs.p_ != nullptr);
assert(rhs.p_ != nullptr);
return *lhs != *rhs;
}

friend auto operator<=>(const indirect& lhs, const indirect& rhs)
requires std::three_way_comparable<T>
{
assert(lhs.p_ != nullptr);
assert(rhs.p_ != nullptr);
return *lhs <=> *rhs;
}

private:
void reset() noexcept {
if (p_ == nullptr) return;
Expand All @@ -157,9 +192,14 @@ class indirect {
p_ = nullptr;
}
};

template <class T>
concept is_hashable = requires(T t) { std::hash<T>{}(t); };

} // namespace xyz

template <class T>
requires xyz::is_hashable<T>
struct std::hash<xyz::indirect<T>> {
constexpr std::size_t operator()(const xyz::indirect<T>& key) const {
return std::hash<typename xyz::indirect<T>::value_type>{}(*key);
Expand Down
21 changes: 14 additions & 7 deletions indirect_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,15 @@ TEST(IndirectTest, Optional) {
EXPECT_EQ(**a, 42);
}

#if false // Indirect does not support == and != yet.
TEST(IndirectTest, Equality) {
xyz::indirect<int> a(std::in_place, 42);
xyz::indirect<int> b(std::in_place, 42);
xyz::indirect<int> c(std::in_place, 43);
EXPECT_EQ(a, a); // Same object.
EXPECT_NE(a, b); // Same value.
EXPECT_NE(a, c); // Different value.
EXPECT_EQ(a, a); // Same object.
EXPECT_EQ(a, b); // Same value.
EXPECT_NE(a, c); // Different value.
}
#endif // Indirect does not support == and != yet.

#if false // Indirect does not support >, >=, <, <= yet.
TEST(IndirectTest, Comparison) {
xyz::indirect<int> a(std::in_place, 42);
xyz::indirect<int> b(std::in_place, 42);
Expand Down Expand Up @@ -166,7 +163,17 @@ TEST(IndirectTest, Comparison) {
EXPECT_FALSE(c <= a);
EXPECT_TRUE(c >= a);
}
#endif // Indirect does not support >, >=, <, <= yet.

TEST(IndirectTest, ThreeWayComparison) {
xyz::indirect<int> a(std::in_place, 42);
xyz::indirect<int> b(std::in_place, 42);
xyz::indirect<int> c(std::in_place, 43);
EXPECT_EQ(a <=> a, *a <=> *a); // Same object.
EXPECT_EQ(a <=> b, *a <=> *b); // Same value.
EXPECT_EQ(b <=> a, *b <=> *a); // Same value.
EXPECT_EQ(a <=> c, *a <=> *c); // Different value.
EXPECT_EQ(c <=> a, *c <=> *a); // Different value.
}

template <typename T>
struct TrackingAllocator {
Expand Down
14 changes: 12 additions & 2 deletions polymorphic.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define XYZ_POLYMORPHIC_H_

#include <cassert>
#include <concepts>
#include <memory>
#include <utility>

Expand Down Expand Up @@ -87,7 +88,9 @@ class polymorphic {
using value_type = T;
using allocator_type = A;

polymorphic() {
polymorphic()
requires std::default_initializable<T>
{
using cb_allocator = typename std::allocator_traits<
A>::template rebind_alloc<detail::direct_control_block<T, T, A>>;
using cb_traits = std::allocator_traits<cb_allocator>;
Expand All @@ -103,7 +106,11 @@ class polymorphic {
}

template <class U, class... Ts>
polymorphic(std::in_place_type_t<U>, Ts&&... ts) {
polymorphic(std::in_place_type_t<U>, Ts&&... ts)
requires std::constructible_from<U, Ts&&...> &&
std::copy_constructible<U> &&
(std::derived_from<U, T> || std::same_as<U, T>)
{
using cb_allocator = typename std::allocator_traits<
A>::template rebind_alloc<detail::direct_control_block<T, U, A>>;
using cb_traits = std::allocator_traits<cb_allocator>;
Expand All @@ -121,6 +128,9 @@ class polymorphic {
template <class U, class... Ts>
polymorphic(std::allocator_arg_t, const A& alloc, std::in_place_type_t<U>,
Ts&&... ts)
requires std::constructible_from<U, Ts&&...> &&
std::copy_constructible<U> &&
(std::derived_from<U, T> || std::same_as<U, T>)
: alloc_(alloc) {
using cb_allocator = typename std::allocator_traits<
A>::template rebind_alloc<detail::direct_control_block<T, U, A>>;
Expand Down

0 comments on commit c20885a

Please sign in to comment.