Advertisement

odd behavior with globally declared scoped reference types, is this normal?

Started by October 22, 2014 05:12 PM
3 comments, last by ic0de 10 years, 1 month ago

Hi,

I've was having a little trouble with aligned data types so I tried using scoped reference types to represent these in angelscript. While they seem to work pretty well for managing aligned data I've noticed some funny behavior when they are declared in the global scope. Specifically When I try to access them from the script it causes my application to crash, not the script but my application. So here is an example of this behavior:


vec pos = vec(-74.25679016113281f, 0.0f, 27.4027156829834f); //this is a scoped reference type

void loop()
{	
	if(playerInSphere(pos, 25)) //it crashes where I access pos
	{
		//do stuff
	}
}

My guess is that the engine is somehow erroneously determining that 'pos' is out of scope and deleting it so that it ends up passing my application a null pointer which it tries to access and then crashes. Of course I could be wrong, I may have registered the type incorrectly or misunderstood the use of scoped reference types. Does anyone know what might be causing this or is it normal?

It is likely a bug in AngelScript with regards to scoped reference types.

Can you show me how you've registered the vec type so I can more easily reproduce the same problem and investigate the cause of the crash?

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
Well it's real ugly but here it is, I'm using extensive macros and lambdas so I can declare wrapper functions inline and I apologize for the messiness. The crash still happens without the lambda approach.

//these do lambda magic to allow the definition of wrappers inline
#define WRAPFUNC(ret, args, body) asFUNCTION(static_cast<ret(*)args>([]args -> ret body))
#define WRAPEXPR(ret, args, expr) WRAPFUNC(ret, args, {return expr;})

