Interfaces, hiding concrete implementation details in C++ - 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.

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().

Related

Can I provide an incomplete header for a C++ class to hide the implementation details?

I would like to split a class implementation into three parts, to avoid that users need to deal with the implementation details, e.g., the libaries that I use to implement the functionality:
impl.cpp
#include <api.h>
#include <impl.h>
Class::Class() {
init();
}
Class::init() {
myData = SomeLibrary::Type(42);
}
Class::doSomething() {
myData.doSomething();
}
impl.h
#include <somelibrary.h>
class Class {
public:
Class();
init();
doSomething();
private:
SomeLibary::Type myData;
}
api.h
class Class {
Class();
doSomething();
}
The problem is, that I am not allowed to redefine headers for the class definition. This does not work when I define Class() and doSomething() only in api.h, either.
A possible option is to define api.h and do not use it in the project at all, but install it (and do not install impl.h).
The obvious drawback is, that I need to make sure, that the common methods in api.h and impl.h always have the same signature, otherwise programs using the library will get linker errors, that I cannot predict when compiling the library.
But would this approach work at all, or will I get other problems (e.g. wrong pointers to class members or similar issues), because the obj file does not match the header?
The short answer is "No!"
The reason: any/all 'client' projects that need to use your Class class have to have the full declaration of that class, in order that the compiler can properly determine such things as offsets for member variables.
The use of private members is fine - client programs won't be able to change them - as is your current implementation, where only the briefest outlines of member functions are provided in the header, with all actual definitions in your (private) source file.
A possible way around this is to declare a pointer to a nested class in Class, where this nested class is simply declared in the shared header: class NestedClass and then you can do what you like with that nested class pointer in your implementation. You would generally make the nested class pointer a private member; also, as its definition is not given in the shared header, any attempt by a 'client' project to access that class (other than as a pointer) will be a compiler error.
Here's a possible code breakdown (maybe not error-free, yet, as it's a quick type-up):
// impl.h
struct MyInternal; // An 'opaque' structure - the definition is For Your Eyes Only
class Class {
public:
Class();
init();
doSomething();
private:
MyInternal* hidden; // CLient never needs to access this! Compiler error if attempted.
}
// impl.cpp
#include <api.h>
#include <impl.h>
struct MyInternal {
SomeLibrary::Type myData;
};
Class::Class() {
init();
}
Class::init() {
hidden = new MyInternal; // MUCH BETTER TO USE unique_ptr, or some other STL.
hidden->myData = SomeLibrary::Type(42);
}
Class::doSomething() {
hidden->myData.doSomething();
}
NOTE: As I hinted in a code comment, it would be better code to use std::unique_ptr<MyInternal> hidden. However, this would require you to give explicit definitions in your Class for the destructor, assignment operator and others (move operator? copy constructor?), as these will need access to the full definition of the MyInternal struct.
The private implementation (PIMPL) idiom can help you out here. It will probably result in 2 header and 2 source files instead of 2 and 1. Have a silly example I haven't actually tried to compile:
api.h
#pragma once
#include <memory>
struct foo_impl;
struct foo {
int do_something(int argument);
private:
std::unique_ptr<foo_impl> impl;
}
api.c
#include "api.h"
#include "impl.h"
int foo::do_something(int a) { return impl->do_something(); }
impl.h
#pragma once
#include <iostream>
struct foo_impl {
foo_impl();
~foo_impl();
int do_something(int);
int initialize_b();
private:
int b;
};
impl.c
#include <iostream>
foo_impl::foo_impl() : b(initialize_b()} { }
foo_impl::~foo_impl() = default;
int foo_impl::do_something(int a) { return a+b++; }
int foo_impl::initialize_b() { ... }
foo_impl can have whatever methods it needs, as foo's header (the API) is all the user will see. All the compiler needs to compile foo is the knowledge that there is a pointer as a data member so it can size foo correctly.

C++ dynamic library with public api that obscures dependent libraries

