This is not exactly a hot topic, but I have a factory class that allows a dll to create an instance and return it as a DLL. It is what I came looking for but couldn't find exactly.
It is called like,
IHTTP_Server *server = SN::SN_Factory<IHTTP_Server>::CreateObject(); IHTTP_Server *server2 = SN::SN_Factory<IHTTP_Server>::CreateObject(IHTTP_Server_special_entry);
where IHTTP_Server is the pure virtual interface for a class created either in another DLL, or the same one.
DEFINE_INTERFACE is used to give a class id an interface. Place inside interface;
An interface class looks like,
class IMyInterface { DEFINE_INTERFACE(IMyInterface); public: virtual ~IMyInterface() {}; virtual void MyMethod1() = 0; ... };
The header file is like this
#if !defined(SN_FACTORY_H_INCLUDED) #define SN_FACTORY_H_INCLUDED #pragma once
The libraries are listed in this macro definition. One line per library/executable. It would be cool if we could call into another executable.
#define SN_APPLY_LIBRARIES(L, A) \ L(A, sn, "sn.dll") \ L(A, http_server_lib, "http_server_lib.dll") \ L(A, http_server, "")
Then for each dll/exe you define a macro and list its implementations. Def means that it is the default implementation for the interface. If it is not the default, you give a name for the interface used to identify it. Ie, special, and the name will be IHTTP_Server_special_entry.
#define SN_APPLY_ENTRYPOINTS_sn(M) \ M(IHTTP_Handler, SNI::SNI_HTTP_Handler, sn, def) \ M(IHTTP_Handler, SNI::SNI_HTTP_Handler, sn, special) #define SN_APPLY_ENTRYPOINTS_http_server_lib(M) \ M(IHTTP_Server, HTTP::server::server, http_server_lib, def) #define SN_APPLY_ENTRYPOINTS_http_server(M)
With the libraries all setup, the header file uses the macro definitions to define the needful.
#define APPLY_ENTRY(A, N, L) \ SN_APPLY_ENTRYPOINTS_##N(A) #define DEFINE_INTERFACE(I) \ public: \ static const long Id = SN::I##_def_entry; \ private: namespace SN { #define DEFINE_LIBRARY_ENUM(A, N, L) \ N##_library,
This creates an enum for the libraries.
enum LibraryValues { SN_APPLY_LIBRARIES(DEFINE_LIBRARY_ENUM, "") LastLibrary }; #define DEFINE_ENTRY_ENUM(I, C, L, D) \ I##_##D##_entry,
This creates an enum for interface implementations.
enum EntryValues { SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_ENUM) LastEntry }; long CallEntryPoint(long id, long interfaceId);
This defines the factory class. Not much to it here.
template <class I> class SN_Factory { public: SN_Factory() { } static I *CreateObject(long id = I::Id ) { return (I *)CallEntryPoint(id, I::Id); } }; } #endif //SN_FACTORY_H_INCLUDED
Then the CPP is,
#include "sn_factory.h" #include <windows.h>
Create the external entry point. You can check that it exists using depends.exe.
extern "C" { __declspec(dllexport) long entrypoint(long id) { #define CREATE_OBJECT(I, C, L, D) \ case SN::I##_##D##_entry: return (int) new C(); switch (id) { SN_APPLY_CURRENT_LIBRARY(APPLY_ENTRY, CREATE_OBJECT) case -1: default: return 0; } } }
The macros set up all the data needed.
namespace SN { bool loaded = false; char * libraryPathArray[SN::LastLibrary]; #define DEFINE_LIBRARY_PATH(A, N, L) \ libraryPathArray[N##_library] = L; static void LoadLibraryPaths() { SN_APPLY_LIBRARIES(DEFINE_LIBRARY_PATH, "") } typedef long(*f_entrypoint)(long id); f_entrypoint libraryFunctionArray[LastLibrary - 1]; void InitlibraryFunctionArray() { for (long j = 0; j < LastLibrary; j++) { libraryFunctionArray[j] = 0; } #define DEFAULT_LIBRARY_ENTRY(A, N, L) \ libraryFunctionArray[N##_library] = &entrypoint; SN_APPLY_CURRENT_LIBRARY(DEFAULT_LIBRARY_ENTRY, "") } enum SN::LibraryValues libraryForEntryPointArray[SN::LastEntry]; #define DEFINE_ENTRY_POINT_LIBRARY(I, C, L, D) \ libraryForEntryPointArray[I##_##D##_entry] = L##_library; void LoadLibraryForEntryPointArray() { SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_POINT_LIBRARY) } enum SN::EntryValues defaultEntryArray[SN::LastEntry]; #define DEFINE_ENTRY_DEFAULT(I, C, L, D) \ defaultEntryArray[I##_##D##_entry] = I##_def_entry; void LoadDefaultEntries() { SN_APPLY_LIBRARIES(APPLY_ENTRY, DEFINE_ENTRY_DEFAULT) } void Initialize() { if (!loaded) { loaded = true; LoadLibraryPaths(); InitlibraryFunctionArray(); LoadLibraryForEntryPointArray(); LoadDefaultEntries(); } } long CallEntryPoint(long id, long interfaceId) { Initialize(); // assert(defaultEntryArray[id] == interfaceId, "Request to create an object for the wrong interface.") enum SN::LibraryValues l = libraryForEntryPointArray[id]; f_entrypoint f = libraryFunctionArray[l]; if (!f) { HINSTANCE hGetProcIDDLL = LoadLibraryA(libraryPathArray[l]); if (!hGetProcIDDLL) { return NULL; } // resolve function address here f = (f_entrypoint)GetProcAddress(hGetProcIDDLL, "entrypoint"); if (!f) { return NULL; } libraryFunctionArray[l] = f; } return f(id); } }
Each library includes this "cpp" with a stub cpp for each library/executable. Any specific compiled header stuff.
#include "sn_pch.h"
Setup this library.
#define SN_APPLY_CURRENT_LIBRARY(L, A) \ L(A, sn, "sn.dll")
An include for the main cpp. I guess this cpp could be a .h. But there are different ways you could do this. This approach worked for me.
#include "../inc/sn_factory.cpp"