Advertisement

How do i Register this?

Started by December 11, 2011 11:37 PM
1 comment, last by WitchLord 13 years ago
Hello,

Im sorry for my englisch skills.

I encapsulate a smartpointer of an class:


typedef boost::shared_ptr<Interacive> InteracivePtr;


Interactive is a base class for gui objects.
in C++ its easy to access attributes of such an object:


button->m_X=0;


To access a Method of a derived class:

InteracivePtr button(new Button(pointer_to_an_connected_object));
((Button*)button.get())->setText("Start");


Finaly i add the interactive object to an eventmanager and register eventhanlder(pointer to a function):


m_EventManager.registerElement(button,"Button");
m_EventManager.addEventHandler("Button",sf::Event::EventType::MouseButtonReleased,handlerfunc);


I know how i register a class for AngelScript but i have no idea for InteracivePtr oder handlerfunc.
Also i dont know how to use them inside a script.[((Button*)button.get())->setText("Start");]

I think i will change handlerfunc to a string wo is named after a function in my script ( "void button1clickhandler(sfEvent in, void &in)")?????
But i would like to give these function a void* Pointer to an "connected" object.

I how call this function at the moment:

void Interacive::callHandler(sf::Event p_Event)
{
if(m_EventHandler.find(p_Event.Type)!=std::end(m_EventHandler))
{
m_EventHandler[p_Event.Type](p_Event,m_ConnectedObject);
}
}


So how could i register these functions for my script?

I hope you could understand what i mean


regards

Cyprhon
For the first part of your question, in order to get AngelScript to understand the relationship between base and derived smart pointer objects you need to register both classes with AngelScript and then create conversion behaviors between the two classes. In general this is an implicit cast from derived to base and an explicit cast from base to derived. You then need to provide wrappers to access the various members and register them with the object type. For member variables, this can take the form of property accessors. Basic example:

#include "angelscript.h"
#include "scriptstdstring.h"

#include <iostream>
#include <cassert>
#include <cstdio>

#include <memory>

// Types to export

struct Base {
virtual ~Base() {}
};

struct Derived : Base {
int x;
int y;
};

typedef std::shared_ptr<Base> BasePtr;
typedef std::shared_ptr<Derived> DerivedPtr;

// Helper functions

template <typename T>
void construct(void * address) {
new (address) T;
}

template <typename T>
void destroy(T * object) {
object->~T();
}

template <typename T>
void copy_construct(void * address, T * other) {
new (address) T(*other);
}

template <typename T>
void assign(T * lhs, T* rhs) {
*lhs = *rhs;
}

template <typename T, typename U, U T::*member_ptr>
U getter(const std::shared_ptr<T> & ptr) {
if (!ptr) {
asIScriptContext * ctx = asGetActiveContext();
ctx->SetException("Attempting to access member of a null pointer");
return U();
} else {
return ptr.get()->*member_ptr;
}
}

template <typename T, typename U, U T::*member_ptr>
void setter(const std::shared_ptr<T> & ptr, const U & value) {
if (!ptr) {
asIScriptContext * ctx = asGetActiveContext();
ctx->SetException("Attempting to access member of a null pointer");
} else {
(ptr.get())->*member_ptr = value;
}
}

#define GETTER(ClassName, MemberType, member_name) getter<ClassName, MemberType, &ClassName::member_name>
#define SETTER(ClassName, MemberType, member_name) setter<ClassName, MemberType, &ClassName::member_name>

template <typename T, typename U>
std::shared_ptr<T> checked_cast(const std::shared_ptr<U> & ptr) {
std::shared_ptr<T> result = std::dynamic_pointer_cast<T>(ptr);
if (!result) {
asIScriptContext * ctx = asGetActiveContext();
ctx->SetException("Illegal cast");
}
return result;
}


void MessageCallback(const asSMessageInfo *msg, void *param)
{
const char *type = "ERR ";
if( msg->type == asMSGTYPE_WARNING )
type = "WARN";
else if( msg->type == asMSGTYPE_INFORMATION )
type = "INFO";

printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
}

void __cdecl print(const std::string & str) {
std::cout << str;
}

int main(int, char **) {
asIScriptEngine * engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL);
RegisterStdString(engine);

int r;
r = engine->RegisterGlobalFunction("void print(string & in)", asFUNCTION(print), asCALL_CDECL); assert(r >= 0);

