Sign in to follow this  
justin12343

Get value of property from script class

Recommended Posts

justin12343    217
How do you get the value of properties from script classes? My Object class needs access to XY coordinates to pass to other parts of my game engine. My script class looks like this:

[code]class OBJECT_NAME
{
Object @object;
OBJECT_NAME(Object @object)
{
@this.object = object;
}
void Update()
{
}
void Render()
{
draw_sprite( object.mask_index, 0, object.x, object.y);
}
}[/code]

As you see, I have to do "object.WHATEVER" to access anything from the object class in the script class. Instead I want to define the properties in the script class and make the object class access the values of those properties through some sort of proxy function like, float GetX(). This really makes things simple in the scripting side. Is there a way to check if a property exists before I try to get the value?

Share this post


Link to post
Share on other sites
KuroSei    580
If you use a proxy you could try to receive the function from the context i guess. If it returns 0 there is no such function. If it returns a function you have your proxy.
...i guess.

Share this post


Link to post
Share on other sites
justin12343    217
I'd rather not have to do that in the script itself even though that would be the easiest way. I tried this but it doesn't seem to work:

[code]float Object::GetX() const
{
if (!createCalled)
return x;
for (int i = 0; i < scrType->GetPropertyCount(); ++i)
{
const char *decl = scrType->GetPropertyDeclaration( i);
if (decl == "float x");
{
float value = *static_cast<float*>(scrObj->GetAddressOfProperty( i));
return value;
}
}
return x;
}

float Object::GetY() const
{
if (!createCalled)
return y;
for (int i = 0; i < scrType->GetPropertyCount(); ++i)
{
const char *decl = scrType->GetPropertyDeclaration( i);
if (decl == "float y");
{
float value = *static_cast<float*>(scrObj->GetAddressOfProperty( i));
return value;
}
}
return y;
}[/code]

If it finds x or y in the script, then it is supposed to return that value, otherwise default to the value that object class has. I'm sure there is a simpler way to do this.

Share this post


Link to post
Share on other sites
KuroSei    580
Im confused to be honest.

You have a scriptclas,s lets say this one:

[code]
class myScript {

private string mName;

myScript(string name) {
mName=name;
}

string getName() {
return mName;
}
}
[/code]

And you are not sure, wether the asIScriptObject* you have has the ProxyMethod getName(), right?
You can simply check that. The proxys all have the same declarationpattern, have they?
get[PropName](void)
So you could try to retrieve the method get[PropName]() from the scriptcontext by declaration. If it returns 0 there is no such method in the scriptclass, otherwise you can simply call the function you received and get the return value.

Here you would try to get the "string getName()" method.
I realized a similar matter with a template method in my "angelscript handler".

Or did i misudnerstood your problem?
Im quite new to angelscript myself, so i would rather listen to the older members here than to my poor advice. ^^

Share this post


Link to post
Share on other sites
WitchLord    4677
There are 3 errors in your code:

1. You compare the pointer of the returned decl, rather than the value of the string.
2. You have a ; after the if condition.
3. A reinterpret_cast is more appropriate than a static_cast. Though I suppose in the end C++ end up doing the same thing for both in this case.


Also, rather than getting the property declaration and then comparing the string it is better to compare the property name and type separately to get a little better performance.


Here's what I suggest:

[code]

float Object::GetY() const
{
if (!createCalled)
return y;
for (int i = 0; i < scrObj->GetPropertyCount(); ++i)
{
const char *name = scrObj->GetPropertyName(i);
int type = scrObj->GetPropertyTypeId(i);
if( strcmp(name, "y") == 0 && type == asTYPEID_FLOAT )
{
float value = *reinterpret_cast<float*>(scrObj->GetAddressOfProperty(i));
return value;
}
}
return y;
}

[/code]

If this is something you'll do a lot, you'll probably want to cache the offset of the property, so you don't have to search for it for each access.

[code]


float Object::GetY() const
{
if (!createCalled)
return y;

if( not in cache )
{
for( int i = 0; i < srcType->GetPropertyCount(); ++i )
{
const char *name;
int type;
int offset;
srcType->GetProperty(i, &name, &type, 0, &offset);
if( strcmp(name, "y" == 0) && type == asTYPEID_FLOAT )
{
store offset in cache for the 'y' property

break;
}
}
}

if( in cache )
{
float value = *reinterpret_cast<float*>(srcObj + offset);
return value;
}

return y;
}

[/code]


If you decide to go with using the offset instead of GetAddressOfProperty() you need to remember that object types are not stored inline, so you'll have to dereference the pointer twice to get to the value. Of course, if you only plan to access primitive values, then you don't have to worry about this.

Share this post


Link to post
Share on other sites
WitchLord    4677
KuroSei

I guess he want to do the following:

[code]

class OBJECT_NAME
{
Object @object;
float x; // These will be used instead of the object's x and y
float y;
OBJECT_NAME(Object @object)
{
@this.object = object;
}
void Update()
{
}
void Render()
{
draw_sprite( object.mask_index, 0, x, y);
}
}
[/code]

From the script side it will be easier to access the object's position this way. But from the application side he'll have to look up the properties in the script class instead, if they are really there.

Share this post


