
mhx
Enthusiast
Jun 30, 2001, 11:53 AM
Post #1 of 6
(1219 views)
|
|
Plugins & Interfaces
|
Can't Post
|
|
Hi all! First, this is a problem that I have solved, I don't mind if I receive no replies. I'm just curious if there's a better or shorter or more elegant or more perlish solution than the one I applied. Here's my problem: I was writing a tool, actually a parser for messages from a debugger I've written that runs in an embedded system, as it occurred to me that this parser should be easily extendable by other Perl modules - which I'm going to call plugins from now on - without touching the source of the parser script. I wanted the parser to have an option '-P plugin' so the user can pass the name of the plugin he has written. The only thing that a user has to take care of when writing a plugin is that he has to provide an interface description in his plugin. I solved this with a hash table. Perhaps all this is easier to understand with some code. So here's what a plugin template looks like in general. I left out some sections that have nothing to do with the problem. I left in the original comments, so it'll be easier to understand.
package EcgDebug; use strict; # ( I left out all the header stuff ) #----- Initialization Code --------------------------------------------- BEGIN { no strict 'refs'; $DBENV = \%{'main::DBENV'}; } #----- Interface Definition -------------------------------------------- # The interface between the debug parser and the plugin is a hash table # called `Interface'. It tells the debug parser which routines to call # for certain messages or debug message ids. our %Interface = ( # Debugger Control & Status Message Headers # ````````````````````````````````````````` # The debug parser needs to know these in case you want to put # routines for debug messages in your plugin. The two keys must be # both existing or both missing, otherwise the parser will fail. DebugControlHeader => 'ThreeChanEcg_1_General_9_Int_1', DebugStatusHeader => 'ThreeChanEcg_1_General_10_Int_1', # Parser Table # ```````````` # The parser table again is a hash table with either debug message # ids or message headers as keys and subroutine references as values. # You may add as much entries as you like. If your parser subroutine # can handle more than one header or message id, feel free to use # the same subroutine reference for different keys. Parsers => { ThreeChanEcg_1_Ecg_1_MdilRTSA_2_ObsVSPk => \&MyParser, DB_MY_DEBUG_ID => \&MyParser, }, # Routine Table # ````````````` # The routine table is used for describing special properties of # your parser routines. At the moment, the only special property # of a routine is `options', which means that this routine can # handle several option. Routines only need to be entered in the # routine table if they have special properties. So if your # routine doesn't take options, you don't have to put it in the # routine table. # An option is defined as a key => value pair with the key being # the name of the option (an upper- or lowercase character) and # the value being a string describing the option. The option # name may be followed by a colon (`:') if the option takes an # argument. Routines => { \&MyParser => { options => { 'w' => 'write wave files', 'e:' => 'do something mysterious', }, }, } ); ###################################################################### # # ROUTINE: MyParser # # WRITTEN BY: ON: # CHANGED BY: ON: # ###################################################################### # # DESCRIPTION: This is my favourite message parser. # ###################################################################### sub MyParser { return whoami unless @_; # if called without arguments, return name my $opt = shift; # get options Print "No Options :-(\n" unless scalar %{$opt}; foreach( keys %{$opt} ) { Print "Option `-$_' => $opt->{$_}\n"; } if( exists $DBENV->{compiler} ) { Print "ah, we're using the $DBENV->{compiler} compiler...\n"; } else { Print "no compiler info available...\n"; } # just print the rest of the arguments Print "@_\n"; } 1; To use the plugin code from within the parser, I run the following loop after parsing the arguments and preparing some other things in the parser script. Again, I left out some further checking code that is not relevant here.
foreach( keys %plugin ) { eval "require '$_.pm'" or die "$@\n"; { no strict 'refs'; die "Invalid interface for plugin `$_'\n" unless defined %{$_.'::Interface'}; $plugin{$_}->{interface} = \%{$_.'::Interface'}; } # ... } Now, there are two things that I'm curious about: First, is there a better way to do it? I'm sure there are other ways, but which is the most perlish? Is there generally a better way to implement an interface with the requirements I have? The second thing I'm sure there's a better solution for: I have a hash table DBENV in the parser script, which I'd like to share with all plugins. You can see my solution in the BEGIN block in the plugin. I would like it better if I could access the hash as a hash and not as a hash reference from within the plugins. It's now up to you: complain about my code, tell me what I could do better, any feedback is appreciated. -- Marcus
|