An interface descriptor can now contain validation information for individual parameters, so that clients such as MAXScript can validate values given as parameters to FPInterface
calls, prior to making the call. The validation info can be in the form of a range of values for int and float types, or more generally, a validator object that is called the validate a parameter value.
MAXScript now applies the validations if supplied and generates descriptive runtime errors if the validation fails.
The validation info is specified in the FPInterface
descriptor in optional tagged entries following the parameters to be validated. The two possible tags are f_range
and f_validator
. Here's an example from a possible mixin interface to Cylinder:
static FPInterfaceDesc cylfpi (
CYL_INTERFACE, _T("cylMixin"), 0, &cylinderDesc, FP_MIXIN,
//...
cyl_setRadius, _T("setRadius"), 0, TYPE_VOID, 0, 1,
_T("radius"), 0, TYPE_FLOAT, f_range, 0.0, 10000.0,
cyl_setDirection, _T("setDirection"), 0, TYPE_VOID, 0, 1,
_T("vector"), 0, TYPE_POINT3, f_validator, &cylValidator,
//...
end
);
In this above example, the "radius" parameter is defined to have a range 0.0 to 10000.0. An f_range
spec can only be used for int (TYPE_INT
,TYPE_TIMEVALUE
, TYPE_RADIOBTN_INDEX
, TYPE_INDEX
, TYPE_ENUM
) and float parameters (TYPE_FLOAT
,TYPE_ANGLE
, TYPE_PCNT_FRAC
, TYPE_WORLD
, TYPE_COLOR_CHANNEL
) and is given as a pair of low and high range values. The values must be floating point or integer as needed by the TYPE_xxx code, you cannot specify integer range values for float types and vice versa, hence the 0.0 and 10000.0 in the example above. MAXScript checks parameter values against these supplied ranges and will generate a descriptive error message for out-of-range values.
The "vector" parameter in the above example has a validator object specified. This must be a pointer to an instance of a class derived from the new class, FPValidator
, defined in iFnPub
.h. This is a virtual base class, containing a single method, Validate()
, that is called to validate a prospective value for a parameter. You would typically subclass FPValidator
in your code and provide an implementation of Validate()
to do the validation. Here is the FPValidator
virtual base class:
class FPValidator
{
public:
// validate val for the given param in function in interface
virtual bool Validate(FPInterface* fpi, FunctionID fid,
int paramNum, FPValue& val, TSTR& msg)=0;
};
The Validate()
function is called with interface, function-within-interface and parameter-within-function identifiers and an FPValue
to validate. It can optionally install an error message string in the 'msg' TSTR& parameter for the user of the validator to display. If there are many parameters to validate this way, you can choose to provide a separate subclass for each parameter or a single subclass and switch on the parameter identification supplied.
For the Cylinder example above, the "vector" parameter might be required to be given as a unit vector, so the validator might check for this as follows:
class CylValidator : public FPValidator
{
bool Validate(FPInterface* fpi,
FunctionID fid,
int paramNum,
FPValue& val,
TSTR& msg)
{
if (fabs(Length(*val.p) - 1.0) > 1e-6)
{
msg = "Direction vector must be unit length."
return false;
}
else
{
return true;
}
}
};
Note that the type is already checked by the caller, since it has type info for the parameter, so you don't also need to check the FPValue
type.
Note, also, that the FPValue
& val argument given to Validate()
is a reference to the actual parameter value to be given to the called function, so it is also possible to massage the value rather than reporting an error. For example, the above sample Validate()
method could normalize the vector and always return true. The type must not be changed in this process, only the value can be adjusted.
A singleton instance of this FPValidator
subclass would created, typically as a static instance, to be given in the f_validator
specification: static
CylValidator cylValidator;
If you need to get at the validation metadata, it is available in the new 'options' data member in an FPParamDef
instance for a parameter. If non-NULL, this points to a FPParamOptions instance which contains the range or validator information for the parameter. See class FPParamOptions in iFnPub
.h for details.