pimpl, eliminating include files, encapsulation

S

Simon Elliott

(Note: this is a real C++ question and not an MS Windows question. The
window handle I introduce is just an example of OS specific data, which
I've used to illustrate my question.)

I have some code which is dependent on MS Windows. I want to encapsulate
this in a pimpl for all sorts of reasons, the main ones being:

1/ To encapsulate the OS dependent code

2/ To allow the header not to include the OS specific header files.
(In this particular case windows.h)
This allows the header to be OS independent and further
encapsulates the OS dependent code.

Unfortunately, the code needs a window handle (HWND). What's the best
way of passing this down without having to include windows.h in the
header? (HWND is defined in windows.h.)

Effectively I want to do something like this:

=== bloggs.cpp: low level OS dependent code ===

#include "bloggs.hpp"
#include "windows.h"

class bloggs::bloggsImpl
{
private:
HWND windowHandle_; // how does this get set?
public:
bloggsImpl(void){};
};

bloggs::bloggs():pimpl_(new bloggsImpl){}
bloggs::bloggs(const bloggs &C):pimpl_(new bloggsImpl)
{
*pimpl_ = *(C.pimpl_);
}

bloggs::~bloggs()
{
delete pimpl_;
}

bloggs& bloggs::eek:perator=(const bloggs &C)
{
*pimpl_ = *(C.pimpl_);
return(*this);
}

=== bloggs.hpp: header for low level OS dependent code ===
class bloggs
{
public:
bloggs();
bloggs(const bloggs &C);
~bloggs();
bloggs& operator=(const bloggs &C);
private:
class bloggsImpl;
bloggsImpl* pimpl_;
};

=== smith.cpp: portable code ===
#include "bloggs.hpp"
#include "smith.hpp"
class smith::smithImpl
{
private:
bloggs privateBloggs_; // must somehow give this a window handle
public:
smithImpl(void){};
};

smith::smith():pimpl_(new smithImpl){}
smith::smith(const smith &C):pimpl_(new smithImpl)
{
*pimpl_ = *(C.pimpl_);
}

smith::~smith()
{
delete pimpl_;
}

smith& smith::eek:perator=(const smith &C)
{
*pimpl_ = *(C.pimpl_);
return(*this);
}

=== smith.hpp: header for portable code ===
class smith
{
public:
smith();
smith(const smith &C);
~smith();
smith& operator=(const smith &C);
private:
class smithImpl;
smithImpl* pimpl_;
};

=== winbits.cpp: high level OS dependent code ===
#include "smith.hpp"
#include "windows.h"
....
HWND windowHandle;
....
smith* pSmith = new smith;
pSmith->CallOneOfSmithsMethods();
delete pSmith;

I would need to add a method to tell bloggs about the window handle. But
this would have to be passed through smith, which isn't allowed to know
about such things as window handles because it must be portable and OS
independent.

In this specific example I could cheat and pass the window handle as an
unsigned long, because I happen to know the type of HWND and I happen to
know that it isn't going to change anytime soon. But a) this seems like
a bit of a kludge and b) it's a very specific solution.

It's a pity that typedefs and enums can't be forward declared like
classes can. Otherwise I could pass a pointer to HWND.

Thoughts?
 
R

red floyd

Simon Elliott wrote:
[redacted]

Insert the line "class bloggsImpl;" before your bloggs def.

=== bloggs.hpp: header for low level OS dependent code ===

class bloggsImpl; // NEW LINE.
 
S

Simon Elliott

red floyd said:
Insert the line "class bloggsImpl;" before your bloggs def.

class bloggsImpl; // NEW LINE.

I don't understand how this allows me to solve the problem. Can you
expand a bit?
 
J

John Carson

Simon Elliott said:
(Note: this is a real C++ question and not an MS Windows question. The
window handle I introduce is just an example of OS specific data,
which I've used to illustrate my question.)

I have some code which is dependent on MS Windows. I want to
encapsulate this in a pimpl for all sorts of reasons, the main ones
being:

1/ To encapsulate the OS dependent code

2/ To allow the header not to include the OS specific header files.
(In this particular case windows.h)
This allows the header to be OS independent and further
encapsulates the OS dependent code.

Unfortunately, the code needs a window handle (HWND). What's the best
way of passing this down without having to include windows.h in the
header? (HWND is defined in windows.h.)

Platform independent software development is not my specialty, but here is
my 2 cents worth.

I think you need to identify where the HWND value is coming from in the
first place. If, for example, the HWND is the return value from a
CreateWindow call, then you could provide a function that abstracts the
window creation process on all supported platforms and which, in the case of
Windows, uses the return value from CreateWindow to initialise the HWND
variable without the client code ever having to see it.
 
D

Dan Cernat

This is a lame atempt to write C++. Can you write a Hello World program? Do
you have a C++ book?
What I see here is duplicate declarations of classes (e.i. you declare twice
the "bloggs" class)
Can you shrink your example?
 
T

Thomas Wintschel

snip
...
smith* pSmith = new smith;
pSmith->CallOneOfSmithsMethods();
delete pSmith;

I would need to add a method to tell bloggs about the window handle. But
this would have to be passed through smith, which isn't allowed to know
about such things as window handles because it must be portable and OS
independent.

In this specific example I could cheat and pass the window handle as an
unsigned long, because I happen to know the type of HWND and I happen to
know that it isn't going to change anytime soon. But a) this seems like
a bit of a kludge and b) it's a very specific solution.

It's a pity that typedefs and enums can't be forward declared like
classes can. Otherwise I could pass a pointer to HWND.

Let's see... we need a void pointer but we don't want to be attacked
by anyone, so...

Your 'impl' classes will all want something different but need to be
called by the innocent unknowing wrapper so they will need a method
that takes a void pointer which they can static_cast appropriately.

Your wrapper classes will need to wrap the call so they will need an
equivalent method but since we don't want anyone doing anything that
isn't typesafe it must be private. And since we don't want anyone
using an improperly initialized wrapper, the constructor must be
private as well.

Moving to your sample implementation, the header file for 'smith' gets
a forward declaration for an OS dependent class called smithFactory
that will be it's friend. The header file for smithFactory will
include the header for smith.

smithFactory, being OS dependent, has a static method that takes a
pointer to whatever the corresponding 'impl' class expects (to allow
type checking), constructs a smith, makes the call that passes the
pointer along (could be a constructor argument) and returns a pointer
to the new object.

Any help?
Do I need help?
Tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,996
Messages
2,570,238
Members
46,826
Latest member
robinsontor

Latest Threads

Top