Sign in to follow this  
lxnyce2

Binding variables to typedefs?

Recommended Posts

Hey folks, been a while sine I been here..so long that they wiped out my username :). Anyways, I am using AngelScript for an online compiler, and I have a question : I define a typedef as such :
typedef uint Color;
Is there any way to bind attributes to the typedef (like we do for classes), such that I can do :
Color col;
col.r = 255;
col.g = 128;
col.b = 128;
col.a = 64;

// My current implementation created functions to set and get the colors
Color c2 = RGBA( CR(col), CG(col), CB(col), CA(col) );

// What I want to do is
Color c2 = RGBA(c.r, c.g, c.b, c.a);

Share this post


Link to post
Share on other sites
Why don't you implement the color as a Class and then write a copy constructor?
class Color {
float r, g, b, a;

Color(float r, float g, float b, float a) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}

Color(const Color &in c) {
r = c.r;
g = c.g;
b = c.b;
a = c.a;
}
}

Color col1(0.5, 0.3, 0.7, 1.0); // Assigns the appropriate colors to the class.
Color col2(col1); // Copies the color values from col1 to col2.



You can also implement some more methods, which will then easily allow you to subtract, add, multiply, divide, etc.

Color col3 = col1 * col2;


I've been trying to do something very similar just two hours ago, but I decided I need to study the blending modes a bit.


Share this post


Link to post
Share on other sites
Quote:
Original post by lxnyce2
Is there any way to bind attributes to the typedef (like we do for classes)


No, this is not possible. A typedef is just an alias for the real type, you cannot change the real type.

However, if you do what Bismuth suggests you can get what you want, i.e. a primitive type with member attributes for each color channel.

Share this post


Link to post
Share on other sites
Thanks guys. I was trying to avoid the overhead of creating a class for two reasons :

1. The native type for color is really a uint, so that worked out nicely
2. It will be called thousands of times so I didn't want to add the overhead of a class variable to the mix.

I guess I will have to take some time out to benchmark and see which method is faster. Surely the class method is a little bit slower, but surely accessing components via a function call rather than a reference might slow things down also.

Share this post


Link to post
Share on other sites
If you register the type as a value type, it can still be treated as an uint by the C++ application. Currently the value types have quite a high overhead in AngelScript as they are allocated on the heap, but I plan on changing this to allocating the on the stack as ordinary primitives. This change will be transparent to the application once I implement it, so you got nothing to loose by doing it like this.


engine->RegisterObjectType("color", sizeof(UINT), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_PRIMITIVE);
engine->RegisterObjectProperty("color", "uint8 a", 3);
engine->RegisterObjectProperty("color", "uint8 r", 2);
engine->RegisterObjectProperty("color", "uint8 g", 1);
engine->RegisterObjectProperty("color", "uint8 b", 0);
engine->RegisterObjectBehaviour("color", asBEHAVE_CONSTRUCT, "void f(uint8 a, uint8 r, uint8 g, uint8 b)", asFUNCTION(color_construct), asCALL_CDECL_OBJLAST);

void color_construct(BYTE a, BYTE r, BYTE g, BYTE b, UINT *self)
{
*self = (a << 24)|(r << 16)|(g << 8)|(b);
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Molle85

union Color
{
unsigned int value;
struct
{
unsigned char r,g,b,a;
};
};

Congratulations that is illegal in two ways :)
unions can not define types and you can not access both of the union types yet only the active member.

Share this post


Link to post
Share on other sites
I don't think angelscript supports unions, but who amongst us hasn't posted in a forum not realizing what we're doing :). Anyways, I have some preliminary benchmark numbers and one more issue.

Old Code Using Typedef Method
void PostProcess() {
Color col;
int avg;
for (int off=canvas.Width()*canvas.Height()-1; off>=0; off--) {
col = canvas.GetPixelOffset(off);
avg = REDVAL(col) + GREENVAL(col) + BLUEVAL(col);
avg /= 3;
canvas.SetPixelOffset(off, RGBA(avg, avg, avg, ALPHAVAL(col)));
}
}


New Code Using Class Method
void PostProcess() {
Color4 col;
int avg;
for (int off=canvas.Width()*canvas.Height()-1; off>=0; off--) {
col = canvas.GetPixelOffset(off);
avg = col.r + col.g + col.b;
col.r = avg;
col.g = avg;
col.b = avg;
canvas.SetPixelOffset(off, col.ToInt());
}
}


Using the old method, best time over 10+ runs was 109ms.
Using the new method, best time over 10+ runs was 98ms.

So I figure throw in one more optimization since i'm using a class for the color now :
void PostProcess() {
Color4 col;
int avg;
for (int off=canvas.Width()*canvas.Height()-1; off>=0; off--) {
col = canvas.GetPixelOffset(off);
col.MakeGray();
canvas.SetPixelOffset(off, col.ToInt());
}
}

Using this method, best time over 10+ runs was 73ms.

Conclusion - New method is definitely faster. It looks like function call overhead was larger than the class usage overhead after all. Now there is just one issue I can't seem to get my head around.

Notice in the examples I had to use "col.ToInt()". I registered an asBEHAVE_VALUE_CAST in my color test class as such :
	r = engine->RegisterObjectType("Color4", sizeof(UINT), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_PRIMITIVE);
r = engine->RegisterObjectProperty("Color4", "uint8 a", 3);
r = engine->RegisterObjectProperty("Color4", "uint8 r", 2);
r = engine->RegisterObjectProperty("Color4", "uint8 g", 1);
r = engine->RegisterObjectProperty("Color4", "uint8 b", 0);
r = engine->RegisterObjectBehaviour("Color4", asBEHAVE_CONSTRUCT, "void f(uint8 r, uint8 g, uint8 b, uint8 a)", asFUNCTION(color_construct4), asCALL_CDECL_OBJLAST); assert( r >= 0 );
//r = engine->RegisterObjectBehaviour("Color4", asBEHAVE_CONSTRUCT, "void f(uint col)", asFUNCTION(color_construct1), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("Color4", asBEHAVE_ASSIGNMENT, "Color4 &f(uint col)", asFUNCTION(uint_to_color), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("Color4", asBEHAVE_VALUE_CAST, "uint f() const", asFUNCTION(color_to_uint), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("Color4", "uint ToInt() const", asFUNCTION(color_to_uint), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("Color4", "void MakeGray() const", asFUNCTION(color_makegray), asCALL_CDECL_OBJLAST); assert( r >= 0 );


However the following code throws an error : Can't implicitly convert from 'Color4&' to 'uint'.
	Color4 col = 1;
uint ctmp = col;


Any ideas?

Share this post


Link to post
Share on other sites
Well, I changed the cast method to an implicit cast, and that seemed to do the trick. No clue why, though :).

See here for the documentation : http://www.angelcode.com/angelscript/sdk/docs/manual/doc_reg_opbeh.html

FYI - speed didn't change from 73ms, but that's to be expected since they were both using the same function. Thanks for all the help guys. I'll post a live link when I finalize the setup sometime in the next week.

Share this post


Link to post
Share on other sites
Use asBEHAVE_IMPLICIT_VALUE_CAST to tell the compiler that the cast can be done implicitly. asBEHAVE_VALUE_CAST permit the same cast, but only when the cast is done explicitly by the script writer, e.g. with int(color);

It is a subtle difference, but there may be times when you do not want to allow an implicit cast, for example if the cast involves an expensive operation and you want make sure it is only used when absolutely wanted.

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