Skip to content

Instantly share code, notes, and snippets.

@OpenNingia
Last active December 21, 2016 10:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OpenNingia/bb664c35243c9d89f992a2bd305ea341 to your computer and use it in GitHub Desktop.
Save OpenNingia/bb664c35243c9d89f992a2bd305ea341 to your computer and use it in GitHub Desktop.
Implementing a smart pointer that throws when dereferencing a nullptr
#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