Sign in to follow this  
Wavesonics

Passing script objects as base pointers?

Recommended Posts

Well I had a huge long post here. But in writing the post, I worked through the problem and figured it out :) I must say, I don't totally grasp the difference between REF and VALUE. Are VALUE types only valid in the scripts, and REF types actual *real* objects? Anyway. I'm creating an object in my script which is a derived type. But I want to be able to pass it to the application as it's base type. Any ideas for how to accomplish this? I figure there is no direct way since I saw inheritance is on the road map.

Share this post


Link to post
Share on other sites
The value types are allocated on the stack and never outlive the scope in which they were created. The reference types are allocated on the heap, and live on until all references to them have been removed.

Value types are used mainly for smaller, less complex types, e.g. 3D vectors. Reference types are used for all types that need dynamic memory management.




You can register the reference cast behaviour for the derived type. This will allow the script engine to cast the type to the base type.

There is an example for this in the manual: Type behaviours (look for cast operators further down on the page)

Share this post


Link to post
Share on other sites
Ok, I went ahead and implemented the implicit case behavior, and it *works.

* It works in every case but this obscure one:
When trying to call to a function using the implicit case, and the function is virtual, and only implemented in the base class of the object it is actually being called on.

It still compiles and runs, but the function is just never called.

Share this post


Link to post
Share on other sites
Sure can. Also, I did some checking, and the implicit conversion function I registered is definitely firing and succeeding.

Here is the script it's self:

void fireEffects( TurretClient@ t, CreepClient@ c, WorldClient@ w ) {
if( c.health() != 0 ) {
// Create an explosion
OverlayExplosion@ o = OverlayExplosion( 1.0 );

// Add the explosion to the creep we fired on
c.addOverlay( o ); // This one doesn't ever fire off
}
else {
// Create an explosion
OverlayExplosion@ o = OverlayExplosion( 0.5 );

// Give the explosion the world coordinates where the creep died
float x = c.x();
float y = c.y();
o.position( x, y );

// Add the explosion to the world
w.addOverlay( o ); // This on works just fine
}
}





Here is the offending class:

#ifndef CREEPCLIENT_H_INCLUDED
#define CREEPCLIENT_H_INCLUDED

#include "Creep.h"
#include "Drawable.h"

class CreepClient : public Creep, public Drawable {
private:
protected:
public:
CreepClient( const class DescriptionCreep* description );
virtual ~CreepClient();
};

#endif // CREEPCLIENT_H_INCLUDED






And it's base class which is where the function is:



#ifndef GAMEOBJECTCLIENT_H_INCLUDED
#define GAMEOBJECTCLIENT_H_INCLUDED

#include <vector>

#include <SFML/Graphics.hpp>

class Drawable {
private:
class GameObject& m_obj;
std::vector< class Overlay* > m_overlay;
protected:
sf::Sprite m_sprite;

public:
Drawable( GameObject& obj );
virtual ~Drawable();

virtual void addOverlay( Overlay* overlay );
sf::Int32 numOverlays();
Overlay& getOverlay( sf::Uint32 index );
bool removeOverlay( sf::Uint32 index );

virtual void setSprite( const sf::Sprite& s );
virtual sf::Sprite& getSprite();
virtual sf::Drawable& getDrawable();
};

#endif // GAMEOBJECTCLIENT_H_INCLUDED





And here is where I register the class:

// Here is where we register the class

