Advertisement

Application function returning a funcdef handle crashes when called in AS

Started by June 21, 2013 07:53 AM
16 comments, last by Violet CLM 11 years, 4 months ago

Yes, RegisterObjectBehaviour() is used for that. But only for application types. Funcdefs are not application types, they are built-in types and cannot be modified by the application.

I understand that, but earlier you said "Casts involving funcdefs work just like other casts. What exactly is it that you've tried but didn't work." I showed you what I've tried, and you agreed that it doesn't work, but that doesn't get me any closer to knowing what does work.

I'll look into the reason behind RegisterGlobalProperty failing when attempting to register a funcdef property.

No, the registering works just fine. It's only the passing as an argument that crashes.

RegisterGlobalProperty() with funcdef works correctly, what was missing in your code was the @. Here's an example on how to register a property with handle to a funcdef and assigning a function pointer to it.


engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL);
 
asIScriptFunction *f = 0;
engine->RegisterFuncdef("void myfunc()");
r = engine->RegisterGlobalProperty("myfunc @f", &f);
if( r < 0 )
  TEST_FAILED;
 
mod = engine->GetModule("mod", asGM_ALWAYS_CREATE);
mod->AddScriptSection("test",
  "void func() {} \n");
mod->Build();
 
r = ExecuteString(engine, "@f = func; \n", mod);
if( r != asEXECUTION_FINISHED )
  TEST_FAILED;
 
if( f == 0 )
  TEST_FAILED;
if( strcmp(f->GetName(), "func") != 0 )
  TEST_FAILED;
 
f->Release();
f = 0;
 
engine->Release();

When I said that casts for funcdefs work just like other casts, I meant in the script language. I.e, you can use the cast<type>(expr) operator on them. Here's a working example with both explicit and implicit casts:


engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL);
engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC);
 
mod = engine->GetModule("mod", asGM_ALWAYS_CREATE);
mod->AddScriptSection("test",
  "funcdef void myfunc1(); \n"
  "funcdef void myfunc2(); \n"
  "funcdef void myfunc3(); \n"
  "bool called = false; \n"
  "void func() { called = true; } \n"
  "void main() \n"
  "{ \n"
  "  myfunc1 @f1 = func; \n"
  "  myfunc2 @f2 = cast<myfunc2>(f1); \n" // explicit cast
  "  myfunc3 @f3 = f2; \n"                // implicit cast
  "  assert( f1 is f2 ); \n"
  "  assert( f2 is f3 ); \n"
  "  assert( f3 is func ); \n"
  "  f3(); \n"
  "  assert( called ); \n"
  "} \n");
r = mod->Build();
if( r < 0 )
  TEST_FAILED;
 
r = ExecuteString(engine, "main()", mod);
if( r != asEXECUTION_FINISHED )
  TEST_FAILED;
 
engine->Release();

Here's an example of a registered function that receives a function pointer:


bool receivedFuncPtrIsOK = false;
void ReceiveFuncPtr(asIScriptFunction *funcPtr)
{
  if( funcPtr == 0 ) return;
 
  if( strcmp(funcPtr->GetName(), "test") == 0 ) 
    receivedFuncPtrIsOK = true;
 
  funcPtr->Release();
}
 
//---------------------
r = engine->RegisterFuncdef("void AppCallback()");

r = engine->RegisterGlobalFunction("void ReceiveFuncPtr(AppCallback @)", asFUNCTION(ReceiveFuncPtr), asCALL_CDECL); assert( r >= 0 );

script = 
  "void main() \n"
  "{ \n"
  " AppCallback @func = @test; \n"
  "   func(); \n"
  "   ReceiveFuncPtr(func); \n"
  "} \n"
  "void test() \n"
  "{ \n"
  "} \n";
mod->AddScriptSection("script", script);
r = mod->Build();
if( r < 0 )
  TEST_FAILED;
 

r = ExecuteString(engine, "main()", mod);
if( r != asEXECUTION_FINISHED )
TEST_FAILED;
 
