Intrusive ref count w/ action on destruction

373 views Asked by At

I have a mixin class that makes a class reference counted that looks like this:

template<class D>
class ref_count_base
{
protected:
    ref_count_base();
    ref_count_base(ref_count_base const& x);
    ref_count_base(ref_count_base&& x);
    ref_count_base& operator=(ref_count_base const& x) &;
    ref_count_base& operator=(ref_count_base&& x) &;
    ~ref_count_base();

private:
    int* refs_;
};

// omitted constructors...

template<class D>
ref_count_base<D>::~ref_count_base()
{
    if (!refs_)
        return;

    if (1 == *refs_) {
        // C-style cast rather than static_cast allows us to call a member
        // function in the derived class even if we are a private base.
        ((D*)this)->last_ref_released();
        delete refs_;
    } else {
        --*refs_;
    }
}

This class is then used like this:

class program : private ref_count_base<program>
{
private:
    void last_ref_released()
    {
        glDeleteProgram(name_);
    }

private:
    GLuint name_;

    friend ref_count_base;
};

Obviously this doesn't work, because program is no longer alive when we reach the mixin's destructor and so we can't call last_ref_released in the derived class, but is there a similar way to accomplish this (preferrably without adding noise to the derived class)?

Edit: here is an example of client code:

class entity
{
private:
    // some other data members...
    program prog_;
};

std::vector<entity> entities_;
for (auto& i : entities_) {
    //i.bind_program();
    glUseProgram(i.get_program_name());

    // drawing code here...
}

Note that with a shared_ptr<program> instead of a program this will look like glUseProgram(i->get_program_name()).

2

There are 2 answers

6
Useless On BEST ANSWER

Edit: the confusion was that you're not trying to reference count the program class, but to reference count copies of name_ with the same value.

Given this, it seems cleaner to create something with similar semantics to shared_ptr, but for value types ... let's call it shared_value:

template <typename T, void (*Deleter)(T)>
struct shared_value {
    typedef shared_value<T,Deleter> Self;

    // whatever ctors, assignment operators etc. you need here:
    shared_value(Self const &other)
    : value_(other.value_), refs_(other.refs_) {
        ++*refs_;
    }

    ~shared_value() {
        if (resf_) {
            if (*refs_ == 1) {
                Deleter(value_);
                delete refs_;
            }
            else
                --*refs_;
        }
    }

    operator T& () { return value_; }
    operator T const& () const { return value_; }
private:
    T value_;
    mutable int *refs_;
};

and use it like:

class program {
    shared_value<GLuint, glDeleteProgram> name_;
};

You can add flexibility with a polymorphic deleter here - I've just shown the simplest thing that should work for you.


There's an easy way to do what you want, without using CRTP or mixins or touching the program class at all. Simply write it like this:

class program {
    GLuint name_;

public:
    ~program() {
        glDeleteProgram(name_);
    }
};

and then use std::shared_ptr<program> everywhere you currently use program (that is, only create one instance and then pass around shared pointers to it).

If you don't have std::shared_ptr yet, use boost::shared_ptr instead.

0
inkooboo On

As I understand program class is a thin wrapper over OpenGL program implementing RAII idiom. In such case you don't need to have special method last_ref_released() for all such refcounted objects. You just need to perform cleanup in destructor of program class.

//...
template<class D>
ref_count_base<D>::~ref_count_base()
{
    if (!refs_)
        return;

    if (1 == *refs_) {
        delete refs_;   /// cleaup performed there
    } else {
        --*refs_;
    }
}


//...
class program : private ref_count_base<program>
{
private:
    ~program ()
    {
        glDeleteProgram(name_);
    }

private:
    GLuint name_;
};