void registerVec()
{
	r = engine->RegisterObjectType("vec", 0, asOBJ_REF | asOBJ_SCOPED); assert(r >= 0);

	r = engine->RegisterObjectBehaviour("vec", asBEHAVE_FACTORY, "vec @f()",				WRAPEXPR(vec*, (),			new vec()),		asCALL_CDECL);		assert(r >= 0);
	r = engine->RegisterObjectBehaviour("vec", asBEHAVE_FACTORY, "vec @f(const vec &in v)",			WRAPEXPR(vec*, (const vec &o),		new vec(o)),		asCALL_CDECL);		assert(r >= 0);
	r = engine->RegisterObjectBehaviour("vec", asBEHAVE_FACTORY, "vec @f(float nx, float nx, float nz)",	WRAPEXPR(vec*, (float x, float y, float z), new vec(x, y, z)),	asCALL_CDECL);		assert(r >= 0);
	r = engine->RegisterObjectBehaviour("vec", asBEHAVE_FACTORY, "vec @f(float n)",				WRAPEXPR(vec*, (float n),		new vec(n)),		asCALL_CDECL);		assert(r >= 0);
	r = engine->RegisterObjectBehaviour("vec", asBEHAVE_RELEASE, "void f()",				WRAPFUNC(void, (vec* t), {if(t)		{ delete t; }}),	asCALL_CDECL_OBJLAST);	assert(r >= 0);

	r = engine->RegisterObjectMethod("vec", "vec &opAssign(const vec &in v)",	asMETHODPR(vec, operator =, (const vec&), vec&), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("vec", "vec &opMulAssign(const float)",	asMETHODPR(vec, operator *=, (const float), vec&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("vec", "vec &opDivAssign(const float)",	asMETHODPR(vec, operator /=, (const float), vec&), asCALL_THISCALL); assert( r >= 0 );
	
	r = engine->RegisterObjectMethod("vec", "vec @opDiv(const float) const",	WRAPEXPR(vec*, (float o, vec* v), new vec(*v / o)), asCALL_CDECL_OBJLAST); assert(r >= 0);
	
	r = engine->RegisterObjectMethod("vec", "vec @opMul(const float) const",	WRAPEXPR(vec*, (float o, vec* v), new vec(*v * o)), asCALL_CDECL_OBJLAST); assert(r >= 0);

	r = engine->RegisterObjectMethod("vec", "bool opEquals(const vec &in v) const", asMETHODPR(vec, operator==, (const vec &) const, bool), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("vec", "vec &opAddAssign(const vec &in v)", asMETHODPR(vec, operator +=, (const vec&), vec&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("vec", "vec &opSubAssign(const vec &in v)", asMETHODPR(vec, operator -=, (const vec&), vec&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("vec", "vec &opMulAssign(const vec &in v)", asMETHODPR(vec, operator *=, (const vec&), vec&), asCALL_THISCALL); assert( r >= 0 );
	r = engine->RegisterObjectMethod("vec", "vec &opDivAssign(const vec &in v)", asMETHODPR(vec, operator /=, (const vec&), vec&), asCALL_THISCALL); assert( r >= 0 );
	
	r = engine->RegisterObjectMethod("vec", "vec @opDiv(const vec &in) const",	WRAPEXPR(vec*, (const vec &vo, vec* v), new vec(*v / vo)), asCALL_CDECL_OBJLAST); assert(r >= 0);

	r = engine->RegisterObjectMethod("vec", "vec @opMul(const vec &in) const",	WRAPEXPR(vec*, (const vec &vo, vec* v), new vec(*v * vo)), asCALL_CDECL_OBJLAST); assert(r >= 0);
	
	r = engine->RegisterObjectMethod("vec", "vec @opSub(const vec &in) const",	WRAPEXPR(vec*, (const vec &vo, vec* v), new vec(*v - vo)), asCALL_CDECL_OBJLAST); assert(r >= 0);
	
	r = engine->RegisterObjectMethod("vec", "vec @opAdd(const vec &in) const",	WRAPEXPR(vec*, (const vec &vo, vec* v), new vec(*v + vo)), asCALL_CDECL_OBJLAST); assert(r >= 0);

	r = engine->RegisterObjectMethod("vec", "void normalize()",		asMETHOD(vec, normalize), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectMethod("vec", "vec @normalized()",	WRAPEXPR(vec*, (vec* v), new vec(v->normalized())), asCALL_CDECL_OBJLAST); assert(r >= 0);

	r = engine->RegisterObjectMethod("vec", "float magnitude()",	asMETHOD(vec, magnitude), asCALL_THISCALL); assert( r >= 0 );

	r = engine->RegisterObjectProperty("vec", "float x", asOFFSET(vec, x)); assert( r >= 0 );
	r = engine->RegisterObjectProperty("vec", "float y", asOFFSET(vec, y)); assert( r >= 0 );
	r = engine->RegisterObjectProperty("vec", "float z", asOFFSET(vec, z)); assert( r >= 0 );
}

It was indeed a bug in the library. I've fixed this in revision 2023.

Observe, that the new operator is not guaranteed to allocate the memory with the necessary alignment. I assume you're using a __m128 type in your vec class, thus you would need an alignment of 16 bytes. To get the proper alignment you need to use _aligned_malloc to allocate the memory and then the placement new operator to initialize it. The memory must then be freed with _aligned_free.


// Allocate memory with proper alignment then initialize it
r = engine->RegisterObjectBehaviour("vec", asBEHAVE_FACTORY, "vec @f()",  WRAPEXPR(vec*, (), new(_aligned_malloc(sizeof(vec), std::alignment_of<vec>().value)) vec()), asCALL_CDECL); assert(r >= 0);
// Manually call destructor then free the memory
r = engine->RegisterObjectBehaviour("vec", asBEHAVE_RELEASE, "void f()",  WRAPFUNC(void, (vec* t), {if(t) { t->~vec(); _aligned_free(t); }}), asCALL_CDECL_OBJLAST); assert(r >= 0);

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 for the amazingly quick fix. I know that new isn't guaranteed to be aligned but I overloaded the operator to ensure this as I was having trouble in other parts of application.

EDIT: tested your revision with my code and it works like magic, you really are a wizard.

This topic is closed to new replies.

Advertisement