if( !receivedFuncPtrIsOK )
  TEST_FAILED;
 



I've confirmed that engine->RegisterObjectBehaviour("jjBEHAVIOR", asBEHAVE_IMPLICIT_REF_CAST, "DifferentFunctionPointer@ a()", asFUNCTION(0), asCALL_CDECL_OBJLAST); doesn't return an error. I'll have have that fixed.

I've now fixed this in revision 1657.

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
Maybe I should back up a bit, and express the issue in terms of what I would like the result to be, rather than in terms of what happens when I try specific things in order to get there. I want the following code to work in AngelScript:
void main() {
  someObject.someProperty = foo;
  someObject.someProperty = bar;
}

void foo() {
  //do stuff
}
Where "bar" is defined within the application, and is an arbitrary number, not even necessarily a pointer to a location that exists within the application's allotted memory. Maybe 0, maybe 1, maybe 0x87654321. I don't really care what type it has within AngelScript so long as I can send that arbitrary number back into the application again. I think I can make this other style work:
void main() {
  someObject.setProperty(foo);
  someObject.setProperty(bar);
}

void foo() {
  //do stuff
}
That works because methods, unlike property accessors, may be successfully overloaded. That's not nearly as pretty, though, and it makes retrieving the value (and being able to compare it both to "foo" and to "bar") pretty cumbersome.

The latter is the only thing that will work at the moment. The scriptany add-on does it this way, and allows you to store both value types and reference types within a single generic container.

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

Thanks for all the help so far... I'm trying some things out and will have more to report later. In the meantime, speaking of funcdefs, one of my collaborators is having an issue with namespaces. The following code apparently worked a couple versions ago, but doesn't now:
funcdef void simpleFuncDef();

namespace foo {
void simpleFunction() { }
}

void takeSimpleFuncDef(simpleFuncDef@ f) { }

void main() {
takeSimpleFuncDef(foo::simpleFunction);
}

//ERR : No matching signatures to 'takeSimpleFuncDef(foo::simpleFunction)'
//INFO : Candidates are:
//INFO : void takeSimpleFuncDef(simpleFuncDef@)
Likewise:
funcdef void simpleFuncDef();

namespace foo {
void simpleFunction() { }
}

void main() {
simpleFuncDef@ bar = foo::simpleFunction;
}

//ERR : Can't implicitly convert from '<null handle>' to 'simpleFuncDef@&'.
In each case, adding or removing "@" to "foo::simpleFunction" changes nothing. Is this the intended behavior, or was it working before and not now?

It appears to be a bug. I'll look into it.

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

I've fixed the bug in revision 1660.

Thanks,

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

Maybe I should back up a bit, and express the issue in terms of what I would like the result to be, rather than in terms of what happens when I try specific things in order to get there. I want the following code to work in AngelScript:

void main() {
  someObject.someProperty = foo;
  someObject.someProperty = bar;
}

void foo() {
  //do stuff
}
Where "bar" is defined within the application, and is an arbitrary number, not even necessarily a pointer to a location that exists within the application's allotted memory. Maybe 0, maybe 1, maybe 0x87654321. I don't really care what type it has within AngelScript so long as I can send that arbitrary number back into the application again.

For the record, this seems to be working now. smile.png Solution:
  • Register all the arbitary numbers ("bar") as an enum, since that's the easiest way to define true constants in AngelScript, as opposed to read-only values.
  • As ever, register a single funcdef for the AngelScript functions ("foo").
  • Create and register a pointer-length value type ("someproperty"). Overload its opAssign and opEquals methods to accept both funcdef instances and enum instances, and also register an implicit value cast from value type to enum. (That last one not really needed, but it means that other functions need only be overloaded twice -- funcdef or enum -- not thrice -- funcdef, enum, or value type. Less bother.)
  • Add checks in a whole lot of application-internal places to determine when and where addRef() and release() need to be called, including the constructor, destructor, and opAssign for the value type.
Thanks again for your time, both in helping me out and in the more general development process. smile.png

This topic is closed to new replies.

Advertisement