Last active
April 27, 2016 17:38
-
-
Save SeanCline/416e6d42e0a4c9bf8fbf15126fdd6847 to your computer and use it in GitHub Desktop.
A quick (mostly untested) go at a `clone_ptr` implementation that keeps from slicing polymorphic objects by type erasing their copy constructor and storing them in the clone_ptr.
This file contains 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
#pragma once | |
#include <memory> | |
#include <utility> | |
#include <type_traits> | |
#include <functional> // TODO: Stop using functional. | |
template <class T> | |
struct default_clone { | |
T* operator()(const T* ptr) const | |
{ | |
return new T(*ptr); | |
} | |
}; | |
template<class T> | |
class clone_ptr { | |
template <typename T2> friend class clone_ptr; | |
public: // Typedefs. | |
using pointer = T*; | |
using element_type = T; | |
using deleter_type = std::function<void(pointer)>; // TODO: Stop using functional. | |
using cloner_type = std::function<pointer(pointer)>; // TODO: Stop using functional. | |
public: // Rule of whatever. | |
template <class Cloner = default_clone<element_type>, class Deleter = std::default_delete<element_type>> | |
explicit clone_ptr(T* ptr = nullptr, Cloner cloner = default_clone<element_type>(), Deleter deleter = std::default_delete<element_type>()) | |
: ptr_(ptr), | |
deleter_(deleter), | |
cloner_(cloner) | |
{ | |
} | |
clone_ptr(const clone_ptr& other) | |
: ptr_(other.clone()), | |
deleter_(other.deleter_), | |
cloner_(other.cloner_) | |
{ | |
} | |
clone_ptr(clone_ptr&& other) | |
: ptr_(std::move(other.ptr_)), | |
deleter_(std::move(other.deleter_)), | |
cloner_(std::move(other.cloner_)) | |
{ | |
other.ptr_ = nullptr; | |
} | |
template <typename T2> | |
clone_ptr(const clone_ptr<T2>& other) | |
: ptr_(other.clone()), | |
deleter_([deleter = other.deleter_](pointer p) { return deleter(static_cast<T2*>(p)); }), | |
cloner_([cloner = other.cloner_](pointer p) { return cloner(static_cast<T2*>(p)); }) | |
{ | |
} | |
template <typename T2> | |
clone_ptr(clone_ptr<T2>&& other) | |
: ptr_(std::move(other.ptr_)), | |
deleter_([deleter = std::move(other.deleter_)](pointer p) { return deleter(static_cast<T2*>(p)); }), | |
cloner_([cloner = std::move(other.cloner_)](pointer p) { return cloner(static_cast<T2*>(p)); }) | |
{ | |
other.ptr_ = nullptr; | |
} | |
clone_ptr& operator=(const clone_ptr& other) | |
{ | |
reset(other.clone()); | |
deleter_ = other.deleter_; | |
cloner_ = other.cloner_; | |
return *this; | |
} | |
clone_ptr& operator=(clone_ptr&& other) | |
{ | |
reset(std::move(other.ptr_)); | |
deleter_ = std::move(other.deleter_); | |
cloner_ = std::move(other.cloner_); | |
other.ptr_ = nullptr; | |
return *this; | |
} | |
template <typename T2> | |
clone_ptr& operator=(clone_ptr<T2>&& other) { | |
reset(std::move(other.ptr_)); | |
deleter_ = [deleter = std::move(other.deleter_)](pointer p) { return deleter(static_cast<T2*>(p)); }; | |
cloner_ = [cloner = std::move(other.cloner_)](pointer p) { return cloner(static_cast<T2*>(p)); }; | |
other.ptr_ = nullptr; | |
return *this; | |
} | |
void swap(clone_ptr& other) | |
{ | |
swap(ptr_, other.ptr_); | |
swap(deleter_, other.deleter_); | |
swap(cloner_, other.cloner_); | |
} | |
~clone_ptr() | |
{ | |
if (ptr_) reset(); | |
} | |
public: // Indirection/access. | |
element_type& operator*() | |
{ | |
return *ptr_; | |
} | |
const element_type& operator*() const | |
{ | |
return *ptr_; | |
} | |
pointer operator->() | |
{ | |
return ptr_; | |
} | |
const pointer operator->() const | |
{ | |
return ptr_; | |
} | |
pointer get() | |
{ | |
return ptr_; | |
} | |
const pointer get() const | |
{ | |
return ptr_; | |
} | |
public: // Comparison. | |
explicit operator bool() const | |
{ | |
return ptr_ != nullptr; | |
} | |
bool operator==(const clone_ptr& other) const | |
{ | |
return ptr_ == other.ptr_; | |
} | |
bool operator!=(const clone_ptr& other) const | |
{ | |
return ptr_ != other.ptr_; | |
} | |
bool operator<(const clone_ptr& other) const | |
{ | |
return ptr_ < other.ptr_; | |
} | |
bool operator>(const clone_ptr& other) const | |
{ | |
return ptr_ > other.ptr_; | |
} | |
bool operator>=(const clone_ptr& other) const | |
{ | |
return ptr_ >= other.ptr_; | |
} | |
bool operator<=(const clone_ptr& other) const | |
{ | |
return ptr_ <= other.ptr_; | |
} | |
public: // Methods. | |
void reset(pointer ptr = pointer()) | |
{ | |
deleter_(ptr_); | |
ptr_ = ptr; | |
} | |
void release() | |
{ | |
auto old = ptr_; | |
ptr_ = nullptr; | |
return old; | |
} | |
private: | |
pointer clone() const | |
{ | |
return cloner_(ptr_); | |
} | |
private: | |
pointer ptr_; | |
deleter_type deleter_; | |
cloner_type cloner_; | |
}; | |
template<class T, class... Args> | |
clone_ptr<T> make_clone_ptr(Args&&... args) | |
{ | |
return clone_ptr<T>(new T(std::forward<Args>(args)...)); | |
} |
This file contains 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
#include "clone_ptr.h" | |
#include <iostream> | |
using namespace std; | |
struct SqualkB { | |
virtual void squalk() const | |
{ | |
cout << "SqualkB!\n"; | |
} | |
}; | |
struct SqualkD : SqualkB { | |
SqualkD() | |
{ | |
cout << "SqualkD : default ctor\n"; | |
} | |
SqualkD(const SqualkD&) | |
{ | |
cout << "SqualkD : copy ctor\n"; | |
} | |
~SqualkD() | |
{ | |
cout << "SqualkD : dtor\n"; | |
} | |
void squalk() const override | |
{ | |
cout << "SqualkD!\n"; | |
} | |
}; | |
int main() | |
{ | |
auto squalkD = make_clone_ptr<SqualkD>(); | |
auto squalkD2 = squalkD; | |
clone_ptr<SqualkB> squalkB = squalkD; | |
squalkD->squalk(); | |
squalkD2->squalk(); | |
squalkB->squalk(); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment