[getAddressOfArg]getting aruments of angle function in C++

Started by
9 comments, last by WitchLord 14 years, 5 months ago
Hi, How to know the values of the argument passed? Example: AngelScript: dosomething() { callYYY("abc","123","777"); } callYYY(string a,string b,string c) { //make a c++ call CallCPPP(); } C++ Code: //code here is not exact w.r.t to SDK CallCPPP() { getContext(); asIScriptFunction aFunc = getCurrentFunction(); for(int j=0; j<aFunc->getParamCount(); j++) { engine->getType(j); ////////////???????? Now for the type i want to get its value If i use getAddressOfArg, it always returns NULL. } Pls help. regards ragha
Advertisement
I checked the code,
engine->execute set the status as "asExecutionSuspend" and in the method
getAddressOfAgr checks if status != asEXECUTION_PREPARED hence it is not working

Am not sure of the background of this check.

Can anyone help me resolve it?

regards
ragha
You want the C++ function CallCPPP() to be able to see the arguments that was passed to the script function callYYY()?

To do that you can use the debugging functions of the script context, e.g.:

void CallCPPP(){  asIScriptContext *ctx = asGetActiveContext();  if( ctx == 0 ) return;  asIScriptEngine *engine = ctx->GetEngine();    int stackLevel = -1; // Current script function  int numVars = ctx->GetVarCount(stackLevel);  for( int n = 0; n < numVars; n++ )  {    int typeId = ctx->GetVarTypeId(n, stackLevel);     void *varPointer = ctx->GetAddressOfVar(n, stackLevel);    if( typeId == asTYPEID_INT32 )    {      printf(" %s = %d\n", ctx->GetVarDeclaration(n, stackLevel), *(int*)varPointer);    }    else if( typeId == engine->GetTypeIdByDecl("string") )    {      CScriptString *str = (CScriptString*)varPointer;      if( str )        printf(" %s = '%s'\n", ctx->GetVarDeclaration(n, stackLevel), str->buffer.c_str());      else        printf(" %s = <null>\n", ctx->GetVarDeclaration(n, stackLevel));    }    else    {      // For types we're not prepared to format we just write the declaration      printf(" %s\n", ctx->GetVarDeclaration(n, stackLevel));    }  }  }


The GetAddressOfArg() method and its similars are used to set the argument values when the application is calling a script function, they are not meant to be used for investigating the values of arguments.

Regards,
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

Thanks a lot.
I need one more confirmation. Since we make use of addressOfVar, does it
guarantee to return only the function arguments or even the local variables of the function? I want just the function arguments,how to limit that?
Should i have to make use of asIScriptFunction->getParamCount() and
use addressOfVar for these indexes alone?

Another 2 Query:
1.
Script Current function has a User Defined Structure(UDS).
I have to get the list of primitives from this UDS and display.
Example
ScriptFunction:
struct TUDS
{
string abc;
int xyz;
};
void testArgs(in TUDS uds);

in CPPXXX
{
print the primitives of TUDS at runTime. I.e CPPXX should
take help of Context/Engine and then know what this Struct is made
off and the recursively get to the bottom and print it.
}

2.
Also from the code of getAddressOfArg, i find that if the state is in
Suspended or Active it would implicitly mean the context is prepared
right?



regards
ragha


hi,

I got it.
Since the stack is arranged with first Arguments and then FunctionLocalVar
if i iterate through function getParamCount i get the only the arguments.

regards
ragha
1.

You can enumerate the properties of a script class using the asIScriptObject interface. Adding to the function I posted before, you could do something like this:

if( (typeId & asTYPEID_SCRIPTOBJECT) && !(typeId & asTYPEID_OBJHANDLE) ){  asIScriptObject *obj = (asIScriptObject*)varPointer;  for( int n = 0; n < obj->GetPropertyCount(); n++ )  {    int typeId = obj->GetPropertyTypeId(n);     void *propPointer = obj->GetAddressOfProperty(n);    if( typeId == asTYPEID_INT32 )    {      printf(" %s = %d\n", obj->GetPropertyName(n), *(int*)propPointer);    }       else    {      .. handle the other types    }  }}


2.

Yes, if the context is in state ACTIVE or SUSPENDED then it must have been PREPARED first. ACTIVE means that the VM is currently executing the bytecode, SUSPENDED means that the execution has been suspended until resumed by the application.

The PREPARED state, is the state the context is in between the calls to Prepare() and the first call to Execute(). This is the state in which the application should set up all function arguments and in case of a class method the object pointer.

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

Hi,

When i use ctx->getAddressOfVar for a UserDefinedType i get NULL always.
Pls help,code of script and C++ is as below,

Script Code:
class TUDS
{
int a;
string s;
};

float calc(float a, float b)
{


TUDS test;
TUDS test2;
test.a = 20;
test.s = "hhhhhh";

// 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;
}


Sample tutorial Code:
Refer to void LineCallback(asIScriptContext *ctx, DWORD *timeOut)

#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

// 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);



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;
}

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") )
{
// 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 );
}
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)
{



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\n",pObjType->GetName());
}
void *varPointer = (void*) ctx->GetAddressOfVar(n, stackLevel);
if(varPointer == NULL)
{
printf("SomethingWrong in getting variable Address\n");
}

