On windows we have the .dll files, .so files on Linux, and .dylib files on Mac. They all have one thing in common: they can be loaded at runtime and provide entry points to call into. One example is an online chat client that uses plugins to add support for more protocols (Google Chat, ICQ, IRC, etc). There are plenty more examples but you get the idea: drop a binary in plugins folder and restart the app and you have just added more functionality without having to recompile and redeploy your application.
So how do we do it? We could go the difficult route and use OS specific calls to find and load the plugin file, then do some more platform specific code to extract the entry point and call it. Or, we could make it easy on ourselves and use Boost DLL library 🙂
I am no expert on this library nor do I want to write a complete tutorial on it; the documentation is great. I have just started using it today in fact and wanted to see how easy it would be to get a basic plugin built and loaded by another program. It took all of 20 minutes to come up with a basic structure so I decided to make a short blog post about my experience so far. And so far it looks promising!
I started by creating a dynamic library which exports two function calls: one to get the name of the plugin and another to get its version. Below is all the code needed to create such a plugin with Boost:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <boost/dll/alias.hpp> namespace plugin { const char* plugin_name() { return "Vorbrodt's 1st Plugin"; } const char* plugin_version() { return "1.0"; } } BOOST_DLL_ALIAS(plugin::plugin_name, plugin_name); BOOST_DLL_ALIAS(plugin::plugin_version, plugin_version); |
Next I wrote a simple program which accepts the path to the plugin as a command line argument, loads the library, finally extracts and calls the two entry points. Here’s the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> #include <boost/function.hpp> #include <boost/dll/import.hpp> using namespace std; using namespace boost::dll; using namespace boost::filesystem; int main(int argc, char** argv) { path lib_path(argv[1]); typedef const char* (plugin_name_t)(); typedef const char* (plugin_version_t)(); auto plugin_name = import_alias<plugin_name_t>(lib_path, "plugin_name", load_mode::append_decorations); auto plugin_version = import_alias<plugin_version_t>(lib_path, "plugin_version", load_mode::append_decorations); cout << "Plugin name : " << plugin_name() << endl; cout << "Plugin version : " << plugin_version() << endl; return 1; } |
Plugin name : Vorbrodt’s 1st Plugin
Program output.
Plugin version : 1.0
It works! You can take it from here and build a cool plugin engine. Oh I forgot to mention, the same code compiles and behaves the same on Windows, Linux, and Mac 🙂