A script class cannot directly inherit from an application registered class, as the script classes are not compiled into native machine code like the application classes are.
It is however possible to emulate the inheritance by using a proxy class to hide the underlying differences in an abstract layer. The proxy class has two parts, one is the C++ side that the application sees, and the other is the script side that the scripts can see and inherit from.
The following is an example implementation of such a proxy class.
This type is registered with the engine as the following:
The script side is declared as shared so it can be used in all script modules. It is also declared as abstract so it cannot be instantiated by itself, only as a parent class of another script class.
This script section should preferably be included automatically by the application in all the modules that should be able to derive from the FooScripted class.
// On the script side
shared abstract class FooScripted
{
// Allow scripts to create instances
FooScripted()
{
// Create the C++ side of the proxy
@m_obj = FooScripted_t();
}
// The copy constructor performs a deep copy
FooScripted(const FooScripted &o)
{
// Create a new C++ instance and copy content
@m_obj = FooScripted_t();
m_obj = o.m_obj;
"
}
// Do a deep a copy of the C++ object
FooScripted &opAssign(const FooScripted &o)
{
// copy content of C++ instance
m_obj = o.m_obj;
return this;
}
// The script side forwards the call to the C++ side
void CallMe() { m_obj.CallMe(); }
// The C++ side property is exposed to the script through accessors
int m_value
{
get { return m_obj.m_value; }
set { m_obj.m_value = value; }
}
// The script class can be implicitly cast to the C++ type through the opImplCast method
FooScripted_t \@opImplCast() { return m_obj; }
// Hold a reference to the C++ side of the proxy
private FooScripted_t \@m_obj;
}
</pre>
Now the scripts classes can derive from the FooScripted class <br>
and access the properties and methods of the parent class normally.
<pre>
// Implement a script class that derives from the application class
class FooDerived : FooScripted
{
void CallMe()
{
m_value += 1;
}
}
void main()
{
// When newly created the m_value is 0
FooDerived d;
assert( d.m_value == 0 );
// When calling the method the m_value is incremented with 1
d.CallMe();
assert( d.m_value == 1 );
}
</pre>
It is of course also possible to create an instance of the scripted class from
the application and access it through the FooScripted C++ proxy, thus making
it transparent from the rest of the application that the implementation is actually
in the script.
@code
FooScripted *CreateFooDerived(asIScriptEngine *engine)
{
// Create an instance of the FooDerived script class that inherits from the FooScripted C++ class
asIScriptObject *obj = reinterpret_cast<asIScriptObject*>(engine->CreateScriptObject(mod->GetTypeInfoByName("FooDerived")));
// Get the pointer to the C++ side of the FooScripted class
FooScripted *obj2 = *reinterpret_cast<FooScripted**>(obj->GetAddressOfProperty(0));
// Increase the reference count to the C++ object, as this is what will
// be used to control the life time of the object from the application side
obj2->AddRef();
// Release the reference to the script side
obj->Release();
return obj2;
}
void Foo(asIScriptEngine *engine)
{
FooScripted *obj = CreateFooDerived(engine);
// Once the object is created the application can access it normally through
// the FooScripted pointer, without having to know that the implementation
// is actually done in the script.
// When newly created the m_value is 0
assert( obj->m_value == 0 );
// When calling the method the m_value is incremented with 1 by the script
obj->CallMe();
assert( obj->m_value == 1 );
// Release the object to destroy the instance (this will also destroy the script side)
obj->Release();
}