I'm creating a library in C and I would like release some of the objects anytime I want. With raw pointers I couldn't notify the user that the pointer is no longer valid, with shared_ptr
I couldn't release the object if the user has her/his own shared_ptr
to it. So I decided to write my own smart pointer(ish) class. My goal was to create a class which counts the references and releases the memory if the reference count reaches 0, which is similar to shared_ptr
, however it has a destroy
method which releases the memory. Also the user can ask it whether or not the memory is still valid (or released).
The pointer_wrapper
class contains the raw pointer and the reference count. As I said earlier, it releases the raw pointer if the reference count reaches 0 or if the user calls the destroy
method.
template<class T> class pointer_wrapper {
private:
T* raw_pointer;
int32_t reference_count = 1;
public:
pointer_wrapper(T* const raw_pointer): raw_pointer(raw_pointer) { }
T* get_raw_pointer() const { return raw_pointer; }
void increase_reference_count() { reference_count ; }
void decrease_reference_count() {
reference_count--;
if(reference_count == 0) {
delete this;
}
}
int32_t get_reference_count() const { return reference_count; }
void destroy() {
if(raw_pointer != nullptr) {
delete raw_pointer;
raw_pointer = nullptr;
}
}
~pointer_wrapper() { destroy(); }
};
But the pointer_wrapper
class is just for internal use, the library's user will always get a ptr
instance. The user can copy the ptr
object but all the copied ptr
objects' pw
variable will point to the same pointer_wrapper
. This way if I call one of the ptr
objects' destroy
method, all the other ptr
objects' is_valid
method will return false
. So if the library releases an object, the user will know this if she/he calls the is_valid
method before use.
template<class T> class ptr {
private:
pointer_wrapper<T>* pw;
public:
ptr(T* const raw_pointer) { pw = new pointer_wrapper<T>(raw_pointer); }
ptr(pointer_wrapper<T>* const pw): pw(pw) { pw->increase_reference_count(); }
ptr(const ptr<T>& other_ptr) {
pw = other_ptr.pw;
pw->increase_reference_count();
}
ptr<T>& operator=(const ptr<T>& other_ptr) {
pw->decrease_reference_count();
pw = other_ptr.pw;
pw->increase_reference_count();
return *this;
}
T* operator->() const { return pw->get_raw_pointer(); }
int32_t get_reference_count() const { return pw->get_reference_count(); }
bool is_valid() const { return pw->get_raw_pointer() != nullptr; }
// the problem is probably here
template<class X> ptr<X> convert() const { return ptr<X>(reinterpret_cast<pointer_wrapper<X>*>(pw)); }
void destroy() { pw->destroy(); }
~ptr() { pw->decrease_reference_count(); }
};
The general pattern is that I have an exported interface-like class with only pure virtual methods and I have a implementation class (not exported, hidden in the dll) which inherits from the interface-like class.
static ptr<window_system> create() { return ptr<window_system>(new wm_glfw_window_system()); }
This works fine, until I try to cast it by calling the convert
method. If I call a method on a converted ptr
object, I get an error like this:
Exception thrown at 0x0000000000000000 in example_1.exe: 0xC0000005: Access violation executing location 0x0000000000000000.
ptr<window_system>(new wm_glfw_window_system())->create_window(...); // works fine
ptr<wm_glfw_window_system>(new wm_glfw_window_system())->create_window(...); // works fine
ptr<wm_glfw_window_system>(new wm_glfw_window_system()).convert<window_system>()->create_window(...); // error
ptr<window_system>(new wm_glfw_window_system()).convert<wm_glfw_window_system>()->create_window(...); // error
So I guess I have some problem with that convert
method and the reinterpret_cast
. But if I'm correct, I can't use other casts because the class ptr<window_system>
and ptr<wm_glfw_window_system>
are not related even though the window_system
and the wm_glfw_window_system
classes are related.
So my questions are:
- Is there a better way of archiving my goals (eg. a library with the appropriate pointer type)?
- Do my classes make any sense?
- How can I write my
convert
method to work properly (it has to support downcasting)?
CodePudding user response:
Don't reinterpet cast.
Replace the single pointer to a pointer wrapper with a pointer to the object and a reference counting helper.
Suppoet static, implicit and dynamic casts in the pointer to object which keep the same reference counter.
CodePudding user response:
with
shared_ptr
I couldn't release the object if the user has her/his ownshared_ptr
to it. Is there a better way of archiving my goals (eg. a library with the appropriate pointer type)?
It seems you just have to give std::weak_ptr
to your user.
If you don't want them to convert and store the std::shared_ptr
, you might wrap it in a class, something like:
template <class T> class
pointer_wrapper {
private:
std::weak_ptr<T> pointer;
public:
pointer_wrapper(std::weak_ptr<T> pointer): pointer(pointer) {}
template <typename F>
bool do_job(F f) {
if (auto p = pointer.lock()) {
f(*p);
return true;
}
return false;
}
void destroy() {
do_job([](auto& v){
// Inform your library to remove the shared_ptr.
v.release_itself();
});
}
};