views:

254

answers:

2

I am currently writing an application that will serve a similar purpose for multiple clients, but requires adaptations to how it will handle the data it is feed. In essence it will serve the same purpose, but hand out data totally differently.

So I decided to prodeed like this: -Make common engine library that will hold the common functionalities of all ways and present the default interface ensuring that the different engines will respond the same way. -Write a specific engine for each way of functioning....each one compiles into its own .dll.

So my project will end up with a bunch of libraries with some looking like this: project_engine_base.dll project_engine_way1.dll project_engine_way2.dll

Now in the configuration file that we use for the user preferences there will an engine section so that we may decide which engine to use:

[ENGINE]
Way1

So somewhere in the code we will want to do:

If (this->M_ENGINE == "Way1")
  //load dll for way1
Else If (this->M_ENGINE == "Way2")
  //load dll for way2
Else
  //no engines selected...tell user to modify settings and restart application

The question is...How will I import my dll(s) this way? Is it even possible? If not can I get some suggestions on how to achieve a similar way of functioning?

I am aware I could just import all of the dlls right at the start and just choose which engine to use, but the idea was that I didn't want to import too many engines for nothing and waste resources and we didn't want to have to ship all of those dlls to our customers. One customer will use one engine another will use a different one. Some of our customer will use more than one possibly hence the reason why I wanted to externalize this and allow our users to use a configuration file for engine switching.

Any ideas?

EDIT: Just realized that even though each of my engine would present the same interface if they are loaded dynamically at runtime and not all referenced in the project, my project would not compile. So I don't have a choice but to include them all in my project don't I?

That also means they all have to be shipped to my customers. The settings in the configuration would only dictate with class I would use to initialize my engine member.

OR

I could have each of these engines be compiled to the same name. Only import one dll in my main project and that particular engine would be used all the time. That would render my customers unable to use our application for multiple clients of their own. Unless they were willing to manually switch dlls. Yuck

Any suggestions?

EDIT #2: At this point seeing my options, I could also juste make one big dll containing the base engine as well as all the child ones and my configuration to let the user chose. Instead of referencing multiple dlls and shipping them all. Just have one huge one and ship/reference that one only. I am not too fond of this either as it means shipping one big dll to all of my customers instead of just one or two small ones that suit there needs. This is still the best solution that I've come up with though.

I am still looking for better suggestions or answers to my original question. Thanks.

+2  A: 

Use separate DLLs for each engine and use LoadLibrary in your main project to load the specific engine based on the configuration.

Have your engine interface in some common header file that all engines will derive from and this interface will be used in your main project aswell.

It might look like this:

// this should be an abstract class
class engine {
public:
     virtual void func1() = 0;
     virtual void func2() = 0;
...
};

In each different engine implementation export a function from the DLL, something like this:

// might aswell use auto_ptr here
engine* getEngine() { return new EngineImplementationNumberOne(); }

Now in your main project simply load the DLL you're interested in using LoadLibrary and then GetProcAddress the getEngine function.

string dllname;

if (this->M_ENGINE == "Way1")
     dllname = "dllname1.dll";         
else if (this->M_ENGINE == "Way2")
     dllname = "dllname2.dll";
else
     throw configuration_error();

HMODULE h = LoadLibraryA(dllname.c_str());
typedef engine* (*TCreateEngine)();

TCreateEngine func = (TCreateEngine)GetProcAddress(h, "getEngine");
engine* e = func();

The name of the exported function will probably get mangled, so you could either use DEF files or extern "C" in your DLLs, also don't forget to check for errors.

Idan K
I believe the TCreateEngine type definition to be wrong (could it be: typedef engine* (*TCreateEngine)(); ??), but the anwer is good. +1
David Rodríguez - dribeas
Oh, and don't forget to use extern "C" when declaring and defining the function, or else the compiler will mangle the names.
David Rodríguez - dribeas
yes the typedef was wrong, I corrected it, thanks. about the extern "C", I mentioned it in a comment in the code, but I'll move it to the actual answer so it'll be more clear.
Idan K
I am doing all of this in Managed C++ not native. Does it work the same way?
Dewm Solo
Everything is managed ...from my engine base and child engines and the main project....everything is...
Dewm Solo
if your classes aren't managed c++ ones, no reason this shouldn't work. if they are managed classes (declared with the "ref" keyword) you could use the Assembly class instead of LoadLibrary and GetProcAddress.
Idan K
A: 

The solution I came to is the following:

Engine_Base^  engine_for_app;
Assembly^ SampleAssembly;
Type^  engineType;

if (this->M_ENGINE == "A")
{
 SampleAssembly = Assembly::LoadFrom("path\\Engine_A.dll");

 engineType = SampleAssembly->GetType("Engine_A");
 engine_for_app = static_cast<Engine_Base^>(Activator::CreateInstance(engineType, param1, param2));
}
else
{
 SampleAssembly = Assembly::LoadFrom("path\\Engine_B.dll");

 engineType = SampleAssembly->GetType("Engine_B");
 engine_for_app = static_cast<Engine_Base^>(Activator::CreateInstance(engineType, param1, param2, param3, param4));
}

I used the answer from Daniel and the comments that were made on his answer. After some extra research I came across the LoadFrom method.

Dewm Solo