Interfaces, hiding concrete implementation details in C++

I have a question regarding hidinging interface details in C++ libraries. The problem is ilustrated with the following example:

Let's say w have a class called ISystem which exposes methods like Init, Run, Tick, Shutdown, GetXXXSubSystem. Where X are pointers various interfaces like: ISoundSystem, IInputSystem

We also have concrete implementations of ISystem like: Win32System, OSXSystem etc.

These specific implementations use a pimpl idiom to hide internals and for example Win32System instantiates Win32RawInputSystem as input system manager.

All such managers do have their own Init, Run, Tick and Shutdown methods which are not part of the interface (only concrete implementation) and these are run and managed by the concrete system implementation.

The user calling GetXXXSubSystem gets interface pointer without those methods (Init etc..) but still he could cast the pointer he gets to concrete implementation and trigger methods like Init Run Tick etc. which he shouldn't have access to.

The question is, is it possible to hide the concrete class somehow? I tried to make those methods protected in the concrete implementations and template the class on type which would eventually be friend but this appears to be prohobited and existing hacks do not work with VC11.

The only way I can think of right know is to transfer the concrete implementation declaration from header into the cpp file of Win32System class but I see ahuge drawback of doing this (even not sure if this would work), because this way each subsystem would have to be also part of this cpp file and it would become a maintainability nightmare.

Another solution I am thinking about is using factory method like

(RawInput.h) 
IInputSystem* CreateRawInputSystem();

(RawInput.cpp)
class RawInput : public IInputSystem {}; ...

and move definition of the class to cpp file but then, how I would acces this type from other parts of my library (ie in Win32System impl)?

Is it possible to include .cpp files form other .cpp files?

Thanks in advance for any tips.

Answers


If you're developing a library here, then you can simply choose not to export the header files of the concrete classes that you do not want to expose. You cannot cast to a class of which you do not have a definition.

Example : MyProjectFolder/Src/Export/ISystem.h

#ifndef ISYSTEM_H
#define ISYSTEM_H

#include "IInputSystem.h"

class ISystem
{
public:
    virtual ~ISystem() {};
    virtual void Run()=0;
    virtual IInputSystem* GetInputSystem()=0;
};

#endif

MyProjectFolder/Src/Export/IInputSystem.h

#ifndef IINPUTSYSTEM_H
#define IINPUTSYSTEM_H

class IInputSystem
{
public:
    virtual ~IInputSystem() {};
    virtual void Foo()=0;
    virtual void Bar()=0;
};

#endif

MyProjectFolder/Src/Export/Win32System.h

#ifndef WIN32SYSTEM_H
#define WIN32SYSTEM_H

#include "ISystem.h"

class Win32System : public ISystem
{
public:
    Win32System();
    virtual void Run();
    virtual IInputSystem* GetInputSystem();

private:
    struct impl;
    impl* m_pImpl;
};

#endif

MyProjectFolder/Src/Win32RawInputSystem.h

#ifndef WIN32RAWINPUTSYSTEM_H
#define WIN32RAWINPUTSYSTEM_H

#include "IInputSystem.h"

class Win32RawInputSystem : public IInputSystem
{
public:
    virtual void Foo();
    virtual void Bar();

    virtual void Run(); // you do not want to expose that function
};

#endif

MyProjectFolder/Src/Win32System.cpp

#include "Win32System.h"

#include "Win32RawInputSystem.h"

struct Win32System::impl
{
    Win32RawInputSystem inputSys;
};

Win32System::Win32System()
 : m_pImpl(new impl)
{
}

void Win32System::Run()
{ // run run run
}

IInputSystem* Win32System::GetInputSystem()
{
    return &m_pImpl->inputSys;
}

So when building your project its include search path is not only Src/ but also Src/Export/. From within your library project you can use all classes, including Win32RawInputSystem. When deploying your library you only give away those headers that reside in the Src/Export/ folder. Clients can still use the library, but they can never cast IInputSystem* to Win32RawInputSystem* because they do not have that header. Therefore the users of that library can invoke Foo() and Bar() on the IInputSystem*, but they'll never be able to invoke Run().


Need Your Help

Something wrong while passing property from one class to the other

c# winforms class properties

I am having 3 classes. One is AuthenticateUser which let me to set and get user information such as username and password. In other class, AddEntryWindow is a WinForm in which I am trying to display

How do I get the resolution of RandR outputs through the xcb RandR extension?

c linux x11 xcb xrandr

I'm working on a project that is already using xcb and need to get the resolution of individual outputs rather than the resolution of the combined screen. Can I do this with the RandR extension fo...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.