开发者

Avoid leaking out external types in a C++ class

I have a class defined in a header like so (abbreviated):

class CairoRenderer
{
public:
    CairoRenderer();
    ~CairoRenderer();
...
protected:
    cairo_t* m_context;
    cairo_surface_t* m_surface;
};

cairo_t and cairo_surface_t are types defined by the Cairo graphics library.

The problem I have is that if I want to use this class or its derived classes from another library or application, I need to include the cairo headers as well because I am "leaking" the cairo types out through the CairoRenderer header. I want this class (or any subclass of it) in the same library to be usable externally without needing to include the cairo header or link against the cairo libraries.

So the next thing I tried was to use the pimpl technique as per the Wikipedia example because it looked like it could do what I wanted to achieve:

CairoRenderer.h (abbreviated)

class CairoRenderer
{
...
protected:
    struct CairoRendererImpl;
    CairoRendererImpl* m_pimpl;
};

CairoRenderer.cpp (abbreviated)

#incl开发者_JS百科ude "CairoRenderer.h"
#include "cairo.h"

....

struct CairoRenderer::CairoRendererImpl 
{
public:
    CairoRendererImpl() : m_surface(NULL), m_context(NULL) { }
    ~CairoRendererImpl() 
    {
        cairo_surface_destroy(m_surface);
        cairo_destroy(m_context);
    }

    void Initialize(cairo_t* context, cairo_surface_t* surface)
    {
        m_context = context;
        m_surface = surface;
    }

    cairo_surface_t* surface() { return m_surface; }
    cairo_t* context() { return m_context; }

private:
    cairo_surface_t* m_surface;
    cairo_t* m_context;
};

CairoRenderer::CairoRenderer() : m_pimpl(new CairoRendererImpl()) { ... }

CairoRenderer::~CairoRenderer() { delete m_pimpl; }

The problem I have is when I try to access the m_pimpl member from a derived class, I get the compiler error:

error C2027: use of undefined type 'CairoRenderer::CairoRendererImpl'

Am I doing pimpl wrong? Or is what I want to do even possible?


You are using the pimpl idiom correctly, and that is your problem. You have hidden the definition of CairoRendererImpl from all external code, including the code in derived classes.

First of all I would like to call into question the value of having a base class that has data members and a non-virtual destructor. You should look at the underlying reasons that you want to subclass CairoRenderer, and consider alternative solutions.

If you do want to support subclassing, but only for the classes within your library, then you should put the definition of CairoRendererImpl into a shared header file that can be included by the implementation files of all of the relevant classes in the CairoRenderer heirachy.


The pimpl idiom used ok.

The way to avoid having to include the cairo.h file(s) is to have, in your CairoRenderer.h file the statement (ie a forward declaration)

class CairoRendererImpl;

and leave it at that; do not include CairoRendererImpl.h since that will NEED to drag in definitions of cairo.h.

You can do this because CairoRenderer only needs a pointer to an implementation - as long as you do not actually call any methods from the headerfile (e.g. inline functions) the compiler doesn't need to see the full declaration of the CairoRendererImpl class.

In the CairoRenderer.cc you can include the CairoRendererImpl.h file (which will get the cairo.h stuff but at this point that is what you actually want/need).

hth, h


I think struct CairoRenderer::CairoRendererImpl is wrong. Since you do not use namespaces, a simple definition like this would suffice:

struct CairoRendererImpl 
{
    ...
}

Also you have to adjust your CairoRenderer class a bit:

struct CairoRendererImpl;

class CairoRenderer
{
...
protected:
    CairoRendererImpl* m_pimpl;
};
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