views:

237

answers:

9

What are the “normal” ways to do plug-ins in compiled languages (C#/C/C++/D)? I am specifically interested in language agnostic approaches but language specific is not unacceptable.

For the time being, “compile time” plug in approaches (just include the code or not and everything works) are valid but things that can migrate to a more dynamic approach are preferred.

Regarding the runtime type, I'm more interested in the mechanics of loading the plug-in and whatnot than designing the plug-in/app interface

EDIT: BTW the plugin would be a slave not a master. The basic action of a plug-in would be to that under a given situation, it would be called on to "do its thing" and be given an environment object that it should use to get what it needs to operate.

A: 

My first idea was to have the plug in include a static constructor that registers the plug in on startup. However C# (one of the languages I need to work with) doesn’t seem to call static constructors except on demand and that doesn’t work. Any ideas?

BCS
A: 

Interfaces with a void Register(EventSource) seem to work well - see ASP.NET's IHttpModule.Init(HttpApplication) for an example.

This allows the app author (who controls EventSource) to add events as needed, without needing to extend the IPlugin interface (inevitably leading to IPluginEx, IPlugin2, IPlugin2Ex, etc.)

Mark Brackett
OK that may be useful in general but how does the call to Register get invoked? Who does it, how does the main app known to invoke that?
BCS
+1  A: 

I've found that the hard parts about plugins are: finding them, resolving their dependencies, and handling version issues. How you handle these issues should be clear to you and to your plug in authors. If you get these issues wrong, it will cause no end of pain. I would look at scripting languages and applications which use plugins for ideas on what works well.

Static constructors are more often than not, "clever" in the bad sense. Since you are going to have to load (C/C++: dlopen and friends under Linux) the plugins one at a time anyway (in the dynamic case), you may as well manifestly initialized them as you do so. Among other things, that may give you an opportunity to reject plugins without expected api's.

Note, you don't have to use dynamic load libraries for plugins. Other mechanisms can also be used: shared memory, sockets, etc....

ejgottl
I would like to avoid "clever" in the long run. In the short run it might be acceptable for the initial hooking (re compile time plug-ins)
BCS
+2  A: 

Mono.Addins seems to be a good solution for .NET. I believe it includes API to allow you to download plugins (or addins) from a repo and dynamically load it into a running assembly.

Mark A. Nicolosi
<Shudder/> I'm not even /looking/ at how the user gets it yet. I'm still working on how to avoid having to hack every additional feature into the main code base.
BCS
http://codeelegance.blogspot.com/2009/06/monoaddins-in-msnet-20-application-part.html
codeelegance
A: 

An approach I've used (in .NET) is to have the host make an initial call to the plugin (via Reflection), starting the plugin and passing a reference to the host that the plugin saves. The plugin then calls methods on the host (also via reflection) as necessary.

I think with most plugins, the calls would usually be made in the other direction (i.e. the host would call the plugin as necessary). In my case, the plugins themselves had UI elements that needed to make use of host functionality.

MusiGenesis
Mine goes the 2nd direction.
BCS
+2  A: 

For compiled languages (where compiled means the program runs as a native executable, without any sort of virtual machine), you pretty much need to use some sort of platform-specific shared library approach. In Windows, this means using DLLs.

You define your plugin interface in terms of a set of functions (with specific names, arguments, and calling conventions). Then, you load the addresses of the functions within the shared library and go to town. In Windows, this means using GetProcAddress(), and then casting the return value to a function pointer of the appropriate type in C, or whatever the equivalent is in the language you're using.

Another option that may or may not be more desirable would be to run a virtual machine for another language from within your native application, and have plugins be code for that language. For example, you could run a Python VM using CPython and dynamically load Python modules.

Adam Rosenfield
Nice answer. That is the type of info I'm looking for.
BCS
+1  A: 

It really depends on what you want to do. The common Unix pattern as seen in Emacs and the Gimp is to write a program that consists of a small compiled component that exposes essential functionality that an interpreted component uses to do everything. Pluglins that provide new functionality that can be built on top of the app are easy, but you need to be very flexible in the primatives you provide for this to be possible. At the opposite extreme imagine a photo editor which can save in multiple formats. You want to allow people to write their own file format handlers. This requires making your code use a simple set of primitives, then picking an implementation at runtime. In straight (Unix) C use dlopen, in C++ use extern C which limits what you can do and dlopen. In Objective-C you have a class to do it for you. In the first case you are making or reusing an interpreter so you have free reign to do it however you want.

Watson Ladd
A: 

low level module implementation issues aside (e.g. windows DLLs and implementation issues), a game engine I use just has a global registration function in the DLLs, and attempts to find and call it on every dll in the plugin directory. the registartion function does any bookkeeping necessary to expose functionality.

Dustin Getz
+1  A: 

For a slave-type plugin where each plugin encapsulates a different implementation of a common set of functions, I would just deposit the DLLs in a plugin folder known to the host application (e.g. "c:\program files\myapp\plugins), and then call the DLLs from the host via Reflection.

You could do some sort of elaborate registration process when each DLL is installed, but I've never experienced any problems with the simple plugins-in-one-folder approach.

Edit: To do this in C#, you just add a public class to the DLL (named "Plugin" or whatever), and implement needed functions there. From your host, you then create an object of type Plugin and call its methods (all using Reflection).

MusiGenesis
Any specific advice on how to approach this? Things like should the dllmain function do the internal registration or should the app call another function? Should their be some sort of standard shim that the extender links with as part of the dll and builds under?
BCS
In C#, you just add a public class to the DLL (named "Plugin" or whatever), and implement needed functions there. From your host, you then create an object of type Plugin and call its methods (all using Reflection).
MusiGenesis
From what little I know, implementing a plugin architecture in C or C++ is a lot more difficult, and I wouldn't be qualified to comment. But I'd still be more qualified than I would be with D. :)
MusiGenesis
My current thinking is that I will implement some way to get the plug-in to construct an object derived from a common type and hand it to me. (Musi's 1st comment would work for C#) once I have that I think I can get the rest working. Any pitfalls I'm missing?
BCS
@MusiGenesis BTW could you edit your comment into the answer?
BCS
Done. And good luck - there are pitfalls in everything. :)
MusiGenesis