Commits

Lenard Lindstrom committed 9afc7ab

Rework memory management

The previous approach of totally encapsulating a dynamically allocated object
became ungainly. The new approach uses a class template to define class
specific managed references. A managed object is now accessed through the
overloaded `->` element access operator. Managed objects are created with
the new operator and assigned to an initial reference through its constructor:

```
#!c++
#include "imgblit/memory.hpp"
#include <cassert>

// All managed classes inherit from imgblit::Datum.
class Derived : public imgblit::Datum {

public:
Derived() : value(0) { }
int value;
};

// Class Derived specific reference DerivedRef.
typedef imgblit::ClassReference<Derived> DerivedRef;

int main()
{
// Accessing a Derived instance's value member.
DerivedRef d(new Derived);
assert(d->value == 0);
d->value = 12;
assert(d->value == 12);

// Dynamic casting ensures a imgblit::ClassReference<class D> reference
// can be assigned to, or from, a imgblit::Reference reference.
imgblit::Reference r;
DerivedRef d2;
r = d;
d2 = r;
assert(d2->value == 12);

return 0;
}
```

Comments (0)

Files changed (8)

include/imgblit/exceptions.h

-#ifndef IMGBLIT_AST_H
-#define IMGBLIT_AST_H
-
-#include "macros.h"
-#include <string>
-
-namespace imgblit {
-    class Error {
-
-    public:
-        Error(const std::string &message) : msg(message) { }
-        Error(const char *message) : msg(message) { }
-        virtual const char *what() const { return msg.c_str(); }
-    private:
-        std::string msg;
-    };
-
-    class IntegrityError : public Error {
-
-    public:
-        IntegrityError(const char *message) : Error(message) { }
-    };
-
-    class KeyError : public Error {
-
-    public:
-        KeyError(const std::string &message) : Error(message) { }
-        KeyError(const char *message) : Error(message) { }
-    };
-
-    class ValueError : public Error {
-
-    public:
-        ValueError(const std::string &message) : Error(message) { }
-        ValueError(const char *message) : Error(message) { }
-    };
-};
-
-#endif

include/imgblit/exceptions.hpp

+#ifndef IMGBLIT_EXCEPTIONS_HPP
+#define IMGBLIT_EXCEPTIONS_HPP
+
+#include "imgblit/macros.hpp"
+#include <string>
+
+namespace imgblit {
+    class Error {
+
+    public:
+        Error(const std::string &message) : msg(message) { }
+        Error(const char *message) : msg(message) { }
+        virtual const char *what() const { return msg.c_str(); }
+    private:
+        std::string msg;
+    };
+
+    class IntegrityError : public Error {
+
+    public:
+        IntegrityError(const char *message) : Error(message) { }
+    };
+
+    class KeyError : public Error {
+
+    public:
+        KeyError(const std::string &message) : Error(message) { }
+        KeyError(const char *message) : Error(message) { }
+    };
+
+    class ValueError : public Error {
+
+    public:
+        ValueError(const std::string &message) : Error(message) { }
+        ValueError(const char *message) : Error(message) { }
+    };
+};
+
+#endif

include/imgblit/macros.h

-#ifndef IMGBLIT_MACROS_H
-#define IMGBLIT_MACROS_H
-
-#define IMGBLIT_XSTR(m) IMGBLIT_STR(m)
-#define IMGBLIT_STR(v) #v
-
-#define IMGBLIT_HERE __FILE__ ":" IMGBLIT_XSTR(__LINE__)
-
-#endif

include/imgblit/macros.hpp

+#ifndef IMGBLIT_MACROS_HPP
+#define IMGBLIT_MACROS_HPP
+
+#define IMGBLIT_XSTR(m) IMGBLIT_STR(m)
+#define IMGBLIT_STR(v) #v
+
+#define IMGBLIT_HERE __FILE__ ":" IMGBLIT_XSTR(__LINE__)
+
+#endif

include/imgblit/memory.h

