Skip to content

Commit 99db2b4

Browse files
committed
unique: Add conversion constructors and tests
1 parent cd20c20 commit 99db2b4

File tree

2 files changed

+121
-3
lines changed

2 files changed

+121
-3
lines changed

include/frg/unique.hpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#ifndef FRG_UNIQUE_HPP
22
#define FRG_UNIQUE_HPP
33

4+
#include <type_traits>
45
#include <utility>
6+
#include <concepts>
57

68
namespace frg {
79

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

24+
/** Converts a unique_ptr of a different type to the type of this
25+
unique_ptr.
26+
27+
The allocator is taken from the old unique_ptr. It must be
28+
convertible to our allocator type.
29+
30+
The pointer type also, naturally, has to be convertible to the
31+
current pointer type.
32+
*/
33+
template<typename U, typename UAlloc>
34+
requires (
35+
std::is_convertible_v<U*, T*>
36+
&& std::is_constructible_v<Allocator, UAlloc&&>
37+
)
38+
unique_ptr(unique_ptr<U, UAlloc>&& o)
39+
: _ptr { nullptr }, _allocator { std::move(o._allocator) } {
40+
reset(o.release());
41+
}
42+
43+
/** \sa unique_ptr(unique_ptr<U, UAlloc>&&) */
44+
template<typename U, typename UAlloc>
45+
requires std::constructible_from<unique_ptr, unique_ptr<U, UAlloc>&&>
46+
unique_ptr& operator=(unique_ptr<U, UAlloc>&& o) {
47+
auto optr = o.release();
48+
reset(optr);
49+
_allocator = std::forward<UAlloc>(o._allocator);
50+
return *this;
51+
}
52+
53+
2254
~unique_ptr() {
23-
if (_ptr)
24-
_allocator.free(_ptr);
55+
reset();
2556
}
2657

2758
unique_ptr(const unique_ptr &) = delete;
@@ -60,7 +91,7 @@ struct unique_ptr {
6091
return _ptr;
6192
}
6293

63-
void reset(T *ptr) {
94+
void reset(T *ptr = nullptr) {
6495
T *old = _ptr;
6596
_ptr = ptr;
6697

@@ -71,6 +102,9 @@ struct unique_ptr {
71102
private:
72103
T *_ptr;
73104
Allocator _allocator;
105+
106+
template<typename U, typename UA>
107+
friend struct unique_ptr;
74108
};
75109

76110
template <typename T, typename Allocator, typename ...Args>

tests/tests.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <frg/random.hpp>
44

55
#include <gtest/gtest.h>
6+
#include <memory>
67

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

@@ -319,3 +320,86 @@ TEST(bitset, count) {
319320
EXPECT_FALSE(a.all());
320321
EXPECT_FALSE(a.none());
321322
}
323+
324+
#include <frg/unique.hpp>
325+
326+
#include <unordered_set>
327+
328+
struct TestAlloc;
329+
330+
struct TestPool {
331+
std::unordered_map<void*, std::size_t> _allocated;
332+
std::size_t checknum = 0;
333+
334+
TestPool()
335+
: _allocated {} {}
336+
337+
void do_check() {
338+
ASSERT_TRUE (_allocated.empty())
339+
<< "Memory leak?! In " << (checknum++);
340+
}
341+
342+
TestPool& operator=(const TestPool&) = delete;
343+
TestPool(const TestPool&) = delete;
344+
TestPool& operator=(TestPool&& o) {
345+
std::swap(_allocated, o._allocated);
346+
return *this;
347+
}
348+
TestPool(TestPool&& o) {
349+
operator=(std::move (o));
350+
}
351+
352+
void* allocate(std::size_t sz) {
353+
auto x = operator new(sz);
354+
_allocated.emplace(x, sz);
355+
std::cerr << "Allocated " << sz << " bytes at " << x
356+
<< std::endl;
357+
return x;
358+
}
359+
360+
void free(void* x) {
361+
std::cerr << "Deallocating " << x << std::endl;
362+
auto xi = _allocated.find(x);
363+
ASSERT_FALSE(xi == _allocated.cend())
364+
<< "Double or invalid free of " << x;
365+
_allocated.erase(xi);
366+
operator delete(x);
367+
}
368+
369+
TestAlloc get();
370+
};
371+
372+
struct TestAlloc {
373+
TestPool* tp;
374+
375+
void* allocate(std::size_t sz) {
376+
return tp->allocate(sz);
377+
}
378+
379+
void free(void* x) {
380+
tp->free(x);
381+
}
382+
};
383+
384+
TestAlloc TestPool::get() {
385+
return { this };
386+
}
387+
388+
struct Base {};
389+
struct Derived : Base {};
390+
391+
TEST(unique_ptr, construction_destruction) {
392+
using namespace frg;
393+
TestPool tp;
394+
395+
{
396+
auto a = frg::make_unique<Base>(tp.get());
397+
auto b = std::move(a);
398+
a = frg::make_unique<Derived>(tp.get());
399+
b = frg::make_unique<Derived>(tp.get());
400+
401+
tp.get().free(b.release());
402+
a.reset();
403+
};
404+
tp.do_check();
405+
}

0 commit comments

Comments
 (0)