views:

360

answers:

4

I have implemented a very basic "plug-in system" as part of a static library. Each "plug-in" implements the support for a specific image format, e.g. GIF, JPEG, etc.. Furthermore, I have a Singleton (a class called PluginManager) that keeps a list of all available plug-ins.

The tricky part is that I want to disable/enable the plug-ins by adding or removing their source files from the project file. To achieve this, each plug-in creates a global variable (with different names) and registers the plug-in in the constructor of that class to PluginManager.

Something like this for the JPEG format...

struct JPEGPlugin
{
  // constructor will register plugin
  JPEGPlugin()
  {
    PluginManager::Singleton().RegisterPlugin(this);
  }

  // plenty of other code
  ...
};

JPEGPlugin jpeg_instance;  // instantiate in global scope

However, while this works perfectly in theory, it fails when linking this static library to other code to build an executable. As long as this executable does not access the plugin globals (like jpeg_instance), the linker does not see a connection (he completely ignores the side-effects of the constructor) and does not include the code in the final executable. In other words, the JPEG plug-in is not available in the final app.

I ran into the problems a couple of times over the years, and I always searched the net for solutions. Each time, I just found pages that basically say that it's a known problem and that I have to live with it.

But maybe someone on SO knows how to make this working?

+1  A: 
sean e
Thanks for the answer. However, as you point out by yourself this introduces a dependency from the manager to the plug-in - something I would like to avoid! The reason is that other people should implement plug-ins without the need of changing the manager class.
beef2k
The solution here does not preclude users of the static lib from registering their own plugins via the mechanism in your example (the manager calls the same RegisterPlugin method in your original example (after I fixed that)). It just ensures that plugins which are shipped with (inside of) the static library are available to the manager that your users link with (without explicitly referencing the plugins themselves)
sean e
+3  A: 

I don't know if this a solution for the way you solved this problem, but we had a similar problem with static registration of an object factory and in Visual Studio we solved it by declaring the classes involved with __declspec(dllexport) this was necessary even though the libraries involved were not dlls. But without this the linker would omit the not referenced classes.

The registry solution we worked a little bit different and did not involve Stack allocated objects. I lifted parts from CPP-unit, that is also where i discovered the __declspec approach iirc.

[edit] We also had to #include the declaration for the registered class from some part of the code.

Harald Scheirich
OK, I just noticed your edit -- that's a big point! :)
j_random_hacker
+1  A: 

This is a followup to Harald Scheirich's answer.

I've done some experimenting, and it seems that MSVC++ 2005's Release Mode (but not Debug Mode) will turn on the /OPT:REF flag to the linker, which according to the LINK documentation, will cause any unreferenced symbols to be removed from the final EXE. And, the webpage for __declspec(selectany) seems to indicate that constructors for global objects are not regarded as being references to an object (incorrectly IMHO, but there you have it). So my guess is that this problem "goes away" for debug builds -- is that correct?

So, I think Harald's suggestion of using __declspec(dllexport) is a convenient way of marking the symbol as "referenced" because it is specified inside the source code. If for some reason you wanted to avoid exporting the symbol, I suspect you could accomplish the same thing by either using the /INCLUDE:mysymbol linker flag, or turning off the /OPT:REF flag.

j_random_hacker
We were having the same problem with debug builds, the whole system we have is a little bit brittle anyway, another condition for this working was that the .h file has to be included from somewhere that is reached. So the problem does not go away with debug builds. The only other i got the problem to go away is when there is a direct reference in coded that is statically reached from somewhere else.
Harald Scheirich
Interesting. It's a shame that it's so hard to get this right, as registering a class in a global variable's constructor is such a nice, maintainable way to develop a plugin system.
j_random_hacker
We're facing the problem in debug and release build. And not only on Visual Studio, but on GCC as well.
beef2k
A: 

Hi Beef2k,

Did Harald's solution work for you? I also have such a problem now, I tried it and it does not look to help me. So I'm keen to get some info on your final solution on this.

Denis

Denis