Trying to register my class

Started by
6 comments, last by WitchLord 16 years, 3 months ago
I've got a class that I want to register with AngelScript. I don't want scripts to be able to create or delete instances of these classes. So I figured a simple reference type would suffice. But I just can't get it to work. I've been beating on the code, trying different things, trying to get it to work. Right now I've got a class (ScriptableHandle) that acts like a "handle" around the actual class (IScriptable) (I did that when trying to add the assignment behavior, so that assigning one handle to another just copies the pointer to the IScriptable object, instead of creating a new instance of IScriptable.) with the assignment, addref, and release behaviors assigned. But whenever I have a script that looks like this:
void blah(scriptable s)
{
   // ...
}
I get a compiler error that says "Parameter type can't be 'scriptable'". What am I doing wrong? Am I completely misunderstanding how this stuff is supposed to work?
I like the DARK layout!
Advertisement
If you don't want AngelScript to be able to create instances of the registered type, then you're also telling AngelScript that it cannot pass the type by value to functions.

Have you taken a look at the following article in the manual: Registering a C++ class. It explains the various ways of registering classes with different behaviour in the script.

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

I don't want to pass the class to scripts by value, I want to pass by reference. I guess I was under the impression that telling AngelScript that my class is a reference type would get it to treat all "handles" to my class as references instead of new instances, so when I pass a reference to my class to a script function AngelScript doesn't need to create a new instance of anything. Is that wrong?

So if I need to tell AngelScript how to create new instances of my class in order to be able to pass references to the class to scripts, does that mean I need a wrapper class if I don't want AngelScript creating new instances of my class? In other words, if I have a class Foo, I need to create a class like FooHandle with a single member that is a pointer to a Foo, and then tell AngelScript how to assign FooHandle and create new instances of it, which behind the scenes just copies the pointer to Foo around?

OR, could I just tell AngelScript about the pointer to Foo and treat it like a value type? Can I still define methods to Foo that scripts can access?
I like the DARK layout!
I think you've misunderstood what a reference type means in AngelScript.

A reference type is always allocated by the application, either through the factory behaviour or other means. A reference type cannot be passed by value to an application registered function, since that means putting the value on the stack. But it can still be passed by value to script functions, but this requires that AngelScript can tell the application to allocate a new copy through a factory behaviour.

A value type can be automatically allocated by AngelScript, and may be placed on the stack, thus it can be passed to application registered functions by value.

---

If you don't want AngelScript to be able to request allocations of your Foo class, then I suggest you register your class as a reference type, but without the factory behaviour. You'll need to register the ADDREF and RELEASE behaviours, so that AngelScript knows how to manage the instances it does receive. Though, if you don't want to use reference counting for the class you can register these behaviours with dummy functions that doesn't do anything, but then you'll have to be careful to manually make sure all reference to the object are removed before destroying the object.

You won't be able to declare script methods that take the type by value, but they can be taken by reference or by handle, e.g:

void blah(Foo s) {} // ERROR, this requires the type to be instanciablevoid blah(Foo &s) {} // OK, by referencevoid blah(Foo @s) {} // OK, by handle


If you prefer to hide the Foo type behind a FooHandle, then that's ok as well. Then you'll likely want to register the FooHandle as a value type. In this case you'll unfortunately have to write wrappers for all the methods of the Foo class that you'll want to expose through the FooHandle.

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

I'm still having trouble. Since my class that I want to pass to the script is a base class for child classes I'm going to have to use the FooHandle method. But I keep getting a crash when I try to use FooHandle. Here's some code:

class IScriptable{public:    virtual std::string GetName() = 0;    virtual bool OnPlayerUse() = 0;    virtual void SaveState(ResizableBuffer* buffer) = 0;    virtual void LoadState(ResizableBuffer* buffer) = 0;    virtual float GetX() = 0;    virtual float GetY() = 0;};class ScriptableHandle{public:        ScriptableHandle()    {        m_scriptable = NULL;    }    ScriptableHandle(IScriptable* scriptable)    {        m_scriptable = scriptable;    }    ScriptableHandle& operator=(const ScriptableHandle& other)    {        if (this != &other)        {            m_scriptable = other.m_scriptable;        }        return *this;    }    static void ScriptCTor(void* memory)    {        new(memory) ScriptableHandle;    }    static void ScriptDTor(void* memory)    {        // nothing... for now...    }    std::string GetName() { return m_scriptable->GetName(); }private:    IScriptable* m_scriptable;};


Here IScriptable would be Foo and ScriptableHandle would be FooHandle. Here's the code I'm using to register ScriptableHandle:

// register IScriptableif (m_vm->RegisterObjectType("scriptable", sizeof(ScriptableHandle), asOBJ_VALUE | asOBJ_APP_CLASS_ASSIGNMENT) < 0 ||    m_vm->RegisterObjectBehaviour("scriptable", asBEHAVE_ASSIGNMENT, "scriptable &f(const scriptable &in)", asMETHOD(ScriptableHandle,operator=), asCALL_THISCALL) < 0 ||    m_vm->RegisterObjectBehaviour("scriptable", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ScriptableHandle::ScriptCTor), asCALL_CDECL_OBJLAST) < 0 ||    m_vm->RegisterObjectBehaviour("scriptable", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptableHandle::ScriptDTor), asCALL_CDECL_OBJLAST) < 0)    throw ScriptException("Unable to register IScriptable with scripting engine");m_vm->RegisterObjectMethod("scriptable", "string& getName()", asMETHOD(ScriptableHandle,GetName), asCALL_THISCALL);


And when I call the script function I'm giving the script the ScriptHandle like this:
ScriptableHandle handle(scriptable);context->SetArgObject(0, &handle);

where scriptable is an IScriptable*.

Is there something obvious that I'm doing wrong? I tried seeing exactly what's going on in the debugger, but I can't get any values because I get "Error: variable needs stack frame" errors.
I like the DARK layout!
I see one obvious error.

Your class ScriptableHandle declares GetName() to return std::string by value, but when you're registering the method you tell AngelScript that the string is returned by reference. This is definitely going to cause an error, and is likely a cause for the crash.

You have one other error as well. The ScriptableHandle has a declared constructor, so you should tell AngelScript that it does when registering the type. Instead of asOBJ_APP_CLASS_ASSIGNMENT use asOBJ_APP_CLASS_CA (which stands for constructor and assignment). This is a minor error, and may not cause any problem on your compiler.

The last possible problem depends on how you've registered the string type with AngelScript. Are you using the scriptstring add-on, or have you registered the std::string type directly with AngelScript? If you're using the add-on, then you can't return a std::string by value directly to AngelScript. You'd need to create a asCScriptString with the string value and return that instead.

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

Ah, now I think I'm getting somewhere. I changed ScriptableHandle::GetName() to this:
asCScriptString& GetName() { return *(new asCScriptString(m_scriptable->GetName())); }

and changed the declaration of getName() that I was giving to AngelCode to "string @getName()" This seems to work. Is all this correct?
I like the DARK layout!
Yes, that's correct. Though, I don't know why you decided to return the new asCScriptString as a reference instead of a pointer (to AngelScript there's no difference anyway).

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