Advertisement

trouble passing classes to scripts

Started by July 18, 2007 12:10 PM
2 comments, last by WitchLord 17 years, 4 months ago
Hello, I've been playing around with angelscript and ran into a problem with passing classes to script functions. What I want to accomplish is to pass a pointer of an object to a script function, and have the script be able to access the object's methods. The script shouldn't be able to create a new object of this type. Now I'm able to pass the pointer fine and call the methods of the object, but when the object goes out of scope in the script, the object is destroyed. I've registered the ADDREF and RELEASE behaviours, and it seems that the ADDREF isn't called when the object is passed to the script, but RELEASE is called when the object goes out of scope. So the object is then destroyed because the refcount goes to 0. I looked through the test programs, and I can't see anything huge that I'm missing. This is some code with a very simple class that shows what I'm trying to do:

#include <iostream>
#include <angelscript.h>

class SimpleClass
{
public:
	SimpleClass()	{ m_refcount=1; }

	void setint(const int val) { std::cout << "set int to " << val << std::endl; }

	void AddRef()		{ m_refcount++; std::cout << "ref++ = " << m_refcount << std::endl; }
	void RemoveRef()	{ std::cout << "ref-- = " << m_refcount-1 << std::endl; if(--m_refcount==0) { std::cout << "deleting object" << std::endl; delete this; } }

private:
	int m_refcount;
};

int main()
{
	asIScriptEngine *engine=asCreateScriptEngine(ANGELSCRIPT_VERSION);
	
	engine->RegisterObjectType("simpleclass",0,asOBJ_CLASS);
	engine->RegisterObjectBehaviour("simpleclass",asBEHAVE_ADDREF,"void AddRef()",asMETHOD(SimpleClass,AddRef),asCALL_THISCALL);
	engine->RegisterObjectBehaviour("simpleclass",asBEHAVE_RELEASE,"void RemoveRef()",asMETHOD(SimpleClass,RemoveRef),asCALL_THISCALL);
	engine->RegisterObjectMethod("simpleclass","void setint(const int val)",asMETHOD(SimpleClass,setint),asCALL_THISCALL);

	char script[]="void main(simpleclass @obj) { obj.setint(4); }";
	int scriptlen=strlen(script);
	engine->AddScriptSection(0,"script",script,scriptlen);
	engine->Build(0);

	asIScriptContext *ctx=engine->CreateContext();

	SimpleClass *class1=new SimpleClass;

	int funcid=engine->GetFunctionIDByDecl(0,"void main(simpleclass @obj)");
	ctx->Prepare(funcid);
	ctx->SetArgAddress(0,class1);

	std::cout << "BEFORE SCRIPT----" << std::endl;
	ctx->Execute();
	std::cout << "AFTER SCRIPT-----" << std::endl;

	delete class1;

	ctx->Release();

	engine->Release();

	system("PAUSE");

	return 0;
}

The output of the program is:

BEFORE SCRIPT----
set int to 4
ref-- = 0
deleting object
AFTER SCRIPT-----
And of course the program crashes when it tries to delete the object again. It's probably something I'm missing with reference counting, but I don't see what's different between this and the test programs. Sure I can call ADDREFF before I pass the object, but I shouldn't have to should I? Can anyone point me in the right direction on how to get this to work?
The script function will call the destructor on all the parameters it received before returning. For object handles this means that the handle will be released.

It is the caller's responsibility of increasing the reference counter it is wishes to keep the reference after the call returns. There are two ways you can do that:

1. Use SetArgObject instead of SetArgAddress to pass the reference. SetArgObject will correctly call the addref behaviour. SetArgAddress is meant for passing references rather than object handles.

2. Call the addref behaviour yourself and then use GetArgPointer to set the reference on the stack.

In fact number 2 is the recommended way even though it requires a little more work from you. I'm about to remove all SetArg methods from the context interface, after which only the GetArgPointer will be available.

Regards,
Andreas

PS. I'll try to improve the documentation for this as well so that it will be clearer on how to pass object handles to functions.

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, SetArgObject did the trick. I was also successful in using the second method, but it is much less friendly than SetArgObject.
I know. But it is more generic and more efficient. Also the SetArgDWord for example doesn't work for PPC and 8 or 16 bit types.

I'll try to keep something simple to use though, but it will probably be in a layer above the core engine, e.g. a script manager class.

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