Limited Publicity

I’ve got a class that needs to derive from enable_shared_from_this so that it can get a hold of shared_ptrs to itself. This class has private constructors and a static create method that returns a shared_ptr – doing otherwise is just suicidal: someone will create an object on the stack and then the object will blow up in their face the first time shared_from_this is called. Now in the create method I’d like to call make_shared for all the usual reasons (avoid extra allocations, new/delete symmetry, etc.), but there’s a problem: make_shared can’t call the class’ private constructors.

Now you could try making make_shared a friend, but your chances of succeeding seem to be heavily dependent on which compiler you’re using. This also seems like a fragile solution: the C++ standard only specifies that make_sharedmust return a shared_ptr, it says nothing about how this is done and doesn’t forbid delegation of the object creation. So there’s nothing to stop make_shared from calling another function that will then call the constructor, a function that won’t be a friend. I doubt that any implementations of make_shared actually do this, but it is the sort of thing that you need to contend with when declaring code that you don’t control as a friend (and a good reason not to do it).

We could also just make our constructors public and document that they shouldn’t be called. But documentation is easily ignored, especially in this age of intellisense, so the “asking nicely” pattern is best left as a last resort. Self-documenting code is always to be preferred, especially when the documentation is also self-enforcing. We’re going to make things public, but in a way that restricts who gets to call the public method.

There’s no official gang of four pattern name for this idiom, but in most of the places I’ve come across it the name “pass-key idiom” has been used, a name that I’ll stick with. The trick is to add a class (the key class) to the public interface of your class (let’s call it the locked class). The key class is empty except for a private default constructor and a friend declaration for the locked class. Now make the constructor of the locked class public but have it take a key object as an argument.

 class Locked
 {
 public:
    class Key
    {
    private:
       Key () {}

       friend class Locked;
    };

    Locked (Key key)
    {}      
 };

Now the constructor is only callable if you have a reference to the key class, but the locked class is the only class that can create a key class object and thus the arbiter of who can call the constructor. In the create method you authorize make_shared to call the constructor by passing it an instance of the key class. So now no one can call the constructor directly and you can still use make_shared:

shared_ptr<Locked> Locked::create()
{
    return make_shared<Locked>(Key());
}

In the above we didn’t do anything to prevent Key objects from being copied. If we had, our call to make_shared would have failed since as things stand we need to be able to copy Key objects in order to pass them through make_shared. This leaves the door open for someone to store a copy of Key and then call the locked method whenever they want, or even allow other code to call the locked method by giving out their stored Key object. To prevent this you need to add the usual copy prevention machinery to Key and change the constructor to take a const reference to a Key removing the need for a copy.

In my experience being able to copy the key is normally necessary outside of the make_shared case. In all other cases where I’ve used this idiom I have classes that I want to authorize to call the locked methods whenever they need to, so they need their own copy of the key. Another idiom that would work in this case is the attorney-client idiom, which is often confused with the pass-key idiom. But this idiom requires the class that uses the attorney class to be part of the public interface of the attorney’s client through friend declarations. The pass-key idiom doesn’t require this, so if I don’t want the classes that call the locked method to be public I prefer the pass-key idiom. There are times where attorney-client makes more sense that I’ll get into below.

There is one glaring weakness in this edifice though. It’s actually quite easy to forge your own key to the locked method: just write *(Locked::Key*)nullptr whenever you need a key. The code is extremely gross, but it will compile and run fine (since the class is empty the dereference of nullptr is a nop). There are probably ways to prevent this trick from working – most likely making things crash at run-time when shenanigans are being pulled – but I’m fine living with this loophole. Perfect security would be nice, but I feel like the technique as it stands puts up enough hurdles that clients of the class are being steered towards the intended use. Besides, code-review should catch anyone trying to pass off code like *(Locked::Key*)nullptr and bring scorn and opprobrium raining down upon their heads.

The pass-key method does require writing a bit more code than usual. There’s more than the one-line required for a friend declaration or just moving a method from the public to the private section of the interface. Instead you’ve got five to ten lines of class definition (depending on whether you want copy-protection or not). Of course, those extra lines are all boilerplate: the only line that ever changes is the friend declaration for the enclosing class. This is perfectly ripe for templating and once this is done your key declaration just becomes:

typedef KeyTemplate<Locked> Key;

I’ll leave the definition of KeyTemplate as an exercise for the reader. For extra credit you can add a second template parameter that will allow you to have multiple distinct keys in the same class for controlling access to different locked methods. For example:

 class Locked
 {
 public:
    typedef KeyTemplate<Locked> Key1;

    void requires_key1 (const Key1&);

    struct Key2Tag{};
    typedef KeyTemplate<Locked, Key2Tag> Key2;

    void requires_key2 (const Key2&);
 };

At this point we’ve got two ways to use pass-key: either we pass the key to a function that then uses that key to call one of our locked methods or we allow the key to be copied into other objects to be used later (the key being passed to the object’s constructor). Another way to use this idiom is to declare the key class in class A, then put a locked method requiring that key into class B. This creates a method in class B that only class A can call. This is useful, but it is a case where the attorney-client idiom is better suited. Using the pass-key idiom requires putting junk in the interfaces of both classes (the key in one, the method in the other), while the attorney-client idiom concentrates the junk in just one of the classes interface. A is going to be part of B’s public interface either way so we erase the reason not to use attorney-client given above. When everything is out in the open anyways then attorney-client is probably the way to go, otherwise you want pass-key.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: