Value: Server basics

From The Foundry MODO SDK wiki
Jump to: navigation, search

A Value is a plug-in server that defines a datatype and its behaviors.

Overview

Named datatypes are used in nexus when specifying attributes or parameters that can vary in type. For example channels, command arguments, and tool attributes are all given by datatypes. The common datatypes are implemented internally, but these types can also be extended by value plug-in servers.

Headers

Tags

none.

Sample Methods

The plug-in server class derives from CLxImpl_Value, and exports the ILxValue interface. There are only a couple of required methods. Of course that will get your datatype into the system but it won't do anything. More interfaces are required for that.

Value::Type()

The Type() method returns one of the four intrinsic types supported for values. In most cases plug-in types will be objects, so they have to be read through the Value() and Object() methods of the various interfaces that access them.

        unsigned
val_Type ()
{
        return LXi_TYPE_OBJECT;
}
Value::Copy()

The system must be able to copy the value of one of your value object to another of exactly the same type. The Copy() method is called to do that, and the from argument is a COM object implementing your class as a server. The COM object can be unwrapped using the name of the server (or the cached polymorph) and the copying can them be done with two equivalent C++ object pointers.

        LxResult
val_Copy (
        ILxUnknownID		 from)
{
        CMyValue		*vp;

        lx::CastServer ("myValue", from, vp);
        *this = *vp;
        return LXe_OK;
}

File I/O

If your value is a channel, it can be saved to LXO files and reloaded. The plug-in server class needs to derive from CLxImpl_StreamIO, and exports the ILxStreamIO interface.

StreamIO::Write()

The Write() method using a BlockWrite Interface to write the contents of the value to a block-structured stream. Suppose the value consisted of a float, a string, and an optional int. They could be written using Write() methods. These methods are useful because they detect write errors and throw IO exceptions that will be caught by the CLxImpl_StreamIO wrapper and returned as error codes.

        LxResult
io_Write (
        ILxUnknownID		 stream)
{
        CLxUser_BlockWrite	 write (stream);

        write.Write (my_float);
        write.Write (my_string);
        if (my_int_is_set)
                write.Write (my_int);

        return LXe_OK;
}
StreamIO::Read()

The Read() method does the reverse -- parsing the stream and restoring the object. This can be done using forced reads on the float and string (throwing IO errors if missing) and a non-forced read on the int (which just returns false if not found).

        LxResult
io_Read (
        ILxUnknownID		 stream)
{
        CLxUser_BlockRead	 read (stream);

        read.Read (&my_float);
        read.Read (my_string);
        my_int_is_set = read.Read (&my_int, false);
        return LXe_OK;
}

Using Your Datatype

When you access your type by name you have to remember to add "+" to the start of the name. While internal datatypes have names like "distance" and "&item", datatypes implemented by plug-ins have names that start with a "+" prefix to indicate that they are external, like "+intRange". So if your value server is called "myValueServer", your datatype is called "+myValueServer". You have to refer to it that way in any method that requires the name of your datatype.