Last active
December 21, 2016 10:48
-
-
Save OpenNingia/bb664c35243c9d89f992a2bd305ea341 to your computer and use it in GitHub Desktop.
Implementing a smart pointer that throws when dereferencing a nullptr
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define CATCH_CONFIG_MAIN | |
#include "catch.hpp" | |
#include <memory> | |
#include <utility> | |
#include <exception> | |
namespace mem { | |
class null_dereference_exception : public std::exception { }; | |
template<typename T, typename Container = std::unique_ptr<T>> | |
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<typename T, typename... Args> | |
auto make_safe_unique(Args&&... a) -> safe_ptr<T, std::unique_ptr<T>> { | |
return safe_ptr<T, std::unique_ptr<T>>(std::make_unique<T>(std::forward<Args>(a)...)); | |
} | |
template<typename T, typename... Args> | |
auto make_safe_shared(Args&&... a) -> safe_ptr<T, std::shared_ptr<T>> { | |
return safe_ptr<T, std::shared_ptr<T>>(std::make_shared<T>(std::forward<Args>(a)...)); | |
} | |
} | |
TEST_CASE("safe_ptr can be instantiated with a pointer", "[constructor]") { | |
auto str = new std::string(); | |
mem::safe_ptr<std::string> sptr(str); | |
REQUIRE(str == sptr.get()); | |
} | |
TEST_CASE("safe_ptr can be instantiated by moving a unique pointer", "[constructor]") { | |
auto str = std::make_unique<std::string>("hello world!"); | |
mem::safe_ptr<std::string> 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<std::string>("hello world!"); | |
mem::safe_ptr<std::string, std::shared_ptr<std::string>> 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<std::string>("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<std::string>("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<std::string>("hello world!"); | |
auto sptr2 = sptr; | |
REQUIRE(*sptr == *sptr2); | |
// this fails to compile because unique_ptr is not copyable | |
// auto sptr3 = mem::make_safe_unique<std::string>("hello world!"); | |
// auto sptr4 = sptr3; | |
} | |
TEST_CASE("safe_ptr supports simple bool operator", "[operators]") { | |
mem::safe_ptr<std::string> strp1; | |
auto strp2 = mem::make_safe_unique<std::string>("Hello world!"); | |
REQUIRE_FALSE(strp1); | |
REQUIRE(strp2); | |
} | |
TEST_CASE("safe_ptr throw exception if deferencing nullptr", "[exceptions]") { | |
mem::safe_ptr<std::string> strp1; | |
auto strp2 = mem::make_safe_unique<std::string>("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()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment