Sign in to follow this  

script object ctors

This topic is 2639 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello. I've stumbled across a slight dilemma which I don't know how to solve and was hoping someone could help me get this working properly.

The problem is simple: A script object calls create_object(pos,...); which pushes a new object cpp side which in turn calls the following in the constructor:


//context init stuff.
cPtr->object = Engine->CreateScriptObject( type->GetTypeId() );
asIScriptObject* _thisObj = (asIScriptObject*)cPtr->object;
this->pos = (Vector2*)_thisObj->GetAddressOfProperty(0); //
*this->pos = .. //*set to what the script wants it to be*;




However, in the script object constructor there is no way to get the correct pos value set by create_object();. ...How can I achieve this?

Thanks.

Share this post


Link to post
Share on other sites
What you need is to use a non-default constructor where the position is passed as a parameter.

An object is created with a non-default constructor by calling the factory function, just like a global function.


int funcId = type->GetFactoryIdByDecl("MyObj @MyObj(const Vector2 &in)");
ctx->Prepare(funcId);
Vector2 pos;
*(Vector2**)ctx->GetAddressOfArg(1) = &pos;
ctx->Execute();
asIScriptObject *obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue();


Instead of passing the position into the constructor, you might pass something else, perhaps a pointer to your cPtr.

Another option is to not rely on the constructor to do the initialization. Do the initialization in a script class method called by the application after the object has been created.

Share this post


Link to post
Share on other sites
Hmm.. The problem is that the cpp-side script instances simply contain pointers to asIScriptObject members (likely a base class), so they are not valid until the asIScriptObject has been created, which they are then linked by this->ptrData = ()->GetAddressOfProperty(n); so that any changes in those values are reflected immediately no matter what changed them. Until this happens, if any script context were to call a function that accesses that class instance data then the program will die horribly.
What we are doing is letting users do pretty much anything they want in any scripts object constructor. I suppose handling the initialization in a separate script class method would work fine, but it would be sort of weird to tell people "Don't use the constructors!" ;)

I suppose the ideal solution would be something like:


object = (void*)engine->CreateScriptObjectShell(...); //create the object but doesn't call ctor
//link class pointers to script data here, but we need the object handle first
//...
funcId = type->GetFactoryIdByDecl("Class @Class()");
ctx->Prepare(funcId);
ctx->Execute();


Any ideas?

Share this post


Link to post
Share on other sites
What's stopping the constructor from calling a function that accesses the pointers of the C++ class from the application side?


I suppose you could do something like this:


int funcId = type->GetFactoryIdByDecl("MyObj @MyObj()");
ctx->SetLineCallback(asFUNCTION(LinkObj), this, asCALL_CDECL);
ctx->Prepare(funcId);
ctx->Execute();
asIScriptObject *obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue();

...

void LinkObj(asIScriptContext *ctx, void *obj)
{
asIScriptObject *scriptObj = ctx->GetThisPointer();
if( scriptObj )
{
MyObj *myObj = (MyObj*)obj;
myObj->pos = (Vector2*)ctx->GetAddressOfProperty(0);
...

// Remove the callback since we've already linked the objects
ctx->ClearLineCallback();
}
}


The line callback should be called by the engine right after the memory has been allocated and before the actual code of the constructor is executed.

Share this post


Link to post
Share on other sites
Thanks again, and sorry for the late reply but I finally just got around to testing all the code. Everything seems to be working except for the method in your first post. The following causes a crash in line 1054 os as_call_funcs_x86 in 2.19.2:


_ctx->context->Prepare( funcId );
*(Vector2**)_ctx->context->GetAddressOfArg(0) = &temp;
_ctx->context->Execute();
_ctx->object = (asIScriptObject*)_ctx->context->GetAddressOfReturnValue();


Commenting out the second line above prevents this from occurring.

Share this post


Link to post
Share on other sites
I wrote the following test to validate what I had said before:


engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

RegisterScriptMath3D(engine);

const char *script =
"class Obj \n"
"{ \n"
" Obj(const vector3 &in v) \n"
" { \n"
" pos = v; \n"
" } \n"
" vector3 pos; \n"
"} \n";

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

int typeId = mod->GetTypeIdByDecl("Obj");
asIObjectType *type = engine->GetObjectTypeById(typeId);
int funcId = type->GetFactoryIdByDecl("Obj @Obj(const vector3 &in)");
if( funcId < 0 )
fail = true;

asIScriptContext *ctx = engine->CreateContext();
ctx->Prepare(funcId);
Vector3 pos(1,2,3);
*(Vector3**)ctx->GetAddressOfArg(0) = &pos;
r = ctx->Execute();
if( r != asEXECUTION_FINISHED )
fail = true;
asIScriptObject *obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue();
pos = *(Vector3*)obj->GetAddressOfProperty(0);
if( pos != Vector3(1,2,3) )
fail = true;

