[NAME] ALL.daovm.interface.extending [TITLE] Extending Dao VM [DESCRIPTION] In the help entry daovm.interface.embedding, it has been demonstrated that embedding Da o is extremely simple. Here we will demonstrate that extending Dao is also extremely simp le. 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 fr om Dao data types to C/C++ data types. This means writing wrapping functions (Dao-callabl e C functions) is significantly simpler than writing wrapping functions for other langua ges such as Python or even Lua. 1 The First Simple Extending Module 1 The First Simple Extending Module 1 The First Simple Extending Module 1.1 Extension Function Prototype All Dao-callable C functions must have prototype similar to the following example, 1 void MyCFunction( DaoProcess *proc, DaoValue *param[], int nparam ) 2 { 3 printf( "Hello Dao!\n" ); 4 } 1.2 Module Entry Function Each Dao extending module must provide an entry function. The following "DaoOnLoad" is th e basic form of the entry function name, which may also include the module name. 1 // Entry function for each C/C++ module: 2 int DaoOnLoad( DaoVmSpace *vmspace, DaoNamespace *ns ); This function will be called automatically to allow the module to register its functions and types etc. The first parameter is the DaoVmSpace instance which is responsible for lo ading 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 n amespace. As mentioned above, the name of the entry function may also include the module name. Supp ose the module name is "abc" and may appear in load statements as load abc or load path. abc, the module name can appear in the entry function name in the following forms and is also searched in this order: 1. All lower case letters: e.g. Daoabc_OnLoad; 2. First upper case letter: e.g. DaoAbc_OnLoad; 3. All upper case letters: e.g. DaoABC_OnLoad; 1.3 Function Registeration To register functions to a namespace, one can use one of the following interface function s of DaoNamespace, 1 // Function to register a single function: 2 DaoRoutine* DaoNamespace_WrapFunction( DaoNamespace *self, DaoCFunction fp, const char *proto ); 3 4 // Function to register multiple functions: 5 int DaoNamespace_WrapFunctions( DaoNamespace *self, DaoFunctionEntry *items ); 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 (MyCFunction in this case); and the last para meter is the Dao function prototype for the registered function. So you can register the above function MyCFunction as the following, 1 // Register function: 2 DaoNamespace_WrapFunction( nspace, MyCFunction, "HelloDao()" ); So that this function can be called in Dao by name HelloDao without any parameter. 1.4 Summary To sum it up, the simplest Dao extending module could be the following, 1 #include "dao.h" 2 #include "stdio.h" 3 static void MyCFunction( DaoProcess *proc, DaoValue *param[], int nparam ) 4 { 5 printf( "Hello Dao!\n" ); 6 } 7 int DaoOnLoad( DaoVmSpace *vmspace, DaoNamespace *nspace ) 8 { 9 DaoNamespace_WrapFunction( nspace, MyCFunction, "HelloDao()" ); 10 return 0; 11 } To compile it, you will need to add the Dao header file path to your compiling option. An d you will also need to add the following preprocessor definitions: * On Win32: WIN32; * On Unix: UNIX; * On Mac OSX: MACOSX; 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, * On Unix: -rdynamic; * On Mac OSX: -undefined dynamic_lookup; If you use DaoMake (tool.standard.daomake) to build your modules, these will be taken car e of automatically. 2 The Second Simple Extending Module 2 The Second Simple Extending Module 2 The Second Simple Extending Module Now we will demonstrate how to wrap a function that can accept parameters and return a va lue. Suppose we want to wrap the following C function, 1 float Test( int id, const char *name, int extra ); and support a default value for the last parameter "extra". Then we would need to registe r the function with the following Dao function prototype, 1 # Dao function prototype 2 MyTest( id: int, name: string, extra = 0 ) => float 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, 1 void MyTestInC( DaoProcess *proc, DaoValue *param[], int nparam ) 2 { 3 daoint id = param[0]->xInteger.value; 4 char *name = param[1]->xString.value->chars; 5 daoint extra = param[2]->xInteger.value; 6 DaoProcess_PutFloat( proc, Test( id, name, extra ) ); 7 } 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 gu aranteed 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 daoValue.h in your module source file. And you may also need to be familiar with the s tandard Dao data type structures (it is simple actually). If you only want to use the API s from the dao.h header file, you may need to used those DaoValue_TryGetXXX() functions w hich do some mininum data type checking with little overheads and return the proper C dat a. 1 void MyTestInC( DaoProcess *proc, DaoValue *param[], int nparam ) 2 { 3 daoint id = DaoValue_TryGetInteger( param[0] ); 4 char *name = DaoValue_TryGetChars( param[1] ); 5 daoint extra = DaoValue_TryGetInteger( param[2] ); 6 DaoProcess_PutFloat( proc, Test( id, name, extra ) ); 7 } If you register the function with proper Dao function prototype, the type checking will a lways succeed and correct data will always be obtained. The DaoProcess_PutFloat() is for returning a float value at a proper location as the returned value of the C function. Ple ase see the following section(s) for more details. Now this function can be registered as: 1 // Register a function with parameters and returned value: 2 DaoNamespace_WrapFunction( nspace, MyTestInC, "MyTest(id:int,name:string,extra=0)=>float" ); 3 Type Wrapping 3 Type Wrapping 3 Type Wrapping In Dao virtual machine, a Dao data type is usually represented by three structures: * DaoType: The high level representation of the type for type matching and checking; * DaoTypeKernel: The middle level representation of the type for holding the wrapped values and methods, or specialized methods for some generic types; * DaoTypeCore: The low level representation of the type for specifying the basic info rmation, type checking, running time execution, copying, comparison, hashing, print ing, printing, deletion and garbage collection behaviours; The first two structures are usually generated automatically from the last one. For wrapp ing C/C++ types, you only need to define the DaoTypeCore structure. 3.1 Type Definition Structure The type definition structure DaoTypeCore is defined as the following: 1 struct DaoTypeCore 2 { 3 const char *name; // Name of the type; 4 int size; // Size of the value structure; 5 DaoTypeCore *bases[8]; // Type cores for the base types; 6 DaoNumberEntry *numbers; // Member numbers; 7 DaoFunctionEntry *methods; // Member methods; 8 9 DaoType* (*CheckGetField)( DaoType *self, DaoString *field, DaoRoutine *rout ); 10 DaoValue* (*DoGetField)( DaoValue *self, DaoString *field, DaoProcess *proc ); 11 12 int (*CheckSetField)( DaoType *self, DaoString *field, DaoType *value, DaoRoutine *rout ); 13 int (*DoSetField)( DaoValue *self, DaoString *field, DaoValue *value, DaoProcess *proc ); 14 15 DaoType* (*CheckGetItem)( DaoType *self, DaoType *index[], int N, DaoRoutine *rout ); 16 DaoValue* (*DoGetItem)( DaoValue *self, DaoValue *index[], int N, DaoProcess *proc ); 17 18 int (*CheckSetItem)( DaoType *self, DaoType *index[], int N, DaoType *value, DaoRoutine *rout ); 19 int (*DoSetItem)( DaoValue *self, DaoValue *index[], int N, DaoValue *value, DaoProcess *proc ); 20 21 DaoType* (*CheckUnary)( DaoType *self, DaoVmCode *op, DaoRoutine *rout ); 22 DaoValue* (*DoUnary)( DaoValue *self, DaoVmCode *op, DaoProcess *proc ); 23 24 DaoType* (*CheckBinary)( DaoType *self, DaoVmCode *op, DaoType *operands[2], DaoRoutine *rout ); 25 DaoValue* (*DoBinary)( DaoValue *self, DaoVmCode *op, DaoValue *operands[2], DaoProcess *proc ); 26 27 DaoType* (*CheckConversion)( DaoType *self, DaoType *type, DaoRoutine *rout ); 28 DaoValue* (*DoConversion)( DaoValue *self, DaoType *type, int copy, DaoProcess *proc ); 29 30 DaoType* (*CheckForEach)( DaoType *self, DaoRoutine *rout ); 31 int (*DoForEach)( DaoValue *self, DaoTuple *iterator, DaoProcess *proc ); 32 33 void (*Print)( DaoValue *self, DaoStream *stream, DMap *cycmap, DaoProcess *proc ); 34 void (*Slice)( DaoValue *self ); 35 int (*Compare)( DaoValue *self, DaoValue *other, DMap *cycmap ); 36 size_t (*Hash)( DaoValue *self ); 37 DaoValue* (*Create)( DaoType *self ); 38 DaoValue* (*Copy)( DaoValue *self, DaoValue *target ); 39 void (*Delete)( DaoValue *self ); 40 void (*HandleGC)( DaoValue *self, DList *values, DList *lists, DList *maps, int remove ); 41 }; The type definition structure DaoTypeCore not only defines the basic information for the type, but also includes some function pointers that perform some standard operations on t he 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 functi on called at running time to do the operation. The type checking function should return t he resulting type on success and null otherwise. For user defined types, the default implementations are provided and declared as DaoCstru ct_CheckXXX() and DaoCstruct_DoXXX() functions in the header file dao.h. These default im plementations use operator overloading from the member methods to do type checkings and r unning time executions. At running time, the executing DaoProcess object is usually passed to the execution funct ions, 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 i f necessary. 3.1.1 Name and Size 3.1.2 Member constant numbers and methods The third field numbers can be used to specify a list of member constant numbers, which a re defined in an array of the following structure: 1 struct DaoNumberEntry 2 { 3 const char *name; /* contant name; */ 4 int type; /* number type; */ 5 double value; /* number value; */ 6 }; The number type should be one of DAO_BOOLEAN, DAO_INTEGER and DAO_FLOAT. The array should be terminated with a item with null name: 1 static DaoNumberEntry myTypeNumbers[] = 2 { 3 { "MODE_ONE", DAO_INTEGER, MODE_ONE }, 4 { "MODE_TWO", DAO_INTEGER, MODE_TWO }, 5 { NULL, 0, 0 } 6 }; If the type has no member constant numbers, it can be simply set to NULL. The fourth field methods can be used to specify a list of member methods, which are defin ed in an array of the following structure: 1 struct DaoFunctionEntry 2 { 3 DaoCFunction fpter; /* C function pointer; */ 4 const char *proto; /* function prototype: name( parlist ) => return_type */ 5 }; where the two fields fpter and proto should be the same as they would in the parameter li st of: 1 DaoRoutine* DaoNamespace_WrapFunction( DaoNamespace *self, DaoCFunction fp, const char *proto ); 3.1.3 Inheritance The inheritance relationship can be defined with the bases field, by specifying the type definition structures for the base types in this field. Given that a type definition stru cture can be used for both wrapping C/C++ types and defining interface types, the bases f ield can list an array of base types to support multiple inheritance. If an inheritance relationship involves wrapped C++ classes with virtual functions, the c onversion between a base type and a derived type should be handled properly in the DaoTypeCore::DoConversion method (See bellow). 3.1.4 Get Field Operation 1 DaoType* (*CheckGetField)( DaoType *self, DaoString *field, DaoRoutine *rout ); 2 DaoValue* (*DoGetField)( DaoValue *self, DaoString *field, DaoProcess *proc ); 3.1.5 Set Field Operation 1 int (*CheckSetField)( DaoType *self, DaoString *field, DaoType *value, DaoRoutine *rout ); 2 int (*DoSetField)( DaoValue *self, DaoString *field, DaoValue *value, DaoProcess *proc ); 3.1.6 Get Item Operation 1 DaoType* (*CheckGetItem)( DaoType *self, DaoType *index[], int N, DaoRoutine *rout ); 2 DaoValue* (*DoGetItem)( DaoValue *self, DaoValue *index[], int N, DaoProcess *proc ); 3.1.7 Set Item Operation 1 int (*CheckSetItem)( DaoType *self, DaoType *index[], int N, DaoType *value, DaoRoutine *rout ); 2 int (*DoSetItem)( DaoValue *self, DaoValue *index[], int N, DaoValue *value, DaoProcess *proc ); 3.1.8 Unary Operation 1 DaoType* (*CheckUnary)( DaoType *self, DaoVmCode *op, DaoRoutine *rout ); 2 DaoValue* (*DoUnary)( DaoValue *self, DaoVmCode *op, DaoProcess *proc ); 3.1.9 Binary Operation 1 DaoType* (*CheckBinary)( DaoType *self, DaoVmCode *op, DaoType *operands[2], DaoRoutine *rout ); 2 DaoValue* (*DoBinary)( DaoValue *self, DaoVmCode *op, DaoValue *operands[2], DaoProcess *proc ); 3.1.10 Type Conversion Operation 1 DaoType* (*CheckConversion)( DaoType *self, DaoType *type, DaoRoutine *rout ); 2 DaoValue* (*DoConversion)( DaoValue *self, DaoType *type, int copy, DaoProcess *proc ); 3.1.11 Deallcation 1 void (*Delete)( DaoValue *self ); If instance object can be created for the type (not its derived types), a deallcation fun ction must be supplied in the type definition structure, in order to properly delete the object once it becomes unreachable in the program. 3.1.12 GC Handling 1 void (*HandleGC)( DaoValue *self, DList *values, DList *lists, DList *maps, int remove ); If an instance object of the type may hold counted references to non-primitive Dao data v alues, 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 DaoTypeCore, namely HandleGC. This function will be called during garbage collection scans, and it must expor t its reference couned values to the parameter lists; In this function, directly referenced member values should be pushed into values, and DList members holding referenced values should be pushed into lists, and DMap members hol ding referenced values should be pushed into maps. When the user defined value is marked for deletion by the garbage collector, the remove parameter will be set to non-zero. In t he case, references to the member values that are pushed to values should be removed by s etting them to nulls. This is necessary, because some of the objects referenced by this o bject could be deallocated by the collector before this object is deallocated, breaking r eferences to them will avoid posible double deletion. 3.1.13 Type Registraction A C/C++ type can be used in Dao if only if it is registered in a Dao namespace with a typ e definition structure DaoTypeCore through, 1 DaoType* DaoNamespace_WrapType( DaoNamespace *self, DaoTypeCore *core, int tid, int options ); 2 int DaoNamespace_WrapTypes( DaoNamespace *self, DaoTypeCore *core[] ); The tid parameter must be set to DAO_CSTRUCT for customized types, or DAO_CDATA for opaq ue wrapper types. The options parameter can be set to either 0 or DAO_CTYPE_INVAR for inv ariable types. There are two ways to extend Dao with user defined C/C++ types. One is to wrap it around by a DaoCdata object, and access it as an opaque pointer. This is the standard way to wra p existing C/C++ types. The other is to define a customized C type, and use it in the sam e way as the first. A customized C type is a C structure sharing the same header fields a s DaoCdata, which can make sharing types between Dao and C simpler (especially for garbag e collection). Wrapped C/C++ types and customized C types can be added to Dao in almost t he identical way, so I will introduce the wrapped types first, and then the customized ty pes should be very easy to understand. Dao interface types can also be defined in C/C++ extensions. To define interface types, y ou need to defined type definition structions in a similar way as the above wrapped C/C++ data types. And then wrap them using, 1 DaoType* DaoNamespace_WrapInterface( DaoNamespace *self, DaoTypeCore *core ); 2 DaoType* DaoNamespace_WrapCinType( DaoNamespace *self, DaoTypeCore *c, DaoType *a, DaoType *t ); See bellow for example. 3.2 A simple example Given the following C++ class, 1 class ClassOne 2 { 3 public: 4 int value; 5 6 enum{ CLASSONE_AA, CLASSONE_BB }; 7 8 ClassOne( int v ); 9 10 int Method( const char *s ); 11 }; It can be wrapped in the following way, 1 // Declare the wrapper functions first: 2 static void dao_ClassOne_ClassOne( DaoProcess *proc, DaoValue *p[], int n ); 3 static void dao_ClassOne_Method( DaoProcess *proc, DaoValue *p[], int n ); 4 // List of constant member numbers for the enums: 5 static DaoNumberEntry ClassOneNumbers[] = 6 { 7 { "CLASSONE_AA", DAO_INTEGER, CLASSONE_AA }, 8 { "CLASSONE_BB", DAO_INTEGER, CLASSONE_BB }, 9 { NULL, 0, 0 } 10 }; 11 // List of member constructors or methods of ClassOne: 12 static DaoFunctionEntry ClassOneMethods[] = 13 { 14 // Methods with the same name as the type name are constructors: 15 { dao_ClassOne_ClassOne, "ClassOne( v: int )" }, 16 { dao_ClassOne_Method, "Method( self: ClassOne, s: string ) => int" }, 17 { NULL, NULL } 18 }; 19 static void ClassOne_Delete( void *self ) 20 { 21 delete (ClassOne*) self; 22 } 23 // The type definition structure for ClassOne: 24 static DaoTypeCore ClassOne_Typer = 25 { 26 "ClassOne", NULL, ClassOneNumbers, ClassOneMethods, 27 {NULL}, {NULL}, ClassOne_Delete, NULL 28 }; 29 // The Dao type structure for ClassOne: 30 DaoType *dao_type_ClassOne = NULL; 31 32 static void dao_ClassOne_ClassOne( DaoProcess *proc, DaoValue *p[], int n ) 33 { 34 // Get the integer parameter; 35 daoint v = DaoValue_TryGetInteger( p[0] ); 36 // Create a ClassOne instance: 37 ClassOne *self = new ClassOne( v ); 38 // Wrap the instance with Dao type structure: 39 DaoProcess_PutCdata( proc, self, dao_type_ClassOne ); 40 } 41 static void dao_ClassOne_Method( DaoProcess *proc, DaoValue *p[], int n ) 42 { 43 // Get the ClassOne instance: 44 ClassOne *self = (ClassOne*) DaoValue_TryCastCdata( p[0], dao_type_ClassOne ); 45 // Get the string parameter: 46 char *s = DaoValue_TryGetChars( p[1] ); 47 int res = self->Method( s ); 48 // Return the integer result: 49 DaoProcess_PutInteger( proc, res ); 50 } 51 int DaoOnLoad( DaoVmSpace *vmSpace, DaoNamespace *nspace ) 52 { 53 // Wrap ClassOne as an opaque C/C++ type: 54 dao_type_ClassOne = DaoNamespace_WrapType( nspace, & ClassOne_Typer, 1 ); 55 return 0; 56 } Since the value member of ClassOne is a public member, it is reasonable to add a getter a nd a setter method to wrapped ClassOne type. To add a getter, one only needs to register a method with name .field and no extra parameter. And for a setter, the method name must be .field=, and it must also accept a parameter with type the same as the value that can be assigned. For example, for the value member, one can added the following to the ClassOneMethods list, 1 // the getter and setter: 2 { dao_ClassOne_GETF_value, ".value( self: ClassOne ) => int" }, 3 { dao_ClassOne_SETF_value, ".value=( self: ClassOne, value: int )" }, Here the name dao_ClassOne_GETF_value and dao_ClassOne_SETF_value are completely arbitra ry. They can be implemented in the following way, 1 static void dao_ClassOne_GETF_value( DaoProcess *proc, DaoValue *p[], int n ) 2 { 3 ClassOne *self = (ClassOne*) DaoValue_TryCastCdata( p[0], dao_type_ClassOne ); 4 DaoProcess_PutInteger( proc, self->value ); 5 } 6 static void dao_ClassOne_SETF_value( DaoProcess *proc, DaoValue *p[], int n ) 7 { 8 ClassOne *self = (ClassOne*) DaoValue_TryCastCdata( p[0], dao_type_ClassOne ); 9 self->value = DaoValue_TryGetInteger( p[1] ); 10 } 3.3 An advanced example Now given the following class that is derived from ClassOne, 1 class ClassTwo : public ClassOne 2 { 3 public: 4 virtual void VirtualMethod( int i, float f ); 5 }; Because this class has a virtual method, if we want Dao classes can be derived from ClassTwo and reimplement its virtual functions, the wrapping will be a bit more sophistic ated. First, we will need to define a "proxy" class that is derived from ClassTwo and rei mplements 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 ClassTwo needs to be created, an instance of this proxy class will be created and returned instead of the original ClassTwo. Here is an example of such proxy class, 1 class Dao_ClassTwo : public ClassTwo 2 { 3 public: 4 5 DaoCdata *dao_cdata; 6 7 Dao_ClassTwo(); 8 ~Dao_ClassTwo(); 9 10 int VirtualMethod( int i, float f ); 11 }; This proxy class will need to maintain a reference to the wrapper object, so an extra fie ld dao_cdata is declared in the class. This wrapper object can be pre-allocated in the co nstructor of Dao_ClassTwo, 1 Dao_ClassTwo::Dao_ClassTwo() 2 { 3 dao_cdata = DaoCdata_New( dao_type_ClassTwo, this ); 4 DaoGC_IncRC( (DaoValue*)dao_cdata ); 5 } Here the dao_type_ClassTwo is the Dao type object for ClassTwo, and can be obtained in th e same way as dao_type_ClassOne. Now that Dao_ClassTwo has a reference to a DaoCdata obje ct, the GetGCFields field of the type definition structure for ClassTwo should be set to a proper function, which will be provided later. The destructor should also be handled th is reference properly by, 1 Dao_ClassTwo::~Dao_ClassTwo() 2 { 3 if( dao_cdata ){ // Could have been set to NULL by the GC: 4 // Set the opaque pointer of dao_cdata to NULL, so that the deallocator 5 // of DaoCdata will not attempt to call the deallocator of the opaque pointer: 6 DaoCdata_SetData( dao_cdata, NULL ); 7 DaoGC_DecRC( (DaoValue*) dao_cdata ); 8 } 9 } Then the VirtualMethod() could be implemented in the following way, 1 int Dao_ClassTwo::VirtualMethod( int i, float f ) 2 { 3 DaoVmSpace *vmspace = DaoVmSpace_MainVmSpace(); 4 DaoProcess *proc = NULL; 5 6 // Try to get the instance of a derived Dao class: 7 DaoObject *object = DaoCdata_GetObject( dao_cdata ); 8 if( object == NULL ) goto CallDefault; 9 10 // Try to get a method named "VirtualMethod": 11 DaoRoutine *method = DaoObject_GetMethod( object, "VirtualMethod" ); 12 if( method == NULL ) goto CallDefault; 13 14 // Check if the method is a C/C++ wrapper function: 15 if( DaoRoutine_IsWrapper( method ) ) goto CallDefault; 16 17 // Acquire a process object to execute the re-implemented virtual function: 18 proc = DaoVmSpace_AcquireProcess( vmspace ); 19 20 // Prepare function call parameters: 21 DaoProcess_NewInteger( proc, i ); 22 DaoProcess_NewFloat( proc, f ); 23 DaoValue **params = DaoProcess_GetLastValues( proc, 2 ); 24 25 // Resolve possible overloading using the parameters: 26 method = DaoRoutine_ResolveByValue( method, object, params, 2 ); 27 if( method == NULL ) goto CallDefault; 28 29 // Run the re-implemented function: 30 if( DaoProcess_Call( proc, method, object, params, 2 ) ) goto ErrorCall; 31 32 // Check the returned value: 33 DaoValue *res = DaoProcess_GetReturned( proc ); 34 if( DaoValue_CastInteger( res ) ) goto ErrorCall; 35 36 int ires = DaoValue_TryGetInteger( res ); 37 38 // Release the process object: 39 DaoProcess_Release( vmspace, proc ); 40 41 return ires; 42 43 CallDefault: 44 if( proc ) DaoProcess_Release( vmspace, proc ); 45 return ClassTwo::VirtualMethod( i, f ); 46 ErrorCall: 47 DaoProcess_Release( vmspace, proc ); 48 return 0; 49 } Now we will define a function that can be set to the GetGCFields field of the type defini tion structure of ClassTwo. 1 static void Dao_ClassTwo_GetGCFields( void *self0, 2 DList *values, DList *lists, DList *maps, int remove ) 3 { 4 Dao_ClassTwo *self = (Dao_ClassTwo*) self0; 5 if( self->dao_cdata == NULL ) return; 6 DList_Append( values, self->dao_cdata ); 7 if( removeĀ ){ 8 // Parameter "remove" is zero, when this function is called by the 9 // garbage collector during the phase when the candidate garbage 10 // objects are scanned to determine true garbage objects. 11 // 12 // For true garbage objects for wrapped types, this function will be 13 // called again with non-zero "remove" parameter. And in this case, 14 // the "dao_cdata" field should be set to NULL here, since it will be 15 // deleted by the garbage collector directly. 16 // 17 // Care must be taken if there is something else that is responsible 18 // for deallocating the wrapped C/C++ object. For example, in some GUI 19 // libraries, the parent widget will be responsible for deleting its 20 // children widgets. To prevent the deallocator of the wrapped C/C++ 21 // objects from being invoked by Dao garbage collector, something like 22 // the following can be used: 23 // if( self->parent() ) DaoCdata_SetData( self->dao_cdata, NULL ); 24 25 // After this call, "dao_cdata" may become invalid, now set it to NULL 26 // to avoid wrong using: 27 self->dao_cdata = NULL; 28 } 29 } The remaining part for wrapping ClassTwo should be something like, 1 static void dao_ClassTwo_ClassTwo( DaoProcess *proc, DaoValue *p[], int n ) 2 { 3 Dao_ClassTwo *self = new Dao_ClassTwo(); 4 DaoProcess_PutValue( proc, (DaoValue*) self->dao_cdata ); 5 } 6 static DaoFunctionEntry ClassTwoMethods[] = 7 { 8 { dao_ClassTwo_ClassTwo, "ClassTwo()" }, 9 { NULL, NULL } 10 }; 11 static void Dao_ClassTwo_Delete( void *self ) 12 { 13 delete (Dao_ClassTwo*) self; 14 } 15 static void* Dao_ClassTwo_Cast_ClassOne( void *data, int down ) 16 { 17 if( down ) return static_cast<ClassTwo*>((ClassOne*)data); 18 return dynamic_cast<ClassOne*>((ClassTwo*)data); 19 } 20 // The type definition structure for ClassTwo: 21 static DaoTypeCore ClassTwo_Typer = 22 { 23 "ClassTwo", NULL, NULL, ClassTwoMethods, 24 { & ClassOne_Typer, NULL }, 25 { Dao_ClassTwo_Cast_ClassOne, NULL }, 26 Dao_ClassTwo_Delete, NULL 27 }; 28 // The Dao type structure for ClassTwo: 29 DaoType *dao_type_ClassTwo = NULL; 30 31 int DaoOnLoad( DaoVmSpace *vmSpace, DaoNamespace *nspace ) 32 { 33 ... 34 // Wrap ClassTwo as an opaque C/C++ type: 35 dao_type_ClassTwo = DaoNamespace_WrapType( nspace, & ClassTwo_Typer, 1 ); 36 return 0; 37 } 4 Data Conversion between Dao and C/C++ 4 Data Conversion between Dao and C/C++ 4 Data Conversion between Dao and C/C++ 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, 1 dao_integer DaoValue_TryGetInteger( DaoValue *self ); 2 dao_float DaoValue_TryGetFloat( DaoValue *self ); 3 dao_complex DaoValue_TryGetComplex( DaoValue *self ); 4 char* DaoValue_TryGetChars( DaoValue *self ); 5 DString* DaoValue_TryGetString( DaoValue *self ); 6 int DaoValue_TryGetEnum( DaoValue *self ); 7 8 void* DaoValue_TryGetCdata( DaoValue *self ); 9 void** DaoValue_TryGetCdata2( DaoValue *self ); 10 void* DaoValue_TryCastCdata( DaoValue *self, DaoType *totype ); If the DaoValue object is of the requested type, the correct data will be returned, other wise zero or a null pointer is return. The last three functions are execlusively for opaq uely wrapped C/C++ types. For other data types, you may need to cast DaoValue objects to proper types, and then use proper methods to retrieve C data values. There are two ways to cast from DaoValue to oth er types, one is to use DaoValue_Type() to check its type and than do C casting, the othe r is to use one of the DaoValue_CastXXX() series of methods. For example, the following a re the two ways to cast value from DaoValue to DaoTuple, 1 DaoTuple *tup1 = DaoValue_Type( value ) == DAO_TUPLE ? (DaoTuple*) value : NULL; 2 DaoTuple *tup2 = DaoValue_CastTuple( value ); DaoValue_CastXXX() methods will return NULL, if the value is not the correct type.