//printComplex(ctx,varPointer,typeId);

}
}

// 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());
}





You don't always get a null pointer, only if the object variable hasn't been initialized yet. Here's the output from the code you included:

Executing the script.---Line number = 25pFunDescriptor->GetParamCount()=2ctx->GetVarCount(stackLevel)=4varName = avarName = bvarName = testpObjType->GetName=TUDSSomethingWrong in getting variable AddressvarName = test2pObjType->GetName=TUDSSomethingWrong in getting variable AddressLine number = 26pFunDescriptor->GetParamCount()=2ctx->GetVarCount(stackLevel)=4varName = avarName = bvarName = testpObjType->GetName=TUDSvarName = test2pObjType->GetName=TUDSSomethingWrong in getting variable AddressLine number = 27pFunDescriptor->GetParamCount()=2ctx->GetVarCount(stackLevel)=4varName = avarName = bvarName = testpObjType->GetName=TUDSvarName = test2pObjType->GetName=TUDSLine number = 28pFunDescriptor->GetParamCount()=2ctx->GetVarCount(stackLevel)=4varName = avarName = bvarName = testpObjType->GetName=TUDSvarName = test2pObjType->GetName=TUDSLine number = 37pFunDescriptor->GetParamCount()=2ctx->GetVarCount(stackLevel)=4varName = avarName = bvarName = testpObjType->GetName=TUDSvarName = test2pObjType->GetName=TUDS---The script function returned: 8.53973Press any key to quit.


I added a few more lines to the output so it is easier to see what happens, namely the line number and variable names. As you can see, on line 25 in the script the variables test and test2 have not yet been initialized, hence GetAddressOfVar returns null. On line 27 they have both been initialized and GetAddressOfVar is now returning a non-null pointer.

This is the expected behaviour, as you can see in the manual, and your application must be able to handle it.

I will try to make this a bit clearer when I write the article on debugging scripts. Thanks for bringing it up.

Regards,
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

Hi,

Ok. In the same program am trying to print the values of
TUDS.a and TUDS.s but program is crashing. Now i have handled varPointer
being null but it still crashes. Sorry to bother you so much,am new to it
hence asking these greenhorn questions.

void LineCallback(asIScriptContext *ctx, DWORD *timeOut)
{
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\n",pObjType->GetName());
}
void *varPointer = (void*) ctx->GetAddressOfVar(n, stackLevel);
if(varPointer == NULL)
{
printf("SomethingWrong in getting variable Address\n");
continue;
}

printComplex(ctx,varPointer,typeId);=====>CRASH here.

}
}

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:

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:
{
//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\n",
pScriptObj->GetObjectType()->GetName());
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;
}
Hi,

It was a mistake from my side.
I was checking as below,
switch(typeId)
{
case appObject:
{
}
default:
{
}
};

TypeID i received was 0x400000d and appObject is 0x4000000 hence it would
never match and reach the default. In the default i was treating string as
asIScriptObject and trying to get value which was not appropriate since
app registed object is never derived from this appObject and was resulting
in crash.

Another mistake was i was using LineCallBackFunction without knowing
its intent, hence the cause of earlier crash were TUDS was not initialized which
you had clarified. Thanks a lot.

Is it possible to include these as tutorials where users want to
know how to get arguments of the script function and make use of it
in NativeCode,if so do let me know how i can contribute?


best regards
ragha

This topic is closed to new replies.

Advertisement