Here is my example:
class FooScripted
{
public:
// Public interface that we want the script to be able to override
void CallMe()
{
// If the script side is still alive, then call the scripted function
if (!m_isDead->Get())
{
asIScriptEngine* engine = m_obj->GetEngine();
asIScriptContext* ctx = engine->RequestContext();
// GetMethodByDecl returns the virtual function on the script class
// thus when calling it, the VM will execute the derived method
ctx->Prepare(m_obj->GetObjectType()->GetMethodByDecl("void CallMe()"));
ctx->SetObject(m_obj);
ctx->Execute();
engine->ReturnContext(ctx);
}
}
int m_value;
// A factory function that can be used by the script side to create
static FooScripted* Factory()
{
asIScriptContext* ctx = asGetActiveContext();
// Get the function that is calling the factory so we can be certain it is the FooScript script class
asIScriptFunction* func = ctx->GetFunction(0);
if (func->GetObjectType() == 0 || std::string(func->GetObjectType()->GetName()) != "FooScripted")
{
ctx->SetException("Invalid attempt to manually instantiate FooScript_t");
return 0;
}
// Get the this pointer from the calling function so the FooScript C++
// class can be linked with the FooScript script class
asIScriptObject* obj = reinterpret_cast<asIScriptObject*>(ctx->GetThisPointer(0));
return new FooScripted(obj);
}
// Reference counting
void AddRef()
{
m_refCount++;
// Increment also the reference counter to the script side so
// it isn't accidentally destroyed before the C++ side
if (!m_isDead->Get())
m_obj->AddRef();
}
void Release()
{
// Release the script instance too
if (!m_isDead->Get())
m_obj->Release();
if (--m_refCount == 0) delete this;
}
// Assignment operator
FooScripted& operator=(const FooScripted& o)
{
// Copy only the content, not the script proxy class
m_value = o.m_value;
return *this;
}
protected:
// The constructor and destructor are indirectly called
FooScripted(asIScriptObject* obj) : m_obj(0), m_isDead(0), m_value(0), m_refCount(1)
{
// Get the weak ref flag for the script object to
// avoid holding a strong reference to the script class
m_isDead = obj->GetWeakRefFlag();
m_isDead->AddRef();
m_obj = obj;
}
~FooScripted()
{
// Release the weak ref flag
m_isDead->Release();
}
// Reference count
int m_refCount;
// The C++ side holds a weak link to the script side to
// avoid a circular reference between the C++ side and
// script side
asILockableSharedBool* m_isDead;
asIScriptObject* m_obj;
};
void RegisterFooScripted(asIScriptEngine* engine)
{
engine->RegisterObjectType("FooScripted_t", 0, asOBJ_REF);
engine->RegisterObjectBehaviour("FooScripted_t", asBEHAVE_FACTORY, "FooScripted_t @f()", asFUNCTION(FooScripted::Factory), asCALL_CDECL);
engine->RegisterObjectBehaviour("FooScripted_t", asBEHAVE_ADDREF, "void f()", asMETHOD(FooScripted, AddRef), asCALL_THISCALL);
engine->RegisterObjectBehaviour("FooScripted_t", asBEHAVE_RELEASE, "void f()", asMETHOD(FooScripted, Release), asCALL_THISCALL);
engine->RegisterObjectMethod("FooScripted_t", "FooScripted_t &opAssign(const FooScripted_t &in)", asMETHOD(FooScripted, operator=), asCALL_THISCALL);
engine->RegisterObjectMethod("FooScripted_t", "void CallMe()", asMETHOD(FooScripted, CallMe), asCALL_THISCALL);
engine->RegisterObjectProperty("FooScripted_t", "int m_value", asOFFSET(FooScripted, m_value));
}
void MessageCallback(const asSMessageInfo* msg, void* param)
{
stringstream ss;
ss << msg->section << " (" << msg->row << ", " << msg->col << ") :" << msg->message << ". ";
cout << ss.str() << endl;
}
bool DoTestAS001()
{
asIScriptEngine* pEngine = nullptr;
asIScriptModule* pModule = nullptr;
asIScriptContext* pContext = nullptr;
asIScriptFunction* pScriptFunction = nullptr;
pEngine = asCreateScriptEngine();
int res = pEngine->SetMessageCallback(asFUNCTION(MessageCallback), nullptr, asCALL_CDECL);
pModule = pEngine->GetModule("module", asGM_CREATE_IF_NOT_EXISTS);
string strScript;
std::ifstream ifs("tests/test-as001.as");
if (ifs.is_open() == true)
{
std::string strLine;
while (std::getline(ifs, strLine))
{
strScript += (strLine + "\r\n");
}
ifs.close();
}
else // Error opening file
{
}
RegisterFooScripted(pEngine);
res = pModule->AddScriptSection("script", strScript.c_str(), strScript.length());
if (res < 0)
{
cout << "AddScriptSection() failed" << endl;
}
res = pModule->Build();
if (res < 0)
{
cout << "Build() failed" << endl;
}
pContext = pEngine->CreateContext();
if (pContext != nullptr)
{
if (pScriptFunction == nullptr)
{
pScriptFunction = pModule->GetFunctionByDecl("void ExecuteDerived()");
}
res = pContext->Prepare(pScriptFunction);
if (res < 0)
{
}
res = pContext->Execute();
if (res != asEXECUTION_FINISHED)
{
// The execution didn't finish as we had planned. Determine why.
if (res == asEXECUTION_ABORTED)
{
}
else if (res == asEXECUTION_EXCEPTION)
{
string strError;
// Write some information about the script exception
asIScriptFunction* func = pContext->GetExceptionFunction();
strError += util::string_format("fn: %s, ", func->GetDeclaration());
strError += util::string_format("line: %i, ", pContext->GetExceptionLineNumber());
strError += util::string_format("reason: %s.", pContext->GetExceptionString());
cout << strError << endl;
}
else
{
}
}
else
// Ok
{
cout << "Executed successfully" << endl;
}
}
return bReturn;
}
And script "tests/test-as001.as"
:
// 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;
}
// Implement a script class that derives from the application class
class FooDerived : FooScripted
{
void CallMe()
{
m_value += 1;
}
}
void ExecuteScripted()
{
FooScripted s;
s.CallMe();
}
void ExecuteDerived()
{
// When newly created the m_value is 0
FooDerived d;
// When calling the method the m_value is incremented with 1
d.CallMe();
}
The results are:
Calling void ExecuteScripted()
loops inside of void FooScripted::CallMe()
at C++ side for more than 100 times, I didnt counted exactly.
Calling void ExecuteDerived()
not enters void FooScripted::CallMe()
at C++ side.
This was tested on version 2.36.1.
Thanks for answer in advance.