Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object Registration in Static Library

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?

like image 534
beef2k Avatar asked May 17 '09 01:05

beef2k


3 Answers

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.

like image 132
Harald Scheirich Avatar answered Sep 20 '22 23:09

Harald Scheirich


Since it is a static libary, you might consider having the manager register the plugins (instead of the plugins registering themselves). The header file can define some preproc symbol (ie JPEG_PLUGIN) that controls whether or not the manager registers the plugin based on the inclusion of the header:

#include "JpegPlugin.h"

void PluginManager::RegisterPlugins()
{
#idef JPEG_PLUGIN
    RegisterPlugin(&jpeg_instance);
#endif
}

JpegPlugin.h doesn't necessarily need to include the definition of the JpegPlugin. It could be something like this:

#ifndef JPEG_PLUGIN_HEADER
#define JPEG_PLUGIN_HEADER

#if 0 // change this to 1 to use the plugin
#define JPEG_PLUGIN
#include "Jpeg_PluginCls.h"
#endif

#endif
like image 35
sean e Avatar answered Sep 20 '22 23:09

sean e


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.

like image 1
j_random_hacker Avatar answered Sep 23 '22 23:09

j_random_hacker