I am trying to create a multi-platform library in C++ for use by C++ consumer applications. Call my library A. I want to ship a dynamic library file per target platform, and a header file (call this export.h) that the consumer app could use to compile and execute. My library depends on a third-party open-source library, written in c, which is difficult to link to correctly; call this library B.
In order to save my consumers the pains of linking to B, I want to abstract every call to it so that the consumer need not even have a single header file from B. Consumer app (C) should be able to compile with only A.dll, B.dll and export.h; and run with only A.dll and B.dll as dependencies (substituting the platform-specific suffix for a shared library as needed).
B defines a great many types, mostly structs. (B is not written in objective c, although it probably should have been.) Part of A's job is to produce classes that contain and manage groups of structs around logical lines, which are readily apparent. C needs to call functions belonging to classes in A, so the function prototypes need to be in export.h, but the B types cannot be in export.h or else C will need to include headers from B.
Is there a syntax that lets me define the public members of a class (in A) without also defining all the private members?
Obviously, there are no public members in A that rely on types from B. The closest thing I've found so far is this question. Opaque pointers may be part of the solution, but C needs access to functions of classes in A. I really don't want to write a helper function for every public A class member, though that would probably work. Any ideas?
Edit: As requested, explanation code.
Worker.h:
#include <SomeThirdPartyLib.h>
class Worker {
public:
Worker();
~Worker();
void DoWork();
private:
Truck t;
}
SomeThirdPartyLib.h:
typedef struct TruckS {
char data[200];
char* location;
} Truck;
Worker.cpp:
#include "worker.h"
Worker::Worker() {}
Worker::~Worker() {}
Worker::DoWork() {
t.location = "Work";
}
main.cpp:
#include <export.h>
int main(int argc, char** argv) {
Worker w();
w.DoWork();
}
Now I'm looking for the syntax to put in export.h that would allow this external application to be compiled using that header and my dll, but without requiring access to SomeThirdPartyLib.h.
Here is a technique that may be close to what you want to achieve.
You can define an interface that relies only on public interfaces from A, and therefore lacks any dependency on B. The interface class includes a factory. This interface would be part of the header file for your library.
class Interface {
public:
virtual void foo () = 0;
virtual void bar () = 0;
static std::unique_ptr<Interface> make ();
virtual ~Interface () = default;
};
In a source file of your library, you would include header files for both A and B, and create an implementation of the interface as well as a definition of the factory.
class Implementation : public Interface {
//...
};
std::unique_ptr<Interface>
Interface::make () {
return std::make_unique<Implementation>();
}
So, users of your library get access to the interface, and can call the public methods without any knowledge of private members or private methods.
Try it online!

How to create a derived class of base class with no virtual methods?

