1

For the C++ library I am currently developing I have seen the advantages of a plug-in system based on shared libraries. A feature will become available to the user of the library just if one of the shared libraries that are in a directory scanned at initialization time is offering that.

Using dlopen, the shared libraries will be searched for two symbols: one function that returns the string that names the feature they implement, and a create function that instantiates the class and returns a pointer to the basse class. Something like this:

In Library A

#include "Instance.hpp"
extern "C" const char* type() {
  return "InstanceA";
}
//... class InstanceA definition, inherits from Instance
extern "C" Instance* create() {
    return new InstanceA();
}

The Core library will scan the plug-ins directory and keep a map string->pointerToCreateFunction in order to create new instances.

This is really handy, and a pretty standard thing to do. If the user code tries to instantiate an InstanceX but no shared library implement that type, an error will be given, everything will work perfectly.

But this framework will be used also for iOs development, and the App Store doesn't allow loading 3rd party shared objects. I would like to keep this modular plug-in structure even when loading a self contained version of the library that links statically the plugins. Note that at project management level this would be as easy as defining a variable in CMake that creates static versions of the plugins and links them statically. This would also exclude the dynamic loading part of the code.

What I am missing is how to invert the mechanism: while for shared objects the core library will leverage on the file system to learn about the possible type of instances that can be used, I don't know how to "register" them without changing big part of the code, and without going in the static initialization fiasco. Looks like the only way to have that is subsituting the code that scans the types and the create function by including all the possible headers and having a big switch like

Instance* theInstance;
if (instanceRequired == "instanceA") 
  theInstance = new InstanceA();
etc etc...

Do you have any thought on a way to avoid including all the headers and having to change the code in Core each time a new instance is added?

Alex Darsonik
  • 597
  • 5
  • 18

1 Answers1

0

I do such things through those pesky static objects that call register in ctor.

The ordering problem is dodged by making the map itself a local static, so getting constructed on the first client call, wherever it comes from. (Threading and similar issues are dodged by restricting thread launch to until-main time, and by that time all important things are forced.)


class RegMap;  // the registry
RegMap& GetRegistry(); // the singleton access function; you can make a separate const/nonconst version or provide another wrapper for registering

//    implementation in core.cpp:
RegMap& GetRegistry()
{ 
    static RegMap m;
    return m;
}

//in client ctor:
GetRegistry().Register( key, func);
Balog Pal
  • 16,195
  • 2
  • 23
  • 37
  • so, pesky static objects = template instantiation, with constructor calling register? I guess that I should try that, but minding that .o files are not linked unless explicitly referenced, using a linker option, see http://stackoverflow.com/questions/9549714/trying-to-force-static-object-initialization – Alex Darsonik Jun 27 '13 at 11:19
  • Some class with ctor, yes, the 'template' part is optional, but can be handy – Balog Pal Jun 27 '13 at 11:21
  • can you give an example? if the map is local to the Core lib I can be sure that it will be created the first time someone registers to it or will be found empty by the Core code if no one has registered, cause no plugins were linked? – Alex Darsonik Jun 27 '13 at 11:23
  • I normally use win .DLLs that have no problem with dropping stuff, you indeed may look for methods to force usage. – Balog Pal Jun 27 '13 at 11:23