Advertisement

Calling function by pointer

Started by January 14, 2012 04:36 PM
3 comments, last by _Vicious_ 12 years, 10 months ago
Hello!
I have the following question: how can an application call script function by pointer? Imagine there's an app-registered class with funcdef property and an object, which is shared both by the script and the application. The scripts sets funcdef property for this object. Now I want to be able to call that function from an application. How do I do that?
Exactly the same way any other script function is called. :)

The handle to a function in the script is an asIScriptFunction* in C++, so it is just a matter of preparing the context with this pointer and then calling it normally.

I use it like this in my own game engine:



class CGuiElement



{
public:
...
// Event handlers
asIScriptFunction *onClickHandler;
asIScriptFunction *onMouseOverHandler;
asIScriptFunction *onTimerHandler;
...
};



int CScriptMgr::Init()
{
...
// Register a funcdef to allow script to set callback functions

r = engine->RegisterObjectType("CGuiElement", 0, asOBJ_REF); assert( r >= 0 );
...
r = engine->RegisterFuncdef("void OnTimerHandler(int, CGuiElement @)"); assert( r >= 0 );

r = engine->RegisterObjectProperty("CGuiElement", "OnTimerHandler @onTimer", offsetof(CGuiElement, onTimerHandler)); assert( r >= 0 );
...
}

// Call the callback function

void CScriptMgr::CallGuiOnTimerHandler(asIScriptFunction *onTimerHandler, int timerId, CGuiElement *el)
{
int r;
asIScriptContext *ctx = PrepareContext(onTimerHandler->GetId());
if( ctx )
{
r = ctx->SetArgDWord(0, timerId); assert( r >= 0 );
r = ctx->SetArgObject(1, el); assert( r >= 0 );
ExecuteCall(ctx);
ReturnContextToPool(ctx);
}
}


In the script it looks like this:



void onTimer(int id, CGuiElement @el)
{
if( id == 1 && el.scale.x < 1.1 )
{
el.scale += vector2(0.05, 0.05);
el.SetTimer(0.01, 1);
if( el.scale.x > 1.1 )
el.scale = vector2(1.1, 1.1);
}
else if( id == 0 && el.scale.x > 1.0 )
{
el.scale -= vector2(0.025, 0.025);
el.SetTimer(0.01, 0);
if( el.scale.x < 1.0 )
el.scale = vector2(1.0, 1.0);
}
}


CGuiElement @CreateButton(float x, float y, const string &in text, OnClickHandler @handler)
{
CGuiElement @bt = gui.CreateElement(vector2(x,y), vector2(50,50), null);
bt.fontIndex = gui.LoadFont("menu.fnt");
bt.text = text;
@bt.onClick = handler;
@bt.onMouseOver = onMouseOver;
@bt.onTimer = onTimer;
return @bt;
}


Regards,
Andreas

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement
Thanks, Andreas!

This is exactly what I was thinking. It's a bit unfortunate that the asIScriptFunction *[font=monospace] [/font]pointer is passed instead of functon id is passed though since we're not exporting the asIScriptFunction class to our game library which is written in C. I'll have to think how I can workaround this.
Observe that the asIScriptContext::Prepare() method accepts the asIScriptFunction pointer directly since version 2.22.0. With this it is actually better to store the asIScriptFunction pointer rather than the function id. The function ids will be retired in some future release.

Even though your game library is written in C there is no need to expose the full asIScriptFunction interface. You can just expose it as


typedef struct asIScriptFunction asIScriptFunction;


This is how I do it in the C interface for AngelScript (you'll find it in the add-ons).

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Hello Andreas,

yes, this is exactly what I did: export raw pointer to asIScriptFunction * to the C library and use reinterpret_cast in the wrapper library.

Thanks for the tip on asIScriptContext::Prepare(), I think I'll rename our API entry that takes function Id as an argument to PrepareById() then.

This topic is closed to new replies.

Advertisement