Skip to content

Commit

Permalink
SafePtr: support base-derive convert
Browse files Browse the repository at this point in the history
  • Loading branch information
fchn289 committed Feb 8, 2024
1 parent 3d50cb2 commit 4d536d7
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 38 deletions.
42 changes: 27 additions & 15 deletions src/safe_mem/SafePtr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,50 @@

#include <functional>
#include <memory>
#include <iostream>
#include <type_traits>

using namespace std;

namespace RLib
{
// ***********************************************************************************************
class SafePtr
template<class T> class SafePtr
{
public:
template<typename To = void> To* get()
// create
template<class U, class... Args> friend SafePtr<U> make_safe(Args&&... aArgs);
SafePtr() = default;

// any <-> void
template<class From> SafePtr(const SafePtr<From>& aSafeFrom)
: pT_(aSafeFrom.template get<T>())
{
return (&typeid(To) == originType_ || is_same<To, void>::value)
? static_pointer_cast<To>(pT_).get()
: nullptr;
if (pT_ && is_same<T, void>::value && ! is_same<From, void>::value)
voidToType_ = &typeid(From);
}

template<typename To> shared_ptr<To> get() const
{
if (is_convertible<T*, To*>::value) // Derive -> Base
return static_pointer_cast<To>(pT_);
if (is_same<To, void>::value) // any -> void (for storing same type= SafePtr<void>)
return static_pointer_cast<To>(pT_);
if (is_same<T, void>::value && &typeid(To) == voidToType_) // void -> back
return static_pointer_cast<To>(pT_);
return nullptr;
}

const type_info* originType() const { return originType_; }
private:
// -------------------------------------------------------------------------------------------
std::shared_ptr<void> pT_;
const type_info* originType_ = nullptr; // can't static since derived from T

template<class T, class... Args> friend SafePtr make_safe(Args&&... aArgs);
std::shared_ptr<T> pT_;
const type_info* voidToType_ = nullptr; // cast void back
};

// ***********************************************************************************************
template<class T, class... Args> SafePtr make_safe(Args&&... aArgs)
template<class U, class... Args> SafePtr<U> make_safe(Args&&... aArgs)
{
SafePtr sptr;
sptr.pT_ = static_pointer_cast<void>(make_shared<T>(forward<Args>(aArgs)...));
sptr.originType_ = &typeid(T);
SafePtr<U> sptr;
sptr.pT_ = make_shared<U>(forward<Args>(aArgs)...);
return sptr;
}

Expand Down
122 changes: 99 additions & 23 deletions ut/safe_mem/SafePtrTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,117 @@

namespace RLib
{
#define NEW_CP_MV_GET
// - safe create: null & make_safe only
// - safe ship : cp/mv/= base<-derive(s); short->int?
// - safe store : cast any<->void
// - safe use : get self T*
#define CREATE_GET
// ***********************************************************************************************
TEST(SafePtrTest, GOLD_construct_get)
{
auto t1 = make_safe<int>(42);
EXPECT_EQ(42, *(t1.get<int>())) << "REQ: valid construct & get (original type)";
EXPECT_EQ(t1.get(), t1.get<int>()) << "REQ: valid get (void)";
EXPECT_EQ(nullptr, t1.get<unsigned>()) << "REQ: invalid get (diff type)";
auto one = make_safe<int>(42);
EXPECT_EQ(42, *(one.get<int>())) << "REQ: valid construct & get";

*(one.get<int>()) = 43;
EXPECT_EQ(43, *(one.get<int>())) << "REQ: valid update";

EXPECT_EQ(one.get<void>(), one.get<int>()) << "REQ: valid get (void)";
// one.get<unsigned>(); // REQ: invalid get() will fail compile
}
TEST(SafePtrTest, GOLD_cp_get)
{
SafePtr t1(make_safe<int>(42));
SafePtr t2 = t1;
EXPECT_EQ(t1.get(), t2.get<int>()) << "REQ: valid cp & get (original type)";
EXPECT_EQ(t2.get(), t2.get<int>()) << "REQ: valid get (void)";
EXPECT_EQ(nullptr, t2.get<bool>()) << "REQ: invalid get (diff type)";
auto one = make_safe<int>(42);
auto two(one);
EXPECT_EQ(42, *(two.get<int>())) << "REQ: valid cp & get";

*(one.get<int>()) = 43;
EXPECT_EQ(43, *(two.get<int>())) << "REQ: valid update via sharing";
}
TEST(SafePtrTest, GOLD_assign_get)
{
SafePtr<int> one;
EXPECT_EQ(nullptr, one.get<int>()) << "REQ: construct null";

auto two = make_safe<int>(42);
one = two;
EXPECT_EQ(42, *(one.get<int>())) << "REQ: valid assign & get";

two = SafePtr<int>();
EXPECT_EQ(nullptr, two.get<int>()) << "REQ: assign to null";
EXPECT_EQ(42, *(one.get<int>())) << "REQ: valid get after shared is reset";
}
TEST(SafePtrTest, GOLD_mv_get)
{
SafePtr t1;
EXPECT_EQ(nullptr, t1.get()) << "REQ: construct null & get it";
EXPECT_EQ(nullptr, t1.get<int>()) << "REQ: invalid get";
SafePtr<int> one;
auto two = make_safe<int>(42);
one = move(two);
EXPECT_EQ(42, *(one.get<int>())) << "REQ: valid move & get";
EXPECT_EQ(nullptr, two.get<int>()) << "REQ: move src is null";

SafePtr t2(make_safe<char>('a'));
t1 = t2;
EXPECT_EQ('a', *(t1.get<char>())) << "REQ: valid get (original type)";
SafePtr<int> three(move(one));
EXPECT_EQ(42, *(three.get<int>())) << "REQ: valid move & get";
EXPECT_EQ(nullptr, one.get<int>()) << "REQ: move src is null";
}

SafePtr t3(make_safe<bool>(true));
t1 = t3;
EXPECT_TRUE(*(t1.get<bool>())) << "REQ: valid get (original type)";
EXPECT_EQ('a', *(t2.get<char>())) << "REQ: replacement not impact original";
#define DERIVE_VOID
// ***********************************************************************************************
struct Base { virtual int value() { return 0; } };
struct Derive : public Base { int value() override { return 1; } };
struct D2 : public Derive { int value() override { return 2; } };

t3 = SafePtr();
EXPECT_EQ(nullptr, t3.get()) << "REQ: can reset SafePtr";
EXPECT_TRUE(*(t1.get<bool>())) << "REQ: lifecycle valid";
TEST(SafePtrTest, GOLD_base_get)
{
SafePtr<Base> b = make_safe<Derive>();
EXPECT_NE(nullptr, b.get<Base>()) << "REQ: Base can ptr Derive";
EXPECT_EQ(1, b.get<Base>()->value()) << "REQ: get virtual";
}
TEST(SafePtrTest, derive_to_base)
{
SafePtr<D2> d2 = make_safe<D2>();

EXPECT_EQ(2, d2.get<D2>() ->value()) << "REQ: valid & get virtual";
EXPECT_EQ(2, d2.get<Derive>()->value()) << "REQ: valid to Base direction";
EXPECT_EQ(2, d2.get<Base>() ->value()) << "REQ: valid to Base direction";
EXPECT_EQ(d2.get<void>(), d2.get<D2>()) << "REQ: valid to void";

SafePtr<Derive> d = d2;
EXPECT_EQ(2, d.get<Base>() ->value()) << "REQ: valid to Base direction";
EXPECT_EQ(2, d.get<Derive>()->value()) << "REQ: valid to self";
// d.get<D2>(); // fail to Derive direction - compile err
EXPECT_EQ(d.get<void>(), d.get<Base>()) << "REQ: valid to void";

SafePtr<Base> b = d2;
EXPECT_EQ(2, b.get<Base>() ->value()) << "REQ: valid self";
// b.get<Derive>(); // fail to Derive direction - compile err
// b.get<D2>(); // fail to Derive direction - compile err
EXPECT_EQ(b.get<void>(), b.get<Base>()) << "REQ: valid get";
}
TEST(SafePtrTest, GOLD_void_back)
{
SafePtr<D2> d2 = make_safe<D2>();
SafePtr<void> v = d2;
EXPECT_NE(nullptr, v.get<void>()) << "REQ: any -> void";
EXPECT_EQ(2, v.get<D2>()->value()) << "REQ: void back";
EXPECT_EQ(nullptr, v.get<Derive>()) << "REQ: void -> unknown";
EXPECT_EQ(nullptr, v.get<Base>()) << "REQ: void -> unknown";

SafePtr<Derive> d = d2;
v = d;
EXPECT_EQ(nullptr, v.get<D2>()) << "REQ: void -> unknown";
EXPECT_EQ(2, v.get<Derive>()->value()) << "REQ: void back";
EXPECT_EQ(nullptr, v.get<Base>()) << "REQ: void -> unknown";

SafePtr<void> v2 = v;
EXPECT_EQ(nullptr, v2.get<D2>()) << "REQ: void -> unknown";
EXPECT_EQ(2, v2.get<Derive>()->value()) << "REQ: void back";
EXPECT_EQ(nullptr, v2.get<Base>()) << "REQ: void -> unknown";
}

#define ARRAY
// ***********************************************************************************************
TEST(SafePtrTest, GOLD_construct_array)
{
make_safe<int[]>(10);
}

} // namespace

0 comments on commit 4d536d7

Please sign in to comment.