load help; @[name] daovm.interface @[name] @[title] Programming with The C Interfaces @[title] ################################################################################ ################################################################################ #### Embedding Dao Virtual Machine ################################################################################ ################################################################################ @[name] daovm.interface.embedding @[name] @[title] Embedding Dao VM @[title] @[text] @[section] A Simple Example @[section] @[subsection] Initializing Dao Runtime @[subsection] Before doing anything with Dao as a library, one must call @[code]DaoInit()@[code] to initialize it first: @[code(cxx)] // Initialize Dao: DaoVmSpace *vmspace = DaoInit( NULL ); @[code(cxx)] This function will return a @[code]DaoVmSpace@[code] object, which can be used to load Dao scripts or modules. It can optionally take a @[code]char*@[code] parameter, which is assumed to be the path and name of the application, and is used to add additional searching paths. @[comment] Before you start to use the @[code]DaoVmSpace@[code] object to run Dao scripts or load Dao modules, you may set some options in the object, e.g: @[code(cxx)] DaoVmSpace_SetOptions( vmspace, DAO_EXEC_DEBUG ); @[code(cxx)] @[comment] @[subsection] Load A Script File @[subsection] Once you have a @[code]DaoVmSpace@[code] object, you can start to run Dao scripts or load Dao modules using this object. If you have a main script file and all you need to do is to run the file, you can simply call: @[code(cxx)] // Load and execute "myscript.dao": DaoVmSpace_Load( vmspace, "myscript.dao" ); @[code(cxx)] @[subsection] Finalize Dao Runtime @[subsection] After you are done with Dao, you may call the following function to make sure, Dao is properly finalized (waiting for unjoined threads to finish and the garbage collector to finish, and deallocating some internal data structures etc.). @[code(cxx)] // Finalize (or Quit) Dao: DaoQuit(); @[code(cxx)] @[subsection] Putting These Codes Together @[subsection] Putting these codes together, embedding Dao can be as simple as, @[code(cxx)] // Initialize Dao: DaoVmSpace *vmspace = DaoInit( NULL ); // Load "myscript.dao": DaoVmSpace_Load( vmspace, "myscript.dao" ); // Finalize (or Quit) Dao: DaoQuit(); @[code(cxx)] @[section] A Slightly More Advanced Example @[section] In Dao, each single script file is represented by one namespace object. And each namespace object has access to the public global constants and variables defined in the files that this file has loaded. To do anything interesting, one must obtain the namespace object for the file of interest. @[subsection] Obtaining The Namespace Object @[subsection] Following the above example, if you want to call a function defined in "myscript.dao" (or in the files it has loaded), you can simple store the returned namespace object in a variable, @[code(cxx)] // Load "myscript.dao" and obtain the namespace object: DaoNamespace *nspace = DaoVmSpace_Load( vmspace, "myscript.dao" ); @[code(cxx)] @[subsection] Obtaining The Function Object @[subsection] To find the function you want to call, @[code(cxx)] // Find an object named "myfunction": DaoValue *value = DaoNamespace_FindData( nspace, "myfunction" ); // Try to cast it to a function object: DaoRoutine *myfunc = DaoValue_CastRoutine( value ); @[code(cxx)] If "myfunction" is indeed a function, @[code]myfunc@[code] will not be NULL. @[subsection] Obtaining A Process Object @[subsection] Now to call the function, you will need another type of object: @[code]DaoProcess@[code], which represents a virtual machine process and is responsible for executing scripts. You can directly call @[code]DaoProcess_New()@[code] to create a new process object, but normally the better to get a process object is to acquire it from a @[code]DaoVmSpace@[code] object: @[code(cxx)] // Acquire a process object: DaoProcess *proc = DaoVmSpace_AcquireProcess( vmspace ); @[code(cxx)] @[subsection] Prepare Parameter Values @[subsection] Now suppose the function "myfunction" needs to take an integer as its first parameter and a string as the second. To call it, we will need to prepare parameters that can be passed to this function. The simplest way to do this is to use the data "factory" methods that are associated with the process type @[code]DaoProcess@[code]. Such methods are normally begin with @[code]DaoProcess_New@[code]. For example, to prepare an integer value and a string value, one can do, @[code(cxx)] // Prepare an integer and a string: DaoInteger *ivalue = DaoProcess_NewInteger( proc, 123 ); DaoString *svalue = DaoProcess_NewString( proc, "abc", -1 ); @[code(cxx)] The third parameter of @[code]DaoProcess_NewString()@[code] is the number of bytes in the C string, and a negative value can be used to indicate the C string is NULL terminated. To use these two new values in the parameter list to call "myfunction", you can do, @[code(cxx)] DaoValue *params[2]; params[0] = (DaoValue*) ivalue; params[1] = (DaoValue*) svalue; @[code(cxx)] Or you can simply do, @[code(cxx)] // Get the last two values: DaoValue **params = DaoProcess_GetLastValues( proc, 2 ); @[code(cxx)] which will return last created two values in an array. @[subsection] Call The Function @[subsection] Now we are ready to call the function we obtained before with the prepared values, @[code(cxx)] // Call the function: DaoProcess_Call( proc, myfunc, NULL, params, 2 ); @[code(cxx)] For class methods, a class instance object can be passed to this function as the third parameter, or it can be passed as the first value of @[code]params@[code]. Please note that, this function can handle overloaded functions automatically! So you do not need to do anything for that. This function will return zero on success, or other values with errors. @[subsection] Retrieve The Returned Value @[subsection] If the function returns a value, you can obtain it by, @[code(cxx)] // Obtain the returned value: DaoValue *retvalue = DaoProcess_GetReturned( proc ); @[code(cxx)] If the returned value is supposed to be of certain type, you can cast it to that type by using one of the @[code]DaoValue_CastXXX()@[code] functions, or directly convert it to a proper C type by using one of the @[code]DaoValue_TryGetXXX()@[code] functions. For example, if "myfunction" returns an integer, you can get it by, @[code(cxx)] // Get the integer return value daoint retint = DaoValue_TryGetInteger( retvalue ); @[code(cxx)] @[subsection] Release The Process Object @[subsection] After you have done with the process object, you can release it back the @[code]DaoVmSpace@[code] object, @[code(cxx)] // Release the process: DaoVmSpace_ReleaseProcess( vmspace, proc ); @[code(cxx)] But if you want to use a process object frequently, you may simply retain it until you no longer need it. Then you may also need to the follow function to release the cached values @[code(cxx)] // Pop the cached values: DaoProcess_PopValues( proc, 2 ); @[code(cxx)] @[subsection] Putting These Codes Together @[subsection] @[code(cxx)] // Load "myscript.dao" and obtain the namespace object: DaoNamespace *nspace = DaoVmSpace_Load( vmspace, "myscript.dao" ); // Find an object named "myfunction": DaoValue *value = DaoNamespace_FindData( nspace, "myfunction" ); // Try to cast it to a function object: DaoRoutine *myfunc = DaoValue_CastRoutine( value ); // Acquire a process object: DaoProcess *proc = DaoVmSpace_AcquireProcess( vmspace ); // Prepare an integer and a string: DaoInteger *ivalue = DaoProcess_NewInteger( proc, 123 ); DaoString *svalue = DaoProcess_NewString( proc, "abc", -1 ); // Get the last two values: DaoValue **params = DaoProcess_GetLastValues( proc, 2 ); // Call the function: DaoProcess_Call( proc, myfunc, NULL, params, 2 ); // Obtain the returned value: DaoValue *retvalue = DaoProcess_GetReturned( proc ); // Get the integer return value daoint retint = DaoValue_TryGetInteger( retvalue ); // Release the process: DaoVmSpace_ReleaseProcess( vmspace, proc ); @[code(cxx)] @[text] ################################################################################ ################################################################################ #### Extending Dao Virtual Machine ################################################################################ ################################################################################ @[name] daovm.interface.extending @[name] @[title] Extending Dao VM @[title] @[text] In the help entry @[node]daovm.interface.embedding@[node], it has been demonstrated that embedding Dao is extremely simple. Here we will demonstrate that extending Dao is also extremely simple. Since Dao supports explicit type specification in function parameter lists, you will not need to write a lot of boilerplate codes to check and convert function parameters from Dao data types to C/C++ data types. This means writing wrapping functions (Dao-callable C functions) is significantly simpler than writing wrapping functions for other languages such as Python or even Lua. @[section] The First Simple Extending Module @[section] @[subsection] Extension Function Prototype @[subsection] All Dao-callable C functions must have prototype similar to the following example, @[code(cxx)] void MyCFunction( DaoProcess *proc, DaoValue *param[], int nparam ) { printf( "Hello Dao!\n" ); } @[code(cxx)] @[subsection] Module Entry Function @[subsection] Each Dao extending module must provide an entry function. The following "DaoOnLoad" is the basic form of the entry function name, which may also include the module name. @[code(cxx)] // Entry function for each C/C++ module: int DaoOnLoad( DaoVmSpace *vmspace, DaoNamespace *ns ); @[code(cxx)] This function will be called automatically to allow the module to register its functions and types etc. The first parameter is the @[code]DaoVmSpace@[code] instance which is responsible for loading and managing the module. And the second parameter is the namespace object that will represent this module, so all the functions and types etc. should be registered to this namespace. As mentioned above, the name of the entry function may also include the module name. Suppose the module name is "abc" and may appear in load statements as @[code(cxx)]load abc@[code(cxx)] or @[code(cxx)]load path.abc@[code(cxx)], the module name can appear in the entry function name in the following forms and is also searched in this order: @[list] == All lower case letters: e.g. @[code]Daoabc_OnLoad@[code]; == First upper case letter: e.g. @[code]DaoAbc_OnLoad@[code]; == All upper case letters: e.g. @[code]DaoABC_OnLoad@[code]; @[list] @[subsection] Function Registeration @[subsection] To register functions to a namespace, one can use one of the following interface functions of @[code]DaoNamespace@[code], @[code(cxx)] // Function to register a single function: DaoRoutine* DaoNamespace_WrapFunction( DaoNamespace *self, DaoCFunction fp, const char *proto ); // Function to register multiple functions: int DaoNamespace_WrapFunctions( DaoNamespace *self, DaoFunctionEntry *items ); @[code(cxx)] We will come to the second function later. For the first function, the first parameter is the namespace to which the function is registering to; the second is the function pointer to the function that needs to be registered (@[code]MyCFunction@[code] in this case); and the last parameter is the Dao function prototype for the registered function. So you can register the above function @[code]MyCFunction@[code] as the following, @[code(cxx)] // Register function: DaoNamespace_WrapFunction( nspace, MyCFunction, "HelloDao()" ); @[code(cxx)] So that this function can be called in Dao by name @[code]HelloDao@[code] without any parameter. @[subsection] Summary @[subsection] To sum it up, the simplest Dao extending module could be the following, @[code(cxx)] #include "dao.h" #include "stdio.h" static void MyCFunction( DaoProcess *proc, DaoValue *param[], int nparam ) { printf( "Hello Dao!\n" ); } int DaoOnLoad( DaoVmSpace *vmspace, DaoNamespace *nspace ) { DaoNamespace_WrapFunction( nspace, MyCFunction, "HelloDao()" ); return 0; } @[code(cxx)] To compile it, you will need to add the Dao header file path to your compiling option. And you will also need to add the following preprocessor definitions: @[list] -- On Win32: @[code]WIN32@[code]; -- On Unix: @[code]UNIX@[code]; -- On Mac OSX: @[code]MACOSX@[code]; @[list] For linking, on Windows you will need to link the module against the Dao library. But on the other platforms, you can simply use the following flags, @[list] -- On Unix: @[code]-rdynamic@[code]; -- On Mac OSX: @[code]-undefined dynamic_lookup@[code]; @[list] If you use DaoMake (@[node]tool.standard.daomake@[node]) to build your modules, these will be taken care of automatically. @[section] The Second Simple Extending Module @[section] Now we will demonstrate how to wrap a function that can accept parameters and return a value. Suppose we want to wrap the following C function, @[code] float Test( int id, const char *name, int extra ); @[code] and support a default value for the last parameter "extra". Then we would need to register the function with the following Dao function prototype, @[code] # Dao function prototype MyTest( id: int, name: string, extra = 0 ) => float @[code] So this function will take an integer parameter, a string parameter and an extra integer parameter with default value. This prototype also indicates that it will return a float. In the C wrapping function, it is very easy to convert Dao data type to C data type, and to return C data type to Dao, @[code(cxx)] void MyTestInC( DaoProcess *proc, DaoValue *param[], int nparam ) { daoint id = param[0]->xInteger.value; char *name = param[1]->xString.value->chars; daoint extra = param[2]->xInteger.value; DaoProcess_PutFloat( proc, Test( id, name, extra ) ); } @[code(cxx)] As you can see, you can just get the data without boilerplate codes to check the number of parameters or the type of parameters. When Dao programs invoke this function, it is guaranteed to pass parameter values with correct types to the function. But to convert the data in the above way, you may need to include extra header files such as @[code]daoValue.h@[code] in your module source file. And you may also need to be familiar with the standard Dao data type structures (it is simple actually). If you only want to use the APIs from the @[code]dao.h@[code] header file, you may need to used those @[code]DaoValue_TryGetXXX()@[code] functions which do some mininum data type checking with little overheads and return the proper C data. @[code(cxx)] void MyTestInC( DaoProcess *proc, DaoValue *param[], int nparam ) { daoint id = DaoValue_TryGetInteger( param[0] ); char *name = DaoValue_TryGetChars( param[1] ); daoint extra = DaoValue_TryGetInteger( param[2] ); DaoProcess_PutFloat( proc, Test( id, name, extra ) ); } @[code(cxx)] If you register the function with proper Dao function prototype, the type checking will always succeed and correct data will always be obtained. The @[code]DaoProcess_PutFloat()@[code] is for returning a float value at a proper location as the returned value of the C function. Please see the following section(s) for more details. Now this function can be registered as: @[code(cxx)] // Register a function with parameters and returned value: DaoNamespace_WrapFunction( nspace, MyTestInC, "MyTest(id:int,name:string,extra=0)=>float" ); @[code(cxx)] @[section] Type Wrapping @[section] In Dao virtual machine, a Dao data type is usually represented by three structures: @[list] -- @[code]DaoType@[code]: The high level representation of the type for type matching and checking; -- @[code]DaoTypeKernel@[code]: The middle level representation of the type for holding the wrapped values and methods, or specialized methods for some generic types; -- @[code]DaoTypeCore@[code]: The low level representation of the type for specifying the basic information, type checking, running time execution, copying, comparison, hashing, printing, printing, deletion and garbage collection behaviours; @[list] The first two structures are usually generated automatically from the last one. For wrapping C/C++ types, you only need to define the @[code]DaoTypeCore@[code] structure. @[subsection] Type Definition Structure @[subsection] The type definition structure @[code]DaoTypeCore@[code] is defined as the following: @[code(cxx)] struct DaoTypeCore { const char *name; // Name of the type; int size; // Size of the value structure; DaoTypeCore *bases[8]; // Type cores for the base types; DaoNumberEntry *numbers; // Member numbers; DaoFunctionEntry *methods; // Member methods; DaoType* (*CheckGetField)( DaoType *self, DaoString *field, DaoRoutine *rout ); DaoValue* (*DoGetField)( DaoValue *self, DaoString *field, DaoProcess *proc ); int (*CheckSetField)( DaoType *self, DaoString *field, DaoType *value, DaoRoutine *rout ); int (*DoSetField)( DaoValue *self, DaoString *field, DaoValue *value, DaoProcess *proc ); DaoType* (*CheckGetItem)( DaoType *self, DaoType *index[], int N, DaoRoutine *rout ); DaoValue* (*DoGetItem)( DaoValue *self, DaoValue *index[], int N, DaoProcess *proc ); int (*CheckSetItem)( DaoType *self, DaoType *index[], int N, DaoType *value, DaoRoutine *rout ); int (*DoSetItem)( DaoValue *self, DaoValue *index[], int N, DaoValue *value, DaoProcess *proc ); DaoType* (*CheckUnary)( DaoType *self, DaoVmCode *op, DaoRoutine *rout ); DaoValue* (*DoUnary)( DaoValue *self, DaoVmCode *op, DaoProcess *proc ); DaoType* (*CheckBinary)( DaoType *self, DaoVmCode *op, DaoType *operands[2], DaoRoutine *rout ); DaoValue* (*DoBinary)( DaoValue *self, DaoVmCode *op, DaoValue *operands[2], DaoProcess *proc ); DaoType* (*CheckConversion)( DaoType *self, DaoType *type, DaoRoutine *rout ); DaoValue* (*DoConversion)( DaoValue *self, DaoType *type, int copy, DaoProcess *proc ); DaoType* (*CheckForEach)( DaoType *self, DaoRoutine *rout ); int (*DoForEach)( DaoValue *self, DaoTuple *iterator, DaoProcess *proc ); void (*Print)( DaoValue *self, DaoStream *stream, DMap *cycmap, DaoProcess *proc ); void (*Slice)( DaoValue *self ); int (*Compare)( DaoValue *self, DaoValue *other, DMap *cycmap ); size_t (*Hash)( DaoValue *self ); DaoValue* (*Create)( DaoType *self ); DaoValue* (*Copy)( DaoValue *self, DaoValue *target ); void (*Delete)( DaoValue *self ); void (*HandleGC)( DaoValue *self, DList *values, DList *lists, DList *maps, int remove ); }; @[code(cxx)] The type definition structure @[code]DaoTypeCore@[code] not only defines the basic information for the type, but also includes some function pointers that perform some standard operations on the type. Some of these function pointers are grouped into pairs per operation, where the first is the type checking function used at compiling time, and the second is the execution function called at running time to do the operation. The type checking function should return the resulting type on success and null otherwise. For user defined types, the default implementations are provided and declared as DaoCstruct_CheckXXX() and DaoCstruct_DoXXX() functions in the header file @[code]dao.h@[code]. These default implementations use operator overloading from the member methods to do type checkings and running time executions. At running time, the executing DaoProcess object is usually passed to the execution functions, so that these functions can put the resulting values directly on the process stack. Otherwise, they should just return the results, and let the VM to put them on the stack if necessary. @[subsubsection]Name and Size@[subsubsection] @[subsubsection]Member constant numbers and methods@[subsubsection] The third field @[code]numbers@[code] can be used to specify a list of member constant numbers, which are defined in an array of the following structure: @[code(cxx)] struct DaoNumberEntry { const char *name; /* contant name; */ int type; /* number type; */ double value; /* number value; */ }; @[code(cxx)] The number type should be one of @[code]DAO_BOOLEAN@[code], @[code]DAO_INTEGER@[code] and @[code]DAO_FLOAT@[code]. The array should be terminated with a item with null name: @[code(cxx)] static DaoNumberEntry myTypeNumbers[] = { { "MODE_ONE", DAO_INTEGER, MODE_ONE }, { "MODE_TWO", DAO_INTEGER, MODE_TWO }, { NULL, 0, 0 } }; @[code(cxx)] If the type has no member constant numbers, it can be simply set to NULL. The fourth field @[code]methods@[code] can be used to specify a list of member methods, which are defined in an array of the following structure: @[code(cxx)] struct DaoFunctionEntry { DaoCFunction fpter; /* C function pointer; */ const char *proto; /* function prototype: name( parlist ) => return_type */ }; @[code(cxx)] where the two fields @[code]fpter@[code] and @[code]proto@[code] should be the same as they would in the parameter list of: @[code(cxx)] DaoRoutine* DaoNamespace_WrapFunction( DaoNamespace *self, DaoCFunction fp, const char *proto ); @[code(cxx)] @[subsubsection]Inheritance@[subsubsection] The inheritance relationship can be defined with the @[code]bases@[code] field, by specifying the type definition structures for the base types in this field. Given that a type definition structure can be used for both wrapping C/C++ types and defining interface types, the @[code]bases@[code] field can list an array of base types to support multiple inheritance. If an inheritance relationship involves wrapped C++ classes with virtual functions, the conversion between a base type and a derived type should be handled properly in the @[code]DaoTypeCore::DoConversion@[code] method (See bellow). @[subsubsection]Get Field Operation@[subsubsection] @[code] DaoType* (*CheckGetField)( DaoType *self, DaoString *field, DaoRoutine *rout ); DaoValue* (*DoGetField)( DaoValue *self, DaoString *field, DaoProcess *proc ); @[code] @[subsubsection]Set Field Operation@[subsubsection] @[code] int (*CheckSetField)( DaoType *self, DaoString *field, DaoType *value, DaoRoutine *rout ); int (*DoSetField)( DaoValue *self, DaoString *field, DaoValue *value, DaoProcess *proc ); @[code] @[subsubsection]Get Item Operation@[subsubsection] @[code] DaoType* (*CheckGetItem)( DaoType *self, DaoType *index[], int N, DaoRoutine *rout ); DaoValue* (*DoGetItem)( DaoValue *self, DaoValue *index[], int N, DaoProcess *proc ); @[code] @[subsubsection]Set Item Operation@[subsubsection] @[code] int (*CheckSetItem)( DaoType *self, DaoType *index[], int N, DaoType *value, DaoRoutine *rout ); int (*DoSetItem)( DaoValue *self, DaoValue *index[], int N, DaoValue *value, DaoProcess *proc ); @[code] @[subsubsection]Unary Operation@[subsubsection] @[code] DaoType* (*CheckUnary)( DaoType *self, DaoVmCode *op, DaoRoutine *rout ); DaoValue* (*DoUnary)( DaoValue *self, DaoVmCode *op, DaoProcess *proc ); @[code] @[subsubsection]Binary Operation@[subsubsection] @[code] DaoType* (*CheckBinary)( DaoType *self, DaoVmCode *op, DaoType *operands[2], DaoRoutine *rout ); DaoValue* (*DoBinary)( DaoValue *self, DaoVmCode *op, DaoValue *operands[2], DaoProcess *proc ); @[code] @[subsubsection]Type Conversion Operation@[subsubsection] @[code] DaoType* (*CheckConversion)( DaoType *self, DaoType *type, DaoRoutine *rout ); DaoValue* (*DoConversion)( DaoValue *self, DaoType *type, int copy, DaoProcess *proc ); @[code] @[subsubsection]Deallcation@[subsubsection] @[code] void (*Delete)( DaoValue *self ); @[code] If instance object can be created for the type (not its derived types), a deallcation function must be supplied in the type definition structure, in order to properly delete the object once it becomes unreachable in the program. @[subsubsection]GC Handling@[subsubsection] @[code] void (*HandleGC)( DaoValue *self, DList *values, DList *lists, DList *maps, int remove ); @[code] If an instance object of the type may hold counted references to non-primitive Dao data values, this object must be handled properly for garbage collection. In order to do this, a function must be properly defined and set to the last field of @[code]DaoTypeCore@[code], namely @[code]HandleGC@[code]. This function will be called during garbage collection scans, and it must export its reference couned values to the parameter lists; In this function, directly referenced member values should be pushed into @[code]values@[code], and @[code]DList@[code] members holding referenced values should be pushed into @[code]lists@[code], and @[code]DMap@[code] members holding referenced values should be pushed into @[code]maps@[code]. When the user defined value is marked for deletion by the garbage collector, the @[code]remove@[code] parameter will be set to non-zero. In the case, references to the member values that are pushed to @[code]values@[code] should be removed by setting them to nulls. This is necessary, because some of the objects referenced by this object could be deallocated by the collector before this object is deallocated, breaking references to them will avoid posible double deletion. @[subsubsection]Type Registraction@[subsubsection] A C/C++ type can be used in Dao if only if it is registered in a Dao namespace with a type definition structure @[code]DaoTypeCore@[code] through, @[code(cxx)] DaoType* DaoNamespace_WrapType( DaoNamespace *self, DaoTypeCore *core, int tid, int options ); int DaoNamespace_WrapTypes( DaoNamespace *self, DaoTypeCore *core[] ); @[code(cxx)] The @[code]tid@[code] parameter must be set to @[code]DAO_CSTRUCT@[code] for customized types, or @[code]DAO_CDATA@[code] for opaque wrapper types. The @[code]options@[code] parameter can be set to either @[code]0@[code] or @[code]DAO_CTYPE_INVAR@[code] for invariable types. There are two ways to extend Dao with user defined C/C++ types. One is to wrap it around by a @[code]DaoCdata@[code] object, and access it as an opaque pointer. This is the standard way to wrap existing C/C++ types. The other is to define a customized C type, and use it in the same way as the first. A customized C type is a C structure sharing the same header fields as @[code]DaoCdata@[code], which can make sharing types between Dao and C simpler (especially for garbage collection). Wrapped C/C++ types and customized C types can be added to Dao in almost the identical way, so I will introduce the wrapped types first, and then the customized types should be very easy to understand. Dao interface types can also be defined in C/C++ extensions. To define interface types, you need to defined type definition structions in a similar way as the above wrapped C/C++ data types. And then wrap them using, @[code] DaoType* DaoNamespace_WrapInterface( DaoNamespace *self, DaoTypeCore *core ); DaoType* DaoNamespace_WrapCinType( DaoNamespace *self, DaoTypeCore *c, DaoType *a, DaoType *t ); @[code] See bellow for example. @[subsection]A simple example@[subsection] Given the following C++ class, @[code(cxx)] class ClassOne { public: int value; enum{ CLASSONE_AA, CLASSONE_BB }; ClassOne( int v ); int Method( const char *s ); }; @[code(cxx)] It can be wrapped in the following way, @[code(cxx)] // Declare the wrapper functions first: static void dao_ClassOne_ClassOne( DaoProcess *proc, DaoValue *p[], int n ); static void dao_ClassOne_Method( DaoProcess *proc, DaoValue *p[], int n ); // List of constant member numbers for the enums: static DaoNumberEntry ClassOneNumbers[] = { { "CLASSONE_AA", DAO_INTEGER, CLASSONE_AA }, { "CLASSONE_BB", DAO_INTEGER, CLASSONE_BB }, { NULL, 0, 0 } }; // List of member constructors or methods of ClassOne: static DaoFunctionEntry ClassOneMethods[] = { // Methods with the same name as the type name are constructors: { dao_ClassOne_ClassOne, "ClassOne( v: int )" }, { dao_ClassOne_Method, "Method( self: ClassOne, s: string ) => int" }, { NULL, NULL } }; static void ClassOne_Delete( void *self ) { delete (ClassOne*) self; } // The type definition structure for ClassOne: static DaoTypeCore ClassOne_Typer = { "ClassOne", NULL, ClassOneNumbers, ClassOneMethods, {NULL}, {NULL}, ClassOne_Delete, NULL }; // The Dao type structure for ClassOne: DaoType *dao_type_ClassOne = NULL; static void dao_ClassOne_ClassOne( DaoProcess *proc, DaoValue *p[], int n ) { // Get the integer parameter; daoint v = DaoValue_TryGetInteger( p[0] ); // Create a ClassOne instance: ClassOne *self = new ClassOne( v ); // Wrap the instance with Dao type structure: DaoProcess_PutCdata( proc, self, dao_type_ClassOne ); } static void dao_ClassOne_Method( DaoProcess *proc, DaoValue *p[], int n ) { // Get the ClassOne instance: ClassOne *self = (ClassOne*) DaoValue_TryCastCdata( p[0], dao_type_ClassOne ); // Get the string parameter: char *s = DaoValue_TryGetChars( p[1] ); int res = self->Method( s ); // Return the integer result: DaoProcess_PutInteger( proc, res ); } int DaoOnLoad( DaoVmSpace *vmSpace, DaoNamespace *nspace ) { // Wrap ClassOne as an opaque C/C++ type: dao_type_ClassOne = DaoNamespace_WrapType( nspace, & ClassOne_Typer, 1 ); return 0; } @[code(cxx)] Since the @[code]value@[code] member of ClassOne is a public member, it is reasonable to add a getter and a setter method to wrapped ClassOne type. To add a getter, one only needs to register a method with name @[code].field@[code] and no extra parameter. And for a setter, the method name must be @[code].field=@[code], and it must also accept a parameter with type the same as the value that can be assigned. For example, for the @[code]value@[code] member, one can added the following to the @[code]ClassOneMethods@[code] list, @[code(cxx)] // the getter and setter: { dao_ClassOne_GETF_value, ".value( self: ClassOne ) => int" }, { dao_ClassOne_SETF_value, ".value=( self: ClassOne, value: int )" }, @[code(cxx)] Here the name @[code]dao_ClassOne_GETF_value@[code] and @[code]dao_ClassOne_SETF_value@[code] are completely arbitrary. They can be implemented in the following way, @[code(cxx)] static void dao_ClassOne_GETF_value( DaoProcess *proc, DaoValue *p[], int n ) { ClassOne *self = (ClassOne*) DaoValue_TryCastCdata( p[0], dao_type_ClassOne ); DaoProcess_PutInteger( proc, self->value ); } static void dao_ClassOne_SETF_value( DaoProcess *proc, DaoValue *p[], int n ) { ClassOne *self = (ClassOne*) DaoValue_TryCastCdata( p[0], dao_type_ClassOne ); self->value = DaoValue_TryGetInteger( p[1] ); } @[code(cxx)] @[subsection]An advanced example@[subsection] Now given the following class that is derived from @[code]ClassOne@[code], @[code(cxx)] class ClassTwo : public ClassOne { public: virtual void VirtualMethod( int i, float f ); }; @[code(cxx)] Because this class has a virtual method, if we want Dao classes can be derived from @[code]ClassTwo@[code] and reimplement its virtual functions, the wrapping will be a bit more sophisticated. First, we will need to define a "proxy" class that is derived from @[code]ClassTwo@[code] and reimplements its virtual function such that this reimplemented function can check for a Dao reimplementation of the function and invoke it if it exists. When an instance of @[code]ClassTwo@[code] needs to be created, an instance of this proxy class will be created and returned instead of the original @[code]ClassTwo@[code]. Here is an example of such proxy class, @[code(cxx)] class Dao_ClassTwo : public ClassTwo { public: DaoCdata *dao_cdata; Dao_ClassTwo(); ~Dao_ClassTwo(); int VirtualMethod( int i, float f ); }; @[code(cxx)] This proxy class will need to maintain a reference to the wrapper object, so an extra field @[code]dao_cdata@[code] is declared in the class. This wrapper object can be pre-allocated in the constructor of @[code]Dao_ClassTwo@[code], @[code(cxx)] Dao_ClassTwo::Dao_ClassTwo() { dao_cdata = DaoCdata_New( dao_type_ClassTwo, this ); DaoGC_IncRC( (DaoValue*)dao_cdata ); } @[code(cxx)] Here the @[code]dao_type_ClassTwo@[code] is the Dao type object for @[code]ClassTwo@[code], and can be obtained in the same way as @[code]dao_type_ClassOne@[code]. Now that @[code]Dao_ClassTwo@[code] has a reference to a @[code]DaoCdata@[code] object, the @[code]GetGCFields@[code] field of the type definition structure for @[code]ClassTwo@[code] should be set to a proper function, which will be provided later. The destructor should also be handled this reference properly by, @[code(cxx)] Dao_ClassTwo::~Dao_ClassTwo() { if( dao_cdata ){ // Could have been set to NULL by the GC: // Set the opaque pointer of dao_cdata to NULL, so that the deallocator // of DaoCdata will not attempt to call the deallocator of the opaque pointer: DaoCdata_SetData( dao_cdata, NULL ); DaoGC_DecRC( (DaoValue*) dao_cdata ); } } @[code(cxx)] Then the @[code]VirtualMethod()@[code] could be implemented in the following way, @[code(cxx)] int Dao_ClassTwo::VirtualMethod( int i, float f ) { DaoVmSpace *vmspace = DaoVmSpace_MainVmSpace(); DaoProcess *proc = NULL; // Try to get the instance of a derived Dao class: DaoObject *object = DaoCdata_GetObject( dao_cdata ); if( object == NULL ) goto CallDefault; // Try to get a method named "VirtualMethod": DaoRoutine *method = DaoObject_GetMethod( object, "VirtualMethod" ); if( method == NULL ) goto CallDefault; // Check if the method is a C/C++ wrapper function: if( DaoRoutine_IsWrapper( method ) ) goto CallDefault; // Acquire a process object to execute the re-implemented virtual function: proc = DaoVmSpace_AcquireProcess( vmspace ); // Prepare function call parameters: DaoProcess_NewInteger( proc, i ); DaoProcess_NewFloat( proc, f ); DaoValue **params = DaoProcess_GetLastValues( proc, 2 ); // Resolve possible overloading using the parameters: method = DaoRoutine_ResolveByValue( method, object, params, 2 ); if( method == NULL ) goto CallDefault; // Run the re-implemented function: if( DaoProcess_Call( proc, method, object, params, 2 ) ) goto ErrorCall; // Check the returned value: DaoValue *res = DaoProcess_GetReturned( proc ); if( DaoValue_CastInteger( res ) ) goto ErrorCall; int ires = DaoValue_TryGetInteger( res ); // Release the process object: DaoProcess_Release( vmspace, proc ); return ires; CallDefault: if( proc ) DaoProcess_Release( vmspace, proc ); return ClassTwo::VirtualMethod( i, f ); ErrorCall: DaoProcess_Release( vmspace, proc ); return 0; } @[code(cxx)] Now we will define a function that can be set to the @[code]GetGCFields@[code] field of the type definition structure of @[code]ClassTwo@[code]. @[code(cxx)] static void Dao_ClassTwo_GetGCFields( void *self0, DList *values, DList *lists, DList *maps, int remove ) { Dao_ClassTwo *self = (Dao_ClassTwo*) self0; if( self->dao_cdata == NULL ) return; DList_Append( values, self->dao_cdata ); if( removeĀ ){ // Parameter "remove" is zero, when this function is called by the // garbage collector during the phase when the candidate garbage // objects are scanned to determine true garbage objects. // // For true garbage objects for wrapped types, this function will be // called again with non-zero "remove" parameter. And in this case, // the "dao_cdata" field should be set to NULL here, since it will be // deleted by the garbage collector directly. // // Care must be taken if there is something else that is responsible // for deallocating the wrapped C/C++ object. For example, in some GUI // libraries, the parent widget will be responsible for deleting its // children widgets. To prevent the deallocator of the wrapped C/C++ // objects from being invoked by Dao garbage collector, something like // the following can be used: // if( self->parent() ) DaoCdata_SetData( self->dao_cdata, NULL ); // After this call, "dao_cdata" may become invalid, now set it to NULL // to avoid wrong using: self->dao_cdata = NULL; } } @[code(cxx)] The remaining part for wrapping @[code]ClassTwo@[code] should be something like, @[code(cxx)] static void dao_ClassTwo_ClassTwo( DaoProcess *proc, DaoValue *p[], int n ) { Dao_ClassTwo *self = new Dao_ClassTwo(); DaoProcess_PutValue( proc, (DaoValue*) self->dao_cdata ); } static DaoFunctionEntry ClassTwoMethods[] = { { dao_ClassTwo_ClassTwo, "ClassTwo()" }, { NULL, NULL } }; static void Dao_ClassTwo_Delete( void *self ) { delete (Dao_ClassTwo*) self; } static void* Dao_ClassTwo_Cast_ClassOne( void *data, int down ) { if( down ) return static_cast((ClassOne*)data); return dynamic_cast((ClassTwo*)data); } // The type definition structure for ClassTwo: static DaoTypeCore ClassTwo_Typer = { "ClassTwo", NULL, NULL, ClassTwoMethods, { & ClassOne_Typer, NULL }, { Dao_ClassTwo_Cast_ClassOne, NULL }, Dao_ClassTwo_Delete, NULL }; // The Dao type structure for ClassTwo: DaoType *dao_type_ClassTwo = NULL; int DaoOnLoad( DaoVmSpace *vmSpace, DaoNamespace *nspace ) { ... // Wrap ClassTwo as an opaque C/C++ type: dao_type_ClassTwo = DaoNamespace_WrapType( nspace, & ClassTwo_Typer, 1 ); return 0; } @[code(cxx)] @[section] Data Conversion between Dao and C/C++ @[section] Dao provides various C interface functions to make data conversion between Dao and C/C++ simple. For simple data types, one can use the one of the following functions to convert Dao values to C values, @[code(cxx)] dao_integer DaoValue_TryGetInteger( DaoValue *self ); dao_float DaoValue_TryGetFloat( DaoValue *self ); dao_complex DaoValue_TryGetComplex( DaoValue *self ); char* DaoValue_TryGetChars( DaoValue *self ); DString* DaoValue_TryGetString( DaoValue *self ); int DaoValue_TryGetEnum( DaoValue *self ); void* DaoValue_TryGetCdata( DaoValue *self ); void** DaoValue_TryGetCdata2( DaoValue *self ); void* DaoValue_TryCastCdata( DaoValue *self, DaoType *totype ); @[code(cxx)] If the @[code]DaoValue@[code] object is of the requested type, the correct data will be returned, otherwise zero or a null pointer is return. The last three functions are execlusively for opaquely wrapped C/C++ types. For other data types, you may need to cast @[code]DaoValue@[code] objects to proper types, and then use proper methods to retrieve C data values. There are two ways to cast from @[code]DaoValue@[code] to other types, one is to use @[code]DaoValue_Type()@[code] to check its type and than do C casting, the other is to use one of the @[code]DaoValue_CastXXX()@[code] series of methods. For example, the following are the two ways to cast @[code]value@[code] from @[code]DaoValue@[code] to @[code]DaoTuple@[code], @[code(cxx)] DaoTuple *tup1 = DaoValue_Type( value ) == DAO_TUPLE ? (DaoTuple*) value : NULL; DaoTuple *tup2 = DaoValue_CastTuple( value ); @[code(cxx)] @[code]DaoValue_CastXXX()@[code] methods will return NULL, if the value is not the correct type. @[text]