Sign in to follow this  
Vivek G

Accessing an output handle of script whose memory is allocated from CPP

Recommended Posts

Hi, I have a problem using angel script in my application.The scenario is as shown below AS Code --------- int myFunctionCall(string input,string@ &out output) { myCppCall() Print("Output Value = "+output); return 0; } int logicProcessing() { string input = "INPUT VALUE"; string @output; myFunctionCall(input,output); Print("Output Value in logicProcessing stack is "+output); return 0; } cpp Side -------- int myCppCall() { -- -- void *ptr = context->GetAddressOfVar(1); CScriptString **scptr = (CScriptString **)ptr; *scptr = new CScriptString("CPP OUTPUT"); return 0; } Print function of myFunctionCall of AS prints Output Value = CPP OUTPUT But execution of Print("Output Value in logicProcessing stack is "+output); in function "logicProcessing" stops by giving exception "NULL POINTER". What could be the reason for this problem? Why "output" in "logicProcessing" is NULL even though memory is allocted from CPP and it can print the value from "myFunctionCall" ? How to handle the situation like this (I want to allocate the memory from CPP side and that needs to be available in function call chain)? In this case I need the data from functions "logicProcessing" and "myFunctionCall". Regards, Vivek

Share this post


Link to post
Share on other sites
Hi Vivek,

I have to take a closer look at this problem. I don't see any immediate mistake in your code that may cause the unexpected behaviour.


However, I wonder why you're doing it like this. Why are you not just passing the output parameter to myCppCall as a normal argument, instead of manipulating the stack in the context directly?

Example:


int myFunctionCall(string input,string@ &out output)
{
myCppCall(output);
Print("Output Value = "+output);
return 0;
}


or


int myFunctionCall(string input,string@ &out output)
{
@output = @myCppCall();
Print("Output Value = "+output);
return 0;
}


Regards,
Andreas

Share this post


Link to post
Share on other sites
Hi Andreas,

Thanks for your reply.

myCPPCall is a genaric functon called by different AS functions .
The role of myCppCall is to iterate the stack vars , do some processing and populate the out parameters.

For example
int myFunctionCall1(string aa,string bb,string@ &out outparam1,string@ &out outparam2)
{
myCppCall();
}
int anotherFunctionOfAS(TObject zz,AnotherObject@ &out outParam)
{

myCppCall();
}

in such a scenario I have to iterate through stack variables and do the processing at CPP side instead of writing seperate cpp function which does almost similar of logic.

But I am am not able to understand why the handle is NULL in "logicProcessing" function.

Regards,
Vivek

Share this post


Link to post
Share on other sites
I tried out vivek's scenerio and i get a null pointer exception.

Script:
class TUDS
{
int a;
string s;
TUDS(int x,string y)
{
a = x;
s = y;
}
};


void test(TObject@ &out param)
{
TObject testWithThis;
testWithThis.print();
@param = @testWithThis;
Decode(param);
param.print();
}


float calc(float a, float b)
{


TUDS test(20,"hhhh");
TUDS test2;
test2.a = 555;
test2.s = "ragha";

TObject@ tobjH;
test(tobjH);
//tobjH.print();

//Decode(tobjHandle);
//Decode(tobjH);
//tobjH.print();

// Print the value that we received
//Print("Received: " + a + ", " + b + "\n");

// Print the current system time
//Print("System has been running for " + GetSystemTime()/1000.0 + " seconds\n");

// Do the calculation and return the value to the application
return a * b;
}





Code: [Look at void Decode(TObject*& ptr)and printComplex functions]

#include <iostream> // cout
#include <assert.h> // assert()
#ifdef _LINUX_
#include <sys/time.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#else
#include <conio.h> // kbhit(), getch()
#include <windows.h> // timeGetTime()
#endif
#include <angelscript.h>
#include "../../../add_on/scriptstring/scriptstring.h"

using namespace std;

#ifdef _LINUX_

#define UINT unsigned int
typedef unsigned int DWORD;

// Linux doesn't have timeGetTime(), this essintially does the same
// thing, except this is milliseconds since Epoch (Jan 1st 1970) instead
// of system start. It will work the same though...
DWORD timeGetTime()
{
timeval time;
gettimeofday(&time, NULL);
return time.tv_sec*1000 + time.tv_usec/1000;
}

// Linux does have a getch() function in the curses library, but it doesn't
// work like it does on DOS. So this does the same thing, with out the need
// of the curses library.
int getch()
{
struct termios oldt, newt;
int ch;

tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newt );

