AngelScript
|
When registering a value type, the size of the type must be given so that AngelScript knows how much space is needed for it. If the type doesn't require any special treatment, i.e. doesn't contain any pointers or other resource references that must be maintained, then the type can be registered with the flag asOBJ_POD. In this case AngelScript doesn't require the default constructor, assignment behaviour, or destructor as it will be able to automatically handle these cases the same way it handles built-in primitives.
If you plan on passing or returning the type by value to registered functions that uses native calling convention, you also need to inform how the type is implemented in the application, but if you only plan on using generic calling conventions, or don't pass these types by value then you don't need to worry about that.
// Register a primitive type, that doesn't need any special management of the content r = engine->RegisterObjectType("pod", sizeof(pod), asOBJ_VALUE | asOBJ_POD); assert( r >= 0 ); // Register a class that must be properly initialized and uninitialized r = engine->RegisterObjectType("val", sizeof(val), asOBJ_VALUE); assert( r >= 0 );
If a constructor or destructor is needed they shall be registered the following way:
void Constructor(void *memory) { // Initialize the pre-allocated memory by calling the // object constructor with the placement-new operator new(memory) Object(); } void Destructor(void *memory) { // Uninitialize the memory by calling the object destructor ((Object*)memory)->~Object(); } // Register the behaviours r = engine->RegisterObjectBehaviour("val", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Constructor), asCALL_CDECL_OBJLAST); assert( r >= 0 ); r = engine->RegisterObjectBehaviour("val", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(Destructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
Note that you may need to include the <new> header to declare the placement new operator that is used to initialize a preallocated memory block.
If the type will be passed to and from the application by value using native calling conventions, it is important to inform AngelScript of its real type in C++, otherwise AngelScript won't be able to determine exactly how C++ is treating the type in a parameter or return value.
There are a few different flags for this:
asOBJ_APP_CLASS | The C++ type is a class, struct, or union |
asOBJ_APP_CLASS_CONSTRUCTOR | The C++ type has a defined constructor |
asOBJ_APP_CLASS_DESTRUCTOR | The C++ type has a defined destructor |
asOBJ_APP_CLASS_ASSIGNMENT | The C++ type has a defined assignment operator |
asOBJ_APP_CLASS_COPY_CONSTRUCTOR | The C++ type has a defined copy constructor |
asOBJ_APP_PRIMITIVE | The C++ type is a C++ primitive, but not a float or double |
asOBJ_APP_FLOAT | The C++ type is a float or double |
Note that these don't represent how the type will behave in the script language, only what the real type is in the host application. So if you want to register a C++ class that you want to behave as a primitive type in the script language you should still use the flag asOBJ_APP_CLASS. The same thing for the flags to identify that the class has a constructor, destructor, assignment operator, or copy constructor. These flags tell AngelScript that the class has the respective function, but not that the type in the script language should have these behaviours.
For class types there is also a shorter form of the flags for each combination of the 5 flags. They are of the form asOBJ_APP_CLASS_CDAK, where the existance of the last letters determine if the constructor, destructor, and/or assignment behaviour are available. For example asOBJ_APP_CLASS_CDAK is defined as asOBJ_APP_CLASS | asOBJ_APP_CLASS_CONSTRUCTOR | asOBJ_APP_CLASS_DESTRUCTOR | asOBJ_APP_CLASS_ASSIGNMENT | asOBJ_APP_CLASS_COPY_CONSTRUCTOR.
// Register a complex type that will be passed by value to the application r = engine->RegisterObjectType("complex", sizeof(complex), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
Make sure you inform these flags correctly, because if you do not you may get various errors when executing the scripts. Common problems are stack corruptions and invalid memory accesses. In some cases you may face more silent errors that may be difficult to detect, e.g. the function is not returning the expected values.
On some platforms the native calling convention may require further knowledge about the class members in order to work properly; most notable are the Linux 64bit and Mac OSX 64bit systems with the GNUC compiler. On these systems small classes that do not have a destructor or a copy constructor will have different behaviours depending on the type of their members.
AngelScript lets the application inform the two most common variants, i.e. the class should be treated as if all members are integers, or it should be treated as if all members are floats.
asOBJ_APP_CLASS_ALLINTS | The C++ class members can be treated as if all integers |
asOBJ_APP_CLASS_ALLFLOATS | The C++ class members can be treated as if all floats or doubles |
It is difficult to explain when one or the other should be used as it requires indepth knowledge of the ABI for the respective system, so if you find that you really need to use these flags, make sure you perform adequate testing to guarantee that your functions are called correctly by the script engine. If neither of these flags work, and you're not able to change the class to work without them, then the only other option is to use the generic calling convention, preferably with the auto wrappers.