ctx->Release();

engine->Release();



The test above works perfectly.

The error I did before was passing the wrong index for the argument when calling GetAddressOfArg. But in your post you have the correct index, so I wonder if you don't have something else wrong instead.

Your typecast with the call to GetAddressOfReturnValue is wrong, but since you say it works if you remove the GetAddressOfArg I suspect that is just a typo.

Share this post


Link to post
Share on other sites
Hmm... yes, I'm really stumped by this; your code looks good. I've got it to init and execute up to the main update method but still get a crash here:

Quote:

Engine.exe!asCScriptObject::`vcall'{0}'() + 0x2 bytes C++
Engine.exe!CallThisCallFunction(const void * obj=0x09593018, const unsigned long * args=0x095cfbe0, int paramSize=0, unsigned int func=5018058) Line 1054 C++
Engine.exe!CallSystemFunction(int id=18, asCContext * context=0x09592f00, void * objectPointer=0x00000000) Line 212 + 0x1b bytes C++
> Engine.exe!asCContext::ExecuteNext() Line 1960 + 0x12 bytes C++
Engine.exe!asCContext::Execute() Line 1004 + 0x8 bytes C++


Here is my AS object code:

_ctx = contextPool->AquireContext();
if(!_ctx)
throw ("Some wacky scripting error occured...");

asIScriptEngine *Engine = contextPool->GetEngine();

int typeID = Engine->GetModule(0)->GetTypeIdByDecl( class_decl );
if( typeID < 0 )
{
_ctx->destroyed = true;
}
asIObjectType* type = Engine->GetObjectTypeById( typeID );

_ctx->objectType = type;
_ctx->function_id = type->GetMethodIdByDecl( method_decl );
if( _ctx->function_id < 0 )
{
_ctx->script_status = asEXECUTION_SUSPENDED;
_ctx->suspend = INFINITE_SUSPEND;
}

_ctx->SetClassName( class_decl );

//construct object

// create object at position
int funcId = _ctx->objectType->GetFactoryIdByDecl
( contextPool->ClassDeclToCtor( _ctx->GetClassName().c_str(), "(const Vector2 &in)" ) );
if( funcId < 0 )
{
// use default constructor
CreateObject();
}
else
{
contextPool->PushInstance( this );

_ctx->context->Prepare( funcId );
Vector2 test(222,333);
*(Vector2**)_ctx->context->GetAddressOfArg(0) = &test;
//int r = _ctx->context->SetArgObject(0, &test);
_ctx->context->Execute();
_ctx->object = _ctx->context->GetAddressOfReturnValue();
//_ctx->context->GetReturnObject();
contextPool->PopInstance();
}

return (asIScriptObject*)_ctx->object;


//update object

_ctx->context->Prepare( _ctx->function_id );
contextPool->PushInstance( this );
_ctx->context->SetObject( _ctx->object );
_ctx->script_status = _ctx->context->Execute(); //<----- here
contextPool->PopInstance();

return _ctx->script_status;





Stepping through the code shows everything seems to work properly 'till the update method.. Also, forcing the default object ctor via CreateObject() ( which is just this: )

_ctx->object = contextPool->GetEngine()->CreateScriptObject( _ctx->objectType->GetTypeId() );
return (asIScriptObject*)_ctx->object;

Works fine. ?


Also, yes the above was a typo. ;) However Context::object is a void* and I still end up casting it the same way. Is this wrong? -Also just a note, "return (asIScriptObject*)_ctx->object;" is never actually used yet.

[Edited by - arpeggiodragon on September 19, 2010 10:54:21 PM]

Share this post


Link to post
Share on other sites
This is your problem


_ctx->object = _ctx->context->GetAddressOfReturnValue();



it should be


_ctx->object = *(void**)_ctx->context->GetAddressOfReturnValue();



The factory function returns a handle to the object. GetAddressOfReturnValue returns the address to the location where this handle is stored, so you must dereference that to get the actual handle.

You must also increase the reference count of the newly created object. Otherwise it will be destroyed when the context is reused or freed.


if( _ctx->object )
((asIScriptObject*)_ctx->object)->AddRef();




While you don't explicitly use the _ctx->object, AngelScript is still using it in the method call, as it tries to increase the reference to hold on to the object while the method is executing. Since the pointer was incorrect, this is why it crashes.

Share this post


Link to post
Share on other sites
Quote:
The factory function returns a handle to the object. GetAddressOfReturnValue returns the address to the location where this handle is stored, so you must dereference that to get the actual handle.


Yep, this was indeed the problem; coupled with a bug where contexts would get reused and still be in suspended state was causing some major mischief for me. Everything's finally working now. Thank you!

Share this post


Link to post
Share on other sites

This topic is 2639 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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