ch = getchar();

tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}

#endif

class TObject
{

public:
// Class method
TObject()
{
refCount = 1;
}
int t;
string s;

int refCount;

void AddRef() {refCount++;}
void Release()
{
printf("Release Called,when refCount=%d\n",refCount);
if( --refCount == 0 )
delete this;
}

void print()
{

if(s.length() != 0)
{
printf("StringValue is=%s\n",s.c_str());
}
else
{
printf("StringValue something wrong.%d\n",__LINE__);
}
s = "ABCD";
}


};
// Function prototypes
int RunApplication();
void ConfigureEngine(asIScriptEngine *engine);
int CompileScript(asIScriptEngine *engine);
void PrintString(string &str);
void PrintString_Generic(asIScriptGeneric *gen);
void timeGetTime_Generic(asIScriptGeneric *gen);
void LineCallback(asIScriptContext *ctx, DWORD *timeOut);
int printComplex(asIScriptContext *ctx, void* varPointer,int typeId);
void Decode( TObject*& ptr);
TObject* tObjFactory();




int main(int argc, char **argv)
{
RunApplication();

// Wait until the user presses a key
cout << endl << "Press any key to quit." << endl;
while(!getch());

return 0;
}

void MessageCallback(const asSMessageInfo *msg, void *param)
{
const char *type = "ERR ";
if( msg->type == asMSGTYPE_WARNING )
type = "WARN";
else if( msg->type == asMSGTYPE_INFORMATION )
type = "INFO";

printf("%s (%d, %d) : %s : %s\n", msg->section, msg->row, msg->col, type, msg->message);
}

asIScriptEngine* gpEngine = NULL;
int RunApplication()
{
int r;

// Create the script engine
asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
if( engine == 0 )
{
cout << "Failed to create script engine." << endl;
return -1;
}
gpEngine = engine;

// The script compiler will write any compiler messages to the callback.
engine->SetMessageCallback(asFUNCTION(MessageCallback), 0, asCALL_CDECL);

// Configure the script engine with all the functions,
// and variables that the script should be able to use.
ConfigureEngine(engine);

// Compile the script code
r = CompileScript(engine);
if( r < 0 )
{
engine->Release();
return -1;
}

// Create a context that will execute the script.
asIScriptContext *ctx = engine->CreateContext();
if( ctx == 0 )
{
cout << "Failed to create the context." << endl;
engine->Release();
return -1;
}

// We don't want to allow the script to hang the application, e.g. with an
// infinite loop, so we'll use the line callback function to set a timeout
// that will abort the script after a certain time. Before executing the
// script the timeOut variable will be set to the time when the script must
// stop executing.
DWORD timeOut;
r = ctx->SetLineCallback(asFUNCTION(LineCallback), &timeOut, asCALL_CDECL);
if( r < 0 )
{
cout << "Failed to set the line callback function." << endl;
ctx->Release();
engine->Release();
return -1;
}

// Find the function id for the function we want to execute.
int funcId = engine->GetModule(0)->GetFunctionIdByDecl("float calc(float, float)");
if( funcId < 0 )
{
cout << "The function 'float calc(float, float)' was not found." << endl;
ctx->Release();
engine->Release();
return -1;
}

// Prepare the script context with the function we wish to execute. Prepare()
// must be called on the context before each new script function that will be
// executed. Note, that if you intend to execute the same function several
// times, it might be a good idea to store the function id returned by
// GetFunctionIDByDecl(), so that this relatively slow call can be skipped.
r = ctx->Prepare(funcId);
if( r < 0 )
{
cout << "Failed to prepare the context." << endl;
ctx->Release();
engine->Release();
return -1;
}

// Now we need to pass the parameters to the script function.
ctx->SetArgFloat(0, 3.14159265359f);
ctx->SetArgFloat(1, 2.71828182846f);

// Set the timeout before executing the function. Give the function 1 sec
// to return before we'll abort it.
timeOut = timeGetTime() + 1000;

// Execute the function
cout << "Executing the script." << endl;
cout << "---" << endl;
r = ctx->Execute();
cout << "---" << endl;
if( r != asEXECUTION_FINISHED )
{
// The execution didn't finish as we had planned. Determine why.
if( r == asEXECUTION_ABORTED )
cout << "The script was aborted before it could finish. Probably it timed out." << endl;
else if( r == asEXECUTION_EXCEPTION )
{
cout << "The script ended with an exception." << endl;

// Write some information about the script exception
int funcID = ctx->GetExceptionFunction();
asIScriptFunction *func = engine->GetFunctionDescriptorById(funcID);
cout << "func: " << func->GetDeclaration() << endl;
cout << "modl: " << func->GetModuleName() << endl;
cout << "sect: " << func->GetScriptSectionName() << endl;
cout << "line: " << ctx->GetExceptionLineNumber() << endl;
cout << "desc: " << ctx->GetExceptionString() << endl;
}
else
cout << "The script ended for some unforeseen reason (" << r << ")." << endl;
}
else
{
// Retrieve the return value from the context
float returnValue = ctx->GetReturnFloat();
cout << "The script function returned: " << returnValue << endl;
}

// We must release the contexts when no longer using them
ctx->Release();

// Release the engine
engine->Release();

return 0;
}

