(Link to AcmlmWiki) Offline: thank ||bass
Register | Login
Views: 13,040,846
Main | Memberlist | Active users | Calendar | Chat | Online users
Ranks | FAQ | ACS | Stats | Color Chart | Search | Photo album
06-16-24 03:38 PM
0 users currently in Programming.
Acmlm's Board - I3 Archive - Programming - Which method is better for a plugin system? New poll | |
Add to favorites | Next newer thread | Next older thread
Which would you suggest?
READ THE POST FIRST
Method A
 
50.0%, 2 votes
Method B   0.0%, 0 vote
Method C
 
25.0%, 1 vote
Something else (post it!)
 
25.0%, 1 vote
Multi-voting is disabled. 4 users have voted.

User Post
HyperHacker

Star Mario
Finally being paid to code in VB! If only I still enjoyed that. <_<
Wii #7182 6487 4198 1828


 





Since: 11-18-05
From: Canada, w00t!
My computer's specs, if anyone gives a damn.
STOP TRUNCATING THIS >8^(

Last post: 6328 days
Last view: 6328 days
Posted on 01-12-06 12:15 AM Link | Quote
I'm just trying to decide which method I should use. Right now I have a functioning system using method A, but the disadvantages are becoming apparent. The system works like this:
Each plugin is a DLL, loaded by the main program. The program provides functions that the plugins can call, and the plugins have a callback function which is used to provide information about things that happen (the plugin being removed, settings changing, etc). Each plugin can be loaded and unloaded at will, and each has a build ID specifying what build of the program they were designed for, so that old plugins can be supported.
Additionally, there is a permission system that can be used to prevent plugins from doing things like loading/unloading other plugins, creating windows, etc. This is only meant to disable unwanted behaviours or allow for buggy plugins to still be partially useable, not as a security system (since most of the functions could be done via the Windows API anyway).

Method A
Each plugin exports an init function which is passed a few pointers. These pointers point to an array of function pointers (the functions provided by the main program), structures containing info about each plugin (the current plugin's index in this array is also provided), and structures containing info about each window created by plugins. To create a window, a plugin calls the window-creating function, and it returns an index into the array of window structures for the window it just created. For notifications, the plugins pass the address of their callback function to a function to register it, and the callback function is called as necessary. (Right now there's a separate function for each notification, but I plan to merge it into one general-purpose function.) The plugins are compiled with a list of which function pointer does what using #define, like so:
#define DoThis Functions[0]
#define DoThat Functions[1]
etc. (Functions is the pointer to the function pointers.)
Pros:
Very fast, as there is no translation that needs to be done for each function call.
Easy to code; just call the functions directly (you can just write DoThis(blah,blah))
Although not recommended, coders can assign whatever name they want to each function.
Cons:
Hard to support old plugins. The function pointer array has to maintain its order (functions can be added, but not rearranged or replaced), which is messy. Whenever a function is changed, if it affects the result at all, the original code has to be kept, either using if statements or changing the pointer to point to an old version for old plugins.
Not likely to work on 64-bit systems due to using 32-bit function pointers (and if I used 64-bit pointers, it wouldn't work on 32-bit systems).

Method B
Works basically like Windows. Plugins are passed only one function pointer, which points to a function that retrieves other function pointers. Plugins pass this function a name and it gets the pointer to the function it names (like Windows' GetProcAddress). Notifications are done by a callback function, but the message is a string rather than a numeric ID.
Pros:
Still fast.
Functions can still be written as if they were built in via #define.
Still allows functions to be named as the coder sees fit.
Cleaner code and better version support. If a function is changed, it can simply be renamed (Function2 as opposed to Function) with the original version kept under its old name, and new plugins can be rewritten to use the new version. This also makes it easier to update old plugins. With method A, the build ID is used to determine which version of each function to use, so if your plugin relies on a behaviour that was changed, and you need to use a function not available in that version, you need to re-write all code that relies on that behaviour to do the upgrade. With method B, you can just continue to use the old version. (To keep code size small, rather than duplicating a function just to tweak something, old versions may simply be coded to set a global variable and call the new version (parameters could be used, but then the plugins would have to supply them as well), and the new version coded to act like the old one if this variable is set. However, this could cause problems with multiple threads.) Finally, since plugins reference each function by name rather than by number, the main program's internal list can be shuffled as needed to keep it organized.
Plugins can register their own functions, so that others can call them, providing an easy cross-plugin communication method.
Cons:
Still likely to present a problem with 64-bit systems.
When handling notifications, processing a string message would be a bit slower than a numeric one. The message would have to be compared to each one in an array to determine how to react.
Plugin code would be a bit messier as they would have to keep an array of function pointers (or several separate pointers) and retrieve each function's address on startup. This could be partially resolved by having 'all' passed to the pointer-retrieval function return a pointer to a list of function names and their pointers.

Method C
Entirely string-based method. Plugins are passed the pointer to a command function. To call a function, a plugin calls this command function and specifies the name and parameters in a string, like this:
DoCommand("DrawString 10,30,\"Blah blah\")
Or possibly like this (specifying what each parameter is):
DoCommand("DrawString X=10 Y=30 Text=\"Blah blah\")
Message handling would work the same way, with each message being a string specifying both its message and parameters.
Pros:
Easy to modify functions. Parameters can be added, and those that aren't specified can be reset to default values by the command handler.
No need to duplicate functions. Added parameters can determine which behaviour to use. Several names can even be mapped to one function, using the name to determine which behaviour to use. (The functions could be given an additional parameter, specifying what behaviour to use, which is not specified by the plugins.)
Would work on 32, 64, 128, or even 16777216-bit systems.
Any function can easily be called via a debug console, or an external program could send a window message to the main program's window (which is invisible and exists only to recieve messages) pointing to a string to execute.
Multiple function calls could be combined into one string.
Special escape codes could be used to insert things into strings. For example, the name of an internal variable could be specified to have that variable's contents inserted, even if it's not a variable that plugins can access directly. This could also be used to give each function separate read and write access to internal variables (eg other plugin info) by having functions to do this (just pass the variable name to the command handler to read (the handler returns its value) or something like "variablename=6" to write).
Like with method C, plugins can register their own functions. In this case they needn't use a specific set of parameters each time either.
Cons:
Hell of a lot slower due to all the sprintf(), sscanf(), and strcmp() calls needed.
More difficult to code. Instead of doing this:
DrawString(xpos,ypos,text);
You would have to do this:
sprintf(Temp,"DrawString %d %d \"%s\"",xpos,ypos,text);
DoCommand(Temp);

A variable-argument method could potentially be used to call the command function, but it'd essentially be the same thing. #define may be useable to allow you to write it the first way, but it'd still be doing the second method. May be a problem if Temp is too short.
No type safety.

Open to suggestions if anyone has any other ideas.


(edited by HyperHacker on 01-11-06 11:35 PM)
(edited by HyperHacker on 01-12-06 11:31 PM)
DarkPhoenix

Red Goomba


 





Since: 12-27-05

Last post: 6332 days
Last view: 6332 days
Posted on 01-12-06 01:10 AM Link | Quote
As much as my vote tends to disagree with me (did I misclick? I'm quite sure I clicked B), Method B seems like the best compromise. Fighting to support 64-bit systems seems to me (and I may be biased from the fact that I don't use a 64-bit processor) to be overkill. The user base is too small to kill yourself over...though I guess that probably depends on what you're writing. The Windows style solution will probably be the most sane to work with if you plan on releasing newer versions every so often, or releasing the sourcecode, especially since plugin developers might not be too happy if their plugins stop functioning properly whenever you release a major update.
HyperHacker

Star Mario
Finally being paid to code in VB! If only I still enjoyed that. <_<
Wii #7182 6487 4198 1828


 





Since: 11-18-05
From: Canada, w00t!
My computer's specs, if anyone gives a damn.
STOP TRUNCATING THIS >8^(

Last post: 6328 days
Last view: 6328 days
Posted on 01-12-06 01:26 AM Link | Quote
It's small now, but I imagine it'll grow as 64-bit CPUs become cheaper and more supported. And really, I'm not concerned about people having to update their plugins to be compatible as I am with older, abandoned ones not working anymore. When I upgraded to lololol V1.5, a lot of my older extensions didn't work any more, and some of them have never and probably will never be updated to work with it. I want to avoid that.


(edited by HyperHacker on 01-12-06 12:27 AM)
||bass
Administrator








Since: 11-17-05
From: Salem, Connecticut

Last post: 6329 days
Last view: 6327 days
Posted on 01-12-06 01:50 AM Link | Quote
I'd just like to take a second to point out that the 64 bit version of windows creates a virtual 32 bit runtime for any win32 processes. Assuming they ran XP or Vista, anyone running the 32 bit version of your software, even on a 64 bit system would automatically be given a 32 bit runtime enviroment to run in.

Also, you could always make a translation/proxy plugin to run 32 bit plugins on the 64 bit version of your software.
HyperHacker

Star Mario
Finally being paid to code in VB! If only I still enjoyed that. <_<
Wii #7182 6487 4198 1828


 





Since: 11-18-05
From: Canada, w00t!
My computer's specs, if anyone gives a damn.
STOP TRUNCATING THIS >8^(

Last post: 6328 days
Last view: 6328 days
Posted on 01-12-06 02:34 AM Link | Quote
I thought it would do that, but wasn't quite sure. (Guess it kinda has to. )
||bass
Administrator








Since: 11-17-05
From: Salem, Connecticut

Last post: 6329 days
Last view: 6327 days
Posted on 01-12-06 04:27 PM Link | Quote
Originally posted by HyperHacker
I thought it would do that, but wasn't quite sure. (Guess it kinda has to. )
Yea. Somewhat surprisingly though, 16 bit windows applications no longer run. That kinda sucks because alot of GOOD older games like the origional Dr. Brain games were all 16 bit. Then again, you can always install DOSBox.
sloat



 





Since: 11-18-05
From: Delaware, US

Last post: 6431 days
Last view: 6431 days
Posted on 01-12-06 07:37 PM Link | Quote
I'd say keep the main program functions separate and make a plugin SDK -- an interface for the plugin to access the main program (if it really needs it). There might be a way to have the application export functions (like a dll) and use a header/library for the plugin to have access. Or if that doesn't work, you can have a message-only or otherwise hidden window in the application for the plugin to communicate with via messages.

The rest of the plugin would be a bunch of callbacks that it registers (in the init function or whatever) to specific application events. The main application would keep a linked-list of event-handlers so more than one plugin can handle the same event. When that event gets fired, the application simply walks down the chain until it reaches the end, or a plugin has asked the chain to stop.

I wouldn't worry too much about 64-bit and 32-bit at the moment. Yeah, the plugin would probably have to be recompiled, but so would the application.
HyperHacker

Star Mario
Finally being paid to code in VB! If only I still enjoyed that. <_<
Wii #7182 6487 4198 1828


 





Since: 11-18-05
From: Canada, w00t!
My computer's specs, if anyone gives a damn.
STOP TRUNCATING THIS >8^(

Last post: 6328 days
Last view: 6328 days
Posted on 01-12-06 11:16 PM Link | Quote
Well, poop... 4-way tie. (If DarkPhoenix really did accidentally click A when he meant B, that is.)

Sloat, your idea sounds interesting. Basically like method B but not using strings? It could work, but it loses the advantages you get from using strings.

I've thought about this a bit more and I think I could actually implement a combination of B and C. That is, B, but with a function that does string commands like C. One advantage I forgot to add to B and C (which I'll do now) is that plugins could register their own strings for easy cross-plugin communication. That is, plugin X registers the string "MakeXDoSomething". Once this is done, plugin Y can look up the address for "MakeXDoSomething", and if it exists, call it. The actual call would just pass the parameters along to plugin X in a message.

BTW if it makes any difference, this program is only designed for WinXP and up, since it requires a lot of XP-only features. However I'll probably use similar systems in other programs that may be used on older OSes.


(edited by HyperHacker on 01-12-06 11:29 PM)
DarkPhoenix

Red Goomba


 





Since: 12-27-05

Last post: 6332 days
Last view: 6332 days
Posted on 01-13-06 12:54 AM Link | Quote
My apologies for screwing up your poll...

And, some more reasoning - I could be wrong about this, since I haven't thought this all through, but while it might be useful to combine multiple function calls into one string, among other things, Method C seems likely to produce a higher density of errors. Even if it's just a small syntax issue, convoluted code means more coding time and more debug time, which will likely translate into a lower quantity and quality of plugins for your app. These all might be insignificant sacrifices if plugins aren't really a huge part of the program, like if they're only used for reading/writing files. If, however, they're an integral part to the program's functionality, simplifying the code that plugin developers have to write might have a more significant effect on how well the program succeeds at its task in the long run than even the coding you do on the main program.

...I miss Dr.Brain.
Add to favorites | Next newer thread | Next older thread
Acmlm's Board - I3 Archive - Programming - Which method is better for a plugin system? |


ABII

Acmlmboard 1.92.999, 9/17/2006
©2000-2006 Acmlm, Emuz, Blades, Xkeeper

Page rendered in 0.015 seconds; used 421.55 kB (max 523.58 kB)