Sign in to follow this  
SiddharthBhat

Returning an Interface type

Recommended Posts

Hi all!

 

I've been writing a class called "actorList", which is basically a list of all actors. I'm not using the array type because I want to extend this type to support some additional features like filtering, ordering, etc.

 

"Actor" is an interface. there are different types of actors in my game, such as componentActor, Light and Sound. ALL of the derived classes of Actor have turned off garbage collection (I distrust the garbage collector >_>. The script side only has restricted entry points and is not allowed to instantiate these classes. Hence, I decided to turn garbage collection off.)

 

 

 

class actorList{
public:
    int getSize(){
        return this->actors.size();
    };
    Actor *get(int index){
        return this->actors[index];
    };


    void  Add(Actor *a){
        this->actors.push_back(a);
    }


    void Clear(){
        this->actors.clear();
    }


    actorList(std::vector<Actor *>actors){
        this->actors = actors;
    }


    actorList(const actorList &other){
        this->actors = other.actors;
    }


    actorList(){};
    ~actorList(){};


private:
    std::vector<Actor *>actors;
};
 

I've been trying to bind " Actor *get(int index)" and I'm not able to do it :/ 

 

 

--------------------------------------------------------------------------------------------------------------------------------------------------

I've been binding the function this way:

 

 

 

//function is Actor *get(int index)
actorListDef.addMethod(AS_ObjMethodDef("Actor@ Get(int index)", 
        AS_FunctionDef(asMETHODPR(actorList, get, (int index), Actor*), asCALL_THISCALL )));
 

 

 

If I access it from the script side like so:

 

 

Actor @actor;

//this statement makes sense right? *p = *q;
@actor = @allActors.Get(i);
 
 

 

 

or like so:

 
 
Actor @actor;

//*p = q I don't know what this means 
@actor = allActors.Get(i);
 

 

 

      It goes and crashes at as_engine.cpp at line number 3333. These are the surrounding lines:

void asCScriptEngine::CallObjectMethod(void *obj, int func)
{
    asCScriptFunction *s = scriptFunctions[func]; <-- this is where it crashes
    asASSERT( s != 0 );
    CallObjectMethod(obj, s->sysFuncIntf, s);
}
 

 Here, the  "this" appears to have been freed and has been set to Visual Studio's "memory freed" debug pattern - 0xfeeefeee

 

 

 

- this    0xfeeefeee {isPrepared=??? memoryMgr={cs={cs={DebugInfo=??? LockCount=??? RecursionCount=??? ...} } ...} ...}    asCScriptEngine *
 

 

 

//------------------------------------------------------------------------------------------------------------------------------------------

I also tried binding it this way:

 

 

void get(int index, Actor **a){
        *a = this->actors[index];
    }
 

 Here's how I bound it to the script side:

 

 

actorListDef.addMethod(AS_ObjMethodDef("void Get(int index, Actor &out)", 
        AS_FunctionDef(asMETHODPR(actorList, get, (int index, Actor **a), void), asCALL_THISCALL )));
 

because the documentation said this:



&out : A reference to an unitialized value is passed to the function. When the function returns the value is copied to the true reference. The argument expression is evaluated only after the function call. This is the best way to have functions return multiple values.

 

 

 

But on doing this, it says "No default constructor for object of type 'Actor'. " as well as "There is no copy operator for the type 'Actor' available." , and then the usual unable to Prepare, unable to Exexute.

 

 

Help, Anyone? sad.png I have no clue as to what I'm doing wrong / what I'm supposed to do. 

Thanks

~Bollu

 
Edited by bollµ

Share this post


Link to post
Share on other sites

@a = @b; means a handle assignment, i.e. handle a will point to the same object as handle b

@a = b; same thing, the compiler knows you want to do a handle assignment because of the left-hand @ and thus implicitly takes the handle of the right-hand expression

a = b; means a value assignment, i.e. the content of b will be copied to the content of a

 

 

In your first attempt, i.e. when get returned an Actor*. You forgot to increase the reference counter before returning the pointer. AngelScript will call Release() on the pointer it receives, so you must call AddRef() or else the object will be freed too early. Alternatively you can tell AngelScript to automatically increase the reference count by registering the method like this:

 

 

//function is Actor *get(int index)
actorListDef.addMethod(AS_ObjMethodDef("Actor@+ Get(int index)", 
        AS_FunctionDef(asMETHODPR(actorList, get, (int index), Actor*), asCALL_THISCALL )));

 

