A major part of MediaLab is the “interop” layer between the managed C# code and the unmanaged C++ OpenCV layer.
This interop has some special constraints, but this post is about a more general-purpose issue between C# and C++: the try/finally
construct. In C++ there is no such thing. Instead, they leverage the “automatic” scope finalization built into the language, i.e. “smart pointers” in their many forms.
The bad news is, std::unique_ptr
and the gang do not accept ref class
types as the type parameter, e.g. if I want to make a std::unique_ptr<Windows::Foundation::SomeType^>
that does not compile.
Why is this important? In .NET there is the IDisposable
interface, responsible for resource lifecycle. Programmers may use try/finally
or the using
construct to ensure it is invoked, regardless of exceptions. Neither of these constructs is viable in the C++/CX interop world, so instead, we need to “go with the flow” and leverage the automatic scope finalization built-in.
Why is this even something to worry about? Because eventually you obtain something that implements IDisposable
and you “own” its lifecycle and must now ensure it is disposed under all circumstances!
This code is adopted from the final_act
implementation, widely used as a “finalizer”. We just wanted to use that, but it also did not compile, so we just stripped it down. In C++/CX, the operator delete
actually invokes the IDisposable
machinery. In fact, you cannot even compile a call to Dispose()
!
template <class F>
class disposer {
public:
explicit disposer(F inst) noexcept :f_(inst), invoke_(true) {};
~disposer() noexcept {
if(invoke_) delete f_;
invoke_ = false;
}
F operator->() const { return f_; }
private:
F f_;
bool invoke_;
};
As you can see, everything is hard-wired for simply invoking operator delete
on the way out. A convenience override for operator->
makes it all work like butter!
// this invokes IDispose (via delete) at end-of-scope
disposer<BitmapBuffer^> bb(sb->LockBuffer(bbam));
IMemoryBufferReference^ imbr = bb->CreateReference();
...