Link to post
Share on other sites
justin12343    217
[quote name='Andreas Jonsson' timestamp='1333410944' post='4927703'] There are 3 errors in your code: 1. You compare the pointer of the returned decl, rather than the value of the string. 2. You have a ; after the if condition. 3. A reinterpret_cast is more appropriate than a static_cast. Though I suppose in the end C++ end up doing the same thing for both in this case. Also, rather than getting the property declaration and then comparing the string it is better to compare the property name and type separately to get a little better performance. Here's what I suggest: [code] float Object::GetY() const { if (!createCalled) return y; for (int i = 0; i < scrObj->GetPropertyCount(); ++i) { const char *name = scrObj->GetPropertyName(i); int type = scrObj->GetPropertyTypeId(i); if( strcmp(name, "y") == 0 && type == asTYPEID_FLOAT ) { float value = *reinterpret_cast(scrObj->GetAddressOfProperty(i)); return value; } } return y; } [/code] If this is something you'll do a lot, you'll probably want to cache the offset of the property, so you don't have to search for it for each access. [code] float Object::GetY() const { if (!createCalled) return y; if( not in cache ) { for( int i = 0; i < srcType->GetPropertyCount(); ++i ) { const char *name; int type; int offset; srcType->GetProperty(i, &name, &type, 0, &offset); if( strcmp(name, "y" == 0) && type == asTYPEID_FLOAT ) { store offset in cache for the 'y' property break; } } } if( in cache ) { float value = *reinterpret_cast(srcObj + offset); return value; } return y; } [/code] If you decide to go with using the offset instead of GetAddressOfProperty() you need to remember that object types are not stored inline, so you'll have to dereference the pointer twice to get to the value. Of course, if you only plan to access primitive values, then you don't have to worry about this. [/quote]

I tried the cache technique you described above and it works perfect! Now lets say I would want to change the value of the property from the C++ object class. I want to set the script class's x and y initial values in the object class since now they are not being placed correctly (they all are placed at (0,0) unless I explicitly set them to whats in @object). How would that work?

Share this post


Link to post
Share on other sites
WitchLord    4677
The same way. With the offset you know the address of the properties. You just need to dereference it to assign its value.

[code]
*reinterpret_cast<float*>(scrObj + offset) = value;
[/code]

Share this post


Link to post
Share on other sites
justin12343    217
[quote name='Andreas Jonsson' timestamp='1333502974' post='4928060']
The same way. With the offset you know the address of the properties. You just need to dereference it to assign its value.

[code]
*reinterpret_cast<float*>(scrObj + offset) = value;
[/code]
[/quote]

ok, that makes sense. Thanks for your help!

Share this post


Link to post
Share on other sites
justin12343    217
[quote name='Andreas Jonsson' timestamp='1333502974' post='4928060']
The same way. With the offset you know the address of the properties. You just need to dereference it to assign its value.

[code]
*reinterpret_cast<float*>(scrObj + offset) = value;
[/code]
[/quote]

This was causing a heap corruption at first and now is crashing somewhere in the garbage collector.

Share this post


Link to post
Share on other sites
WitchLord    4677
It looks like you're modifying something else than the property of the object. Make sure you have the right address.

Compare the address you get with the offset to what you get with GetAddressOfProperty(). It must be the same, otherwise there is something wrong.

Share this post


Link to post
Share on other sites
justin12343    217
[quote name='Andreas Jonsson' timestamp='1333549754' post='4928206']
It looks like you're modifying something else than the property of the object. Make sure you have the right address.

Compare the address you get with the offset to what you get with GetAddressOfProperty(). It must be the same, otherwise there is something wrong.
[/quote]

I'm getting different addresses. If I use the address returned from GetAddressOfProperty() it doesn't crash.

Share this post


Link to post
Share on other sites
WitchLord    4677
This is what GetAddressOfProperty() looks like:

[code]

void *asCScriptObject::GetAddressOfProperty(asUINT prop)
{
if( prop >= objType->properties.GetLength() )
return 0;

// Objects are stored by reference, so this must be dereferenced
asCDataType *dt = &objType->properties[prop]->type;
if( dt->IsObject() && !dt->IsObjectHandle() )
return *(void**)(((char*)this) + objType->properties[prop]->byteOffset);

return (void*)(((char*)this) + objType->properties[prop]->byteOffset);
}
[/code]

As you can see from the last statement, once it has the offset it simply adds this to the object pointer.

Is the offset you cached really the same?

Share this post


Link to post
Share on other sites
justin12343    217
In my case, the offset in GetAddressOfProperty() is 16, but I'm getting 20 from GetProperty().

EDIT:

It does return the same offset. I misread the the local variable window. The pointers just point to something different.

[CODE]
void *address = scrObj->GetAddressOfProperty( i);
void *offsetAddress = scrObj + offset;
float a = *(float*)address;
float b = *(float*)offsetAddress;
[/CODE]

a is 0 which seems correct and b is 7.221e-042#DEN which is obviously not correct, right?

Share this post


Link to post
Share on other sites
WitchLord    4677
The problem is the pointer arithmetic. It is adding more than you intended because the type of the pointer is asIScriptObject.

You need to do what I do in GetAddressOfProperty and cast the pointer to char* before you add the offset. This will give you the right address of the property.

I really should have remembered this earlier when I first suggested this solution, but somehow it slipped my mind.

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