Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pimpl for a templated class

I want to use the pimpl idiom to avoid having users of my library need our external dependencies (like boost, etc) however when my class is templated that seems to be impossible because the methods must be in the header. Is there something I can do instead?

like image 257
David Avatar asked Oct 22 '11 08:10

David


People also ask

What is a templated class?

Template class. is an instance of a class template. A template definition is identical to any valid class definition that the template might generate, except for the following: The class template definition is preceded by template< template-parameter-list >

Can templates be used for classes?

A template is not a class or a function.

What can be templated in C++?

A template is a C++ entity that defines one of the following: a family of classes (class template), which may be nested classes. a family of functions (function template), which may be member functions.


2 Answers

If the class is templated, your users essentially need to compile it (and this is literally true in the most widely-used C++ implementations) and so they need your external dependencies.

The simplest solution is to put the bulk of your class's implementation in a non-template base class (or encapsulated member object of some class). Solve the module-hiding problem there.

And then write the template derived (or enclosing) class to add type safety to it.

For example, suppose you have a template that provides the amazing ability to allocate on first access (omitting the necessary copy constructor, assignment, destructor):

template <class T>
class MyContainer
{
    T *instance_;

public:
    MyContainer() : instance_(0) {}

    T &access()
    {
        if (instance_ == 0)
            instance_ = new T();

        return *instance_;
    }
};

If you wanted the "logic" to be separated into a non-template base class, you'd have to parameterise the behaviour in the non-template way, which is to say, use virtual functions:

class MyBase
{
    void *instance_;

    virtual void *allocate() = 0;

public:
    MyBase() : instance_(0) {}

    void *access()
    {
        if (instance_ == 0)
            instance_ = allocate();

        return instance_;
    }
};

Then you can add the type-awareness in the outer layer:

template <class T>
class MyContainer : MyBase
{
    virtual void *allocate()
        { return new T(); }

public:
    T &access()
        { return *(reinterpret_cast<T *>(MyBase::access())); }
};

i.e. You use virtual functions to allow the template to "fill in" the type-dependent operations. Obviously this pattern would only really make sense if you have some business logic that is worth the effort of hiding.

like image 183
Daniel Earwicker Avatar answered Sep 21 '22 04:09

Daniel Earwicker


You can explicitly instantiate templates in the source file, but that is possible only if you know what the template type is going to be. Otherwise, do not use pimpl idiom for templates.

Something like this :

header.hpp :

#ifndef HEADER_HPP
#define HEADER_HPP

template< typename T >
class A
{
  // constructor+methods + pimpl
};

#endif

source.cpp :

#include "header.hpp"

// implementation

// explicitly instantiate for types that will be used
template class A< int >;
template class A< float >;
// etc...
like image 39
BЈовић Avatar answered Sep 20 '22 04:09

BЈовић