Within your plug-in, each interface is usually organized into 3 separate sections of code:
The following is an (imaginary & simplified) example of an interface on EditMesh called 'FaceOps' with two functions, delete and extrude:
The public interface (in a public header file)
#include"iFnPub.h" #define EM_FO_INTERFACE Interface_ID(0x434455, 0x65654) #define GetEMFaceOpsInterface(cd) \ (EMFaceOps *)(cd)->GetFPInterface(EM_FO_INTERFACE) enum { em_delete, em_extrude, }; class EMFaceOps : public FPStaticInterface { public: virtualint Delete (Mesh* m, BitArray* faces)=0; virtualvoid Extrude(Mesh* m, BitArray* faces, float amt)=0; };
This defines the Interface_ID for the 'FaceOps' interface and provides a helper macro for getting the interface pointer from the plug-in's ClassDesc. This is followed by an enum for the two function IDs and then the virtual interface class itself in which each function is declared as a pure virtual function. The return and parameter types of these functions are restricted to a fixed set, defined below. Fixing this set is necessary so that systems that access the interface metadata or use the dispatch form of calling have a known set of types to deal with. External code that wishes to call one of these functions, and has access to this public header can do so as in the following example:
EMFaceOps* efi = GetEMFaceOpsInterface(edmeshCD); //... efi->Extrude(mesh, faces, 10.0);
To call this function indirectly, you call the Invoke() method on the interface, giving it the function ID and an FPParams object containing all the parameters, as in the following example:
FPParams p (3, TYPE_MESH, mesh, TYPE_BITARRAY, faces, TYPE_FLOAT, 10.0); FPValue result; FPInterface efi = edmeshCD->GetFPInterface("FaceOps"); efi->Invoke(em_extrude, result, &p); x = result.i;
Any function result is passed back in an FPValue instance, given as one of the args to Invoke, which is a type-tagged variant structure that can hold any one of the FnPub supported types. The FPParams class contains a convenience varargs constructor for building parameter sets as shown. An FPParams instance contains a Tab<> of FPValue instances, each holding a parameter. This example demonstrates how to look up an interface by name in a ClassDesc. The Invoke() function has several overloads, for calling functions with and without results and parameters.
The implementation class (in the .cpp implementation file)
class EMFaceOpsImp : publicEMFaceOps { DECLARE_DESCRIPTOR(EMFaceOpsImps) BEGIN_FUNCTION_MAP FN_2(em_delete, TYPE_INT, Delete, TYPE_MESH, TYPE_BITARRAY) VFN_3(em_extrude, Extrude, TYPE_MESH, TYPE_BITARRAY, TYPE_FLOAT) END_FUNCTION_MAP intDelete(Mesh* m, BitArray* faces) { //... do the delete return face_count; } voidExtrude(Mesh* m, BitArray* faces, float amt) { //... do the extrude } };
The interface implementation class specializes the virtual interface class defined in the public header. It can contain any implementation-required data members and utility methods, but must at least contain implementations for each of the virtual methods defined in the virtual interface class.
The key component shown above is the function map which generates the indirect call dispatcher used by the Invoke() method. This dispatcher unbundles the parameters from the indirect call parameter structure, forwards them to the correct implementation method and bundles the return value into an FPValue. It is specified here using the map macros that come with the FnPub system (in iFnPub.h), in a manner somewhat similar to the message maps in MFC. You can implement this dispatcher by hand, but it is advised that you use the map macros.
The map is bounded by BEGIN_FUNCTION_MAP and END_FUNCTION_MAP and contains one entry for each function in the interface. The map entry macros used come from a set named according to the number of arguments and whether the function is void. In this case, FN_2 is used for a 2 argument function returning a value and VFN_3 is used for a void function taking 3 arguments. The FN_2 macro, for example, takes the function ID, the return type, the virtual interface method name, and the types of the two arguments. There are more details on these macros in the ref section below. There is a separate declaration macro, DECLARE_DESCRIPTOR(< class>) that must be specified in the interface descriptor class. In the implementation class for a static or action or core interface, you provide a single descriptor declarator with the DECLARE_DESCRIPTOR() macro, giving the current class as the macro parameter and a function map using BEGIN_FUNCTION_MAP and END_FUNCTION_MAP macros as before. The DECLARE_DESCRIPTOR(< class>) is only required for FPStaticInterface subclasses; FPMixinInterface subclasses should only have the FUNCTION_MAP/END_FUNCTION_MAP map table.
The interface definition (in the .cpp implementation file)
static EMFaceOpsImp emfi ( EM_FO_INTERFACE, _T("FaceOps"), IDS_EMFO, &edmeshCD, 0, em_delete, _T("delete"), IDS_DELETE, TYPE_INT, 0, 2, _T("mesh"), IDS_MESH, TYPE_MESH, _T("faces"), IDS_FACES, TYPE_BITARRAY, em_extrude, _T("extrude"), IDS_EXTRUDE, TYPE_VOID, 0, 3, _T("mesh"), IDS_MESH, TYPE_ MESH, _T("faces"), IDS_FACES, TYPE_BITARRAY, _T("amount"), IDS_AMOUNT, TYPE_FLOAT, end );
A distinguished instance of the interface implementation class is constructed and registered with the owning ClassDesc. This is the instance given out when the CD is asked for an interface via its GetFPInterface() method. The instance has data members that contain all the metadata for the interface. You normally build this instance as a static in the implementing .cpp file, in a manner similar to ParmBlock2 descriptor instances.
In the example above, the instance 'emii' is statically declared using the FPInterface's varargs constructor. The initial arguments to this constructor provide the Interface_ID, the internal fixed name, localizable descriptor string resID, owning ClassDesc and flags bits. This is followed by a list of function descriptors which themselves each have lists of parameter descriptors. The em_delete function description, for example, consists of a fixed name, a localizable description string resID, a return type, flag bits and a count of the number of parameters. For each parameter, there is a line giving parameter name, description string resID and type.