What's This Do?

Programming etc.

Three Fun C++ Techniques

1. The Pimpl Idiom

Frankly, if you don’t know about the Pimpl idiom, you’re not a real C++ programmer. Pimpl stands for “Pointer to implementation”, sometimes referred to as the Cheshire Cat. It’s a way to hide the implementation of a class from the user by defining all private members in the class definition (.cpp) file.

Here is an example:

//car.hpp
class car {
public:
    car();                          //constructor
    car(const car &c);              //copy constructor
    car &operator=(const car &rhs); //copy assignment operator
    car(const car &&c);             //move constructor      
    car &operator=(const car &&rhs);//move assignment operator
    /* Other public methods */

private:
    class under_the_hood; //Not defined here
    std::shared_ptr<under_the_hood> pImpl; //POINTER TO IMPLEMENTATION
};
//car.cpp

class car::under_the_hood {
public:
    /* Implement car stuff here */
};

car::car() : pImpl(std::make_shared<under_the_hood>()) {}

car::car(const car &c) : pImpl(std::make_shared<under_the_hood>(*c.pImpl)) {}

car &car::operator=(const car &rhs) {
    pImpl = std::make_shared<under_the_hood>(*rhs.pImpl);
    return *this;
}

car::car(const car &&c) : pImpl(c.pImpl) {}

car &car::operator=(const car &&rhs) {
    pImpl = rhs.pImpl;
    return *this;
}

This idiom has quite a few advantages, especially when authoring libraries. Firstly, you can maintain secrecy regarding your implementation; the client gets the public interface to your class and can use it freely without being able to see the gritty details of how it works “under the hood”.

Secondly, the client gets a compile-time performance increase by not having to parse the private declarations of your class; it is all safely hidden in the implementation file.

Another advantage of the Pimpl idiom is that it allows you to use third-party, header-only libraries in your implementation without requiring that the client has access to these same libraries. A great example is the use of Boost. If you want to use any of the Boost header-only libraries, like Boost.Signals2 or Boost.Geometry, then great! You should use Boost where it’s appropriate. But what if you don’t want your users to require Boost in order to use your library? Easy; use the Pimpl idiom and hide all of your private Signals or Polygons or what have you in the implementation file. Your user need never know. Well, actually they do need to know if you’re using open source libraries, but you get my point.

2. The Curiously Recurring Template Pattern

The Curiously Recurring Template Pattern, or CRTP, is a C++ pattern where a class derives from a template base class, using itself as the template argument:

template <typename T>
class base { };

class derived : public base<derived> { };

There are many uses for this pattern; one interesting one is static polymorphism. Here is a futuristic example to demonstrate:

Suppose teleportation devices have finally been invented, and you want to write an application that teleports things from one computer to another. To do this, both machines require your application to be installed. It’s kind of like Drop Box, but for physical objects. Now, you want this application to be cross-platform; Windows and Linux users should all be able to send items to each other. As usual, the two OSs implement teleportation in very different ways.

The obvious solution is to create a pure virtual base class for teleportation, with Windows and Linux implementations. You can wrap each implementation in #if macros to ensure it won’t be defined on the wrong platform.

class teleporter {};

#ifdef MSVC
class win_teleporter : public teleporter {};
#else
class lin_teleporter : public teleporter {};
#endif


static std::shared_ptr<teleporter> make_teleporter() { 
#ifdef MSVC
    return std::make_shared<win_teleporter>();
#else
    return std::make_shared<lin_teleporter>();
#endif
}

This would work fine, except you now have overhead tied up in dynamic polymorphism (VTBL structures etc.) which you don’t actually need. On each platform, there is only one implementation of teleporter so you simply don’t need to decide which one to use at run time. You can decide at compile time: static polymorphism.

template <typename T>
class teleporter_base {
public:
    void send_item(const item &i, const location &dest) {
        static_cast<T*>(this)->send_item_(i, dest);
    }

    item recv_item(const location &src) {
        return static_cast<T*>(this)->recv_item_(src);
    }
};

#ifdef MSVC

class win_teleporter : public teleporter_base<win_teleporter> {
public:
    void send_item_(const item &i, const location &dest) {
        //implementation
    }

    item recv_item_(const location &src) {
        //implementation
    }
};
typedef win_teleporter teleporter_impl;

#else

class lin_teleporter : public teleporter_base<lin_teleporter> {
public:
    void send_item_(const item &i, const location &dest) {
        //implementation
    }

    item recv_item_(const location &src) {
        //implementation
    }
};
typedef lin_teleporter teleporter_impl;
#endif

typedef teleporter_base<teleporter_impl> teleporter;

std::shared_ptr<teleporter> make_teleporter() {
    return std::make_shared<teleporter_impl>();
}

At compile time, the compiler will define at most one implementation of the teleporter interface depending on the platform used. The make_teleporter function will return a smart pointer to the correct implementation. All calls to the teleporter_base functions will be inlined by the compiler, avoiding the need for runtime method lookups. By using the CRTP, you’ve lost the flexibility of dynamic polymorphism but in this case you don’t need it. The above code results in an easy interface and efficient code:

auto tele = make_teleporter();

tele->send_item(item(), location("my friend"));
auto i = tele->recv_item(location("my other friend"));

3. The Pass-Key Idiom

Sometimes called the Client-Attorney Idiom, this technique allows you to give friend-like access to non-public functions without exposing all of your non-public members. This is particularly useful if you can’t or don’t want to make something a friend of your class, but you do want it to have access to a private or protected member. Take, for instance, a private constructor of a class C, and a static non-member function make_c. The idea is that users can only create a C by calling make_c:

class C {
public:
    //Some public member functions
private:
    //Some private variables/functions etc.

    C(); //<-- Private constructor
};

std::shared_ptr<C> make_c() {
    return std::make_shared<C>();
}

The above code is incorrect. Whether you want to return a raw pointer via return new C; or you go the shared pointer route, the fact is that make_c cannot access the private constructor. The coward’s way out is to delegate make_c as a friend:

class C {
//...
friend std::shared_ptr<C> make_c();
//...
};

There are two problems with this approach. Firstly, make_c now has access to all private members of C. Secondly,if you’re using shared pointers then std::make_shared<C> cannot access the private constructor. Having std::make_shared<C> become a friend of C may not be portable, and leads to the first problem again.

Both problems can be overcome by the Pass-Key Idiom. Make the private constructor public, but have it take as a parameter a type called Key with a private constructor. This way, nothing need be a friend of C, only Key. The only way to construct C is through a friend of Key; the constructor is virtually private, yet still accessible when you want it to be.

class C {
public:
    class Key {
        friend std::shared_ptr<C> make_c();
        Key() {}
    };

    C(const Key &);
    //...
};

std::shared_ptr<C> make_c() {
    return std::make_shared<C>(C::Key());
}
About these ads

One response to “Three Fun C++ Techniques

  1. Gerald 05/03/2014 at 20:50

    Great blog! Do you have any tips for aspiring
    writers? I’m hoping to start my own site soon but I’m a little
    lost on everything. Would you suggest starting with a free platform like WordPress or go for
    a paid option? There are so many choices out
    there that I’m totally confused .. Any tips?
    Thanks!

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

Follow

Get every new post delivered to your Inbox.

Join 188 other followers