Skip to content

Instantly share code, notes, and snippets.

@OpenNingia
Last active December 21, 2016 10:48
Show Gist options
  • Select an option

  • Save OpenNingia/bb664c35243c9d89f992a2bd305ea341 to your computer and use it in GitHub Desktop.

Select an option

Save OpenNingia/bb664c35243c9d89f992a2bd305ea341 to your computer and use it in GitHub Desktop.

Revisions

  1. OpenNingia revised this gist Dec 21, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion safe_ptr.cpp
    Original file line number Diff line number Diff line change
    @@ -110,7 +110,7 @@ TEST_CASE("safe_ptr can be copied if the container supports it", "[constructor]"
    auto sptr2 = sptr;
    REQUIRE(*sptr == *sptr2);

    // this raise a compiler error because unique_ptr is not copyable
    // this fails to compile because unique_ptr is not copyable
    // auto sptr3 = mem::make_safe_unique<std::string>("hello world!");
    // auto sptr4 = sptr3;
    }
  2. OpenNingia created this gist Dec 21, 2016.
    135 changes: 135 additions & 0 deletions safe_ptr.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,135 @@
    #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 raise a compiler error 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());
    }