There are a few ways you can control which type of resources to return. It will depend on how a user of your library uses the library. If they include the full source (like Unity and Unreal Engine do), the easiest way is to require a user of your library to define which system they're using - OpenGL or DirectX - at compile time. You might require the user to define some preprocessor macros like this:
#define IMPLEMENTATION_OPENGL 1
for OpenGL and
#define IMPLEMENTATION_DIRECTX 1
for DirectX.
You'd require that a user of your library defines which of those to use in some pre-compiled header. When the compiler got to the files of your library it would check which one to build, like this:
class ITextureLoader { public: ITextureLoader(const char* imagePath); ~ITextureLoader(); // ... etc. }; #if IMPLEMENTATION_OPENGL class TextureLoaderOpenGL : public ITextureLoader { public: TetxureLoaderOpenGL(const char* imagePath); ~TextureLoaderOpenGL(); //... etc. }; #elif IMPLEMENTATION_DIRECTX class TextureLoaderDirectX : public ITextureLoader { public: TetxureLoaderDirectX(const char* imagePath); ~TextureLoaderDirectX(); //... etc. }; #endif
Then you'd have a TextureLoaderFactory that would return an ITextureLoader object like this:
ITextureLoader* CreateTextureLoader(const char* imagePath) { #if IMPLEMENTATION_OPENGL return new TextureLoaderOpenGL(imagePath); #elif IMPLEMENTATION_DIRECTX return new TextureLoaderDirectX(imagePath); #endif }
If you aren't including the source for users, then you need to decide at runtime. It might be easier in that case to just build 2 different libraries for the user. It's not very frequently that a user wants to use both OpenGL and DirectX in their application.
You could also have them create some sort of context object that defines things like which renderer to use. They'd then pass that context object to the factory methods that create the concrete objects. Something like this:
enum Technology { Technology_OpenGL = 1, Technology_DirectX = 2 } Technology; struct TechnologyContext { Technology tech; int majorVersion; int minorVersion; };
Then your factory would look like this:
ITextureLoader* CreateTextureLoader(const TechnologyContext& ctx, const char* path) { if (ctx.tech == Technology_OpenGL) { return new TextureLoaderOpenGL(path); } else { return new TextureLoaderDirectX(path); } }
As a user, I would find this more cumbersome, personally.