Introduction
Visual C++ 2005 comes with a bunch of helper classes that provide support for smart pointers, synchronization support, and COM wrappers. Most of them are very handy for interop scenarios, and this article will show examples on using these classes.
The auto_handle
class
C++/CLI has syntactic support for deterministic disposal - and you can seemingly declare managed objects on the stack. Now, while this is extremely handy for most scenarios, sometimes, you end up with methods (mostly those in the BCL) that return handles to objects. This forces us to use the ^
syntax and to manually remember to call delete
once the object is not required any more. The following code shows an example of one such method.
void Demo1A()
{
StreamWriter^ sw;
try
{
sw = File::CreateText("d:\\temp.txt");
sw->WriteLine("This is a line of text");
}
finally
{
delete sw;
}
}
This is where the auto_handle
class comes in handy. Add the following #include
declaration to your code.
#include <msclr\auto_handle.h>
Now, you can rewrite the previous method as follows.
void Demo1B()
{
msclr::auto_handle<StreamWriter> sw = File::CreateText("d:\\temp.txt");
sw->WriteLine("This is a line of text");
}
The auto_handle
class overloads the ->
operator and returns the underlying handle. So we can call StreamWriter
methods just as if we were using a StreamWriter
object. And when the destructor is called, delete
is called on the underlying handle. If you need to persist the StreamWriter
beyond the present scope, you can free the handle from the auto_handle
object by calling release
on it, as shown below.
sw.release();
Other operator overloads include those on =
, !
, and bool
, so you can treat it like a pointer. And there's also a swap
helper function that swaps handles between two auto_handle
objects.
The gcroot
and auto_gcroot
classes
These two are probably quite well known and very commonly used, specially gcroot
, since it existed in the old syntax too. But for the sake of completion, I include these here too. The gcroot
template class wraps the BCL GCHandle
class and allows us to declare and use a managed object as a member of a non-CLI class. The following code snippet shows a demonstration of the use of this class.
class Demo2A
{
msclr::gcroot<StreamWriter^> m_sw;
public:
Demo2A()
{
m_sw = File::CreateText("d:\\temp.txt");
}
~Demo2A()
{
delete m_sw;
}
};
Notice how, I had to manually delete
the StreamWriter
object in the destructor. Using the auto_gcroot
class, we can actually avoid having to do that on our own. Here's a modified class that uses auto_gcroot
.
class Demo2B
{
msclr::auto_gcroot<StreamWriter^> m_sw;
public:
Demo2B()
{
m_sw = File::CreateText("d:\\temp.txt");
}
};
Well, now the destructor is gone. For most purposes, you should use the auto_gcroot
class instead of the gcroot
class, for obvious reasons. Remember to #include <msclr\gcroot.h>
or <msclr\auto_gcroot.h>
appropriately.
The _safe_bool
class
_safe_bool
is a value class
that can be used in place of bool
when providing an operator overload for bool
. It prevents an implicit conversion to any integral type. Consider the following class.
ref class Demo3A
{
public:
operator bool()
{
return true;
}
};
Now the following code would compile fine, even when it may not have been intended to do so.
Demo3A a;
int y = a;
Here's how you would rewrite the class using a _safe_bool
.
ref class Demo3B
{
public:
operator msclr::_detail_class::_safe_bool()
{
return msclr::_detail_class::_safe_true;
}
};
Now the following code will not compile.
Demo3B a;
int y = a;
You can also treat it like a bool
, for most purposes.
if(a == msclr::_detail_class::_safe_false)
{
}
Both auto_handle
and auto_gcroot
use _safe_bool
for their bool
operator overloads. You need to #include <msclr\safebool.h>
to use this class.
The ptr
template - a COM wrapper class
This class is quite handy when you need to use a COM object as a member of a CLI class. The following #include
is needed for this class.
#include <msclr\com\ptr.h>
The below code sample shows how to use the class.
ref class ManLink
{
private:
msclr::com::ptr<ILink> lnkptr;
public:
ManLink()
{
lnkptr.CreateInstance(__uuidof(Link)); }
void Resolve()
{
lnkptr->ResolveLink(. . .); }
};
The destructor will release the COM object (so you don't need to worry about it). There are several methods provided such as Attach
which allows you to associate a COM pointer with a com::ptr
object, Detach
which makes the com::ptr
object give up ownership of the COM object, and Release
which releases the COM object. Note that Detach
, increases the ref count before returning the interface pointer, and you are then responsible for releasing the object, once you are done using it.
The lock
class
C# has the lock
keyword which is internally implemented using the BCL Monitor
class. C++/CLI does not have equivalent syntactic support, but the support library includes the lock
class which (like the C# lock
keyword) internally uses the Monitor
class. You need to #include
the following header file.
#include <msclr\lock.h>
Now, here's a sample showing how the lock
class can be used.
ref class Demo4
{
public:
void DoSafeStuff()
{
msclr::lock lk(this);
Console::WriteLine("Starting work on thread {0}",
Thread::CurrentThread->ManagedThreadId);
Thread::Sleep(300); Console::WriteLine("Work ended on thread {0}",
Thread::CurrentThread->ManagedThreadId);
}
};
The lock
class also comes with methods such as is_locked
which returns true
if a lock is held, a release
method that releases the lock, an acquire
method that gets a lock (and throws an exception if the lock could not be obtained), and a try_acquire
method which is similar except that it does not throw an exception (instead it returns a bool
). The following code shows how an instance of the above class is used from multiple threads, and how try_acquire
is used to wait until all the threads are done executing.
int main(array<System::String ^> ^args)
{
Demo4 d;
msclr::lock lk(%d,msclr::lock_later); for(int i=0; i<5; i++,(
gcnew Thread(
gcnew ThreadStart(%d, &Demo4::DoSafeStuff)))->Start());
while(!lk.try_acquire(300));
Console::WriteLine("Exiting main");
return 0;
}
I think it's fantastic that C++/CLI allows us to smoothly write library implementations where a language feature does not directly exist.
Mapping delegates to non-CLI class methods
There's a delegate_proxy_factory
class declared in <msclr\event.h>
that allows you to map an event handler to a non-CLI class's member function. If you've done Windows Forms - MFC interop, then you presumably already know how to do this. But it's very useful even outside MFC Forms interop scenarios, and is also trivial to implement. Here's some code that shows how to map the FileSystemWatcher
class's Renamed
event to a native class method (albeit with managed arguments).
class Demo5
{
msclr::auto_gcroot<FileSystemWatcher^> m_fsw;
public:
BEGIN_DELEGATE_MAP(Demo5)
EVENT_DELEGATE_ENTRY(OnRenamed, Object^, RenamedEventArgs^)
END_DELEGATE_MAP()
Demo5()
{
m_fsw = gcnew FileSystemWatcher("d:\\tmp");
m_fsw->Renamed += MAKE_DELEGATE(RenamedEventHandler, OnRenamed);
m_fsw->EnableRaisingEvents = true;
}
void OnRenamed(Object^, RenamedEventArgs^ e)
{
Console::WriteLine("{0} -> {1}",e->OldName, e->Name);
}
};
Conclusion
Essentially, these support library classes, make up for missing syntactic support in C++/CLI. Why add compiler overhead, when library implementations are trivial and equally effective? As usual, feedback (both critical and otherwise) is welcome.