// Base
r = engine->RegisterObjectType("Base", sizeof(BasePtr), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert(r >= 0);
r = engine->RegisterObjectBehaviour("Base", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(construct<BasePtr>), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectBehaviour("Base", asBEHAVE_CONSTRUCT, "void f(const Base & in)", asFUNCTION(copy_construct<BasePtr>), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectBehaviour("Base", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(destroy<BasePtr>), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectMethod("Base", "Base &opAssign(const Base &in)", asFUNCTION(assign<BasePtr>), asCALL_CDECL_OBJFIRST); assert(r >= 0);

// Derived
r = engine->RegisterObjectType("Derived", sizeof(BasePtr), asOBJ_VALUE | asOBJ_APP_CLASS_CDAK); assert(r >= 0);
r = engine->RegisterObjectBehaviour("Derived", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(construct<DerivedPtr>), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectBehaviour("Derived", asBEHAVE_CONSTRUCT, "void f(const Derived & in)", asFUNCTION(copy_construct<DerivedPtr>), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectBehaviour("Derived", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(destroy<DerivedPtr>), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectMethod("Derived", "Derived &opAssign(const Derived & in)", asFUNCTION(assign<DerivedPtr>), asCALL_CDECL_OBJFIRST); assert(r >= 0);

r = engine->RegisterObjectMethod("Derived", "int get_x() const", asFUNCTION(GETTER(Derived, int, x)), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectMethod("Derived", "void set_x(const int & in)", asFUNCTION(SETTER(Derived, int, x)), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectMethod("Derived", "int get_y() const", asFUNCTION(GETTER(Derived, int, y)), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectMethod("Derived", "void set_y(const int & in)", asFUNCTION(SETTER(Derived, int, y)), asCALL_CDECL_OBJFIRST); assert(r >= 0);

// conversions
r = engine->RegisterObjectBehaviour("Base", asBEHAVE_VALUE_CAST, "Derived f() const", asFUNCTION((checked_cast<Derived, Base>)), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectBehaviour("Derived", asBEHAVE_IMPLICIT_VALUE_CAST, "Base f() const", asFUNCTION((std::static_pointer_cast<Base, Derived>)), asCALL_CDECL_OBJFIRST); assert(r >= 0);

const char script[] =
"void foo(const Base & in ptr) {\n"
" Derived d = Derived(ptr);\n"
" print(d.x + \"\\n\");\n"
" print(d.y + \"\\n\");\n"
" d.x = 3;\n"
" d.y = 4;\n"
"}\n"
;

asIScriptModule * mod = engine->GetModule(0, asGM_ALWAYS_CREATE);
r = mod->AddScriptSection("script", script, sizeof(script) - 1); assert(r >= 0);
r = mod->Build(); assert(r >= 0);

int func_id = mod->GetFunctionIdByDecl("void foo(const Base & in)");

Derived * d = new Derived;
d->x = 1;
d->y = 2;
BasePtr base(d);


asIScriptContext * ctx = engine->CreateContext();
ctx->Prepare(func_id);
ctx->SetArgObject(0, &base);
ctx->Execute();

ctx->Release();

engine->Release();

std::cout << d->x << std::endl;
std::cout << d->y << std::endl;
}

For more information you might want to look at the class hierarchies section of the documentation.

For the second part of your question, I'm not entirely sure what you're asking. Though, if you want to use script objects as callbacks from C++, you generally need to move from function pointers to function objects like boost::/std::function. One possibility to is to register an interface that script classes implement. This interface would generally have a single function that matches the signature of the callback. Then your function object stores a script object pointer as well as a function to call that interface function on the script object.
Advertisement
Thanks SiCrane, I don't think I could have given a better answer myself :)



I'm not quite sure either what exactly you wish to do in your second question. But I guess it has to do with implementing callbacks in the scripts, that can then be invoked from the application upon certain events.

I'll give you a little snippet on how I implemented this myself in the gui for my game engine:

My gui consists basically of two entities, the gui manager, and the gui elements. The gui manager is pretty self explanatory so I won't give much detail on it. The gui elements on the other hand is a generic entity that the script can use to implement buttons, windows, text labels, or just groups of other elements with. In order to implement a button for example, the script will implement some event handlers that they then attach to the gui element. Examples of the event handlers are onClick, onMouseOver, and onTimer.

Here's how the gui entities are registered with the script in my engine:

[source]


// Register the gui element type
r = engine->RegisterObjectType("CGuiElement", 0, asOBJ_REF); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("CGuiElement", asBEHAVE_ADDREF, "void f()", asMETHOD(CGuiElement, AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("CGuiElement", asBEHAVE_RELEASE, "void f()", asMETHOD(CGuiElement, Release), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "string text", offsetof(CGuiElement, text)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "vector2 center", offsetof(CGuiElement, center)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "int srcImageIndex", offsetof(CGuiElement, srcImageIndex)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "vector2 srcBottomLeft", offsetof(CGuiElement, srcBottomLeft)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "vector2 srcTopRight", offsetof(CGuiElement, srcTopRight)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "float rotation", offsetof(CGuiElement, rotation)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "int zPosition", offsetof(CGuiElement, zPosition)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "vector2 scale", offsetof(CGuiElement, scale)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "int fontIndex", offsetof(CGuiElement, fontIndex)); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "uint align", offsetof(CGuiElement, align)); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGuiElement", "void SetTimer(float, int)", asMETHOD(CGuiElement, SetTimer), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGuiElement", "void KillTimer(int)", asMETHOD(CGuiElement, KillTimer), asCALL_THISCALL); assert( r >= 0 );

// Register the event handler
r = engine->RegisterFuncdef("void OnClickHandler(CGuiElement @)"); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "OnClickHandler @onClick", offsetof(CGuiElement, onClickHandler)); assert( r >= 0 );
r = engine->RegisterFuncdef("void OnMouseOverHandler(bool, CGuiElement @)"); assert( r >= 0 );
r = engine->RegisterObjectProperty("CGuiElement", "OnMouseOverHandler @onMouseOver", offsetof(CGuiElement, onMouseOverHandler)); 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 );

// Register the gui manager as a singleton
r = engine->RegisterObjectType("CGui", 0, asOBJ_REF | asOBJ_NOHANDLE); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGui", "int LoadScript(const string &in)", asMETHOD(CGuiMgr, LoadScript), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGui", "int LoadImage(const string &in)", asMETHOD(CGuiMgr, LoadImage), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGui", "int LoadFont(const string &in)", asMETHOD(CGuiMgr, LoadFont), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGui", "CGuiElement @CreateElement(const vector2 &in, const vector2 &in, CGuiElement @+ parent = null)", asMETHOD(CGuiMgr, CreateElement), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CGui", "void SetMouseElement(CGuiElement @+)", asMETHOD(CGuiMgr, SetMouseElement), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterGlobalProperty("CGui gui", app->guiMgr); assert( r >= 0 );
[/source]

And this is how the application invokes an event handler, in this case the onMouseOver

[source]

void CScriptMgr::CallGuiOnMouseOverHandler(asIScriptFunction *onMouseOverHandler, bool isMouseOver, CGuiElement *el)
{
int r;
asIScriptContext *ctx = PrepareContext(onMouseOverHandler->GetId());
if( ctx )
{
r = ctx->SetArgByte(0, isMouseOver); assert( r >= 0 );
r = ctx->SetArgObject(1, el); assert( r >= 0 );
ExecuteCall(ctx);
ReturnContextToPool(ctx);
}
}

asIScriptContext *CScriptMgr::PrepareContext(int funcId)
{
asIScriptContext *ctx = 0;
if( contextPool.size() )
{
ctx = *contextPool.rbegin();
contextPool.pop_back();
}
else
ctx = engine->CreateContext();

// TODO: Error handling
int r = ctx->Prepare(funcId); assert( r >= 0 );

return ctx;
}

void CScriptMgr::ReturnContextToPool(asIScriptContext *ctx)
{
contextPool.push_back(ctx);

// Unprepare the context as we don't know when it will be used again
ctx->Unprepare();
}

int CScriptMgr::ExecuteCall(asIScriptContext *ctx)
{
int r = ctx->Execute();
if( r != asEXECUTION_FINISHED )
{
LOG(("Script failed to execute (%d)", r));
if( r == asEXECUTION_EXCEPTION )
{
LOG(("Exception: %s", ctx->GetExceptionString()));
LOG(("Function: %s", engine->GetFunctionDescriptorById(ctx->GetExceptionFunction())->GetDeclaration()));
LOG(("Line: %d", ctx->GetExceptionLineNumber()));

char buf[256];
sprintf_s(buf, 256, "Exception: %s", ctx->GetExceptionString());
app->console->WriteLine(buf);

sprintf_s(buf, 256, "Function: %s", engine->GetFunctionDescriptorById(ctx->GetExceptionFunction())->GetDeclaration());
app->console->WriteLine(buf);

sprintf_s(buf, 256, "Line: %d", ctx->GetExceptionLineNumber());
app->console->WriteLine(buf);
}
}

return r;
}
[/source]

And finally, here's what a script might look like:

[source]

#include "common.as"

CGuiElement @mouse;

// This handler is called whenever the menu should be shown
void onShowMenu()
{
// Set up the gui elements for the start menu
CreateMouse();

// Create background image
CGuiElement @background = gui.CreateElement(vector2(0,0), vector2(1024,768), null);
background.srcImageIndex = gui.LoadImage("background.png");

// Create the menu options
if( game.isGameOn )
CreateButton(512,480,200,50, 'Resume game', onResume);
CreateButton(512,410,200,50, 'Start new game', onStart);
CreateButton(512,340,200,50, 'Quit game', onQuit);
}

void onMouseOver(bool isMouseOver, CGuiElement @bt)
{
if( isMouseOver )
{
bt.KillTimer(0);
bt.SetTimer(0, 1);
}
else
{
bt.KillTimer(1);
bt.SetTimer(0, 0);
}
}

void onStart(CGuiElement @)
{
game.StartNewGame();
}

void onQuit(CGuiElement @)
{
game.QuitGame();
}

void onResume(CGuiElement @)
{
game.ResumeGame();
}

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);
}
}

void CreateMouse()
{
@mouse = @gui.CreateElement(vector2(32,32), vector2(32,32));
mouse.center = vector2(16,24);
gui.SetMouseElement(mouse);
mouse.srcImageIndex = gui.LoadImage("arrow.png");
mouse.zPosition = -1000; // Make sure the cursor is in front of all other elements
}

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

I know it might be a bit difficult to understand snippets like this, so don't hesitate to ask whatever you don't understand.

I hope it will give you some ideas on how to implement your own event handlers.

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

This topic is closed to new replies.

Advertisement