Wednesday, February 22, 2012

Generic constructors

C++ gives us two ways of enabling implicit type conversions - single-parameter constructors and overloaded conversion operators. Both of those, however, are limited in that they must be defined within the class that the conversion is occurring from or two. As C++ does not have the concept of partial classes this can be an undesirable limitation.

Generic constructors are a handy idiom which can be used to circumvent this limitation. A generic constructor in C++11 looks like this:

class Foo
{
public:
    Foo();

    Foo(Foo&&);

    template <typename T>
    Foo(T&& param)
    : Foo(make_foo(std::forward<T>(param))
    {
    }
};

First, a couple of requirements - either a copy or move constructor is required for this idiom to work. (This isn't a hard requirement, strictly speaking, but forwarding to the move constructor is the cleanest implementation.) A move constructor is preferable due to better performance. Second, some other sort of constructor is necessary so that it's possible to implement make_foo() functions. A default constructor works fine. Once we've defined this class, we can now define make_foo() functions for all of the implicit conversions
we'd like to take place. For example:

Foo make_foo(int i)
{
    Foo f;
    f.set_value(i);
    return std::move(f);
}

Foo make_foo(std::string s)
{
    Foo f;
    f.set_value(s);
    return std::move(f);
}

The key advantage here is that make_foo can be defined somewhere else in the code, possibly not even in the same library. If Foo is provided in one library, and Bar in another, we can define a make_foo(Bar) function which allows Foo to be implicitly constructed from Bar, even though neither class is visible to the other.

Although implicit conversions are something to be avoided in general, in particular cases this can be extremely useful. I use this pattern in polymorphic_collections to allow the various adapters (such as enumerator) to be extensible, so if you need to interface with a non-STL collections library such as Qt, it's as straightforward as writing an adapter implementation and make_enumerator function. 

Monday, February 13, 2012

Constructing static class members on-demand

A little pattern I like to use when I have resources shared among all instances of a class which should only be constructed when an object of the class is instantiated (and destroyed with the last object):

class Foo
{
private:
    struct SharedProperties
    {
        std::unique_ptr baz;
    };
    static std::weak_ptr<SharedProperties> m_weakShared;
    std::shared_ptr<SharedProperties> m_shared;
};

Foo::Foo()
{
    if (!m_weakShared.expired())
    {
        m_shared = m_weakShared.lock();
    }
    else
    {
        m_shared = std::make_shared<SharedProperties>();
        m_weakShared = std::weak_ptr<SharedProperties>(m_shared);

        // Initialize the shared properties here
        m_shared->baz.reset(new Baz());
    }
}

Introducing polymorphic_collections: enumerator

Polymorphic_collections is a C++ library which provides a set of wrapper objects which expose an interface for performing operations on a collection without specifying the underlying type of the collection. For example, enumerator<> is an adapter which allows a collection to be enumerated through:

int sum(enumerator<int> e)
{
     int result = 0;
     while (auto i = e.next())
     {
      // Note: i is a boost::optional<int&>, so we need to dereference it to get
      // at the value.
      result += *i;
     }
}

We can then call this function using enumerator<> to wrap a variety of collection types:

std::vector<int> v;
sum(v);

std::list<int> l;
sum(l);

int a[10];
sum(a);

Using enumerator<> allows some of the same flexibility in design that templates provide, but in locations where it would be impossible or unfeasible to use templates. For example, virtual functions:

struct Foo
{
     virtual void Baz(enumerator<int> e) = 0;
};

Enumerator is an extremely simple interface. It exposes only a single method, next(). The return type of next() is boost::optional<T&>, where T is enumerator's template parameter. When the enumerator has exhausted the collection, it will return an empty optional object. There is no mechanism for resetting an enumerator, retrieving an object without advancing the enumeration, or traversing the collection in any other way. Enumerators cannot be copied, but they can be moved.

Enumerators can wrap collections of a different type, so long as a reference of the enumerator's type can be taken of the collection's type. For example:

std::vector<int> v;
enumerator<const int> e1 = v; // Okay, const int& = int is allowed
enumerator<float> e2 = v; // Error, float& = int is not allowed

struct Base { };
struct Derived : public Base { };
std::vector<Derived> v;
enumerator<Base> e = v; // Okay, Base& = Derived is allowed

Enumerators normally allow modification of the objects within the collection. You can prevent this by specifying a const type as the enumerator's template parameter.

std::vector<int> v;
enumerator<int> e = v;
*e.next() = 1;        // Equivalent to *v.begin() = 1

enumerator<const int> e = v;
*e.next() = 1;        // Error, can't assign to const int&

Normally, an enumerator contains only a reference to the underlying collection. If assigned to an rvalue, the enumerator can embed the collection in itself. This is handy for returning enumerators from functions:

enumerator<int> foo()
{
     std::vector<int> v;
     v.push_back(0);
     v.push_back(1);
     v.push_back(2);
     return std::move(v);      // Note: without the std::move, it will return an enumerator
            // referencing a temporary, which will crash.
}

Any STL-compatible collection exposing an iterator-based interface can be embedded in an enumerator.

Enumerators can encapsulate pointers with the size-specified at runtime, via the make_enumerator function:

int ar[5];
int* ptr = ar;
enumerator<int> e = make_enumerator(ptr, sizeof(ar) / sizeof(ar[0]));

Enumerators can wrap functions and function-like objects:

int a = 0, b = 1;
enumerator<int> fibo = [&] () -> boost::optional<int>
{
     int r = b + a;
     a = b;
     b = r;
     return r;
}

In order to be wrapped by enumerator, the callable object must return either a boost::optional<T> or a boost::optional<T&>. If it returns a value, then next() will return a reference to a temporary which will be destroyed on the following call to next().

Enumerators can be made thread-safe by specifying a locking policy via a template parameter:

void worker(enumerator<Task, atomic>& tasks)
{
     while (auto task = tasks.next())
     {
      // Perform task...
     }
}

void manager(enumerator<Task> tasks)
{
     // Create a locking enumerator from a non-locking one.
     // As enumerators cannot be copied, this requires consuming the
     // original enumerator.
     enumerator<Task, atomic> atomicTasks = std::move(tasks);
     
     for (size_t i = 0; i < THREAD_LIMIT; i++)
     {
      spawn([] () { worker(atomicTasks); });
     }
}

The atomic policy inserts a mutex lock into every call to next(). Because of its simple interface design, it is extremely easy to write thread-safe code using enumerator.

Sunday, February 5, 2012

Using intrusive_ptr with COM

Writing COM code in C++ can be greatly simplified by using a reference-counting smart pointers. Microsoft provides an implementation in the ATL (CComPtr), however I typically don't use ATL in my projects and aren't interested in introducing it as a dependency just for the sake of a single utility class. Boost provides a lightweight reference-counting smart pointer in the form of intrusive_ptr, although it takes a few tweaks to get it to play nice with COM. First, we need to write a couple of functions to tell intrusive_ptr how to interface with COM objects. Since all COM objects are derived from IUnknown, which also defines the reference-counting interface, this is trivial:

inline void intrusive_ptr_add_ref(IUnknown* ptr)
{
    ptr->AddRef();
}

inline void intrusive_ptr_release(IUnknown* ptr)
{
    ptr->Release();
}

With these defined, intrusive_ptr will play nice with COM objects. However, getting them into the intrusive_ptr is a little trickier. The standard technique for COM functions is to take as a parameter a pointer-to-pointer, which, if the operation is successful, will be assigned to the address of the object being created or acquired. Also, incrementing the object's reference count is handled by the callee, not the caller. This behavior is the opposite of what intrusive_ptr expects. Both problems can be solved via a small helper class and function:

template <typename T>
class WrapPtr
{
public:
    WrapPtr(boost::intrusive_ptr<T>& ref)
    : m_ref(ref), m_ptr(0)
    {
    }
   
    ~WrapPtr()
    {
        // The second parameter indicates that the reference count should not be incremented
        m_ref = boost::intrusive_ptr(m_ptr, false);
    }
   
    operator T**()
    {
        return &m_ptr;
    }
   
    operator void**()
    {
        // Some COM functions ask for a pointer to void pointer, such as QueryInterface
        return reinterpret_cast<void**>(&m_ptr);
    }
   
private:
    T* m_ptr;
    boost::intrusive_ptr<T> m_ref;
};

template <typename T>
WrapPtr<T> AttachPtr(boost::intrusive_ptr<T>& ref)
{
    return WrapPtr<T>(ref);
}

The WrapPtr class acts as a proxy for the intrusive_ptr object when calling COM functions. It is designed to be used in the following manner:

D3D11_TEXTURE3D_DESC desc;
boost::intrusive_ptr<ID3D11Texture3D> texture;
HRESULT hr = m_d3dDevice->CreateTexture3D(&desc, NULL, AttachPtr(texture));

Using intrusive_ptr for RAII vastly reduces the verbosity involved in interfacing with COM objects, and makes it much easier to write exception safe code.