#define CATCH_CONFIG_MAIN #include "catch.hpp" #include #include #include namespace mem { class null_dereference_exception : public std::exception { }; template> class safe_ptr { public: safe_ptr() { } safe_ptr(T* ptr) : data_(ptr) { } // to support make_safe_* safe_ptr(Container && src) : data_(std::move(src)) { } // only enabled if container supports it safe_ptr(safe_ptr && o) : data_(std::move(o.data_)) { } // only enabled if container supports it safe_ptr(safe_ptr const & o) : data_(o.data_) { } T & operator*() { throw_if_null(); return *data_; } T const & operator*() const { throw_if_null(); return *data_; } T const * operator->() const { throw_if_null(); return get(); } T * operator->() { throw_if_null(); return get(); } T* get() { return data_.get(); } T const * get() const { return data_.get(); } void reset() { data_.reset(); } operator bool() const { return (bool)data_; } private: void throw_if_null() { if ( !data_ ) throw null_dereference_exception(); } Container data_; }; template auto make_safe_unique(Args&&... a) -> safe_ptr> { return safe_ptr>(std::make_unique(std::forward(a)...)); } template auto make_safe_shared(Args&&... a) -> safe_ptr> { return safe_ptr>(std::make_shared(std::forward(a)...)); } } TEST_CASE("safe_ptr can be instantiated with a pointer", "[constructor]") { auto str = new std::string(); mem::safe_ptr sptr(str); REQUIRE(str == sptr.get()); } TEST_CASE("safe_ptr can be instantiated by moving a unique pointer", "[constructor]") { auto str = std::make_unique("hello world!"); mem::safe_ptr sptr(std::move(str)); REQUIRE(!str); REQUIRE(*sptr == std::string("hello world!")); } TEST_CASE("safe_ptr can be instantiated by moving a shared pointer", "[constructor]") { auto str = std::make_shared("hello world!"); mem::safe_ptr> sptr(std::move(str)); REQUIRE(!str); REQUIRE(*sptr == std::string("hello world!")); } TEST_CASE("safe_ptr can be instantiated with the make_safe_unique idiom", "[constructor]") { auto sptr = mem::make_safe_unique("hello world!"); REQUIRE(*sptr == std::string("hello world!")); } TEST_CASE("safe_ptr can be instantiated with the make_safe_shared idiom", "[constructor]") { auto sptr = mem::make_safe_shared("hello world!"); REQUIRE(*sptr == std::string("hello world!")); } TEST_CASE("safe_ptr can be copied if the container supports it", "[constructor]") { auto sptr = mem::make_safe_shared("hello world!"); auto sptr2 = sptr; REQUIRE(*sptr == *sptr2); // this fails to compile because unique_ptr is not copyable // auto sptr3 = mem::make_safe_unique("hello world!"); // auto sptr4 = sptr3; } TEST_CASE("safe_ptr supports simple bool operator", "[operators]") { mem::safe_ptr strp1; auto strp2 = mem::make_safe_unique("Hello world!"); REQUIRE_FALSE(strp1); REQUIRE(strp2); } TEST_CASE("safe_ptr throw exception if deferencing nullptr", "[exceptions]") { mem::safe_ptr strp1; auto strp2 = mem::make_safe_unique("Hello world!"); REQUIRE_THROWS_AS(*strp1, mem::null_dereference_exception); REQUIRE_NOTHROW(*strp2); REQUIRE_THROWS_AS(strp1->c_str(), mem::null_dereference_exception); REQUIRE_NOTHROW(strp2->c_str()); }