-#ifndef IMGBLIT_MANAGED_PTR
-#define IMGBLIT_MANAGED_PTR
-
-#include <ostream>
-
-namespace imgblit {
-    struct datum_t;
-
-    class Reference {
-    
-    public:
-        Reference();
-        Reference(const Reference &initial_ref);
-        virtual ~Reference();
-        virtual Reference &operator =(const Reference &rhs_ref);
-        virtual operator bool() const;
-        long get_ref_count() const;
-    protected:
-        friend std::ostream &operator <<(std::ostream &os,
-                                         const Reference &rhs);
-        datum_t *get_datum_p() const;
-        Reference(datum_t *initial_p);
-    private:
-        datum_t *ref_datum_p;
-    };
-};
-
-#endif

include/imgblit/memory.hpp

+#ifndef IMGBLIT_MEMORY_HPP
+#define IMGBLIT_MEMORY_HPP
+
+#include <ostream>
+#include <stdint.h>
+#include <cassert>
+
+namespace imgblit {
+    class Datum {
+        intptr_t dm_ref_count;
+        friend class Reference;
+    public:
+        Datum() : dm_ref_count(0) { }
+        virtual ~Datum() { }
+        intptr_t get_ref_count() const { return dm_ref_count; }
+        virtual void write(std::ostream &os) const;
+    };
+
+
+    class Reference {
+
+    public:
+        Reference() : ref_datum_p(0) { }
+        Reference(Datum *p) : ref_datum_p(add_ref(p)) { }
+        Reference(const Reference &other) :
+            ref_datum_p(add_ref(other.ref_datum_p)) { }
+        virtual ~Reference() { dec_ref(ref_datum_p); };
+        Datum *operator ->() const { assert(ref_datum_p); return ref_datum_p; }
+        Reference &operator =(const Reference &rvalue);
+        bool operator !() const { return ref_datum_p == 0; }
+    protected:
+        static void inc_ref(Datum *r);
+        static void dec_ref(Datum *r);
+        static Datum *add_ref(Datum *d) { inc_ref(d); return d; }
+        Datum *ref_datum_p;
+        friend class _Reference;
+    };
+
+    std::ostream &operator <<(std::ostream &os, const Reference &rhs_ref);
+
+
+    class _Reference {
+
+    public:
+        virtual ~_Reference() { }
+    protected:
+        static void inc_ref(Datum *d) { Reference::inc_ref(d); }
+        static void dec_ref(Datum *d) { Reference::dec_ref(d); }
+        static Datum *as_ptr(const Reference &r) { return r.ref_datum_p; }
+    };
+
+
+    template<class D> class ClassReference : protected _Reference {
+        D *ref_datum_p;
+    public:
+        ClassReference() : ref_datum_p(0) { }
+        ClassReference(D *p) : ref_datum_p(add_ref(p)) { }
+        ClassReference(const ClassReference<D> &other) :
+            ref_datum_p(add_ref(other.ref_datum_p)) { }
+        ClassReference(const Reference &other) :
+            ref_datum_p(add_ref(dynamic_cast<D *>(as_ptr(other)))) { }
+        virtual ~ClassReference();
+        ClassReference<D> &operator =(const ClassReference<D> &rvalue);
+        ClassReference<D> &operator =(const Reference &rvalue);
+        operator Reference() { return Reference(ref_datum_p); }
+        D *operator ->() const { assert(ref_datum_p); return ref_datum_p; }
+        bool operator !() const { return ref_datum_p == 0; }
+    protected:
+        static D *add_ref(D *d) { inc_ref(d); return d; }
+    };
+
+    template<class D>
+    ClassReference<D>::~ClassReference()
+    {
+        dec_ref(ref_datum_p);
+    }
+
+    template<class D> ClassReference<D> &
+    ClassReference<D>::operator =(const ClassReference<D> &rvalue)
+    {
+        D *prev = ref_datum_p;
+        ref_datum_p = add_ref(rvalue.ref_datum_p);
+        dec_ref(prev);
+        return *this;
+    }
+
+    template<class D> ClassReference<D> &
+    ClassReference<D>::operator =(const Reference &rvalue)
+    {
+        D *next = add_ref(dynamic_cast<D *>(as_ptr(rvalue)));
+        D *prev = ref_datum_p;
+        ref_datum_p = next;
+        dec_ref(prev);
+        return *this;
+    }
+};
+
+#endif
-#include "imgblit/memory.h"
-#include "imgblit/exceptions.h"
-#include "imgblit/internal.h"
-#include <cassert>
-#include <ostream>
-#include <string>
+#include "imgblit/memory.hpp"
+#include "imgblit/exceptions.hpp"
 
 using namespace imgblit;
 using std::ostream;
