WRL implementations of IVector and IAsyncOperation

Originally posted to Shawn Hargreaves Blog on MSDN, Monday, November 10, 2014

tl; dr

If you are developing your own Windows Runtime component using WRL, you might be interested in borrowing these implementations of standard interfaces:

More detail:

Windows Runtime components can be implemented using .NET, C++/CX, or standard C++ with WRL.  We chose WRL for the Win2D project, because it’s the lowest level option and gives the most control over every detail of the implementation.  Of course, this also means it is the hardest to work with and requires us to do the most work!

The Windows Runtime defines standard interfaces representing collections (IVector) and asynchronous computations (IAsyncOperation).  Both .NET and C++/CX provide rich language projections on top of these interfaces, mapping them to specialized APIs that make it easy to create and consume them.  WRL, not so much :-)

We looked around for existing WRL vector and async implementations that we could use in Win2D, but could not find anything that met all our requirements  (complete, robust, well tested, under a suitable open source license, and not too badly tangled up with other code).  So we rolled our own.  Each interface is implemented in a single header with no dependencies on the rest of Win2D, so we hope these will prove suitable for anyone else who finds themselves needing the same thing in future.

 

Vector.h

This header provides the class Vector<T>, which implements the Windows Runtime interfaces IVector<T>, IVectorView<T>, IIterable<T>, and IIterator<T>.  The vector can be fixed size or dynamically resizable, and tracks a dirty flag so you can efficiently check if its contents have changed.  T may be any value type, interface, or runtime class, but strings are not currently supported.

Usage example (error handling skipped for brevity):

    IFACEMETHODIMP CreateVectorOfInts(IVector<int>** returnValue)
    {
        ComPtr<Vector<int>> v = Make<Vector<int>>(initialSize, isFixedSize);

        // Access the vector using Windows Runtime interface methods.
        v->Append(42);
        v->InsertAt(0, 23);

        // Internally to your implementation module, it is also possible
        // to get direct access to the underlying STL collection.
        std::vector<int>& raw = v->InternalVector();
        std::sort(raw.begin(), raw.end());

        v.CopyTo(returnValue);
return S_OK; }

 

AsyncOperation.h

This header provides the classes AsyncOperation<T> and AsyncAction, which implement the Windows Runtime interfaces IAsyncOperation<T*> and IAsyncAction respectively.  It runs arbitrary code on the system threadpool, and reports the result or error status through standard async interfaces.  It is also possible to register one async operation to run as the continuation of another.

Example (error handling skipped for brevity) which uses an asynchronous thread pool task to add two integers:

    IFACEMETHODIMP AddAsync(int a, int b, IAsyncOperation<int>** returnValue)
    {
        auto asyncOperation = Make<AsyncOperation<int>>([=]
        {
            return a + b;
        });

        asyncOperation.CopyTo(returnValue);
        return S_OK; 
}

Even sillier example, which runs an asynchronous multiply task as the continuation of an asynchronous addition:

    IFACEMETHODIMP AddAndThenMultiplyAsync(int a, int b, int c, IAsyncOperation<int>** returnValue)
    {
        // Start the first asynchronous computation.
// This computes a + b.
ComPtr<IAsyncOperation<int>> addOperation; AddAsync(a, b, &addOperation); // Register a second asynchronous computation to run as the continuation of the first.
// This computes <previous async result> * c.
auto multiplyOperation = Make<AsyncOperation<int>>(addOperation, [=] { int addResult; addOperation->GetResults(&addResult); return addResult * c; }); multiplyOperation.CopyTo(returnValue);
return S_OK; }
Blog index   -   Back to my homepage