Skip to content

Commit a9ebda9

Browse files
adding smart ptr impl
1 parent da7eb50 commit a9ebda9

File tree

5 files changed

+424
-0
lines changed

5 files changed

+424
-0
lines changed

smart-ptr/Makefile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
clean:
13+
rm -f $(TARGET)
14+
15+
.PHONY: all clean

smart-ptr/cpp_utility.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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 {using type = T;};
8+
template <class T> struct remove_reference<T&> {using type = T;};
9+
template <class T> struct remove_reference<T&&> {using type = T;};
10+
11+
12+
// move
13+
template <class T>
14+
constexpr typename remove_reference<T>::type&& move(T&& t) noexcept{
15+
return static_cast<typename remove_reference<T>::type&&>(t);
16+
}
17+
18+
19+
// forward (optional, for perfect forwarding)
20+
template <class T>
21+
constexpr T&& forward(typename remove_reference<T>::type& t) noexcept{
22+
return static_cast<T&&>(t);
23+
}
24+
25+
template <class T>
26+
constexpr T&& forward(typename remove_reference<T>::type&& t) noexcept{
27+
return static_cast<T&&>(t);
28+
}
29+
30+
}
31+
32+
#endif //CPP_UTILITY_H

smart-ptr/main.cpp

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

smart-ptr/ref_block_base.h

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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+
12+
struct RefBlockBase {
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 dispose_resource() = 0;
21+
virtual void destroy_self() = 0;
22+
virtual void* get_resource_ptr() { return nullptr; }
23+
24+
25+
// ---- thread safe counter operation ----
26+
void increment_shared() noexcept{
27+
m_shared_count.fetch_add(1, std::memory_order_relaxed);
28+
}
29+
30+
void increment_weak() noexcept{
31+
m_weak_count.fetch_add(1, std::memory_order_relaxed);
32+
}
33+
34+
void decrement_shared() noexcept {
35+
// fetch_sub return the value before minus
36+
// use acq_rel (Acquire-Release) ensure memory safe
37+
if(m_shared_count.fetch_sub(1, std::memory_order_acq_rel) == 1){
38+
dispose_resource();
39+
decrement_weak();
40+
}
41+
}
42+
43+
void decrement_weak() noexcept{
44+
if (m_weak_count.fetch_sub(1, std::memory_order_acq_rel) == 1){
45+
// weak counter oges 0, destroy control block
46+
destroy_self();
47+
}
48+
}
49+
50+
bool try_increment_shared() noexcept {
51+
size_t count = m_shared_count.load(std::memory_order_relaxed);
52+
53+
while(count != 0){
54+
// try to replace count with count + 1
55+
if(m_shared_count.compare_exchange_weak(count, count + 1, std::memory_order_acq_rel)){
56+
return true; // success
57+
}
58+
}
59+
return false;
60+
}
61+
};
62+
63+
// for 'new'
64+
// Y is the actual type, D is the del type
65+
template <typename Y, typename D>
66+
struct RefBlockImpl : public RefBlockBase {
67+
Y* m_resource;
68+
D m_deleter;
69+
70+
RefBlockImpl(Y* res, D del)
71+
: m_resource(res), m_deleter(utility::move(del)){}
72+
73+
void dispose_resource() override {
74+
// call the deleter
75+
m_deleter(m_resource);
76+
}
77+
78+
void destroy_self() override {
79+
// destroy self
80+
delete this;
81+
}
82+
};
83+
84+
template <typename T>
85+
struct RefBlockMakeShared : public RefBlockBase{
86+
// T's data will followed directly after this struct
87+
// use an aligned char array for padding
88+
alignas(T) char m_storage[sizeof(T)];
89+
90+
void* get_resource_ptr() override{
91+
return reinterpret_cast<T*>(m_storage);
92+
}
93+
94+
void dispose_resource() override {
95+
// call the deconstruct but not release the memory
96+
reinterpret_cast<T*>(m_storage) -> ~T();
97+
}
98+
99+
void destroy_self() override{
100+
delete this;
101+
}
102+
};
103+
104+
#endif
105+

0 commit comments

Comments
 (0)