February 13, 2008
Building Your Own Plugin Framework: Part 5Dynamic C Plugins
C plugins register objects that implement the C_Actor interface. The plugin itself -- and even the C_Actor implementation -- may be C++ classes (using static methods). In this case, I implemented everything in C, just to ensure the system supports pure C plugins (there were a few compilation gotchas before it worked). The C plugin registers a single monster called MellowMonster. The header file of this quaint monster is presented in Example 4. This is a C object, so there is no class definition only the global functions MellowMonster_create() and MellowMonster_destroy(), which correspond to PF_CreateFunc and PF_DestroyFunc. The names are qualified with the monster type because, in general, a single plugin may register monster types with different pairs of create()/destroy() functions and in C we can't hide them in a namespace or as static methods of a class.
#ifndef MELLOW_MONSTER_H #define MELLOW_MONSTER_H
Example 4
Example 5 presents the actual monster. It's just a struct that contains a C_Actor member and optionally more monster-specific data. Not much of a monster so far.
typedef struct MellowMonster_
{
C_Actor actor;
Example 5
Example 6 is the implementation of the C_Actor interface and consists of two static functions (not visible outside of this compilation unit) -- MellowMonster_getInitialInfo() and MellowMonster_play() -- that correspond to the IActor methods. The big difference is that the C++ methods get the object instance as the implicit 'this' pointer. In C, you must pass a C_ActorHandle explicitly (well, not you, but the PluginManager) and the C functions laboriously cast the handle to a MellowMonster pointer. When the C_Turn object is used in the play() function, you must pass it its own handle too.
void MellowMonster_getInitialInfo(C_ActorHandle handle, C_ActorInfo * info)
{
MellowMonster * mm = (MellowMonster *)handle;
strcpy((char *)info->name, "MellowMonster");
info->attack = 10;
info->damage = 3;
info->defense = 8;
info->health = 20;
info->movement = 2;
/* Irrelevant. Will be assigned by system later */
info->id = 0;
info->location_x = 0;
info->location_y = 0;
}
void MellowMonster_play(C_ActorHandle handle, C_Turn * turn)
{
MellowMonster * mm = (MellowMonster *)handle;
C_ActorInfoIterator * friends = turn->getFriends(turn->handle);
}
Example 6
Example 7 contains the MellowMonster_create() and MellowMonster_destroy() functions and ties up loose ends. The MellowMonster_create() function allocates a MellowMonster struct (using malloc, of course), assigns the pointer to the handle member of the actor field (without checking if the memory allocation failed, boo :-), and goes one to assign the MellowMonster_getInitialInfo() and MellowMonster_play() functions to proper function pointers. Finally it returns the MellowMonster pointer as an opaque void pointer. It is important that the C_Actor interface be the first member of the MellowMonster struct, because the PluginManager (via the adapter) casts the returned void pointer to a C_Actor pointer and treats it as such from then on.
The MellowMonster_destroy() frees the memory. If there is any need for destructor-like cleanup, it can do it too.
Let's check the initialization code of the C plugin in Listing Four. It looks just like the C++ plugin. This isn't surprising because it is a C function that needs to prepare a C struct and call yet another C function. The only real difference is that the registered programming language for MellowMonster is PF_ProgrammingLanguage_C. That tells the PluginManager that it's dealing with a C object and it should adapt it.
#ifdef WIN32 #include "stdafx.h" #endif
Listing Four
As you can see, working at the C API level is much thornier. You need pass explicit handles around, cast a lot, pay attention to function names, and hook up free functions to your monster C_Actor interfaces. It's not fun, but it's survivable if you must work with C.
|
|
||||||||||||||||||||||||||||||
|
|
|
|