Skip to content

Commit

Permalink
SafePtr: dynPtrCast->safe_cast
Browse files Browse the repository at this point in the history
  • Loading branch information
fchn289 committed Feb 27, 2025
1 parent 407e964 commit aad55ec
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 39 deletions.
14 changes: 7 additions & 7 deletions src/safe_mem/SafePtr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class SafePtr
// - can't create by SafePtr(ConstructArgs...) that confuse cp constructor, make_safe() instead
// - T(ConstructArgs) SHALL mem-safe
constexpr SafePtr(std::nullptr_t = nullptr) noexcept {}
template<typename U, typename... ConstructArgs> friend SafePtr<U> make_safe(ConstructArgs&&... aArgs);
template<typename U, typename... ConstructArgs> friend SafePtr<U> make_safe(ConstructArgs&&...);

// safe-only cast (vs shared_ptr, eg static_pointer_cast<any> is not safe)
template<typename From> SafePtr(const SafePtr<From>&) noexcept; // cp ok or compile err
Expand All @@ -52,7 +52,7 @@ class SafePtr
// no assignment, compiler will gen it & enough
template<typename To> std::shared_ptr<To> cast() const noexcept; // ret ok or null
template<typename To, typename From>
friend SafePtr<To> dynPtrCast(const SafePtr<From>&) noexcept; // ret ok or null
friend SafePtr<To> safe_cast(const SafePtr<From>&) noexcept; // ret ok or null

