Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions smart-ptr/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
CXX = g++
CXXFLAGS = -std=c++23 -Wall -Wextra -g -I.

TARGET = main
SRCS = main.cpp

all: $(TARGET)

$(TARGET): $(SRCS)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SRCS)

run: $(TARGET)
./$(TARGET) $(ARGS)

clean:
rm -f $(TARGET)

.PHONY: all clean
45 changes: 45 additions & 0 deletions smart-ptr/cpp_utility.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#ifndef CPP_UTILITY_H
#define CPP_UTILITY_H

namespace utility {

// remove_reference
template <class T> struct remove_reference
{
using type = T;
};
template <class T> struct remove_reference<T&>
{
using type = T;
};
template <class T> struct remove_reference<T&&>
{
using type = T;
};

// move
template <class T>
constexpr typename remove_reference<T>::type&&
move(T&& t) noexcept
{
return static_cast<typename remove_reference<T>::type&&>(t);
}

// forward (optional, for perfect forwarding)
template <class T>
constexpr T&&
forward(typename remove_reference<T>::type& t) noexcept
{
return static_cast<T&&>(t);
}

template <class T>
constexpr T&&
forward(typename remove_reference<T>::type&& t) noexcept
{
return static_cast<T&&>(t);
}

} // namespace utility

#endif // CPP_UTILITY_H
60 changes: 60 additions & 0 deletions smart-ptr/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <iostream>
#include <cassert>
#include "smart_ptr.h"

struct Test
{
int x;
Test(int v) : x(v) { std::cout << "Test(" << x << ") constructed\n"; }
~Test() { std::cout << "Test(" << x << ") destroyed\n"; }
};

int
main()
{
std::cout << "--- Testing SharedPtr ---\\n";
{
SharedPtr<Test> sp1(new Test(10));
assert(sp1->x == 10);
assert(sp1.use_count() == 1);

{
SharedPtr<Test> sp2 = sp1;
assert(sp2->x == 10);
assert(sp1.use_count() == 2);
assert(sp2.use_count() == 2);
}
assert(sp1.use_count() == 1);
}
std::cout << "SharedPtr test passed.\n";

std::cout << "--- Testing MakeShared ---\\n";
{
SharedPtr<Test> sp = MakeShared<Test>(20);
assert(sp->x == 20);
assert(sp.use_count() == 1);
}
std::cout << "MakeShared test passed.\n";

std::cout << "--- Testing WeakPtr ---\\n";
{
SharedPtr<Test> sp = MakeShared<Test>(30);
WeakPtr<Test> wp = sp;
assert(!wp.expired());

SharedPtr<Test> sp2 = wp.lock();
assert(sp2);
assert(sp2->x == 30);
assert(sp.use_count() == 2);

sp2 = nullptr; // release one reference
assert(sp.use_count() == 1);

sp = nullptr; // release last reference
assert(wp.expired());
assert(wp.lock().get() == nullptr);
}
std::cout << "WeakPtr test passed.\n";

return 0;
}
128 changes: 128 additions & 0 deletions smart-ptr/ref_block_base.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#ifndef REF_BLOCK_BASE_H
#define REF_BLOCK_BASE_H

#include <atomic>
#include <memory>
#include <functional>
#include <cstddef>

#include "cpp_utility.h"

struct RefBlockBase
{
// atomic operation
std::atomic<size_t> m_shared_count{1}; // when init, create the shared_ptr and own it
std::atomic<size_t> m_weak_count{1}; // when init, m_shared_count as one "weak reference"

virtual ~RefBlockBase() = default;

// type erasure
virtual void
dispose_resource() = 0;
virtual void
destroy_self() = 0;
virtual void*
get_resource_ptr()
{
return nullptr;
}

// ---- thread safe counter operation ----
void
increment_shared() noexcept
{
m_shared_count.fetch_add(1, std::memory_order_relaxed);
}

void
increment_weak() noexcept
{
m_weak_count.fetch_add(1, std::memory_order_relaxed);
}

void
decrement_shared() noexcept
{
// fetch_sub return the value before minus
// use acq_rel (Acquire-Release) ensure memory safe
if (m_shared_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
dispose_resource();
decrement_weak();
}
}

void
decrement_weak() noexcept
{
if (m_weak_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
// weak counter oges 0, destroy control block
destroy_self();
}
}

bool
try_increment_shared() noexcept
{
size_t count = m_shared_count.load(std::memory_order_relaxed);

while (count != 0) {
// try to replace count with count + 1
if (m_shared_count.compare_exchange_weak(count, count + 1, std::memory_order_acq_rel)) {
return true; // success
}
}
return false;
}
};

// for 'new'
// Y is the actual type, D is the del type
template <typename Y, typename D> struct RefBlockImpl : public RefBlockBase
{
Y* m_resource;
D m_deleter;

RefBlockImpl(Y* res, D del) : m_resource(res), m_deleter(utility::move(del)) {}

void
dispose_resource() override
{
// call the deleter
m_deleter(m_resource);
}

void
destroy_self() override
{
// destroy self
delete this;
}
};

template <typename T> struct RefBlockMakeShared : public RefBlockBase
{
// T's data will followed directly after this struct
// use an aligned char array for padding
alignas(T) char m_storage[sizeof(T)];

void*
get_resource_ptr() override
{
return reinterpret_cast<T*>(m_storage);
}

void
dispose_resource() override
{
// call the deconstruct but not release the memory
reinterpret_cast<T*>(m_storage)->~T();
}

void
destroy_self() override
{
delete this;
}
};

#endif
Loading