TObject* tObjFactory()
{
return new TObject();
}
void ConfigureEngine(asIScriptEngine *engine)
{
int r;

// Register the script string type
// Look at the implementation for this function for more information
// on how to register a custom string type, and other object types.
// The implementation is in "/add_on/scriptstring/scriptstring.cpp"
RegisterScriptString(engine);

if( !strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
{
r = engine->RegisterObjectType("TObject", 0, asOBJ_REF); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("TObject", asBEHAVE_FACTORY, "TObject@ tObjFactory()",
asFUNCTION(tObjFactory), asCALL_CDECL); assert( r >= 0 );

// Registering the addref/release behaviours
r = engine->RegisterObjectBehaviour("TObject", asBEHAVE_ADDREF, "void f()", asMETHOD(TObject,AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("TObject", asBEHAVE_RELEASE, "void f()", asMETHOD(TObject,Release), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("TObject", "void print()", asMETHOD(TObject,print), asCALL_THISCALL); assert( r >= 0 );




// Register the functions that the scripts will be allowed to use.
// Note how the return code is validated with an assert(). This helps
// us discover where a problem occurs, and doesn't pollute the code
// with a lot of if's. If an error occurs in release mode it will
// be caught when a script is being built, so it is not necessary
// to do the verification here as well.
r = engine->RegisterGlobalFunction("void Print(string &in)", asFUNCTION(PrintString), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("uint GetSystemTime()", asFUNCTION(timeGetTime), asCALL_STDCALL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("void Decode(TObject@ &out)", asFUNCTION(Decode), asCALL_CDECL); assert( r >= 0 );



}
else
{
// Notice how the registration is almost identical to the above.
r = engine->RegisterGlobalFunction("void Print(string &in)", asFUNCTION(PrintString_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("uint GetSystemTime()", asFUNCTION(timeGetTime_Generic), asCALL_GENERIC); assert( r >= 0 );
}


// It is possible to register the functions, properties, and types in
// configuration groups as well. When compiling the scripts it then
// be defined which configuration groups should be available for that
// script. If necessary a configuration group can also be removed from
// the engine, so that the engine configuration could be changed
// without having to recompile all the scripts.
}

int CompileScript(asIScriptEngine *engine)
{
int r;

// We will load the script from a file on the disk.
FILE *f = fopen("script.as", "rb");
if( f == 0 )
{
cout << "Failed to open the script file 'script.as'." << endl;
return -1;
}

// Determine the size of the file
fseek(f, 0, SEEK_END);
int len = ftell(f);
fseek(f, 0, SEEK_SET);

// On Win32 it is possible to do the following instead
// int len = _filelength(_fileno(f));

// Read the entire file
string script;
script.resize(len);
int c = fread(&script[0], len, 1, f);
fclose(f);

if( c == 0 )
{
cout << "Failed to load script file." << endl;
return -1;
}

// Add the script sections that will be compiled into executable code.
// If we want to combine more than one file into the same script, then
// we can call AddScriptSection() several times for the same module and
// the script engine will treat them all as if they were one. The script
// section name, will allow us to localize any errors in the script code.
asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE);
r = mod->AddScriptSection("script", &script[0], len);
if( r < 0 )
{
cout << "AddScriptSection() failed" << endl;
return -1;
}

// Compile the script. If there are any compiler messages they will
// be written to the message stream that we set right after creating the
// script engine. If there are no errors, and no warnings, nothing will
// be written to the stream.
r = mod->Build();
if( r < 0 )
{
cout << "Build() failed" << endl;
return -1;
}

// The engine doesn't keep a copy of the script sections after Build() has
// returned. So if the script needs to be recompiled, then all the script
// sections must be added again.

// If we want to have several scripts executing at different times but
// that have no direct relation with each other, then we can compile them
// into separate script modules. Each module use their own namespace and
// scope, so function names, and global variables will not conflict with
// each other.

return 0;
}

void LineCallback(asIScriptContext *ctx, DWORD *timeOut)
{

}

void Decode(TObject*& ptr)
{

asIScriptContext *ctx = asGetActiveContext();
if( ctx == 0 ) return;

int funcID = ctx->GetCurrentFunction();
asIScriptFunction* pFunDescriptor = gpEngine->GetFunctionDescriptorById(funcID);

asIScriptEngine *engine = ctx->GetEngine();

int stackLevel = -1; // Current script function

int numVars = ctx->GetVarCount(stackLevel);

printf("pFunDescriptor->GetParamCount()=%d\n",pFunDescriptor->GetParamCount());
printf("ctx->GetVarCount(stackLevel)=%d\n",ctx->GetVarCount(stackLevel));

for( int n = 0; n < numVars; n++ )
{

int typeId = ctx->GetVarTypeId(n, stackLevel);
asIObjectType* pObjType = engine->GetObjectTypeById(typeId);
if(pObjType != NULL)
{
printf("pObjType->GetName=%s,Ln:%d\n",pObjType->GetName(),__LINE__);
}
void *varPointer = (void*) ctx->GetAddressOfVar(n, stackLevel);
if(varPointer == NULL)
{
printf("SomethingWrong in getting variable Address\n");
continue;
}

printComplex(ctx,varPointer,typeId);

}


//ptr = new TObject;//Constructor would incr the ref count.
//ptr->s = "Hello World";
//ptr->t = 10;
return ;


}

// Function implementation with native calling convention
void PrintString(string &str)
{
cout << str;

}

// Function implementation with generic script interface
void PrintString_Generic(asIScriptGeneric *gen)
{
string *str = (string*)gen->GetArgAddress(0);
cout << *str;
}

// Function wrapper is needed when native calling conventions are not supported
void timeGetTime_Generic(asIScriptGeneric *gen)
{
gen->SetReturnDWord(timeGetTime());
}


int printComplex(asIScriptContext *ctx, void* varPointer,int typeId)
{
asIScriptEngine *engine = ctx->GetEngine();
switch(typeId)
{
case asTYPEID_VOID:
{
printf(" cannot handle void type");
break;
}
//! The type id for bool
case asTYPEID_BOOL:
{
printf(" Bool = %d\n", *(bool*)varPointer);
break;
}

//! The type id for int8
case asTYPEID_INT8:
{
printf(" Char = %c\n", *(char*)varPointer);
break;
}
//! The type id for int16
case asTYPEID_INT16:
{
printf(" short = %d\n", *(short*)varPointer);
break;
}

//! The type id for int
case asTYPEID_INT32:
{
printf(" int4 = %d\n", *(int*)varPointer);
break;
}
//! The type id for int64
case asTYPEID_INT64:
{
printf(" int64 = %d\n", *(long int*)varPointer);
break;
}

//! The type id for uint8
case asTYPEID_UINT8:
{

break;
}
//! The type id for uint16
case asTYPEID_UINT16:
//! The type id for uint
case asTYPEID_UINT32:
//! The type id for uint64
case asTYPEID_UINT64:
//! The type id for float
case asTYPEID_FLOAT:
{
printf(" Float = %f\n", *(float*)varPointer);
break;
}

//! The type id for double
case asTYPEID_DOUBLE:
{
break;
}
case asTYPEID_APPOBJECT:
{
printf("This is a APPObject\n");
break;
}

case asTYPEID_SCRIPTOBJECT:
{
//printf(" %s = %f\n", ctx->GetVarDeclaration(n, stackLevel), *(float*)varPointer);
//asIObjectType* pObjType = engine->GetObjectTypeById(typeId);
//for(int k=0; k<pObjType->GetPropertyCount();k++)
//{
// printf("^^^^^^^^^%s\n",pObjType->GetPropertyName(k));
//
//}
break;

}

default:
{
if(typeId & asTYPEID_APPOBJECT)
{
if(typeId == engine->GetTypeIdByDecl("string") )
{
CScriptString *str = (CScriptString*)varPointer;
if( str )
{
printf(" string = '%s'\n", str->buffer.c_str());
}
else
{
printf(" String is invalid");
}
return 0;
}

if(typeId == engine->GetTypeIdByDecl("TObject@") )
{
TObject *&ptr = (TObject*&)varPointer;
ptr = new TObject;

ptr->s = "test";
ptr->t = 20;

if( ptr )
{
if(ptr->s.length() != 0)
printf(" string = '%s',Ln:%d\n", ptr->s.c_str(),__LINE__);
else
{
printf(" Handle's member string is not initialized");
//
//T
//ptr->refCount = 1;
//ptr->s = "test";
//ptr->t = 20;

}
}
else
{
printf(" String is invalid");
}
return 0;
}

if(typeId == engine->GetTypeIdByDecl("TObject") )
{
TObject *ptr = (TObject*)varPointer;
if( ptr )
{
if(ptr->s.length() != 0)
printf(" string = '%s'\n", ptr->s.c_str());
else
printf(" Handle's member string is not initialized");
}
else
{
printf(" String is invalid");
}
return 0;
}




}
//GetObjectTypeFromTypeId
//printf(" %s = %f\n", ctx->GetVarDeclaration(n, stackLevel), *(float*)varPointer);
//asIObjectType* pObjType = engine->GetObjectTypeById(typeId);
//if(pObjType == NULL)
// {
// return -1;//ObjectType is invalid.
// }
asIScriptObject* pScriptObj = (asIScriptObject*) varPointer;
printf("pScriptObj->GetObjectType->GetName=%s,Ln:%d\n",
pScriptObj->GetObjectType()->GetName(),__LINE__);
for(int k=0; k<pScriptObj->GetPropertyCount();k++)
{
//printf("Name=%s,ID=%d\n",pObjType->GetPropertyName(k),pObjType->GetPropertyTypeId(k));
int result = printComplex(ctx,pScriptObj->GetAddressOfProperty(k),pScriptObj->GetPropertyTypeId(k));

}

//printf("DEFAULT=%d\n",typeId);
return 0;
}

};
return 0;
}


Share this post


Link to post
Share on other sites
I understand. It's an unusual way of doing things, but it should be just fine.

I gave this a try and discovered your problem. You were forgetting one level of indirection when updating the output parameter.

It should be:


int myCppCall()
{
--
--
// Get the address of the output parameter
void *ptr = context->GetAddressOfVar(1);

// We got the address to the reference to the handle
CScriptString **scptr = *(CScriptString ***)ptr;

// Set the handle to point to a new string
*scptr = new CScriptString("CPP OUTPUT");

return 0;
}


You were updating the parameter reference, instead of the handle that the reference is pointer to.

Regards,
Andreas

Share this post


Link to post
Share on other sites
Hi,

Me and Vivek are friends.Hence both are interested in this problem.
Problem seems to be in the below code,

void **d would refer to actual destination address
and this is assigned with the address available on the stack.
Hence there is no direct access api which can provide this stack variable
address even if allocate it is overridden with a NULL since stack
would be untouched.

Is there a way to resolve it?

file: as_context.cpp
case asBC_REFCPY:
{
asCObjectType *objType = (asCObjectType*)(size_t)asBC_PTRARG(l_bc);
asSTypeBehaviour *beh = &objType->beh;

// Pop address of destination pointer from the stack
void **d = (void**)*(size_t*)l_sp;//d= 0x00C68390
l_sp += AS_PTR_SIZE;

// Read wanted pointer from the stack
void *s = (void*)*(size_t*)l_sp;//l_sp = 00C68384 and from this the variable allcoated in CPP is reterived.

// Need to move the values back to the context as the called functions
// may use the debug interface to inspect the registers
regs.programPointer = l_bc;
regs.stackPointer = l_sp;
regs.stackFramePointer = l_fp;

// Release previous object held by destination pointer
if( *d != 0 )
engine->CallObjectMethod(*d, beh->release);
// Increase ref counter of wanted object
if( s != 0 )
engine->CallObjectMethod(s, beh->addref);

// Set the new object in the destination
*d = s;
}
l_bc += 1+AS_PTR_SIZE;
break;

Share this post


Link to post
Share on other sites
Hi,

No. getAddressVar returns a address which is different from the stackVariable
address. Even though getAddressVar is allocated the mem it gets lost as
it is overwritten by stackVariable address. Pls guide us how we can
overcome this problem.

regards
ragha

Share this post


Link to post
Share on other sites
Hmm, maybe I have misunderstood what your problem really is. My understanding is that you want to implement a C++ function that is to be called from the script function. The C++ function will then iterate over the script functions parameter directly on the stack and eventually modify the values so that the script function will return them to its caller. Is that it?

If my understanding is correct, then my previous answer is the correct one. getAddressOfVar() gives you the address of the variable on the stack, which you will need to be able to change it. However, in the scenario that Vivek wrote you actually do not want to change the variable itself, but rather the variable it refers to. Actually, for all output parameters you will always change the variable that the output parameter refers to, not the output parameter itself.

The following is the test that I wrote to make sure this is working properly:


// In this test we'll use the debug functions to update a script parameter directly on the stack

void DebugCall()
{
asIScriptContext *ctx = asGetActiveContext();

// Get the address of the output parameter
void *varPointer = ctx->GetAddressOfVar(0, -1);

// We got the address to the reference to the handle
CScriptString **str = *(CScriptString***)varPointer;

// Set the handle to point to a new string
*str = new CScriptString("test");
}

bool Test2()
{
bool fail = false;
COutStream out;
int r;

asIScriptEngine *engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);
engine->SetMessageCallback(asMETHOD(COutStream, Callback), &out, asCALL_THISCALL);

RegisterScriptString(engine);
engine->RegisterGlobalFunction("void assert(bool)", asFUNCTION(Assert), asCALL_GENERIC);

engine->RegisterGlobalFunction("void debugCall()", asFUNCTION(DebugCall), asCALL_CDECL);

asIScriptModule *mod = engine->GetModule(0, asGM_ALWAYS_CREATE);

const char *script =
"void func(string@ &out output) \n"
"{ \n"
" debugCall(); \n"
" assert( output == 'test' ); \n"
"} \n";

mod->AddScriptSection("script", script);
r = mod->Build();
if( r < 0 )
fail = true;

r = engine->ExecuteString(0, "string @o; func(o); assert( o == 'test' );");
if( r != asEXECUTION_FINISHED )
fail = true;

engine->Release();

return fail;
}



ExecuteString() is calling the script function func() passing in a handle to a string that it expects to be set by func(). func() in turn calls the C++ function debugCall(), which accesses the stack directly to set the output parameter to the string "test".

Both the assert in func() and in ExecuteString() succeed, which shows that the string was properly allocated and returned. Moreover, there are no null pointer exceptions, nor any memory leaks when doing this.

Please let me know if my understanding of your problem is incorrect.

Regards,
Andreas

Share this post


Link to post
Share on other sites
Hi,

You are perfectly right.
But my sample and vivek sample is bit different.

May be my sample is misuse case.

Sample:

script
main()
{
string@ obj;
test(obj);
printf(obj);
}

test(string@ &out)
{
Decode(out);//This the difference. Am passing the script argument to CPP call.
}

C++:
void Decode(string*& pobj)
{
//am not manipulating the pobj (string pass as argument to Decode fn)
//instead try to get the arg via ctx->GetVar(0);
//Then i use the mech
CScriptString** ptr = *(CScriptString***)ctx->GetVar(0);
*ptr = new CScriptString("My Fault");
}

I get a NULL object at line printf(obj); in the script.

What could be the reason?

I have one more question:
How to relate the concepts like
Namespace and static variables?

regards
ragha
}

Share this post


Link to post
Share on other sites

void test(string@ &out s)
{
Decode(s);//This the difference. Am passing the script argument to CPP call.
}


After Decode returns AngelScript is copying the result of the parameter to s, overwriting the changes that Decode did. That is why you get a null-pointer exception in this case.

To illustrate this better, I'll rewrite the code to how it looks to the VM:


void test(string@ &out s)
{
string @temp = null;
Decode(@temp);
@s = @temp;
}


As you can see a hidden temporary variable is created, whose reference is passed to the Decode function. Your Decode function doesn't change this variable, so it remains null when the function returns. Then the handle in the temporary variable is assigned to the s variable that test() received.

Why is AngelScript using a temporary variable, instead of passing the reference to s directly? This is to avoid the risk of accessing invalid references. If the argument to Decode() was evaluated before calling the function, the reference could potentially be invalidated by another expresssion within Decode() or even from another thread.



Namespaces is not yet supported in AngelScript. You'll have to use unique variable names instead.

Static variables are just global variables (though only visible from within the function) and should be registered as such.

Regards,
Andreas

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