Skip to content

Commit 2b77d2c

Browse files
authored
Merge pull request #7 from Interstellarss/smart-ptr
adding smart ptr impl
2 parents da7eb50 + bcfd89d commit 2b77d2c

File tree

5 files changed

+491
-0
lines changed

5 files changed

+491
-0
lines changed

smart-ptr/Makefile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
CXX = g++
2+
CXXFLAGS = -std=c++23 -Wall -Wextra -g -I.
3+
4+
TARGET = main
5+
SRCS = main.cpp
6+
7+
all: $(TARGET)
8+
9+
$(TARGET): $(SRCS)
10+
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SRCS)
11+
12+
run: $(TARGET)
13+
./$(TARGET) $(ARGS)
14+
15+
clean:
16+
rm -f $(TARGET)
17+
18+
.PHONY: all clean

smart-ptr/cpp_utility.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#ifndef CPP_UTILITY_H
2+
#define CPP_UTILITY_H
3+
4+
namespace utility {
5+
6+
// remove_reference
7+
template <class T> struct remove_reference
8+
{
9+
using type = T;
10+
};
11+
template <class T> struct remove_reference<T&>
12+
{
13+
using type = T;
14+
};
15+
template <class T> struct remove_reference<T&&>
16+
{
17+
using type = T;
18+
};
19+
20+
// move
21+
template <class T>
22+
constexpr typename remove_reference<T>::type&&
23+
move(T&& t) noexcept
24+
{
25+
return static_cast<typename remove_reference<T>::type&&>(t);
26+
}
27+
28+
// forward (optional, for perfect forwarding)
29+
template <class T>
30+
constexpr T&&
31+
forward(typename remove_reference<T>::type& t) noexcept
32+
{
33+
return static_cast<T&&>(t);
34+
}
35+
36+
template <class T>
37+
constexpr T&&
38+
forward(typename remove_reference<T>::type&& t) noexcept
39+
{
40+
return static_cast<T&&>(t);
41+
}
42+
43+
} // namespace utility
44+
45+
#endif // CPP_UTILITY_H

smart-ptr/main.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <iostream>
2+
#include <cassert>
3+
#include "smart_ptr.h"
4+
5+
struct Test
6+
{
7+
int x;
8+
Test(int v) : x(v) { std::cout << "Test(" << x << ") constructed\n"; }
9+
~Test() { std::cout << "Test(" << x << ") destroyed\n"; }
10+
};
11+
12+
int
13+
main()
14+
{
15+
std::cout << "--- Testing SharedPtr ---\\n";
16+
{
17+
SharedPtr<Test> sp1(new Test(10));
18+
assert(sp1->x == 10);
19+
assert(sp1.use_count() == 1);
20+
21+
{
22+
SharedPtr<Test> sp2 = sp1;
23+
assert(sp2->x == 10);
24+
assert(sp1.use_count() == 2);
25+
assert(sp2.use_count() == 2);
26+
}
27+
assert(sp1.use_count() == 1);
28+
}
29+
std::cout << "SharedPtr test passed.\n";
30+
31+
std::cout << "--- Testing MakeShared ---\\n";
32+
{
33+
SharedPtr<Test> sp = MakeShared<Test>(20);
34+
assert(sp->x == 20);
35+
assert(sp.use_count() == 1);
36+
}
37+
std::cout << "MakeShared test passed.\n";
38+
39+
std::cout << "--- Testing WeakPtr ---\\n";
40+
{
41+
SharedPtr<Test> sp = MakeShared<Test>(30);
42+
WeakPtr<Test> wp = sp;
43+
assert(!wp.expired());
44+
45+
SharedPtr<Test> sp2 = wp.lock();
46+
assert(sp2);
47+
assert(sp2->x == 30);
48+
assert(sp.use_count() == 2);
49+
50+
sp2 = nullptr; // release one reference
51+
assert(sp.use_count() == 1);
52+
53+
sp = nullptr; // release last reference
54+
assert(wp.expired());
55+
assert(wp.lock().get() == nullptr);
56+
}
57+
std::cout << "WeakPtr test passed.\n";
58+
59+
return 0;
60+
}

smart-ptr/ref_block_base.h

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#ifndef REF_BLOCK_BASE_H
2+
#define REF_BLOCK_BASE_H
3+
4+
#include <atomic>
5+
#include <memory>
6+
#include <functional>
7+
#include <cstddef>
8+
9+
#include "cpp_utility.h"
10+
11+
struct RefBlockBase
12+
{
13+
// atomic operation
14+
std::atomic<size_t> m_shared_count{1}; // when init, create the shared_ptr and own it
15+
std::atomic<size_t> m_weak_count{1}; // when init, m_shared_count as one "weak reference"
16+
17+
virtual ~RefBlockBase() = default;
18+
19+
// type erasure
20+
virtual void
21+
dispose_resource() = 0;
22+
virtual void
23+
destroy_self() = 0;
24+
virtual void*
25+
get_resource_ptr()
26+
{
27+
return nullptr;
28+
}
29+
30+
// ---- thread safe counter operation ----
31+
void
32+
increment_shared() noexcept
33+
{
34+
m_shared_count.fetch_add(1, std::memory_order_relaxed);
35+
}
36+
37+
void
38+
increment_weak() noexcept
39+
{
40+
m_weak_count.fetch_add(1, std::memory_order_relaxed);
41+
}
42+
43+
void
44+
decrement_shared() noexcept
45+
{
46+
// fetch_sub return the value before minus
47+
// use acq_rel (Acquire-Release) ensure memory safe
48+
if (m_shared_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
49+
dispose_resource();
50+
decrement_weak();
51+
}
52+
}
53+
54+
void
55+
decrement_weak() noexcept
56+
{
57+
if (m_weak_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
58+
// weak counter oges 0, destroy control block
59+
destroy_self();
60+
}
61+
}
62+
63+
bool
64+
try_increment_shared() noexcept
65+
{
66+
size_t count = m_shared_count.load(std::memory_order_relaxed);
67+
68+
while (count != 0) {
69+
// try to replace count with count + 1
70+
if (m_shared_count.compare_exchange_weak(count, count + 1, std::memory_order_acq_rel)) {
71+
return true; // success
72+
}
73+
}
74+
return false;
75+
}
76+
};
77+
78+
// for 'new'
79+
// Y is the actual type, D is the del type
80+
template <typename Y, typename D> struct RefBlockImpl : public RefBlockBase
81+
{
82+
Y* m_resource;
83+
D m_deleter;
84+
85+
RefBlockImpl(Y* res, D del) : m_resource(res), m_deleter(utility::move(del)) {}
86+
87+
void
88+
dispose_resource() override
89+
{
90+
// call the deleter
91+
m_deleter(m_resource);
92+
}
93+
94+
void
95+
destroy_self() override
96+
{
97+
// destroy self
98+
delete this;
99+
}
100+
};
101+
102+
template <typename T> struct RefBlockMakeShared : public RefBlockBase
103+
{
104+
// T's data will followed directly after this struct
105+
// use an aligned char array for padding
106+
alignas(T) char m_storage[sizeof(T)];
107+
108+
void*
109+
get_resource_ptr() override
110+
{
111+
return reinterpret_cast<T*>(m_storage);
112+
}
113+
114+
void
115+
dispose_resource() override
116+
{
117+
// call the deconstruct but not release the memory
118+
reinterpret_cast<T*>(m_storage)->~T();
119+
}
120+
121+
void
122+
destroy_self() override
123+
{
124+
delete this;
125+
}
126+
};
127+
128+
#endif

0 commit comments

Comments
 (0)