3

The C++ Priv idiom: an alternative to Pimpl

 2 years ago
source link: https://yairchu.github.io/posts/the-priv-idiom
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
an alternative to Pimpl

The C++ Priv idiom: an alternative to Pimpl

by Yair Chuchem 2021.03.09

C++ is infamous for long compilation times.

Part of the problem is that editing private declarations of a class causes recompilation for all of its users.

There are several ways to work around this problem and reduce incremental compile times:

  • Use interfaces. A lot of code using a concrete class could use an interface instead. This comes with a run-time price of using virtual functions.
  • The Pimpl idiom (private pointer to implementation), achieves the same compile-time benefit as interfaces without the virtual calls, but comes at a run-time price of allocating and using additional heap objects.
  • The Priv idiom, introduced below, does not sacrifice runtime performance at all, but it also only brings part of the compilation time benefits of the other approaches.

Below is a short introduction to Pimpl and Priv, some benchmark results, and closing commentary.

Illustrating Pimpl with a simple example

class SpamFilter
{
public:
    SpamFilter (const std::set<std::string>& forbiddenWords);
    ~SpamFilter();

    bool isSpam (const std::vector<std::string>& words) const;

private:
    class Impl;

    std::unique_ptr<Impl> impl;
};

Using the Pimpl idiom, our class doesn't have any private declarations apart from the internal Impl class and impl member.

The actual SpamFilter::Impl class is defined in the .cpp file and does all of the work.

The benefit of this approach is that when we change implementation details, no recompilation of other modules will trigger.

The down-sides are:

  • Performance is sacrificed due to forcing additional heap allocations (for the Impl object) and additional indirections.
  • Boiler-plate, public methods of the class just call the same method of the Impl class.

Priv is an alternative idiom without the down-sides of Pimpl, but also with only a part of its benefit, as we do declare the private members normally:

class SpamFilter
{
public:
    SpamFilter (const std::set<std::string>& forbiddenWords);
    bool isSpam (const std::vector<std::string>& words) const;

private:
    struct Priv;

    std::set<std::string> m_forbiddenWords;
};

No private methods are declared in the .h file. According to this idiom they all belong to the Priv subclass, declared in the .cpp file:

struct SpamFilter::Priv
{
    static bool isSpam (const SpamFilter&, const std::string& word);
};

The private methods turned into static methods of the Priv nested class, so we use them like so:

bool SpamFilter::isSpam (const std::vector<std::string>& words) const
{
    for (const auto& x : words)
        if (Priv::isSpam (*this, x))
            return true;
    return false;
}

Comparison of idioms

Boilerplate Slowdown Extra Recompilations Plain None (baseline) On any change Interface +37% less than 1% When the interface changes Pimpl +53% 10% Priv +13% None Also when private members change

The comparison numbers are based on a simple benchmark (run-time was measured using time on a 2020 M1 Macbook Pro)

Should one use any of these idioms just to reduce compilation times? As C++ already has plenty of boiler-plate with header files, I'd be relunctant to add more. And why are we even using C++ in the first place if not to achieve the best run-time performance? Therefore I would prefer not to use the Pimpl idiom, and consider Priv over it, but also after exausting any other approaches to reduce compilation times without any weird workaround (like using forward declarations and include-what-you-use).

Notes

  • The Priv idiom as presented here is an improvement over my original formulation, and was suggested by 9cantthinkofgoodname on reddit
  • This post was written in response of Pimpl being used in the code-base of Pajam, and I intend to present it to my colleagues to discuss the trade-offs of this idiom. Will update on the results!
  • Regardless of whether it uses the optimal internal implementation idioms, Pajam is really cool! If you want to jam musically with your remote friends, be sure to try it out!
  • Title image credit: CoolClips.com
  • redditr/cpp discussion

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK