The implementation of the template type is not a C++ template though, instead it must be implemented as a generic class that can determine what to do dynamically at runtime based on the subtype for which it was instanciated. This is obviously a lot less efficient than having specific implementations for each type, and for that reason AngelScript permits the application to register a template specialization where the extra performance is needed.
This gives the best of both worlds, performance where the subtype is known before hand, and support for all other types that cannot be pre-determined.
// Register the template type r = engine->RegisterObjectType("myTemplate<class T>", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 );
The template type doesn't have to be garbage collected, but since you may not know which subtypes it will be instanciated for, it is usually best to implement that support.
When registering the behaviours, methods, and properties for the template type the type is identified with the name and subtype within angle brackets, but without the class token, e.g. myTemplate<T>
. The sub type is identified by just the name of the subtype as it was declared in the call to RegisterObjectType.
The factory behaviour for the template type is also different. In order for the implementation to know which subtype it is instanciated for the factory receives the asIObjectType of the template instance as a hidden first parameter. When registering the factory this hidden parameter is reflected in the declaration, for example as int &in
.
// Register the factory behaviour r = engine->RegisterObjectBehaviour("myTemplate<T>", asBEHAVE_FACTORY, "myTemplate<T>@ f(int&in)", asFUNCTIONPR(myTemplateFactory, (asIObjectType*), myTemplate*), asCALL_CDECL); assert( r >= 0 );
Remember that since the subtype must be determined dynamically at runtime, it is not possible to declare functions to receive the subtype by value, nor to return it by value. Instead you'll have to design the methods and behaviours to take the type by reference. It is possible to use object handles, but then the script engine won't be able to instanciate the template type for primitives and other values types.
The callback function must be a global function that receives an asIObjectType pointer, and should return a boolean. If the template instance is valid the return value should be true.
// Register the template callback r = engine->RegisterObjectBehaviour("myTemplate<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int &in)", asFUNCTION(myTemplateCallback), asCALL_CDECL); assert( r >= 0 );
Here's an example callback function:
bool myTemplateCallback(asIObjectType *ot) { // This template will only support primitive types int typeId = ot->GetSubTypeId(); if( typeId & asTYPEID_MASK_OBJECT ) { // The script is attempting to instantiate the // template with an object type, this is not allowed. return false; } // Primitive types are allowed return true; }
With the exception of the type name, a template specialization is registered exactly like a normal type.
// Register a template specialization for the float subtype r = engine->RegisterObjectType("myTemplate<float>", 0, asOBJ_REF); assert( r >= 0 ); // Register the factory (there are no hidden parameters for specializations) r = engine->RegisterObjectBehaviour("myTemplate<float>", asBEHAVE_FACTORY, "myTemplate<float>@ f()", asFUNCTION(myTemplateFloatFactory, (), myTemplateFloat*), asCALL_CDECL); assert( r >= 0 );