So I have the following situation: I have a (C++) driver that is not in my control that I have the headers and library files for but no source code. I want to simulate the driver in my SW (not just for tests, but use the same code as for the actual implementation). It would be easy to just create a derived class from the base class and re-implement all the methods except none of the methods in the driver base are marked as virtual so I cannot inherit them and still use the same base type in my code. I cannot create a custom header just for the simulation version because I want to be able to use the same code for simulation or real driver. I'm a bit stumped, how do I create a derived class from this non-virtual base?
If Driver does not offer virtual functions, then having code which uses Driver * use implementations from a class derived from Driver is not possible.
However, you could change your code to be templated by the type of driver use and use two instantiations, one for Driver and one for SimulationDriver (which even wouldn't have to be derived from Driver). Note that in this particular case (where you know all desired instantiations), you can sidestep the usual requirement of "all template code must be in header files" by using explicit instatiation. I use something similar in one of my projects.
Basically, code which currently looks like this:
file.hpp
class Driver;
void process(const Driver *d);
file.cpp
#include "file.hpp"
#include "Driver.h"
void process(const Driver *d)
{
d->doStuff();
}
will change to look like this:
file.hpp
template <class T_Driver>
void process(const T_Driver *d);
file.cpp
#include "file.hpp"
#include "Driver.h"
#include "SimulationDriver.h"
template <class T_Driver>
void process(const T_Driver *d)
{
d->doStuff();
}
template void process(const Driver *d);
template void process(const SimulationDriver *d);

Separating Interface and Implementation in C++

If I have a simple header file:
namespace aNamespace {
class AClass {
public:
AClass();
~AClass();
bool Init();
void Shutdown();
};
}
What is the 'correct' way to implement this class in the corresponding CPP file? I can see two options:
Option A
namespace aNamespace {
class AClass {
public:
AClass() { ... }
~AClass() { ... }
bool Init() { ... }
void Shutdown() { ... }
};
}
Option B
namespace aNamespace {
AClass::AClass() { ... }
AClass::~AClass() { ... }
bool AClass::Init() { ... }
void AClass::Shutdown() { ... }
}
The problem I see with Option B is that it's hard to add implementation-specific members to AClass - e.g. what if the implementation requires a std::wstring or so as a storage variable; but that variable isn't defined in the header file?
The reason I'm asking this is because I may wish to have multiple implementations of AClass, and select which one to link according to some external variable (e.g. the target platform or architecture).
Another option would be to actually make name of each implementation platform specific and have a simple typedef switch in header to control which one is chosen based on target/architecture:
#ifdef target1
typedef AClass Target1ClassImplementation;
#elif defined target2
typedef AClass Target2ClassImplementation;
#else
#error AClass is not implemented for current target
#endif
If desired, common interface can be encapsulated in a base class implementations derive from. It is less error prone since is more explicit in sense which implementation is for what target, while allows using AClass regardlesss of a platform target outside of header.
B is much better in most cases:
Advantages:
Hide implementation details.
Less #includes in header files (less exposed dependencies!):
Faster builds
2 classes can call each other's functions. Very tricky to do if both are in headers.
Changes to implementation do affect other classes (build time).
Disadvantages:
- Functions in CPP file do not inline in other modules (across library boundaries)
Optimal: Decide per function which is best. Short one liners to the header and longer ones to the cpp(s). You can have more than 1 source file for the class implementation.

Partial class definition on C++?

Anyone knows if is possible to have partial class definition on C++ ?
Something like:
file1.h:
class Test {
public:
int test1();
};
file2.h:
class Test {
public:
int test2();
};
For me it seems quite useful for definining multi-platform classes that have common functions between them that are platform-independent because inheritance is a cost to pay that is non-useful for multi-platform classes.
I mean you will never have two multi-platform specialization instances at runtime, only at compile time. Inheritance could be useful to fulfill your public interface needs but after that it won't add anything useful at runtime, just costs.
Also you will have to use an ugly #ifdef to use the class because you can't make an instance from an abstract class:
class genericTest {
public:
int genericMethod();
};
Then let's say for win32:
class win32Test: public genericTest {
public:
int win32Method();
};
And maybe:
class macTest: public genericTest {
public:
int macMethod();
};
Let's think that both win32Method() and macMethod() calls genericMethod(), and you will have to use the class like this:
#ifdef _WIN32
genericTest *test = new win32Test();
#elif MAC
genericTest *test = new macTest();
#endif
test->genericMethod();
Now thinking a while the inheritance was only useful for giving them both a genericMethod() that is dependent on the platform-specific one, but you have the cost of calling two constructors because of that. Also you have ugly #ifdef scattered around the code.
That's why I was looking for partial classes. I could at compile-time define the specific platform dependent partial end, of course that on this silly example I still need an ugly #ifdef inside genericMethod() but there is another ways to avoid that.
This is not possible in C++, it will give you an error about redefining already-defined classes. If you'd like to share behavior, consider inheritance.
Try inheritance
Specifically
class AllPlatforms {
public:
int common();
};
and then
class PlatformA : public AllPlatforms {
public:
int specific();
};
You can't partially define classes in C++.
Here's a way to get the "polymorphism, where there's only one subclass" effect you're after without overhead and with a bare minimum of #define or code duplication. It's called simulated dynamic binding:
template <typename T>
class genericTest {
public:
void genericMethod() {
// do some generic things
std::cout << "Could be any platform, I don't know" << std::endl;
// base class can call a method in the child with static_cast
(static_cast<T*>(this))->doClassDependentThing();
}
};
#ifdef _WIN32
typedef Win32Test Test;
#elif MAC
typedef MacTest Test;
#endif
Then off in some other headers you'll have:
class Win32Test : public genericTest<Win32Test> {
public:
void win32Method() {
// windows-specific stuff:
std::cout << "I'm in windows" << std::endl;
// we can call a method in the base class
genericMethod();
// more windows-specific stuff...
}
void doClassDependentThing() {
std::cout << "Yep, definitely in windows" << std::endl;
}
};
and
class MacTest : public genericTest<MacTest> {
public:
void macMethod() {
// mac-specific stuff:
std::cout << "I'm in MacOS" << std::endl;
// we can call a method in the base class
genericMethod();
// more mac-specific stuff...
}
void doClassDependentThing() {
std::cout << "Yep, definitely in MacOS" << std::endl;
}
};
This gives you proper polymorphism at compile time. genericTest can non-virtually call doClassDependentThing in a way that gives it the platform version, (almost like a virtual method), and when win32Method calls genericMethod it of course gets the base class version.
This creates no overhead associated with virtual calls - you get the same performance as if you'd typed out two big classes with no shared code. It may create a non-virtual call overhead at con(de)struction, but if the con(de)structor for genericTest is inlined you should be fine, and that overhead is in any case no worse than having a genericInit method that's called by both platforms.
Client code just creates instances of Test, and can call methods on them which are either in genericTest or in the correct version for the platform. To help with type safety in code which doesn't care about the platform and doesn't want to accidentally make use of platform-specific calls, you could additionally do:
#ifdef _WIN32
typedef genericTest<Win32Test> BaseTest;
#elif MAC
typedef genericTest<MacTest> BaseTest;
#endif
You have to be a bit careful using BaseTest, but not much more so than is always the case with base classes in C++. For instance, don't slice it with an ill-judged pass-by-value. And don't instantiate it directly, because if you do and call a method that ends up attempting a "fake virtual" call, you're in trouble. The latter can be enforced by ensuring that all of genericTest's constructors are protected.
or you could try PIMPL
common header file:
class Test
{
public:
...
void common();
...
private:
class TestImpl;
TestImpl* m_customImpl;
};
Then create the cpp files doing the custom implementations that are platform specific.
#include will work as that is preprocessor stuff.
class Foo
{
#include "FooFile_Private.h"
}
////////
FooFile_Private.h:
private:
void DoSg();
How about this:
class WindowsFuncs { public: int f(); int winf(); };
class MacFuncs { public: int f(); int macf(); }
class Funcs
#ifdef Windows
: public WindowsFuncs
#else
: public MacFuncs
#endif
{
public:
Funcs();
int g();
};
Now Funcs is a class known at compile-time, so no overheads are caused by abstract base classes or whatever.
As written, it is not possible, and in some cases it is actually annoying.
There was an official proposal to the ISO, with in mind embedded software, in particular to avoid the RAM ovehead given by both inheritance and pimpl pattern (both approaches require an additional pointer for each object):
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0309r0.pdf
Unfortunately the proposal was rejected.
As written, it is not possible.
You may want to look into namespaces. You can add a function to a namespace in another file. The problem with a class is that each .cpp needs to see the full layout of the class.
Nope.
But, you may want to look up a technique called "Policy Classes". Basically, you make micro-classes (that aren't useful on their own) then glue them together at some later point.
Either use inheritance, as Jamie said, or #ifdef to make different parts compile on different platforms.
For me it seems quite useful for definining multi-platform classes that have common functions between them that are platform-independent.
Except developers have been doing this for decades without this 'feature'.
I believe partial was created because Microsoft has had, for decades also, a bad habit of generating code and handing it off to developers to develop and maintain.
Generated code is often a maintenance nightmare. What habits to that entire MFC generated framework when you need to bump your MFC version? Or how do you port all that code in *.designer.cs files when you upgrade Visual Studio?
Most other platforms rely more heavily on generating configuration files instead that the user/developer can modify. Those, having a more limited vocabulary and not prone to be mixed with unrelated code. The configuration files can even be inserted in the binary as a resource file if deemed necessary.
I have never seen 'partial' used in a place where inheritance or a configuration resource file wouldn't have done a better job.
Since headers are just textually inserted, one of them could omit the "class Test {" and "}" and be #included in the middle of the other.
I've actually seen this in production code, albeit Delphi not C++. It particularly annoyed me because it broke the IDE's code navigation features.
Dirty but practical way is using #include preprocessor:
Test.h:
#ifndef TEST_H
#define TEST_H
class Test
{
public:
Test(void);
virtual ~Test(void);
#include "Test_Partial_Win32.h"
#include "Test_Partial_OSX.h"
};
#endif // !TEST_H
Test_Partial_OSX.h:
// This file should be included in Test.h only.
#ifdef MAC
public:
int macMethod();
#endif // MAC
Test_Partial_WIN32.h:
// This file should be included in Test.h only.
#ifdef _WIN32
public:
int win32Method();
#endif // _WIN32
Test.cpp:
// Implement common member function of class Test in this file.
#include "stdafx.h"
#include "Test.h"
Test::Test(void)
{
}
Test::~Test(void)
{
}
Test_Partial_OSX.cpp:
// Implement OSX platform specific function of class Test in this file.
#include "stdafx.h"
#include "Test.h"
#ifdef MAC
int Test::macMethod()
{
return 0;
}
#endif // MAC
Test_Partial_WIN32.cpp:
// Implement WIN32 platform specific function of class Test in this file.
#include "stdafx.h"
#include "Test.h"
#ifdef _WIN32
int Test::win32Method()
{
return 0;
}
#endif // _WIN32
Suppose that I have:
MyClass_Part1.hpp, MyClass_Part2.hpp and MyClass_Part3.hpp
Theoretically someone can develop a GUI tool that reads all these hpp files above and creates the following hpp file:
MyClass.hpp
class MyClass
{
#include <MyClass_Part1.hpp>
#include <MyClass_Part2.hpp>
#include <MyClass_Part3.hpp>
};
The user can theoretically tell the GUI tool where is each input hpp file and where to create the output hpp file.
Of course that the developer can theoretically program the GUI tool to work with any varying number of hpp files (not necessarily 3 only) whose prefix can be any arbitrary string (not necessarily "MyClass" only).
Just don't forget to #include <MyClass.hpp> to use the class "MyClass" in your projects.
Declaring a class body twice will likely generate a type redefinition error. If you're looking for a work around. I'd suggest #ifdef'ing, or using an Abstract Base Class to hide platform specific details.
You can get something like partial classes using template specialization and partial specialization. Before you invest too much time, check your compiler's support for these. Older compilers like MSC++ 6.0 didn't support partial specialization.
This is not possible in C++, it will give you an error about redefining already-defined
classes. If you'd like to share behavior, consider inheritance.
I do agree on this. Partial classes is strange construct that makes it very difficult to maintain afterwards. It is difficult to locate on which partial class each member is declared and redefinition or even reimplementation of features are hard to avoid.
Do you want to extend the std::vector, you have to inherit from it. This is because of several reasons. First of all you change the responsibility of the class and (properly?) its class invariants. Secondly, from a security point of view this should be avoided.
Consider a class that handles user authentication...
partial class UserAuthentication {
private string user;
private string password;
public bool signon(string usr, string pwd);
}
partial class UserAuthentication {
private string getPassword() { return password; }
}
A lot of other reasons could be mentioned...
Let platform independent and platform dependent classes/functions be each-others friend classes/functions. :)
And their separate name identifiers permit finer control over instantiation, so coupling is looser. Partial breaks encapsulation foundation of OO far too absolutely, whereas the requisite friend declarations barely relax it just enough to facilitate multi-paradigm Separation of Concerns like Platform Specific aspects from Domain-Specific platform independent ones.
I've been doing something similar in my rendering engine. I have a templated IResource interface class from which a variety of resources inherit (stripped down for brevity):
template <typename TResource, typename TParams, typename TKey>
class IResource
{
public:
virtual TKey GetKey() const = 0;
protected:
static shared_ptr<TResource> Create(const TParams& params)
{
return ResourceManager::GetInstance().Load(params);
}
virtual Status Initialize(const TParams& params, const TKey key, shared_ptr<Viewer> pViewer) = 0;
};
The Create static function calls back to a templated ResourceManager class that is responsible for loading, unloading, and storing instances of the type of resource it manages with unique keys, ensuring duplicate calls are simply retrieved from the store, rather than reloaded as separate resources.
template <typename TResource, typename TParams, typename TKey>
class TResourceManager
{
sptr<TResource> Load(const TParams& params) { ... }
};
Concrete resource classes inherit from IResource utilizing the CRTP. ResourceManagers specialized to each resource type are declared as friends to those classes, so that the ResourceManager's Load function can call the concrete resource's Initialize function. One such resource is a texture class, which further uses a pImpl idiom to hide its privates:
class Texture2D : public IResource<Texture2D , Params::Texture2D , Key::Texture2D >
{
typedef TResourceManager<Texture2D , Params::Texture2D , Key::Texture2D > ResourceManager;
friend class ResourceManager;
public:
virtual Key::Texture2D GetKey() const override final;
void GetWidth() const;
private:
virtual Status Initialize(const Params::Texture2D & params, const Key::Texture2D key, shared_ptr<Texture2D > pTexture) override final;
struct Impl;
unique_ptr<Impl> m;
};
Much of the implementation of our texture class is platform-independent (such as the GetWidth function if it just returns an int stored in the Impl). However, depending on what graphics API we're targeting (e.g. Direct3D11 vs. OpenGL 4.3), some of the implementation details may differ. One solution could be to inherit from IResource an intermediary Texture2D class that defines the extended public interface for all textures, and then inherit a D3DTexture2D and OGLTexture2D class from that. The first problem with this solution is that it requires users of your API to be constantly mindful of which graphics API they're targeting (they could call Create on both child classes). This could be resolved by restricting the Create to the intermediary Texture2D class, which uses maybe a #ifdef switch to create either a D3D or an OGL child object. But then there is still the second problem with this solution, which is that the platform-independent code would be duplicated across both children, causing extra maintenance efforts. You could attempt to solve this problem by moving the platform-independent code into the intermediary class, but what happens if some of the member data is used by both platform-specific and platform-independent code? The D3D/OGL children won't be able to access those data members in the intermediary's Impl, so you'd have to move them out of the Impl and into the header, along with any dependencies they carry, exposing anyone who includes your header to all that crap they don't need to know about.
API's should be easy to use right and hard to use wrong. Part of being easy to use right is restricting the user's exposure to only the parts of the API they should be using. This solution opens it up to be easily used wrong and adds maintenance overhead. Users should only have to care about the graphics API they're targeting in one spot, not everywhere they use your API, and they shouldn't be exposed to your internal dependencies. This situation screams for partial classes, but they are not available in C++. So instead, you might simply define the Impl structure in separate header files, one for D3D, and one for OGL, and put an #ifdef switch at the top of the Texture2D.cpp file, and define the rest of the public interface universally. This way, the public interface has access to the private data it needs, the only duplicate code is data member declarations (construction can still be done in the Texture2D constructor that creates the Impl), your private dependencies stay private, and users don't have to care about anything except using the limited set of calls in the exposed API surface:
// D3DTexture2DImpl.h
#include "Texture2D.h"
struct Texture2D::Impl
{
/* insert D3D-specific stuff here */
};
// OGLTexture2DImpl.h
#include "Texture2D.h"
struct Texture2D::Impl
{
/* insert OGL-specific stuff here */
};
// Texture2D.cpp
#include "Texture2D.h"
#ifdef USING_D3D
#include "D3DTexture2DImpl.h"
#else
#include "OGLTexture2DImpl.h"
#endif
Key::Texture2D Texture2D::GetKey() const
{
return m->key;
}
// etc...