C++ : Movable but not copyable

C++11 allows an interesting new kind of classes to be created : ones that can be moved around but can’t be copied. This will be useful if one wants to have a “handle” object that manages a resource that is potentially expensive to create/open and destroy/close. C++ has often been notorious for making copies of objects behind the scenes, which often even get very quickly destroyed. (Temporary objects.) C++11 move semantics, implementable by so called R-value references, offer a solution for this.

Here’s a rough example how the DSP Dimension Dirac processing object could be wrapped into a movable but not copyable object. Dirac has a typical public C API (assumably it’s written as C++ code privately though) where an object needs to be created and promptly destroyed when no longer needed, so C++’s capabilities to implement resource managing objects comes in really handy. Pre C++11, however, this kind of an object would have been quite tricky (if not impossible) to implement in a useful way.

In the old C++98/03 standard, if one lets the copy constructor and copy assignment operator be public, lots of unnecessary creation and destruction of the underlying Dirac objects will occur under various circumstances if the code is to be semantically correct. A copied object must be a real fully usable copy, therefore a new underlying resource must be created, even if it is going to be soon thrown away.

It could be argued that it isn’t necessary to create a new resource for the copied object and some kind of machinery could be built that deals with it all so that different copied objects can hold the same underlying resource. However, this kind of design easily leads to problems where it isn’t clear what object instance will do what and what object ultimately is responsible for destroying the resources and so on. (Objects that share things can be extremely useful though, except when they are not. I will later probably write more on such things.)

In C++98/03, if the copy constructor and copy assignment are written as private methods to disable copying, like in the following code, the manager objects can’t be returned from functions or put into container classes (like std::vector). One could use the resource manager object via pointers, but now something is starting to smell a bit funny. We want to encapsulate managing a resource but then we would deal with that resource manager object itself via pointers, new and delete…Hmm. Not good, even if we would utilize smart pointers, also offered by C++11. If there’s a solution that doesn’t involve pointers in the client code, it’s almost always better. (Technically, the underlying resource pointer is available to be used from the API in the presented class, but it is to be understood by the client code that pointer should be used very carefully.)

In C++11 we can let the class have a public move constructor and a move assignment operator. These new kinds of things allow us to perform fast moving and swapping of the internal resources involved. And in turn these new methods are compatible under some desirable circumstances like when we want to place our wrapper objects into a container or return them from functions.

<code>

class DiracObject
{
public:
    DiracObject() : m_dirac(0) {}
    DiracObject(void *ctx, int numchans,float sr=44100.0, int lambda=kDiracLambdaPreview, int qual=kDiracQualityPreview)
    {
        m_dirac=DiracCreateInterleaved(lambda,qual,numchans,sr,dirac_callback_,ctx);
    }
    ~DiracObject()
    {
        if (m_dirac)
        {
            DiracDestroy(m_dirac);
        }
    }
    // move assignment, takes a rvalue reference &&
    DiracObject& operator=(DiracObject&& other)
    {
        // "other" is soon going to be destroyed, so we let it destroy our current resource instead and we take "other"'s current resource via swapping
        std::swap(m_dirac,other.m_dirac);
        return *this;
    }
    // move constructor, takes a rvalue reference &&
    DiracObject (DiracObject&& other)
    {
        // we "steal" the resource from "other"
        m_dirac=other.m_dirac;
        // "other" will soon be destroyed and its destructor will do nothing because we null out its resource here
        other.m_dirac=0;
    }
    // Returned pointer should only be used so that it isn't stored anywhere long term
    void* getDirac() const { return m_dirac; }
private:
    DiracObject(const DiracObject&); // prevent copy constructor to be used
    DiracObject& operator=(const DiracObject&); // prevent copy assignment to be used
    void* m_dirac;
};

</code>

The motivation to implement this sort of a resource managing object should become apparent when the limitations of the free Dirac LE library are explained. It does not allow more than 1 channel to be processed or allow processing parameters to be set after the object has made its first audio processing cycle. Therefore, client code using the Dirac LE library has to manage creating and destroying the objects from the library a lot. (Basically the number of processing channels multiplied by the times the user changed the processing settings.)  It is best then to have that part abstracted away with a resource manager class. C++11’s move semantics allow that class to work as efficiently as possible, without the client code having to resort to the use of pointers.

The presented class is just the first layer of abstraction to get a nicely usable C++ class to do processing with the Dirac LE library. I will soon post at some location a more complete example which includes the next layer of abstraction too.

Disclaimer : I am not endorsing DSP Dimension or Dirac in any way here, I don’t think Dirac is a particularly convenient and good way to do time stretching and pitch shifting. (Especially when using the LE version.) It only serves as an example here and might or might not ever be used in HourGlass, for example.

Advertisements
This entry was posted in C++. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s