Next: 4.4 Using function dispatch Up: 4. Function dispatch and Previous: 4.2 A more complicated Contents

# 4.3 Adding new types

The VIPS type mechanism is extensible. User plug-ins can add new types and user-interfaces can (to a certain extent) provide interfaces to these user-defined types.

Here is the definition of `im_arg_desc`:

    /* Describe a VIPS command argument.
     */
    typedef struct {
        char *name;
        im_type_desc *desc;
        im_print_obj_fn print;
    } im_arg_desc;

The `name` field is the argument name above. The `desc` field points to a structure defining the argument type, and the `print` field is an (optionally `NULL`) pointer to a function which VIPS will call for output arguments after your function successfully completes and before the object is destroyed. It can be used to print results to the terminal, or to copy results into a user-interface layer.

    /* Success on an argument. This is
     * called if the image processing
     * function succeeds and should be used
     * to (for example) print output.
     */
    typedef int (*im_print_obj_fn)
        ( im_object obj );

`im_type_desc` is defined as:

    /* Describe a VIPS type.
     */
    typedef struct {
        im_arg_type type;
        int size;
        im_type_flags flags;
        im_init_obj_fn init;
        im_dest_obj_fn dest;
    } im_type_desc;

Where `im_arg_type` is defined as

    /* Type names. You may define your own,
     * but if you use one of these, then you
     * should use the built-in VIPS type
     * converters.
     */
    #define IM_TYPE_IMAGEVEC "imagevec"
    #define IM_TYPE_REALVEC "realvec"
    #define IM_TYPE_DOUBLE "double"
    #define IM_TYPE_INT "integer"
    #define IM_TYPE_COMPLEX "complex"
    #define IM_TYPE_STRING "string"
    #define IM_TYPE_IMASK "intmask"
    #define IM_TYPE_DMASK "doublemask"
    #define IM_TYPE_IMAGE "image"
    #define IM_TYPE_DISPLAY "display"
    typedef char *im_arg_type;

In other words, it's just a string. When you add a new type, you just need to choose a new unique string to name it. Be aware that the string is printed to the user by various parts of VIPS, and so needs to be ``human-readable''. The flags are:

    /* These bits are ored together to make
     * the flags in a type descriptor.
     *
     * IM_TYPE_OUTPUT: set to indicate
     * output, otherwise input.
     *
     * IM_TYPE_ARG: Two ways of making an
     * im_object --- with and without
     * a command-line string to help you
     * along. Arguments with a string
     * are thing like IMAGE descriptors,
     * which require a filename to
     * initialise. Arguments without are
     * things like output numbers, where
     * making the object simply involves
     * allocating storage.
     */
    typedef enum {
        IM_TYPE_OUTPUT = 0x1,
        IM_TYPE_ARG = 0x2
    } im_type_flags;

And the `init` and `destroy` functions are:

    /* Initialise and destroy objects. The
     * "str" argument to the init function
     * will not be supplied if this is not an
     * ARG type.
     */
    typedef int (*im_init_obj_fn)
        ( im_object *obj, char *str );
    typedef int (*im_dest_obj_fn)
        ( im_object obj );

As an example, here is the definition for a new type of unsigned integers. First, we need to define the `init` and `print` functions. These transform objects of the type to and from string representation.

    /* Init function for unsigned int input.
     */
    static int
    uint_init( im_object *obj, char *str )
    {
        unsigned int *i = (int *) *obj;

        if( sscanf( str, "%d", i ) != 1 ||
            *i < 0 ) {
            im_error( "uint_init", "bad format" );
            return( -1 );
        }

        return( 0 );
    }

    /* Print function for unsigned int
     * output.
     */
    static int
    uint_print( im_object obj )
    {
        unsigned int *i =
            (unsigned int *) obj;

        printf( "%d\n", (int) *i );

        return( 0 );
    }

Now we can define the type itself. We make two of these -- one for unsigned int used as input, and one for output.

    /* Name our type.
     */
    #define TYPE_UINT "uint"

    /* Input unsigned int type.
     */
    static im_type_desc input_uint = {
        TYPE_UINT,        /* Its an int */
        sizeof( unsigned int ),/* Memory */
        IM_TYPE_ARG,      /* Needs arg */
        uint_init,        /* Init */
        NULL              /* Destroy */
    };

    /* Output unsigned int type.
     */
    static im_type_desc output_uint = {
        TYPE_UINT,        /* Its an int */
        sizeof( unsigned int ),/* Memory */
        IM_TYPE_OUTPUT,   /* Its an output */
        NULL,             /* Init */
        NULL              /* Destroy */
    };

Finally, we can define two macros to make structures of type `im_arg_desc` for us.

    #define INPUT_UINT( S ) \
        { S, &input_uint, NULL }
    #define OUTPUT_UINT( S ) \
        { S, &output_uint, uint_print }

For more examples, see the definitions for the built-in VIPS types:

    $VIPSHOME/libsrc/iofuncs/dispatch_types.c
    $VIPSHOME/include/vips/dispatch.h

Next: 4.4 Using function dispatch Up: 4. Function dispatch and Previous: 4.2 A more complicated Contents