Creating Lasso Types¶
Creating a new Lasso type in LCAPI is similar to creating a custom method. When
Lasso Server starts up, it scans the “LassoModules” directory for module files
(Windows DLLs, OS X DYLIBs, or Linux SOs). As it encounters each module, it
executes the registerLassoModule
function for that module. The developer
registers the LCAPI types or methods implemented by the module inside this
function. Registering type initializers differs from registering normal methods
in that the third parameter in lasso_registerTagModule
is the value
“REG_FLAGS_TYPE_DEFAULT”:
void registerLassoModule()
{
lasso_registerTagModule( "test", "type", myTypeInitFunc,
REG_FLAGS_TYPE_DEFAULT, "simple test LCAPI type" );
}
The prototype of an LCAPI type initializer is the same as a regular LCAPI custom method function. Lasso will call the type initializer each time a new instance of the type is created:
osError myTypeInitFunc( lasso_request_t token, tag_action_t action );
When the type initializer function is called, a new instance of the type is
created using lasso_typeAllocCustom
. This new instance will be created without
data members or member methods:
osError myTypeInitFunc( lasso_request_t token, tag_action_t action );
{
lasso_type_t theNewInstance = NULL;
lasso_typeAllocCustom( token, &theNewInstance, "test_type" );
Once the type is created, new data members and member methods can be added to it
using lasso_typeAddMember
. Data members can be of any type and should be
allocated using any of the LCAPI type allocation calls. Member methods are
allocated using lasso_typeAllocTag
. LCAPI member method functions are
implemented just like any other LCAPI method. In the example below,
myTagMemberFunction
is a function with the standard LCAPI prototype:
const char * kStringData = "This is a string member.";
lasso_type_t stringMember = NULL;
lasso_typeAllocString( token, &stringMember, kStringData, strlen(kStringData) );
lasso_typeAddDataMember( token, theNewInstance, "member1", stringMember );
lasso_type_t tagMember = NULL;
lasso_typeAllocTag( token, &tagMember, myTagMemberFunction );
lasso_typeAddMember( token, theNewInstance, "member2", tagMember );
The final step in creating a new LCAPI type instance is to return the new type to Lasso as the initializer’s return value. After the type is returned, Lasso will complete the creation of the type by instantiating the new type’s parent types:
lasso_returnTagValue( token, theNewInstance );
return osErrNoErr;
}
Basic Custom Type¶
This tutorial walks through the main points of creating a custom type using
LCAPI. The resulting type is an example_file
type, and the ability to open,
close, read, and write to the file are implemented via the following member
methods: example_file->open
, example_file->close
,
example_file->read
, example_file->write
.
The example project is the “CAPIFile” project in the LCAPI
examples
downloadable from this site. Due
to the length of the code in that file, the entire code is not reproduced
here. Instead, this section provides a conceptual overview of the
example_file
type and describes the basic LCAPI functions used to
implement it.
The first step in creating a custom type is to register the type’s initializer. Type initializers are registered in the same way that regular method functions are registered. The only difference being that “REG_FLAGS_TYPE_DEFAULT” should be passed for the fourth (flags) parameter.
This concept is illustrated in lines 247–282 of the
CAPIFile.cpp
file:void registerLassoModule() { ... lasso_registerTagModule("", kFileTypeName, file_init, REG_FLAGS_TYPE_DEFAULT, "Initializer for the file type."); }
The registered type initializer will be called when the module is loaded. In the above case, the LCAPI function
file_init
was registered as being the initializer. The prototype forfile_init
should look like any other LCAPI function, as shown on line 285 of theCAPIFile.cpp
file:osError file_init(lasso_request_t token, tag_action_t action)
The
file_init
function will now be called whenever the module is loaded. Within the type initializer, the type’s member methods are added. Each member method is implemented by its own LCAPI function. However, before members can be added, the new blank type must be created usinglasso_typeAllocCustom
.lasso_typeAllocCustom
can only be used within a properly registered type initializer. The value it produces should always be the return value of the method as set by thelasso_returnTagValue
function. See lines 289–290 of theCAPIFile.cpp
file:lasso_type_t file; lasso_typeAllocCustom(token, &file, kFileTypeName);
Once the blank type has been created, members can be added to it. LCAPI types often need to store pointers to allocated structures or memory. LCAPI provides a means to accomplish this by using the
lasso_setPtrMember
andlasso_getPtrMember
functions. These functions allow storing a pointer with a specific name. The pointer is stored as a regular integer data member. The names of all pointer members should begin with an underscore. Naming a pointer as such will indicate to Lasso that it should not be copied when a copy is made of the type instance. In the initializer function, add the integer data member as seen on lines 293–295:lasso_type_t i; lasso_typeAllocInteger(token, &i, 0); lasso_typeAddDataMember(token, file, kPrivateMember, i);
This LCAPI
example_file
type stores its private data in a structure namedfile_desc_t
. The actual call tolasso_setPtrMember
is in the method’sonCreate
method as shown on lines 344–345 of theCAPIFile.cpp
file:file_desc_t * desc = new file_desc_t; lasso_setPtrMember(token, self, kPrivateMember, desc, &cleanUp);
Member methods for
open
,close
,read
, andwrite
could be written like this:lasso_type_t mem; lasso_typeAllocTag(token, &mem, file_open); lasso_typeAddMember(token, file, "open", mem); lasso_typeAllocTag(token, &mem, file_close); lasso_typeAddMember(token, file, "close", mem); lasso_typeAllocTag(token, &mem, file_read); lasso_typeAddMember(token, file, "read", mem); lasso_typeAllocTag(token, &mem, file_write); lasso_typeAddMember(token, file, "write", mem);
But to avoid the repetitive nature of this, the
CAPIFile.cpp
file defines a macro namedADD_TAG
to do the work as seen on lines 300–309:#define ADD_TAG(NAME, FUNC) { lasso_type_t mem;\ lasso_typeAllocTag(token, &mem, FUNC);\ lasso_typeAddMember(token, file, NAME, mem);\ } // Add the type's member tags ADD_TAG(kMemOpen, file_open); ADD_TAG(kMemClose, file_close); ADD_TAG(kMemRead, file_read); ADD_TAG(kMemWrite, file_write);
At this point, the return value should be set. Keep in mind that the new
example_file
type is completely blank except for the members that were added above. No inherited members are available at this point. Inherited members are only added after the LCAPI type initializer returns. Line 324 of theCAPIFile.cpp
file sets the return value:lasso_returnTagValue(token, file);
There were no errors in the type initialization process, so return a “no error” code to Lasso, completing the type’s initialization. See line 325 of the
CAPIFile.cpp
file:return osErrNoErr;
Note
For brevity, this example will not cover accepting parameters in the type’s
onCreate
method. The full “CAPIFile” project illustrates accepting parameters in theonCreate
member method to open the file under various read and write permissions.The new file type has now been initialized and made available to the caller in the script. The first member method of the file type is
example_file->open
, which is implemented as the LCAPI functionfile_open
beginning on line 385 of theCAPIFile.cpp
file:osError file_open(lasso_request_t token, tag_action_t action) {
The first step in implementing a member method is to acquire the “self” instance. The “self” is the instance upon which the member call was made. This is illustrated on lines 387–390 of the
CAPIFile.cpp
file:lasso_type_t self = NULL; lasso_getTagSelf(token, &self); if(!self) return osErrInvalidParameter;
Once the “self” is successfully acquired and is not “null”, the rest of the member method can proceed. This member method accepts one parameter for the path to the file to be opened. Since the path is a string value, it can be acquired using
lasso_getTagParam
. If the path parameter was not passed to the open member method, an error should be returned and presented to the user. All of this can be seen on lines 400–418 of theCAPIFile.cpp
file:// See what parameters we are being initialized with int count; lasso_getTagParamCount(token, &count); if( count < 2 ) { lasso_setResultMessage(token, "file->open requires at least a file path and open mode."); return osErrInvalidParameter; } if( count > 0 ) // We are given *at the least* a path { // First param is going to be a string, so use the LCAPI call to get it auto_lasso_value_t pathParam; pathParam.name = ""; lasso_getTagParam(token, 0, &pathParam); desc->fPath = pathParam.name; }
Once the path is properly converted, the actual file can be opened using the file system calls supplied by the operating system. This concept is illustrated on line 225 of the
CAPIFile.cpp
file:FILE * f = fopen(xformPath, openMode);
The
FILE
pointer can now be retrieved using thelasso_typeGetCustomPtr
LCAPI function. No error has occurred while opening the file, so complete the function call and return “no error”. See line 449 of theCAPIFile.cpp
file:return osErrNoErr;
The remaining method functions are implemented in a similar manner. Study the CAPIFile example for a more in-depth and complete example of how to properly construct custom Lasso types in LCAPI.