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.
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
MyProjectFolder/Src/Export/IInputSystem.h
MyProjectFolder/Src/Export/Win32System.h
MyProjectFolder/Src/Win32RawInputSystem.h
MyProjectFolder/Src/Win32System.cpp
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*toWin32RawInputSystem*because they do not have that header. Therefore the users of that library can invokeFoo()andBar()on theIInputSystem*, but they’ll never be able to invokeRun().