Pirx un[blog]ged

Mike is reading four blogs. This is none of them.

Resource Oriented Programming

Sometimes a classical OO approach fails, when actually a resource should be mapped and not an object. I would like to briefly discuss the difference between objects and resources and will present an example in C++11.

Resources are objects with some special requirements. Typical examples are IP sockets, ODBC-handles, files, GDI objects and large objects allocated on the heap. The problem is that resources, other than normal object, must not be copied. For example if you have created multiple copies of an IP socket, it's hard to tell which copy is the last and have to close the IP connection. But do not confuse resources with singletons. This is another story.

So to copy a resource is dangerous but to prevent it (by making the copy constructor private) renders this object nearly useless, because it could not be passed in any way.

To overcome such problems a typical C++ solution is to use RAII (Resource Allocation Is Initialisation) and shared pointers. Using RAII the constructor is responsible for proper resource initialization and the destructor for releasing this resource. To avoid zombie resources, the constructor should throw an exception if something is going wrong. The copy constructor and assignment operator are declared private to prevent the resource from copying. And the resource is wrapped in a shared pointer to pass it around. If the reference counter in the shared pointer reach zero the destructor is called and the resource will be released. For convenience the resource class should implement a static factory method to create the shared pointer.

// formal example, not intended to compile
template < typename R >
class resource {
public:
    // resource initialization
    resource()
    : res_(allocate< R >()) {
        if (!is_valid(res_) {
            BOOST_THROW_EXCEPTION(std::error("initialization failed"));
        }
    }

    //  release
    ~resource() {
        if (is_valid(res_)) {
            release< R >(res_);
        }
    }

    // factory
    std::shared_ptr< resource > factory() {
        return std::make_shared< resource >();
    }

private:
    //  no definition to prevent copying
    resource( resource const& );    // = delete;

private:
    R res_;
};

This works in most cases. The disadvantage of this method is the inability to transfer ownership of the resource. The resource belongs to the shared pointer. This limitation reduces the usage of such a resource class, especially in constructors. It's not possible to transfer ownership to another object which itself is wrapped in shared pointer. We get hassle with many indirections and no real control over the resource lifetime.

But C++11 move semantics come to rescue.It allows to transfer ownership and disable copying at the same time.

// formal example, not intended to compile
template < typename R >
class resource {
public:
    // resource initialization
    resource()
    : res_(allocate< R >)   {
        if (!is_valid(res_) {
            BOOST_THROW_EXCEPTION(std::error("initialization failed"));
        }
    }

    //  release
    ~resource() {
        if (is_valid(res_)) {
            release< R >(res_);
        }
    }

    // transfer ownership
    resource(resource&& other)
        : res_(std::forward< R >(other.res_) {
        // wipe out ownership
        other.res_.mark_as_invalid();
    }

private:
    R res_;
};

It is not necessary to make the copy constructor private, because the C++11 standard says, if a move constructor or move-assignment operator is explicitly declared, then no copy constructor is automatically generated.

To figure out, why is not to use the following copy constructor: resource(resource& other) remains as exercise for the reader.


comments powered by Disqus