[Strmod-devel] Making your own module: Dealing with Plugs
Status: Alpha
Brought to you by:
omnifarious
From: Eric M. H. <ho...@om...> - 2003-08-31 01:33:20
|
This is in response to a query from someone on IRC. I tried to engineer plugs and modules so that plugs could be created either dynamically, or be direct, non-pointer member variables of the module they're a part of. Many of the functions dealing with plugs, modules and their relationship exist to support this goal. Typically, when writing a StreamModule, you will derive your own plug type (or possibly types) for your own module. The reasons for all of this will become clear when I explain the i_Read and i_Write functions for a plug. Also, for reasons related to the four functions described immediately below, you are advised to put the declaration for the plug class in the protected section, as users of your Module will not be able to usefully create Plugs. You are also advised to put the constructor and _destructor_ in the protected section of the Plug class and make the Module class a friend of the Plug class. Protected destructors are highly unusual, but, in this case, it's an excellent idea to do this to prevent users of your Plug from attempting to use normal C++ techniques to manage the lifetime of Plugs, and force them to call the module's createPlug and deletePlug functions instead. First, the functions the need to be implemented in your Module class (MyModule) are these: // Ask if a plug can be created on the given side for this module // instance. bool MyModule::canCreate(int side) const; // Ask if the plug instance belongs to this module instance. bool MyModule::ownsPlug(StreamModule::Plug *plug); // A protected function used by the public, non-virtual function // createPlug to actually create the plug. Your plug creation code // (even if it's just setting a flag) should go here. StreamModule::Plug *MyModule::i_makePlug(int side); // Called by a user when a plug should be deleted. This returns false // if the deletion fails. It should only fail if the module instance // doesn't own the plug instance. bool MyModule::deletePlug(StreamModule::Plug *plug) These functions all exist to provide users a way to create plugs that's mediated by the Module class. This mediation is necessary for two reasons. First, the Module class needs to be aware of all the plugs associated with it. Second, the Module class needs to control how plugs are created, since some plugs will be created by the Module as part of it's own state, and it needs to manage their lifetimes carefully. A 'side', as explained elsewhere, represents a protocol role. Often you will have one Plug class per side. A side refers to the set of actions a Module will take with regards to input or output to a plug on that side. It doesn't refer to the identity of any individual plug. The 'side' concept is a rather abstract concept, so here are a few concrete examples from Unix: The 'egrep' command has two 'sides'. One side only recieves lines of text to check against the regular expression. The other side only produces lines that match the regular expression. The 'tee' command also only has two sides, though it may have many plug instances on one of the two sides. One side of 'tee' reads in data, the other side writes it out. Since tee allows you to specify a number of files to write to in addition to stdout, there are many instances of the 'write it out' side, but since each instance of 'write it out' does essentially the same thing, they're on a single side since they only fulfill one role in the 'tee' protocol. The echo service in Unix has one side. That side has both input and output, it's bidirectional. Anything that comes in on that side also goes out on that side. The side is the network socket. The irc service has at least two sides, possibly more. I don't know it very well. But, I do know there's a host<->client protocol, and a host<->host protocol. So there are many clients, but each client speaks the same protocol. And, there are many other hosts to talk to, but (I think anyway) only one protocol used to talk from host to host. The base Plug class needs to know which StreamModule it's a part of, so its constructor takes a reference to the parent StreamModule. This means that the constructor for your derived plug class should also take a reference to the module it's a part of so it can call the base Plug constructor. This means that the minimum constructor should look something like this: MyPlug::MyPlug(MyPlugParentModule &parent) : Plug(parent) { } I'm tired of writing now, but this should be some useful information to start with. :-) Here are links to the source to a couple fairly simple modules and its associated plugs. http://www.omnifarious.org/StrMod/doxygen/ProcessorModule_8h-source.html http://www.omnifarious.org/StrMod/doxygen/ProcessorModule_8cxx-source.html As you might notice, this module has the plugs as member variables and uses flags to track their created/deleted state. Have fun (if at all possible), -- There's an excellent C/C++/Python/Unix/Linux programmer with a wide range of other experience and system admin skills who needs work. Namely, me. http://www.omnifarious.org/~hopper/resume.html -- Eric Hopper <ho...@om...> |