-using std::string;
 
-namespace {
-    void
-    inc_ref(datum_t *datum_p)
+
+namespace imgblit {
+    ostream &
+    operator <<(ostream &os, const Reference &rhs_ref)
     {
-        ++datum_p->d_ref_count;
+        if (!rhs_ref) {
+            return os << "Reference()";
+        }
+        rhs_ref->write(os);
+        return os;
     }
-
-    void
-    dec_ref(datum_t *datum_p)
-    {
-        if (--datum_p->d_ref_count > 0) {
-            return;
-        }
-        if (datum_p->d_ref_count < 0) {
-            throw IntegrityError(IMGBLIT_HERE);
-        }
-        delete datum_p;
-    }
-
-    datum_t *
-    add_ref(datum_t *datum_p)
-    {
-        inc_ref(datum_p);
-        return datum_p;
-    }
-
-    long
-    ref_count(datum_t *datum_p)
-    {
-        return static_cast<long>(datum_p->d_ref_count);
-    }
-
-
-    struct singleton_t : public datum_t {
-        singleton_t(const char *name) : st_name(name) { }
-        void write(ostream &os) const;
-        const string st_name;
-    private:
-        singleton_t();
-        singleton_t(const singleton_t &);
-    };
-
-    void
-    singleton_t::write(ostream &os) const
-    {
-        os << st_name;
-    }
-
-
-    class Singleton : public Reference {
-
-    public:
-        Singleton(const char *name) :
-            Reference(new singleton_t(name)) { }
-    };
-
-    static const Singleton non_reference("Reference()");
 };
 
+
 void
-datum_t::write(ostream &os) const
+Datum::write(ostream &os) const
 {
     os << "<Empty>";
 }
 
-Reference::Reference(const Reference &initial_ref) :
-    ref_datum_p(add_ref(initial_ref.ref_datum_p))
-{
-}
-
-Reference::Reference(datum_t *initial_p) :
-    ref_datum_p(add_ref(not_null_guard(initial_p)))
-{
-}
-
-Reference::~Reference()
-{
-    dec_ref(ref_datum_p);
-}
 
 Reference &
-Reference::operator =(const Reference &replacement_ref)
+Reference::operator =(const Reference &rvalue)
 {
-    datum_t *discard_p = ref_datum_p;
-    ref_datum_p = add_ref(replacement_ref.ref_datum_p);
-    dec_ref(discard_p);
+    Datum *prev = ref_datum_p;
+    ref_datum_p = add_ref(rvalue.ref_datum_p);
+    dec_ref(prev);
     return *this;
 }
 
-long
-Reference::get_ref_count() const
+void
+Reference::inc_ref(Datum *datum_p)
 {
-    return ref_count(ref_datum_p);
+    if (datum_p) {
+        ++datum_p->dm_ref_count;
+    }
 }
 
-datum_t *
-Reference::get_datum_p() const
+void
+Reference::dec_ref(Datum *datum_p)
 {
-     return ref_datum_p;
+    if (!datum_p) {
+        return;
+    }
+    if (--datum_p->dm_ref_count > 0) {
+        return;
+    }
+    if (datum_p->dm_ref_count < 0) {
+        throw IntegrityError(IMGBLIT_HERE);
+    }
+    delete datum_p;
 }
-
-Reference::Reference() :
-    ref_datum_p(add_ref(non_reference.ref_datum_p))
-{
-}
-
-Reference::operator bool() const
-{
-    return ref_datum_p != non_reference.ref_datum_p;
-}
-
-namespace imgblit {
-    ostream &
-    operator <<(ostream &os, const Reference &rhs)
-    {
-        rhs.get_datum_p()->write(os);
-        return os;
-    }
-};
 // Test the ImgBlit library reference counted memory management.
 // If all assertions pass the program exits quitely.
 
-#include "imgblit/memory.h"
-#include "imgblit/exceptions.h"
-#include "imgblit/internal.h"
+#include "imgblit/memory.hpp"
 #include <cassert>
 #include <sstream>
+#include <ostream>
 
 using namespace imgblit;
 using std::stringstream;
+using std::ostream;
+
 
 void test_delete();
 void test_ref_count();
-void test_bool_cast();
+void test_null_check();
 void test_ostream();
 
+int main()
+{
+    test_ref_count();
+    test_null_check();
+    test_delete();
+    test_ostream();
+
+    return 0;
+}
+
+
+struct Managed : public Datum {
+    Managed() : Datum() { ++alive_count; }
+    ~Managed() { --alive_count; }
+    static int alive_count;
+};
+int Managed::alive_count = 0;
+
+typedef ClassReference<Managed> ManagedRef;
+
 void
 test_delete()
 {
-    static int alive_count = 0;
-
-    struct managed_t : public datum_t {
-        managed_t() : datum_t() { ++alive_count; }
-        ~managed_t() { --alive_count; }
-    };
-
-    class DeleteCheck : public Reference {
-
-    public:
-        DeleteCheck() : Reference(new managed_t) { }
-        DeleteCheck(const Reference &initial_ref) :
-            Reference(initial_ref) { }
-    };
-
     // Test assignment.
     Reference a;
-    assert(alive_count == 0);
-    a = DeleteCheck();
-    assert(alive_count == 1);
+    assert(Managed::alive_count == 0);
+    a = ManagedRef(new Managed);
+    assert(Managed::alive_count == 1);
     a = a;
-    assert(alive_count == 1);
+    assert(Managed::alive_count == 1);
     a = Reference();
-    assert(alive_count == 0);
+    assert(Managed::alive_count == 0);
 
     // Test copy constructor.
-    Reference b = DeleteCheck();
-    assert(alive_count == 1);
+    Reference b = ManagedRef(new Managed);
+    assert(Managed::alive_count == 1);
     b = Reference();
-    assert(alive_count == 0);
+    assert(Managed::alive_count == 0);
 
     // Test Reference destructor.
     {
-        DeleteCheck d;
-        assert(alive_count == 1);
-        DeleteCheck e;
-        assert(alive_count == 2);
+        ManagedRef d(new Managed);
+        assert(Managed::alive_count == 1);
+        ManagedRef e(new Managed);
+        assert(Managed::alive_count == 2);
         a = e;
     }
-    assert(alive_count == 1);
+    assert(Managed::alive_count == 1);
     a = Reference();
-    assert(alive_count == 0);
+    assert(Managed::alive_count == 0);
 }
 
+
 void
 test_ref_count()
 {
-    class RefCountCheck : public Reference {
+    // New datum, only one reference.
+    Reference a(new Datum);
+    assert(a->get_ref_count() == 1);
 
-    public:
-        RefCountCheck() : Reference(new datum_t()) { }
-        RefCountCheck(const Reference &initial_ref) :
-            Reference(initial_ref) { }
-    };
+    // Self assignment does not alter the reference count.
+    a = a;
+    assert(a->get_ref_count() == 1);
 
-    long non_ref_rc = Reference().get_ref_count() - 1;
+    // Reference b refers to a's datum.
+    Reference b(a);
+    assert(b->get_ref_count() == 2);
+    assert(a->get_ref_count() == 2);
 
-    // The non reference datum had only one global reference, a private
-    // instance in memory.cpp.
-    assert(non_ref_rc == 1);
-
-    // Test Reference constructor and destructor.
-    {
-        Reference a;
-        assert(Reference().get_ref_count() == non_ref_rc + 2);
-    }
-    assert(Reference().get_ref_count() == non_ref_rc + 1);
-
-    // Test assignment.
-    {
-        // New datum, only one reference.
-        RefCountCheck a;
-        assert(a.get_ref_count() == 1);
-
-        // Self assignment does not alter the reference count.
-        a = a;
-        assert(a.get_ref_count() == 1);
-
-        // Reference to non_refereence.
-        Reference b;
-        assert(b.get_ref_count() == non_ref_rc + 1);
-        assert(Reference().get_ref_count() == non_ref_rc + 2);
-
-        // Reference b now refers to a's datum.
-        b = a;
-        assert(Reference().get_ref_count() == non_ref_rc + 1);
-        assert(b.get_ref_count() == 2);
-        assert(a.get_ref_count() == 2);
-
-        // Now only b refers the the datum created by RefCountCheck a.
-        a = Reference();
-        assert(b.get_ref_count() == 1);
-        assert(a.get_ref_count() == non_ref_rc + 1);
-
-        // The reference count to the RefCountCheck datum drops to 0.
-        b = Reference();
-        assert(b.get_ref_count() == non_ref_rc + 2);
-        assert(Reference().get_ref_count() == non_ref_rc + 3);
-    }
-    assert(Reference().get_ref_count() == non_ref_rc + 1);
+    // Now only b refers the the datum created by Reference a(new Datum).
+    a = Reference();
+    assert(b->get_ref_count() == 1);
 }
 
+
 void
-test_bool_cast()
+test_null_check()
 {
-    class TrueCheck : public Reference {
-
-    public:
-        TrueCheck() : Reference(new datum_t()) { }
-        TrueCheck(const Reference &initial_ref) :
-            Reference(initial_ref) { }
-    };
-
     assert(!Reference());
     Reference b;
     assert(!b);
-    TrueCheck t;
-    assert(t);
+    Reference t(new Datum);
+    assert(!!t);
     b = t;
-    assert(b);
+    assert(!!b);
     b = Reference();
     assert(!b);
 }
 
+
+class WriteSomething : public Datum {
+    void write(ostream &os) const;
+};
+
+void
+WriteSomething::write(ostream &os) const {
+    os << "<Something>";
+}
+
 void
 test_ostream()
 {
-    class WriteCheck : public Reference {
-
-    public:
-        WriteCheck() : Reference(new datum_t()) { }
-        WriteCheck(const Reference &initial_ref) :
-            Reference(initial_ref) { }
-    };
-
     stringstream capture;
     Reference r;
-    WriteCheck wc;
+    Reference ws(new WriteSomething);
 
     // Non reference.
     capture << r;
     assert(capture.str() == "Reference()");
 
-    // Class datum_t default.
+    // Class Datum default.
     capture.str("");
-    capture << WriteCheck();
+    capture << Reference(new Datum);
     assert(capture.str() == "<Empty>");
 
-    // The datum_t write method is virtual.
+    // The Datum write method is virtual.
     capture.str("");
-    r = wc;
-    capture << r;
-    assert(capture.str() == "<Empty>");
+    capture << ws;
+    assert(capture.str() == "<Something>");
 }
-
-int main()
-{
-    long rc = Reference().get_ref_count() - 1;
-    assert(rc > 0);
-
-    test_ref_count();
-    test_bool_cast();
-    test_delete();
-    test_ostream();
-
-    // The only remaining references to the non reference datum are those
-    // that existed at the start of the program.
-    assert(Reference().get_ref_count() == rc + 1);
-
-    return 0;
-}