Skip to content

Commit

Permalink
unique: Add conversion constructors and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ArsenArsen committed Apr 10, 2023
1 parent cd20c20 commit 7494a7f
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 3 deletions.
40 changes: 37 additions & 3 deletions include/frg/unique.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#ifndef FRG_UNIQUE_HPP
#define FRG_UNIQUE_HPP

#include <type_traits>
#include <utility>
#include <concepts>

namespace frg {

Expand All @@ -19,9 +21,38 @@ struct unique_ptr {
unique_ptr(Allocator allocator, T *ptr)
:_ptr{ptr}, _allocator(std::move(allocator)) {}

/** Converts a unique_ptr of a different type to the type of this
unique_ptr.
The allocator is taken from the old unique_ptr. It must be
convertible to our allocator type.
The pointer type also, naturally, has to be convertible to the
current pointer type.
*/
template<typename U, typename UAlloc>
requires (
std::is_convertible_v<U*, T*>
&& std::is_constructible_v<Allocator, UAlloc&&>
)
unique_ptr(unique_ptr<U, UAlloc>&& o)
: _ptr { nullptr }, _allocator { std::move(o._allocator) } {
reset(o.release());
}

/** \sa unique_ptr(unique_ptr<U, UAlloc>&&) */
template<typename U, typename UAlloc>
requires std::constructible_from<unique_ptr, unique_ptr<U, UAlloc>&&>
unique_ptr& operator=(unique_ptr<U, UAlloc>&& o) {
auto optr = o.release();
reset(optr);
_allocator = std::forward<UAlloc>(o._allocator);
return *this;
}


~unique_ptr() {
if (_ptr)
_allocator.free(_ptr);
reset();
}

unique_ptr(const unique_ptr &) = delete;
Expand Down Expand Up @@ -60,7 +91,7 @@ struct unique_ptr {
return _ptr;
}

void reset(T *ptr) {
void reset(T *ptr = nullptr) {
T *old = _ptr;
_ptr = ptr;

Expand All @@ -71,6 +102,9 @@ struct unique_ptr {
private:
T *_ptr;
Allocator _allocator;

template<typename U, typename UA>
friend struct unique_ptr;
};

template <typename T, typename Allocator, typename ...Args>
Expand Down
86 changes: 86 additions & 0 deletions tests/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <frg/random.hpp>

#include <gtest/gtest.h>
#include <memory>

using string = frg::string<frg::stl_allocator>;

Expand Down Expand Up @@ -319,3 +320,88 @@ TEST(bitset, count) {
EXPECT_FALSE(a.all());
EXPECT_FALSE(a.none());
}

#include <frg/unique.hpp>

#include <unordered_set>

struct TestAlloc;

struct TestPool {
std::unordered_map<void*, std::size_t> _allocated;
std::size_t checknum = 0;

TestPool()
: _allocated {} {}

void do_check() {
ASSERT_TRUE (_allocated.empty())
<< "Memory leak?! In " << (checknum++);
}

TestPool& operator=(const TestPool&) = delete;
TestPool(const TestPool&) = delete;
TestPool& operator=(TestPool&& o) {
std::swap(_allocated, o._allocated);
return *this;
}
TestPool(TestPool&& o) {
operator=(std::move (o));
}

void* allocate(std::size_t sz) {
auto x = operator new(sz);
_allocated.emplace(x, sz);
std::cerr << "Allocated " << sz << " bytes at " << x
<< std::endl;
return x;
}

void free(void* x) {
std::cerr << "Deallocating " << x << std::endl;
auto xi = _allocated.find(x);
ASSERT_FALSE(xi == _allocated.cend())
<< "Double or invalid free of " << x;
_allocated.erase(xi);
operator delete(x);
}

TestAlloc get();
};

struct TestAlloc {
TestPool* tp;

void* allocate(std::size_t sz) {
return tp->allocate(sz);
}

void free(void* x) {
tp->free(x);
}
};

TestAlloc TestPool::get() {
return { this };
}

struct Base {};
struct Derived : Base {};

TEST(unique_ptr, construction_destruction) {
using namespace frg;
TestPool tp;

{
auto a = frg::make_unique<Base>(tp.get());
auto b = std::move(a);
a = frg::make_unique<Derived>(tp.get());
b = frg::make_unique<Derived>(tp.get());

tp.get().free(b.release());
ASSERT_FALSE(b && b.get() == nullptr);
a.reset();
ASSERT_FALSE(a && a.get() == nullptr);
};
tp.do_check();
}

0 comments on commit 7494a7f

Please sign in to comment.