Observe the + sign after the @ symbol. This is called auto handles in the manual.

 

 

In your second attempt that returns the Actor as an output parameter, you registered the method incorrectly. It should have been registered as:

 

 

actorListDef.addMethod(AS_ObjMethodDef("void Get(int index, Actor @ &out)", 
        AS_FunctionDef(asMETHODPR(actorList, get, (int index, Actor **a), void), asCALL_THISCALL )));

 

The Actor @&out in AngelScript matches Actor ** in C++. Observe that here too you need to increase the reference count, or use auto handles to have AngelScript do it.

 

You had registered it as 'Actor &out', which matches 'Actor &' in C++, i.e. a reference to an existing instance is passed to the function to be updated with the return value. This is why you got the error 'No default constructor' when AngelScript tried to create the instance you told it to pass to the function.

 

 

Here's a few articles from the manual that may help clarify these subjects:

 

Regards,

Andreas

Edited by Andreas Jonsson

Share this post


Link to post
Share on other sites

Ah! that makes perfect sense biggrin.png. Thank you! I used the second method and It's perfectly fine now.

 

But I have a question: I've disabled garbage collection on these objects (Actor is an interface, so I'm not sure how to disable the GC on that) by adding the flag asOBJ_NOCOUNT while binding the interface. so shouldn't the 1st method work independent of whether I call AddRef or not? I mean, isn't the GC not supposed to clear objects that have disabled reference counting?

 

Thanks

~Bollu

 

EDIT: It's not fine, it's not crashing, but it's still returning a null pointer. I used the Angelscript debugger. The memory location of Actor @actor is still NULL (0x00...). So it's not getting copied properly. 

 

The C++ side function:

 

void get(int index, Actor **a){
        *a = this->actors[index];
    }

 

Binding:

 

 

actorListDef.addMethod(AS_ObjMethodDef("void Get(int index, Actor @ &out)", 
        AS_FunctionDef(asMETHODPR(actorList, get, (int index, Actor **a), void), asCALL_THISCALL )));
 

 

script-side:

 

 

 

Actor @a;
actorList allActors = getAllActors();

allActors.Get(0, @a);
 

 

And yes, a valid pointer exists inside the actorList class. I put a breakpoint to check. everything is fine and all the pointers are present. The pointer to pointer even gets equated to the right memory address. but after returning from the Get function call, in the next statement, (I know this because I was stepping using angelscript's debugger), it's back to NULL.

 

I have no idea >.<; help, please?

 

 

 

Edited by bollµ

Share this post


Link to post
Share on other sites

I'm sorry. I missunderstood what you meant by disabling garbage collection. When you said that I though you simply didn't register the garbage collector behaviours or even turned off the automatic execution of the garbage collector, not that you registered the object types with asOBJ_NOCOUNT to skip the reference counting.

 

To me garbage collecting and reference counting are two different things. :)

 

Anyway, if you register the types with asOBJ_NOCOUNT then AngelScript won't call the AddRef or Release behaviours. It won't allow you to use autohandles either for these types. 

 

 

Still, you say you disabled garbage collector on all types, except Actor. What do you mean? How exactly have you registered the Actor type? Is it not registered with asOBJ_NOCOUNT as well?

Share this post


Link to post
Share on other sites

My bad :) you're right, I've disabled reference counting.

 

"Actor" is an interface, it's not an object per se. I was of the opinion that one cannot register behaviours to interfaces.  

 

using this function:

 

 

asIScriptEngine::RegisterInterface(const char * name)
 

I didn't see a parameter to pass the flags like you can to RegisterObjectType. So, I didn't do anything. just used registerInterface with Actor, and a few trivial functions (getName, getPosition) and the like.

 

All of the concrete derived classes of Actor (which are all reference types) have been registered with the asOBJ_NOCOUNT flag.

 

thanks

~Bollu

Share this post


Link to post
Share on other sites

Ah. There is the error. smile.png

 

RegisterInterface() is meant to allow you to register script interface declaration that the application needs to know of. This is not meant to be used for C++ interfaces.

 

When you return the Actor handle, AngelScript believes you're returning a asCScriptObject *, i.e. a script class, not a C++ class. Exactly what will happen in this case is unknown, but it is certain that you'll end up with unexpected results as you noticed. :)

 

You need to register your C++ Actor interface just like an ordinary C++ type, i.e. with RegisterObjectType("Actor", 0, asOBJ_REF | asOBJ_NOCOUNT);

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this