// safe usage: convenient(compatible shared_ptr), equivalent & min
// . ret shared_ptr is safer than T* (but not safest since to call T's func easily)
Expand Down Expand Up @@ -83,9 +83,9 @@ class SafePtr
// ***********************************************************************************************
// - safe cp to self/base/void, otherwise compile-err (by shared_ptr's cp which is safe)
// . cp/implicit-converter is useful & convenient
// - void->T: compile-err, can dynPtrCast() if need
// - void->T: compile-err, can safe_cast() if need
// . pro: simpler cp/mv constructors
// . con: not coherent as dynPtrCast(), surprise usr?
// . con: not coherent as safe_cast(), surprise usr?
// * pro: compile-err is safer than construct-null when invalid?
template<typename T>
template<typename From>
Expand Down Expand Up @@ -209,9 +209,9 @@ bool operator<(SafePtr<T> lhs, SafePtr<U> rhs)
// - cast all possible eg base->derived (more than cp constructor)
// - explicit cast so ok or nullptr (cp constructor is implicit & ok/compile-err)
// - unified-ret is predictable & simple
// - std not allow overload dynamic_pointer_cast so dynPtrCast & DYN_PTR_CAST
// - std not allow overload dynamic_pointer_cast so safe_cast & DYN_PTR_CAST
template<typename To, typename From>
rlib::SafePtr<To> dynPtrCast(const rlib::SafePtr<From>& aSafeFrom) noexcept
rlib::SafePtr<To> safe_cast(const rlib::SafePtr<From>& aSafeFrom) noexcept
{
rlib::SafePtr<To> safeTo;
safeTo.pT_ = aSafeFrom.template cast<To>();
Expand Down Expand Up @@ -278,7 +278,7 @@ struct std::hash<rlib::SafePtr<T>>
// 2024-04-17 CSZ 3)strict constructor - illegal->compile-err (while *cast() can ret null)
// 2024-05-06 CSZ - AI-gen-code
// 2024-06-28 CSZ - dynamic_pointer_cast ok or ret null
// 2024-10-16 CSZ - dynamic_pointer_cast to dynPtrCast since std not allowed
// 2024-10-16 CSZ - dynamic_pointer_cast to safe_cast since std not allowed
// 2025-02-13 CSZ 4)SafeWeak
// ***********************************************************************************************
// - Q&A
Expand Down
4 changes: 2 additions & 2 deletions src/safe_mem/UniPtr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ using UniPtr = SafePtr<void>;
#define MAKE_PTR make_safe
#define S_PTR SafePtr
#define W_PTR SafeWeak
#define DYN_PTR_CAST rlib::dynPtrCast
#define STATIC_PTR_CAST rlib::dynPtrCast
#define DYN_PTR_CAST rlib::safe_cast
#define STATIC_PTR_CAST rlib::safe_cast
#endif

} // namespace
Expand Down
56 changes: 29 additions & 27 deletions ut/safe_mem/SafePtrTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,29 +78,29 @@ struct Derive : public Base { int value() const override { return 1; } };
TEST(SafePtrTest, GOLD_safeCast_self_base_void_back)
{
auto d = make_safe<Derive>();
EXPECT_EQ(1, dynPtrCast<Derive>(d)->value()) << "REQ: cast to self";
EXPECT_EQ(1, dynPtrCast<Base >(d)->value()) << "REQ: cast to base";
EXPECT_EQ(1, safe_cast<Derive>(d)->value()) << "REQ: cast to self";
EXPECT_EQ(1, safe_cast<Base >(d)->value()) << "REQ: cast to base";

EXPECT_EQ(1, dynPtrCast<Derive>(dynPtrCast<Base>(d))->value()) << "REQ: (derived->)base->derived";
EXPECT_EQ(1, safe_cast<Derive>(safe_cast<Base>(d))->value()) << "REQ: (derived->)base->derived";

EXPECT_EQ(1, dynPtrCast<Derive>(dynPtrCast<void>(dynPtrCast<Base>(d)))->value()) << "REQ: ->void & void->origin";
EXPECT_EQ(1, dynPtrCast<Base >(dynPtrCast<void>(dynPtrCast<Base>(d)))->value()) << "REQ: ->void & void->preVoid";
EXPECT_EQ(1, safe_cast<Derive>(safe_cast<void>(safe_cast<Base>(d)))->value()) << "REQ: ->void & void->origin";
EXPECT_EQ(1, safe_cast<Base >(safe_cast<void>(safe_cast<Base>(d)))->value()) << "REQ: ->void & void->preVoid";

//EXPECT_EQ(1, dynPtrCast<Base>(dynPtrCast<void>(make_safe<Derive>())).get()) << "safe cast, but not support";
//EXPECT_EQ(1, safe_cast<Base>(safe_cast<void>(make_safe<Derive>())).get()) << "safe cast, but not support";
}
struct D_protect : protected Derive { int value() const override { return 2; } };
struct D_private : private Derive { int value() const override { return 3; } };
TEST(SafePtrTest, invalidCast_retNull)
{
EXPECT_EQ(nullptr, dynPtrCast<char >(make_safe<int >(7)).get()) << "REQ: invalid int ->char";
EXPECT_EQ(nullptr, dynPtrCast<Derive>(make_safe<Base>() ).get()) << "REQ: invalid base->derived";
EXPECT_EQ(nullptr, safe_cast<char >(make_safe<int >(7)).get()) << "REQ: invalid int ->char";
EXPECT_EQ(nullptr, safe_cast<Derive>(make_safe<Base>() ).get()) << "REQ: invalid base->derived";

//EXPECT_EQ(nullptr, dynPtrCast<Base>(make_safe<D_private>()).get()); // invalid, not ret null but compile err
//EXPECT_EQ(nullptr, dynPtrCast<Base>(make_safe<D_protect>()).get()); // invalid, not ret null but compile err
//EXPECT_EQ(nullptr, safe_cast<Base>(make_safe<D_private>()).get()); // invalid, not ret null but compile err
//EXPECT_EQ(nullptr, safe_cast<Base>(make_safe<D_protect>()).get()); // invalid, not ret null but compile err

auto msgInQ = SafePtr<void>(SafePtr<Base>(make_safe<Derive>()));
auto msgOutQ = dynPtrCast<char>(msgInQ);
EXPECT_EQ(1, dynPtrCast<Base>(msgInQ)->value ()) << "REQ: failed cast -> keep src";
auto msgOutQ = safe_cast<char>(msgInQ);
EXPECT_EQ(1, safe_cast<Base>(msgInQ)->value ()) << "REQ: failed cast -> keep src";
EXPECT_EQ(type_index(typeid(Derive)), msgInQ.realType()) << "REQ: failed cast -> keep src";
EXPECT_EQ(type_index(typeid(Base )), msgInQ.lastType()) << "REQ: failed cast -> keep src";

Expand All @@ -113,8 +113,8 @@ TEST(SafePtrTest, safe_cast_bugFix)
{
SafePtr<Base> b = make_safe<D2>(); // realType_ is D2
SafePtr<void> v = b; // lastType_ is Base
auto vv = dynPtrCast<void>(v); // bug fix for multi-void
EXPECT_EQ(2, dynPtrCast<Base>(vv)->value()) << "REQ: can cast D2->Base->void->void->Base";
auto vv = safe_cast<void>(v); // bug fix for multi-void
EXPECT_EQ(2, safe_cast<Base>(vv)->value()) << "REQ: can cast D2->Base->void->void->Base";
}

#define COPY
Expand All @@ -125,33 +125,35 @@ TEST(SafePtrTest, GOLD_safe_cp_sameType)
{
auto two(one);
EXPECT_EQ(42, *two.get()) << "REQ: valid cp & get";
EXPECT_EQ(2, one.use_count()) << "REQ: shared ownership";

*one.get() = 43;
EXPECT_EQ(43, *two.get()) << "REQ: valid update via sharing";
}
EXPECT_EQ(43, *one.get()) << "REQ: 1 del not impact another";
EXPECT_EQ(1, one.use_count()) << "REQ: ownership restored";
}
TEST(SafePtrTest, GOLD_safeCp_self_base_void)
{
auto d = make_safe<Derive>();
EXPECT_EQ(1, SafePtr<Derive>(d)->value()) << "REQ: cp to self";
EXPECT_EQ(1, SafePtr<Base >(d)->value()) << "REQ: cp to base";

EXPECT_EQ(0, dynPtrCast<Base >(SafePtr<void>(make_safe<Base >()))->value()) << "REQ: cp any->void";
EXPECT_EQ(1, dynPtrCast<Derive>(SafePtr<void>(make_safe<Derive>()))->value()) << "req: cp any->void";
EXPECT_EQ(0, safe_cast<Base >(SafePtr<void>(make_safe<Base >()))->value()) << "REQ: cp any->void";
EXPECT_EQ(1, safe_cast<Derive>(SafePtr<void>(make_safe<Derive>()))->value()) << "req: cp any->void";
}
TEST(SafePtrTest, invalidCp_compileErr) // cp's compile-err is safer than dynPtrCast that may ret nullptr
TEST(SafePtrTest, invalidCp_compileErr) // cp's compile-err is safer than safe_cast that may ret nullptr
{
//SafePtr<Derive>(SafePtr<Base>(make_safe<Derive>())); // derived->base->derive: cp compile err, can dynPtrCast instead
//SafePtr<Derive>(SafePtr<void>(make_safe<Derive>())); // void->origin: cp compile err, can dynPtrCast instead
//SafePtr<Base >(SafePtr<void>(make_safe<Derive>())); // derive->void->base: cp compile err, dynPtrCast ret nullptr
//SafePtr<Derive>(SafePtr<Base>(make_safe<Derive>())); // derived->base->derive: cp compile err, can safe_cast instead
//SafePtr<Derive>(SafePtr<void>(make_safe<Derive>())); // void->origin: cp compile err, can safe_cast instead
//SafePtr<Base >(SafePtr<void>(make_safe<Derive>())); // derive->void->base: cp compile err, safe_cast ret nullptr

//SafePtr<Derive>(make_safe<Base>()); // base->derived: cp compile-err; dynPtrCast ret nullptr
//SafePtr<Derive>(make_safe<Base>()); // base->derived: cp compile-err; safe_cast ret nullptr

//SafePtr<char>(make_safe<int>(7)); // int->char: both cp & dynPtrCast will compile err
//SafePtr<char>(make_safe<int>(7)); // int->char: both cp & safe_cast will compile err

//SafePtr<Base>(make_safe<D_private>()); // private->base: both cp & dynPtrCast will compile err
//SafePtr<Base>(make_safe<D_protect>()); // protect->base: both cp & dynPtrCast will compile err
//SafePtr<Base>(make_safe<D_private>()); // private->base: both cp & safe_cast will compile err
//SafePtr<Base>(make_safe<D_protect>()); // protect->base: both cp & safe_cast will compile err
}
TEST(SafePtrTest, GOLD_const_and_back)
{
Expand Down Expand Up @@ -193,9 +195,9 @@ TEST(SafePtrTest, GOLD_mtQ_req_mv)
EXPECT_EQ(type_index(typeid(Base)), msg.realType()) << "REQ: reset src all";
EXPECT_EQ(type_index(typeid(Base)), msg.lastType()) << "REQ: reset src all";

EXPECT_EQ(1 , dynPtrCast<Base>(msgInQ)->value() ) << "REQ: takeover msg";
EXPECT_EQ(type_index(typeid(Derive)), dynPtrCast<Base>(msgInQ).realType()) << "REQ: reset src all";
EXPECT_EQ(type_index(typeid(Base )), dynPtrCast<Base>(msgInQ).lastType()) << "REQ: reset src all";
EXPECT_EQ(1 , safe_cast<Base>(msgInQ)->value() ) << "REQ: takeover msg";
EXPECT_EQ(type_index(typeid(Derive)), safe_cast<Base>(msgInQ).realType()) << "REQ: reset src all";
EXPECT_EQ(type_index(typeid(Base )), safe_cast<Base>(msgInQ).lastType()) << "REQ: reset src all";
}
TEST(SafePtrTest, nothing_cp_mv_assign)
{
Expand Down
6 changes: 3 additions & 3 deletions ut/thread/ThreadBackTest.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ TEST_F(THREAD_BACK_TEST, GOLD_entryFnResult_toBackFn_withoutTimedWait)
// TaskBackFN
[idxThread](SafePtr<void> aRet)
{
EXPECT_EQ(idxThread % 2 != 0, *(dynPtrCast<bool>(aRet).get())) << "REQ: check true & false";
EXPECT_EQ(idxThread % 2 != 0, *(safe_cast<bool>(aRet).get())) << "REQ: check true & false";
}
));
}
Expand Down Expand Up @@ -258,7 +258,7 @@ TEST_F(THREAD_BACK_TEST, GOLD_integrate_MsgSelf_ThreadBack_MtInQueue) // simula
viaMsgSelf( // REQ: via MsgSelf
[this, &cb_info](SafePtr<void> aRet)
{
EXPECT_TRUE(*(dynPtrCast<bool>(aRet).get())) << "entryFn succ";
EXPECT_TRUE(*(safe_cast<bool>(aRet).get())) << "entryFn succ";
cb_info.emplace("REQ: a's backFn via MsgSelf");
}
)
Expand All @@ -273,7 +273,7 @@ TEST_F(THREAD_BACK_TEST, GOLD_integrate_MsgSelf_ThreadBack_MtInQueue) // simula
viaMsgSelf(
[this, &cb_info](SafePtr<void> aRet)
{
EXPECT_TRUE(*(dynPtrCast<bool>(aRet).get())) << "entryFn succ";
EXPECT_TRUE(*(safe_cast<bool>(aRet).get())) << "entryFn succ";
cb_info.emplace("REQ: 2's backFn via MsgSelf");
}
)
Expand Down

0 comments on commit aad55ec

Please sign in to comment.