So far we have concentrated on the 'AII!' part of RAII, so now let's look at the important bit, the R, the Resource.
Here's the class interface to our managed MIDI handle so far:
class CMidiOutHandle
{
public:
CMidiOutHandle();
~CMidiOutHandle();
private:
CMidiOutHandle(const CMidiOutHandle &);
CMidiOutHandle& operator=(const CMidiOutHandle&);
private:
HMIDIOUT m_hMidiOutHandle;
};
The constructor creates the handle, the destructor tears it down, and we prevent any copying of the class because we've decided our handle should be non-copyable. So far so good. Unfortunately, while a stunning piece of software architecture, the class as it stands is completely useless. We have encapsulated the resource a little too well.
Here's a sneak preview of the raw MIDI API function we will be calling into:
MMRESULT midiOutShortMsg(
HMIDIOUT hmo, DWORD dwMsg );
Quite selfishly, when Microsoft wrote their API, they did not consider our class. So how best to get the managed handle to the API?
When we first learn about object-oriented techniques, we are told it is all about encapsulation. And it is! We should hide our internals. But RAII is different - RAII is a special kind of object oriented technique. It encapsulates the resource management, but does not hide away the resource itself. So we're going to add a simple get function:
const HMIDIOUT GetHandle() const {return m_hMidiOutHandle;}
Let's go const
-mad. It is member function const
because we're not modifying the class, and returns a const
despite being a simple value type to emphasize the fact it is to be used only as an rvalue
. This prevents such errors as:
if(GetHandle() = null)
We can now use the class as follows:
CMidiOutHandle midiOutHandle;
midiOutShortMsg(midiOutHandle.GetHandle(), ...
Let's go one step further for ultra-convenience and define an operator:
operator HMIDIOUT() const { return GetHandle(); }
And now we can call the API as if CMidiHandle
is a HMIDIOUT
handle, which is nice:
CMidiOutHandle midiOutHandle;
midiOutShortMsg(midiOutHandle, ...
Here is our interface so far. To top it off, I have inlined the two functions mentioned above:
class CMidiOutHandle
{
public:
CMidiOutHandle();
~CMidiOutHandle();
private:
CMidiOutHandle(const CMidiOutHandle &);
CMidiOutHandle& operator=(const CMidiOutHandle &);
public:
inline const HMIDIOUT GetHandle() const;
inline operator HMIDIOUT() const;
private:
HMIDIOUT m_hMidiOutHandle;
};
The danger with returning a HMIDIOUT
like this is that someone can use this value to close the handle outside of the RAII class. This is a situation we need to handle carefully, so the next article is going to look at error handling. We are then going to move on to the very new and exciting topic of move construction and assignment!
And then, finally we might get on with writing an actual MIDI wrapper.