The Function Publishing API

The Function Publishing API consists of two main components, a function descriptor system and a function calling mechanism. The descriptor system is used by the publishing component to declare the functions it is publishing and provide necessary descriptive data and may be used by a component's client to enquire about published functions. The calling system is used by a component's client to invoke one of the published functions.

Interfaces

Functions are published in one or more Interfaces by your plug-in. If you have a large number of functions to be published, you might organize them functionally into different interfaces to make them more manageable for the user. For example, EditMesh might publish vertex functions, edge functions and face functions in separate interfaces. Action functions must be published in their own (set of) interface(s). Each interface is represented by an instance of a class derived from the base class FPInterface. You normally create these instances as static objects using the FPInterface constructor, in much the same way as ParamBlock2 descriptors are created. Each interface contains a list of the functions it publishes and the parameters they take, along with type and name and other descriptive info for all these things. All the interfaces for a particular plug-in class are kept in its ClassDesc object. An external system can find out about the interfaces you publish by calling various query methods on ClassDesc that access these interface definition objects. As well as these enquiry or 'reflection' methods, an FPInterface also has the calling methods for actually invoking a particular function in the interface, so that if something has hold of one of your interfaces, it can call any of its published functions.

Additionally, a Mixin interface is provided which can be multiply-inherited by a plug-in's class and returned via its implementation of the above new GetInterface(Interface_ID) method, in the way that most existing object-based interfaces are now implemented.

The FPInterface class is defined in the header fileiFnPub.h, along with all the other FnPub classes & macros described in these notes.

Direct and Indirect Calling

The function publishing system lets you set up interfaces so that functions in them can be called directly, as virtual member functions of the interface object, or indirectly via a dispatching method that takes a runtime function ID and a table of parameters. This is roughly equivalent to the dual vtable and IDispatch interface schemes in COM. Typically, you provide the virtual interface in a public header file so that it can be compiled against by other plug-ins and they can call the virtual functions on an interface object directly. The indirect call mechanism is used by MAXScript and other external systems that look for the published interface functions at runtime using the interface metadata.

Interface and Function IDs

Each interface is uniquely indentified by an Interface_ID, which is a new class in the R4 SDK. This class is structurally very similar to Class_ID, containing two randomly-chosen longwords to provide a unique global ID. It is defined in maxtypes.h

Each function in an interface is identified by an integer ID of type FunctionID. This is similar to the BlockID and ParamID's used in the paramblock2 system. This ID is used in the dispatch-based calling mechanism to identify the function to call.

Interface Organization

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. FN_VA, VFN_VA, FNT_VA, etc., FUNCTION_MAP entry macro variants have same args as _0 macros and specify that the function takes a variable number of arguments (passed directly in FPParams instances). 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.