// CreepClient
r = engine->RegisterObjectType("CreepClient", sizeof(CreepClient), asOBJ_REF ); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("CreepClient", asBEHAVE_ADDREF, "void f()", asFUNCTION(DumbyAddRef), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("CreepClient", asBEHAVE_RELEASE, "void f()", asFUNCTION(DumbyReleaseRef), asCALL_CDECL_OBJFIRST); assert( r >= 0 );

r = engine->RegisterObjectMethod("CreepClient", "void addOverlay( Overlay@+ overlay )", asMETHOD(CreepClient, addOverlay), asCALL_THISCALL); assert( r >= 0 );

r = engine->RegisterObjectMethod("CreepClient", "int16 health() const", asMETHODPR(CreepClient, health, (void) const, sf::Int16), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CreepClient", "void health( int16 )", asMETHODPR(CreepClient, health, (sf::Int16), void), asCALL_THISCALL); assert( r >= 0 );

r = engine->RegisterObjectMethod("CreepClient", "float speed() const", asMETHODPR(CreepClient, speed, (void) const, float), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CreepClient", "void speed( float )", asMETHODPR(CreepClient, speed, (float), void), asCALL_THISCALL); assert( r >= 0 );

r = engine->RegisterObjectMethod("CreepClient", "int8 reward() const", asMETHODPR(CreepClient, reward, (void) const, sf::Int8), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CreepClient", "void reward( int8 )", asMETHODPR(CreepClient, reward, (sf::Int8), void), asCALL_THISCALL); assert( r >= 0 );

r = engine->RegisterObjectMethod("CreepClient", "float x() const", asMETHOD(CreepClient, x), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("CreepClient", "float y() const", asMETHOD(CreepClient, y), asCALL_THISCALL); assert( r >= 0 );

r = engine->RegisterObjectMethod("CreepClient", "void position( float, float )", asMETHODPR(CreepClient, position, (float, float), void), asCALL_THISCALL); assert( r >= 0 );

// Global object behavior
r = engine->RegisterGlobalBehaviour(asBEHAVE_IMPLICIT_REF_CAST, "Overlay@+ f( OverlayExplosion@+ )", asFUNCTION(castOverlayExplosionToOverlay), asCALL_CDECL); assert( r >= 0 );



Share this post


Link to post
Share on other sites
How are you calling the 'fireEffects' function?

This isn't a problem with the implicit cast behaviour, because c is already a handle to a CreepClient, thus AngelScript won't cast it to anything else when calling the addOverlay method.

When you pass the pointer to the CreepClient to the context, is the pointer declared as a CreepClient or one of the Creep or Drawable classes? It's important that is a CreepClient, because the value of the pointer won't be the same if it is one of the other (due to multiple inheritance).

Share this post


Link to post
Share on other sites
Damn, i thought thats what it was when I read it, but looking at my source, I am passing it as a CreepClient:


void ProcessFireTurretsClient::fireAction( Turret& t, Creep& c ) {
TurretClient& tc = dynamic_cast< TurretClient& >( t );
CreepClient& cc = dynamic_cast< CreepClient& >( c );

// Run the turrets "void fireEffects()" script
const DescriptionTurret* description = t.getDescription();
asIScriptContext* ctx = gScriptEngine.prepare( description->effectsScript, "void fireEffects( TurretClient@, CreepClient@, WorldClient@ )" );
ctx->SetArgObject( 0, &tc );
ctx->SetArgObject( 1, &cc );
ctx->SetArgObject( 2, &m_world );
gScriptEngine.execute( ctx );
}

Share this post


Link to post
Share on other sites
It could be a bug with how AngelScript handles virtual methods for classes with multiple inheritance. I'll have to look into this.

Could you make a quick test?

Instead of registering the addOverlay method directly, try registering the following wrapper function:


void CreepClient_addOverlay( Overlay* overlay, CreepClient *c )
{
c->addOverlay(overlay);
}

r = engine->RegisterObjectMethod("CreepClient", "void addOverlay( Overlay@+ overlay )", asFUNCTION(CreepClient_addOverlay), asCALL_CDECL_OBJLAST); assert( r >= 0 );


If it is a problem with the way multiple inheritance is handled, the above should be a work around until I get the bug fixed. If the above doesn't work either, then the problem is another.

Share this post


Link to post
Share on other sites
What compiler are you using?

In the test framework I have a test for multiple inheritance already (testmultipleinheritance.cpp). It should have covered this scenario, but I'll try recreate your problem to see if there is a difference.

Share this post


Link to post
Share on other sites
MinGW GCC 4.3.x (.5 i think? i'm not at home right now so I can't check for sure)

Also, that proxy function is declared virtual, if that makes any difference.

Maybe it has something to do with multiple-inheritance with an implicit cast? idk.

Share this post


Link to post
Share on other sites
I've written the following test based on the info you gave me. On MSVC it doesn't have any problems, but perhaps on MinGW it reproduces the problem you're having. Would you mind giving it a try?


bool addOverlayCalled = false;

class Drawable {
private:
int somestuff;
protected:
int somethingElse;

public:
Drawable( ) {}
virtual ~Drawable() {}

virtual void addOverlay( ) { addOverlayCalled = true; }
};

class Creep {
private:
int somestuffOfMine;
protected:
int duh;
public:
Creep() {}
virtual ~Creep() {}

};

class CreepClient : public Creep, public Drawable {
private:
protected:
public:
CreepClient( ) {}
virtual ~CreepClient() {}
};

void Dummy() {}


bool Exec(asIScriptEngine *engine, Creep &c)
{
bool fail = false;

CreepClient &cc = dynamic_cast<CreepClient&>(c);

asIScriptModule *mod = engine->GetModule("mod");
int funcId = mod->GetFunctionIdByIndex(0);
asIScriptContext *ctx = engine->CreateContext();
ctx->Prepare(funcId);
ctx->SetArgObject(0, &cc);
int r = ctx->Execute();
if( r != asEXECUTION_FINISHED )
{
fail = true;
}

ctx->Release();

return fail;

}

bool TestMultipleInheritance2()
{
bool fail = false;
int r;

asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

r = engine->RegisterObjectType("CreepClient", sizeof(CreepClient), asOBJ_REF ); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("CreepClient", asBEHAVE_ADDREF, "void f()", asFUNCTION(Dummy), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("CreepClient", asBEHAVE_RELEASE, "void f()", asFUNCTION(Dummy), asCALL_CDECL_OBJFIRST); assert( r >= 0 );

r = engine->RegisterObjectMethod("CreepClient", "void addOverlay( )", asMETHOD(CreepClient, addOverlay), asCALL_THISCALL); assert( r >= 0 );

const char *script =
"void fireEffects( CreepClient@ c ) { \n"
" c.addOverlay( ); // This one doesn't ever fire off \n"
"} \n";

asIScriptModule *mod = engine->GetModule("mod", asGM_ALWAYS_CREATE);
mod->AddScriptSection("script", script);
r = mod->Build();
if( r < 0 )
{
fail = true;
}

CreepClient cc;

fail = Exec(engine, cc) || fail;

if( addOverlayCalled == false )
fail = true;

engine->Release();

return fail;
}

Share this post


Link to post
Share on other sites
Ok I compiled and tested the code as such:


#include <iostream>
#include <assert.h>

#include "angelscript.h"

using namespace std;

{
// Your code here
}

int main() {
if( TestMultipleInheritance2() )
cout << "Success!" << endl;
else
cout << "FAIL" << endl;
}



and it fails. I tested it with MinGW on both GCC 4.x and 3.x.

Share this post


Link to post
Share on other sites
I downloaded Code::Blocks as you recommended, but unfortunately the problem you're having isn't reproducable. The test I wrote above works without a problem.

I just noticed that you were running the test slightly wrong. The function TestMultipleInheritance2() returns 'true' if it fails, but in your test you interpret 'true' as success. So I guess this means that my test didn't reproduce the problem on your setup either.

However, downloading Code::Blocks with MinGW wasn't fruitless. There were plenty of other bugs that showed up for MinGW/Win32. I've fixed them now, so you may want to download the new version from the SVN. Perhaps some of the bugs I fixed was also the cause for the problem you were having.



Share this post


Link to post
Share on other sites
It's possible that it is compiler settings, try to turn off all optimizations, and see if the problem goes away.


Try commenting out code from your application and script so that as little as possible remains that still reproduces the problem.


If you can come up with a single line script that reproduces the problem, then you can set a break point in as_callfunc_x86.cpp : CallSystemFunction. You ought to be able to determine what may be going wrong then.

Share this post


Link to post
Share on other sites
Interestingly, the MinGW Release target in the Code::Blocks project had no optimizations turned on, and other than that, I was testing it in a Debug build for my code.

I'm going to begin with creating a one line script and doing what you said.

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