🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Destructor crash

Started by
9 comments, last by WitchLord 17 years, 3 months ago
AngelScript 2.8.0a is crashing after it leaves the destructor of my C++ class ScriptObject. I don't know why. Base class:

ScriptObject::ScriptObject()
{
	scriptRefCount=1;
	scriptContext = ScriptManager::engine->CreateContext();
}
ScriptObject::~ScriptObject()
{
	if (scriptContext)
		scriptContext->Release();
}
void ScriptObject::ScriptAddRef(void)
{
	++scriptRefCount;
}
void ScriptObject::ScriptReleaseRef(void)
{
	if (--scriptRefCount<=0)
	{
		delete this;
	}
}




Base class registration:

ScriptManager::engine->RegisterObjectType(className, classSize, supportedOps);
ScriptManager::engine->RegisterObjectBehaviour(className, asBEHAVE_ADDREF, "void f()", asMETHOD(ScriptObject, ScriptAddRef),asCALL_THISCALL);
ScriptManager::engine->RegisterObjectBehaviour(className, asBEHAVE_RELEASE, "void f()", asMETHOD(ScriptObject, ScriptReleaseRef),asCALL_THISCALL);




Derived class:

#include "OgreVector3.h"
#include "ScriptObject.h"

class ScriptVector3 : public Ogre::Vector3, public ScriptObject
{
public:
	static void RegisterScriptClass(void);
};


Derived class code:

void ScriptVector3_Construct(ScriptVector3 *o)
{
	new(o) ScriptVector3();
}
void ScriptVector3_ConstructFloat3(const float _x, const float _y, const float _z, ScriptVector3 *o)
{
	ScriptVector3 *obj = new(o) ScriptVector3();
	obj->x=_x;
	obj->y=_y;
	obj->z=_z;
}
void ScriptVector3_Destruct(ScriptVector3 *o)
{
	o->~ScriptVector3();
}




Derived class registration:

ScriptObject::RegisterScriptClass("Vector3", sizeof(Vector3), asOBJ_CLASS_CA);

ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ScriptVector3_Construct), asCALL_CDECL_OBJLAST);
ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_CONSTRUCT, "void f(float, float, float)", asFUNCTIONPR(ScriptVector3_ConstructFloat3, (const float, const float, const float), void), asCALL_CDECL_OBJLAST);




Script:

void ModuleUpdate(GMScript &this, uint curTimeMS, uint elapsedTimeMS)
{
	Vector3 vec(1,2,3.5);
}




Behavior: ScriptVector3_ConstructFloat3 is called ok and the values are as in script. Immediately after, because the Vector3 leaves the scope of the function, it calls ScriptObject::ScriptReleaseRef The destructor is called,scriptContext is released. All the addresses are correct. After it leaves the Destructor, it crashes with the message: Windows has triggered a breakpoint in GalacticMelee.exe This may be due to a corruption of the heap, and indicates a bug in GalacticMelee.exe or any of the DLLs it has loaded I'm linking with a debug DLL with debug info. Here's the relevant callstack:

 	GalacticMelee.exe!ScriptObject::~ScriptObject()  Line 16 + 0x10 bytes	C++
 	GalacticMelee.exe!ScriptVector3::`scalar deleting destructor'()  + 0x3c bytes	C++
 	GalacticMelee.exe!ScriptObject::ScriptReleaseRef()  Line 25 + 0x34 bytes	C++
 	angelscriptDebug.dll!asCScriptEngine::CallObjectMethod(void * obj=0x10d9eac0, asSSystemFunctionInterface * i=0x0b43e340, asCScriptFunction * s=0x0b43e3b0)  Line 2487 + 0x8 bytes	C++
 	angelscriptDebug.dll!asCScriptEngine::CallObjectMethod(void * obj=0x10d9eac0, int func=57)  Line 2460	C++
 	angelscriptDebug.dll!asCContext::ExecuteNext()  Line 1909	C++
 	angelscriptDebug.dll!asCContext::Execute()  Line 918 + 0x8 bytes	C++
>	GalacticMelee.exe!ScriptObject::ExecuteContext()  Line 31 + 0x17 bytes	C++




[Edited by - Rakkar2 on April 3, 2007 10:53:35 PM]
Advertisement
Post the relevant code in between [source] and [/source] tags, the way you have it formatted now (with really long lines) doesn't play nice in firefox.
Are you overloading the memory management functions? If not, that may be your problem.

Since you're using AngelScript from a dll, it will do its allocation in it's own memory heap, which is separate from the main application's. Without overloading the memory management functions AngelScript will allocate the memory for your object in the dll heap before calling the constructor, and then you try to free the memory in application heap in the release method.

By overloading the memory management functions, AngelScript will call the application's allocation function which will allocate the memory from the same heap as the release method will free it.

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 reply. I stopped using the DLL and included the source to AngelScript directly. I rebuilt all to make sure it took effect. Yet, I still get the exact same problem.

Could it be because I'm using AddRef and RemoveRef in a base class?

*** EDIT ***

Changing from:

class ScriptObject{public:	ScriptObject();	virtual ~ScriptObject();


to

class ScriptObject{public:	ScriptObject();	~ScriptObject();


And I no longer get the crash. But I do need the destructor virtual so the base class destructor is called if I later use a derived class with a destructor. Currently it is unimplemented for both ScriptVector3 and Ogre::Vector3. Maybe this is the problem.
* Update Again *

OK I figured this out, and I'm not sure the best way to proceed.

I'm deriving from Ogre::Vector3, which has a ton of functions I want to use.

However, I'm also deriving from class ScriptObject, which has

int scriptRefCount;void ScriptObject::ScriptAddRef(void){	++scriptRefCount;}void ScriptObject::ScriptReleaseRef(void){	if (--scriptRefCount<=0)	{		delete this;	}}


Now the problem is, if I call the Ogre::Vector3 functions that return references to themselves (assigment operators), they don't return instances of my class, but of Ogre::Vector3. So the extra data held by ScriptObject (the virtual destructor, and scriptRefCount) isn't there and it's messed up on deletion.

The obvious solution is just to create wrappers for each of the functions in Ogre::Vector3, in cases where object references are returned.

This is what I was doing, and what I would like to do (wrong?)
ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_ASSIGNMENT, "Vector3 &f(const Vector3 &in)", asMETHODPR(Ogre::Vector3, operator =, (const Ogre::Vector3&), Ogre::Vector3&), asCALL_THISCALL);ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_ADD_ASSIGN, "Vector3 &f(const Vector3 &in)", asMETHODPR(Ogre::Vector3, operator+=, (const Ogre::Vector3&), Ogre::Vector3&), asCALL_THISCALL);ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_SUB_ASSIGN, "Vector3 &f(const Vector3 &in)", asMETHODPR(Ogre::Vector3, operator-=, (const Ogre::Vector3&), Ogre::Vector3&), asCALL_THISCALL);ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_MUL_ASSIGN, "Vector3 &f(const Vector3 &in)", asMETHODPR(Ogre::Vector3, operator*=, (const Ogre::Vector3&), Ogre::Vector3&), asCALL_THISCALL);ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_DIV_ASSIGN, "Vector3 &f(const Vector3 &in)", asMETHODPR(Ogre::Vector3, operator/=, (const Ogre::Vector3&), Ogre::Vector3&), asCALL_THISCALL);ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_NEGATE,   "Vector3 f()", asFUNCTION(NEG), asCALL_CDECL_OBJLAST);


What I may end up doing
ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_ASSIGNMENT, "Vector3 &f(const Vector3 &in)", asMETHODPR(ScriptVector3, operator =, (const ScriptVector33&), ScriptVector33&), asCALL_THISCALL);ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_ADD_ASSIGN, "Vector3 &f(const Vector3 &in)", asMETHODPR(ScriptVector3, operator+=, (const ScriptVector33&), ScriptVector33&), asCALL_THISCALL);ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_SUB_ASSIGN, "Vector3 &f(const Vector3 &in)", asMETHODPR(ScriptVector3, operator-=, (const ScriptVector33&), ScriptVector33&), asCALL_THISCALL);ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_MUL_ASSIGN, "Vector3 &f(const Vector3 &in)", asMETHODPR(ScriptVector3, operator*=, (const ScriptVector33&), ScriptVector33&), asCALL_THISCALL);ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_DIV_ASSIGN, "Vector3 &f(const Vector3 &in)", asMETHODPR(ScriptVector3, operator/=, (const ScriptVector33&), ScriptVector33&), asCALL_THISCALL);ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_NEGATE,   "Vector3 f()", asFUNCTION(NEG), asCALL_CDECL_OBJLAST);


So is there a way to get my first approach working, so I can save the trouble of creating all these extra wrappers?
You're going to need a wrapper to call that virtual destructor. You just are. Angelscript can't know to look in a vtable just from a function pointer. However, you can mitigate that by using templates!

template <typename T>void wrap_destructor(T* this_pointer) { T->~T(); }Now, when binding to AS, use asFUNCTION(wrap_destructor<type>) and asCALL_OBJ_LAST. [This line in source to preserver <>s.]


I've actually got a fairly complete set of templated wrappers that I use. I just use these by default, instead of messing around with asMETHODPR. It really saves me a lot of headache, as it allows the compiler to figure out the exact name and parameters to the function for me, whether it be implemented as a member or a free function or whatever.

/*Angelscript Class Binding Utility TemplatesCopyright (c) 2006 Anthony CasteelThis software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you usethis software in a product, an acknowledgment in the product documentation would be appreciated but is not required.2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.3. This notice may not be removed or altered from any source distribution.Anthony Casteeljm@omnisu.com*//* Important Notes --Motivation --Binding operators to angelscript is HARD. If there are overloads involved, you have to specify exactly which overload you meanusing complicated macros that the preprocessor often chokes on.For example, try binding operator[]. The preprocessor often failswhen it encounters that [] construct in the name of the function.These templates solve that problem by invoking C++ overloadresolution system. These templates wrap the operator and makeunqualified calls to the operator, allowing the compiler to choosethe best match using argument dependant lookup and overloadresolution. All variable types are templated for maximum flexibility.The templates do not enforce proper reference usage; you mustexplicitly add & to your template parameters. No quarantees aremade as to how well this will work if you don't use cannonicaloperator signatures.AngelScript requires that some operators - such as += - be bound asmembers. C++ makes no such restriction. In the case where operator+=is declared globally, as T& operator+=(T& lhs, const T& rhs), thereis no way to directly bind it to AngelScript, as AngelScript requiresthis operator to be bound with either the asCALL_THISCALL orasCALL_CDECL_OBJLAST convention. A wrapper is neccessary in thiscase. These templates can also be used to automate the generationof such a wrapper.If your class is not const correct, these templates will not work. Notice that the wrappers do NOT take anything by reference. If a parametershould be passed by reference, include the reference in the type when instantiating the template. This decision was made because, by default, AngelScript cannot use C++ references directly. To use references properly,you must either use AutoHandles or turn on asUNSAFE_REFERENCES.The constructors-with-arguments wrappers take up to only 5 arguments.This is easily extendable.Naming Conventions --When a return value is appropriate, it's type is first.Constructors take the class first, followed by the parameters.Binary operators take the return type, the first parameter, and thesecond, in that order.Binary Operator: R operater???(F lhs, S rhs)Binary Member operators take template parameters in the same order asglobal Binary Operators.Binary Member Operator: R S::operator???(F lhs)Operator-> is experimentally wrapped in MemberSelection. This may or may not work.Some operators not supported --()		- Would require something similiar to Construct#, if supported by AS++		- Not supported by AS (Implementations still provided)--		- Not supported by AS (Implementations still provided)*		- Not supported by AS&		- Don't overload this. ...(type)	- Not supported by AS,		- Don't overload this.new		- AS provides alternate mechanism?new[]	- See abovedelete	- See abovedelete[]- See above->		- Experimental ImplementationExample Usage --Binding a few operators for the type 'foo'//copy constructorengine->RegisterObjectBehaviour("foo",asBEHAVE_CONSTRUCT,"void f(const foo&)",asFUNCTION((Wrap::Construct1<foo,foo&>)),asCALL_CDECL_OBJLAST);//operator+engine->RegisterGlobalBehaviour(asBEHAVE_ADD,"foo f(const foo&,const foo&)",asFUNCTION((Wrap::Add<foo,foo&,foo&>)),asCALL_CDECL);//operator+=engine->RegisterObjectBehaviour("foo",asBEHAVE_ADD_ASSIGN,"foo& f(const foo&)",asFUNCTION((Wrap::AddAssign<foo&,foo,foo&>)),asCALL_CDECL_OBJLAST);*/#ifndef JM_CLASS_BINDING_UTILITY_TEMPLATES_H#define JM_CLASS_BINDING_UTILITY_TEMPLATES_Hnamespace Wrap{	//Constructors (with up to 5 parameters)	//Use asCALL_CDECL_OBJLAST	template<typename T> void Construct(T* ptr) { new (ptr) T(); }	//Can also be used as copy constructor	template<typename T, typename P1> 	void Construct1(P1 p1, T* ptr) { new (ptr) T(p1); }	template<typename T, typename P1, typename P2> 	void Construct2(P1 p1, P2 p2, T* ptr) { new (ptr) T(p1,p2); }	template<typename T, typename P1, typename P2, typename P3> 	void Construct3(P1 p1, P2 p2, P3 p3, T* ptr) { new (ptr) T(p1,p2,p3); }	template<typename T, typename P1, typename P2, typename P3, typename P4> 	void Construct4(P1 p1, P2 p2, P3 p3, P4 p4, T* ptr) { new (ptr) T(p1,p2,p3,p4); }	template<typename T, typename P1, typename P2, typename P3, typename P4, typename P5>	void Construct5(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, T* ptr) { new (ptr) T(p1,p2,p3,p4,p5); }	//Bind as type behavior; use asCALL_CDECL_OBJLAST	template<typename R,typename F,typename S> R Assign(const S rhs, F* ptr) { return (*ptr) = rhs; }	template<typename F> void Destroy(F* ptr) { ptr->~F(); }	//Comparison operators.	//Bind as global behaviors; use asCALL_CDECL	template<typename F,typename S> bool Equal(const F lhs, const S rhs) { return lhs == rhs; }	template<typename F,typename S> bool NotEqual(const F lhs, const S rhs) { return lhs != rhs; }	template<typename F,typename S> bool GreaterEqual(const F lhs, const S rhs) { return lhs >= rhs; }	template<typename F,typename S> bool LessEqual(const F lhs, const S rhs) { return lhs <= rhs; }	template<typename F,typename S> bool Greater(const F lhs, const S rhs) { return lhs > rhs; }	template<typename F,typename S> bool Less(const F lhs, const S rhs) { return lhs < rhs; }	//Unary operators.	//Bind as type behavior; use asCALL_CDECL_OBJLAST	template<typename R,typename F> R Not(F* thisp) { return !(*thisp); }	template<typename R,typename F> R Negate(F* thisp) { return -(*thisp); }	template<typename R,typename F> R Positate(F* thisp) { return +(*thisp); }	template<typename R,typename F> R BinaryComplement(F* thisp) { return ~(*thisp); }	//Binary operators. Operator, Assign variety. 	//Bind operator as global behavior; use asCALL_CDECL	//Bind assign variety as type behavior; use asCALL_CDECL_OBJLAST	template<typename R,typename F,typename S> R Add(const F lhs, const S rhs) { return lhs+rhs; }	template<typename R,typename F,typename S> R AddAssign(const S rhs, F* thisp) { return (*thisp) += rhs; }	template<typename R,typename F,typename S> R Subtract(const F lhs, const S rhs) { return lhs-rhs; }	template<typename R,typename F,typename S> R SubtractAssign(const S rhs, F* thisp) { return (*thisp) -= rhs; }	template<typename R,typename F,typename S> R Multiply(const F lhs, const S rhs) { return lhs*rhs; }	template<typename R,typename F,typename S> R MultiplyAssign(const S rhs, F* thisp) { return (*thisp) *= rhs; }	template<typename R,typename F,typename S> R Devide(const F lhs, const S rhs) { return lhs/rhs; }	template<typename R,typename F,typename S> R DevideAssign(const S rhs, F* thisp) { return (*thisp) /= rhs; }	template<typename R,typename F,typename S> R Modulus(const F lhs, const S rhs) { return lhs%rhs; }	template<typename R,typename F,typename S> R ModulusAssign(const S rhs, F* thisp) { return (*thisp) %= rhs; }	template<typename R,typename F,typename S> R ShiftLeft(const F lhs, const S rhs) { return lhs << rhs; }	template<typename R,typename F,typename S> R ShiftLeftAssign(const S rhs, F* thisp) { return (*thisp) <<= rhs; }	template<typename R,typename F,typename S> R ShiftRight(const F lhs, const S rhs) { return lhs >> rhs; }	template<typename R,typename F,typename S> R ShiftRightAssign(const S rhs, F* thisp) { return (*thisp) >>= rhs; }	template<typename R,typename F,typename S> R BinaryAnd(const F lhs, const S rhs) { return lhs & rhs; }	template<typename R,typename F,typename S> R BinaryAndAssign(const S rhs, F* thisp) { return (*thisp) &= rhs; }	template<typename R,typename F,typename S> R BinaryOrRight(const F lhs, const S rhs) { return lhs | rhs; }	template<typename R,typename F,typename S> R BinaryOrAssign(const S rhs, F* thisp) { return (*thisp) |= rhs; }	template<typename R,typename F,typename S> R BinaryXorRight(const F lhs, const S rhs) { return lhs ^ rhs; }	template<typename R,typename F,typename S> R BinaryXortAssign(const S rhs, F* thisp) { return (*thisp) ^= rhs; }	//These binary operators do not have an assign variety	template<typename R,typename F,typename S> R LogicalAndRight(const F lhs, const S rhs) { return lhs && rhs; }	template<typename R,typename F,typename S> R LogicalOrRight(const F lhs, const S rhs) { return lhs || rhs; }	//Index operator	//Bind as type behavior; use asCALL_CDECL_OBJLAST	template<typename R,typename F,typename S> R Index(const S i, F* thisp) { return (*thisp); }	//Increment operators (Not supported by AngelScript)	//Bind as type behavior; use asCALL_CDECL_OBJLAST	template<typename R,typename F> R PreIncrement(F* thisp) { return ++(*thisp); }	template<typename R,typename F> R PostIncrement(F* thisp) { return (*thisp)++; }	template<typename R,typename F> R PreDecrement(F* thisp) { return --(*thisp); }	template<typename R,typename F> R PostDecrement(F* thisp) { return (*thisp)--; }	//MemberSelection operator - MAY OR MAY NOT WORK.	//Bind as type behavior; use asCALL_CDECL_OBJLAST	template<typename R,typename F> R MemberSelection(F* thisp) { return thisp->operator->(); }	//Method through smart pointer wrapper	//Type guide:	//	R - return type	//	P - smart pointer type	//	C - type smart pointer points too	//	M - method on C	//	rest - parameters	//use asCALL_CDDECL_OBJLAST	template<typename R,typename P, typename C, R(C::*M)()>	R MethodThroughPointer0(P* thisp) { return ((**thisp).*M)(); }	template<typename R,typename P, typename C, R(C::*M)(), typename P1>	R MethodThroughPointer1(P1 p1, P* thisp) { return ((**thisp).*M)(p1); }	template<typename R,typename P, typename C, R(C::*M)(), typename P1, typename P2>	R MethodThroughPointer2(P1 p1, P2 p2, P* thisp) { return ((**thisp).*M)(p1,p2); }	template<typename R,typename P, typename C, R(C::*M)(), typename P1, typename P2, typename P3>	R MethodThroughPointer3(P1 p1, P2 p2, P3 p3, P* thisp) { return ((**thisp).*M)(p1,p2,p3); }	template<typename R,typename P, typename C, R(C::*M)(), typename P1, typename P2, typename P3, typename P4>	R MethodThroughPointer4(P1 p1, P2 p2, P3 p3, P4 p4, P* thisp) { return ((**thisp).*M)(p1,p2,p3,p4); }	template<typename R,typename P, typename C, R(C::*M)(), typename P1, typename P2, typename P3, typename P4, typename P5>	R MethodThroughPointer5(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P* thisp) { return ((**thisp).*M)(p1,p2,p3,p4,p5); }}#endif


This header has fairly good documentation of it's usage and even some examples. I never actually wrote direct method wrappers, as I didn't need them - but, looking at it now, the MethodThroughPointer wrappers should work as well with a raw pointer as a smart pointer.
OK I got it working without having to write any wrappers at all :)

void ScriptVector3::RegisterScriptClass(void){	int result;	result = ScriptManager::engine->RegisterObjectType("Vector3", sizeof(Ogre::Vector3), asOBJ_CLASS_CDA); RakAssert(result>=0);//	result = ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_ADDREF, "void f()", asMETHOD(ScriptObject, ScriptAddRef),asCALL_THISCALL); RakAssert(result>=0);//	result = ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_RELEASE, "void f()", asMETHOD(ScriptObject, ScriptReleaseRef),asCALL_THISCALL); RakAssert(result>=0);	// Member behaviors	result = ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_CONSTRUCT, "void f(const Vector3& in)", asFUNCTION((Wrap::Construct1<Ogre::Vector3, Ogre::Vector3&>)), asCALL_CDECL_OBJLAST); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_CONSTRUCT, "void f(float, float, float)", asFUNCTION((Wrap::Construct3<Ogre::Vector3, float, float, float>)), asCALL_CDECL_OBJLAST); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_ASSIGNMENT, "Vector3 &f(const Vector3 &in)", asFUNCTION((Wrap::Assign<Ogre::Vector3&,Ogre::Vector3,Ogre::Vector3&>)), asCALL_CDECL_OBJLAST); assert( result >= 0 );	result = ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_ADD_ASSIGN, "Vector3 &f(const Vector3 &in)", asFUNCTION((Wrap::AddAssign<Ogre::Vector3&,Ogre::Vector3,Ogre::Vector3&>)), asCALL_CDECL_OBJLAST); assert( result >= 0 );	result = ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_SUB_ASSIGN, "Vector3 &f(const Vector3 &in)", asFUNCTION((Wrap::SubtractAssign<Ogre::Vector3&,Ogre::Vector3,Ogre::Vector3&>)), asCALL_CDECL_OBJLAST); assert( result >= 0 );	result = ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_MUL_ASSIGN, "Vector3 &f(const Vector3 &in)", asFUNCTION((Wrap::MultiplyAssign<Ogre::Vector3&,Ogre::Vector3,Ogre::Vector3&>)), asCALL_CDECL_OBJLAST); assert( result >= 0 );	result = ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_DIV_ASSIGN, "Vector3 &f(const Vector3 &in)", asFUNCTION((Wrap::DivideAssign<Ogre::Vector3&,Ogre::Vector3,Ogre::Vector3&>)), asCALL_CDECL_OBJLAST); assert( result >= 0 );	result = ScriptManager::engine->RegisterObjectBehaviour("Vector3", asBEHAVE_NEGATE,     "Vector3 f()",					 asFUNCTION((Wrap::Negate<Ogre::Vector3,Ogre::Vector3>)), asCALL_CDECL_OBJLAST); RakAssert(result>=0);	// Global behaviors	result = ScriptManager::engine->RegisterGlobalBehaviour(asBEHAVE_EQUAL,       "bool f(const Vector3 &in, const Vector3 &in)",    asFUNCTION((Wrap::Equal<Ogre::Vector3&,Ogre::Vector3&>)), asCALL_CDECL); assert( result >= 0 );	result = ScriptManager::engine->RegisterGlobalBehaviour(asBEHAVE_NOTEQUAL,       "bool f(const Vector3 &in, const Vector3 &in)",    asFUNCTION((Wrap::NotEqual<Ogre::Vector3&,Ogre::Vector3&>)), asCALL_CDECL); assert( result >= 0 );	result = ScriptManager::engine->RegisterGlobalBehaviour(asBEHAVE_LESSTHAN,       "bool f(const Vector3 &in, const Vector3 &in)",    asFUNCTION((Wrap::Less<Ogre::Vector3&,Ogre::Vector3&>)), asCALL_CDECL); assert( result >= 0 );	result = ScriptManager::engine->RegisterGlobalBehaviour(asBEHAVE_GREATERTHAN,       "bool f(const Vector3 &in, const Vector3 &in)",    asFUNCTION((Wrap::Greater<Ogre::Vector3&,Ogre::Vector3&>)), asCALL_CDECL); assert( result >= 0 );	result = ScriptManager::engine->RegisterGlobalBehaviour(asBEHAVE_ADD,       "Vector3 f(const Vector3 &in, const Vector3 &in)",		 asFUNCTION((Wrap::Add<Ogre::Vector3,Ogre::Vector3&,Ogre::Vector3&>)), asCALL_CDECL);  assert( result >= 0 );	result = ScriptManager::engine->RegisterGlobalBehaviour(asBEHAVE_SUBTRACT,       "Vector3 f(const Vector3 &in, const Vector3 &in)",    asFUNCTION((Wrap::Subtract<Ogre::Vector3,Ogre::Vector3&,Ogre::Vector3&>)), asCALL_CDECL);  assert( result >= 0 );	result = ScriptManager::engine->RegisterGlobalBehaviour(asBEHAVE_MULTIPLY,       "Vector3 f(const Vector3 &in, const Vector3 &in)",    asFUNCTION((Wrap::Multiply<Ogre::Vector3,Ogre::Vector3&,Ogre::Vector3&>)), asCALL_CDECL);  assert( result >= 0 );	result = ScriptManager::engine->RegisterGlobalBehaviour(asBEHAVE_DIVIDE,       "Vector3 f(const Vector3 &in, const Vector3 &in)",    asFUNCTION((Wrap::Divide<Ogre::Vector3,Ogre::Vector3&,Ogre::Vector3&>)), asCALL_CDECL);  assert( result >= 0 );	result = ScriptManager::engine->RegisterGlobalBehaviour(asBEHAVE_MULTIPLY,       "Vector3 f(const Vector3 &in, float)",    asFUNCTION((Wrap::Multiply<Ogre::Vector3,Ogre::Vector3&,float>)), asCALL_CDECL);  assert( result >= 0 );	result = ScriptManager::engine->RegisterGlobalBehaviour(asBEHAVE_DIVIDE,       "Vector3 f(const Vector3 &in, float)",    asFUNCTION((Wrap::Divide<Ogre::Vector3,Ogre::Vector3&,float>)), asCALL_CDECL);  assert( result >= 0 );	// Methods	result = ScriptManager::engine->RegisterObjectMethod("Vector3", "float normalise()", asMETHODPR(Ogre::Vector3, normalise, (), float),asCALL_THISCALL); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectMethod("Vector3", "void makeFloor( const Vector3& in )", asMETHODPR(Ogre::Vector3, makeFloor, (const Ogre::Vector3&), void),asCALL_THISCALL); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectMethod("Vector3", "void makeCeil( const Vector3& in )", asMETHODPR(Ogre::Vector3, makeCeil, (const Ogre::Vector3&), void),asCALL_THISCALL); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectMethod("Vector3", "bool isZeroLength() const", asMETHODPR(Ogre::Vector3, isZeroLength, (), bool),asCALL_THISCALL); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectMethod("Vector3", "Vector3 normalisedCopy() const", asMETHODPR(Ogre::Vector3, normalisedCopy, (), Ogre::Vector3),asCALL_THISCALL); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectMethod("Vector3", "Vector3 crossProduct( const Vector3& in ) const", asMETHODPR(Ogre::Vector3, crossProduct, (const Ogre::Vector3&), Ogre::Vector3),asCALL_THISCALL); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectMethod("Vector3", "Vector3 midPoint( const Vector3& in ) const", asMETHODPR(Ogre::Vector3, midPoint, (const Ogre::Vector3&), Ogre::Vector3),asCALL_THISCALL); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectMethod("Vector3", "Vector3 perpendicular() const", asMETHODPR(Ogre::Vector3, perpendicular, (), Ogre::Vector3),asCALL_THISCALL); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectMethod("Vector3", "Vector3 randomDeviant( float, const Vector3& in )", asMETHODPR(Ogre::Vector3, randomDeviant, (float, const Ogre::Vector3 &), Ogre::Vector3),asCALL_THISCALL); RakAssert(result>=0);	// Member vars	result = ScriptManager::engine->RegisterObjectProperty("Vector3", "float x", (int) offsetof(Ogre::Vector3, x)); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectProperty("Vector3", "float y", (int) offsetof(Ogre::Vector3, y)); RakAssert(result>=0);	result = ScriptManager::engine->RegisterObjectProperty("Vector3", "float z", (int) offsetof(Ogre::Vector3, z)); RakAssert(result>=0);	result = ScriptManager::engine->RegisterGlobalProperty("Vector3 ZERO", (void*) & Ogre::Vector3::ZERO); RakAssert(result>=0);	result = ScriptManager::engine->RegisterGlobalProperty("Vector3 UNIT_X", (void*) & Ogre::Vector3::UNIT_X); RakAssert(result>=0);	result = ScriptManager::engine->RegisterGlobalProperty("Vector3 UNIT_Y", (void*) & Ogre::Vector3::UNIT_Y); RakAssert(result>=0);	result = ScriptManager::engine->RegisterGlobalProperty("Vector3 UNIT_Z", (void*) & Ogre::Vector3::UNIT_Z); RakAssert(result>=0);	result = ScriptManager::engine->RegisterGlobalProperty("Vector3 NEGATIVE_UNIT_X", (void*) & Ogre::Vector3::NEGATIVE_UNIT_X); RakAssert(result>=0);	result = ScriptManager::engine->RegisterGlobalProperty("Vector3 NEGATIVE_UNIT_Y", (void*) & Ogre::Vector3::NEGATIVE_UNIT_Y); RakAssert(result>=0);	result = ScriptManager::engine->RegisterGlobalProperty("Vector3 NEGATIVE_UNIT_Z", (void*) & Ogre::Vector3::NEGATIVE_UNIT_Z); RakAssert(result>=0);	result = ScriptManager::engine->RegisterGlobalProperty("Vector3 UNIT_SCALE", (void*) & Ogre::Vector3::UNIT_SCALE); RakAssert(result>=0);}
If you want to add reference handling without wrapping all the operators, you can implement the script vector class as so:

class ScriptVector3 : public Ogre::Vector3{  public:    ScriptVector3();    void AddRef();    void Release();  protected:    int refCount;};


This will ensure that if the reference of ScriptVector3 is passed to a function it will look the same as the original Ogre::Vector3 class.

You cannot use any virtual methods when you do this, as that would add the virtual function table as the first DWORD of the object, thus changing the layout of the base Ogre::Vector3 object.

You can take a look at how I implemented asCScriptString, which is a wrapper like this on top of the std::string class. You'll find the code for that in add_on/scriptstring/

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

If I do that is refCount going to be incorrectly shallow copied with the assignment operators?
If it's copied to the base type, yes, it will be sliced. If the types involved are the derived type (which they would be in your code) then no.

I'm glad my wrapper templates worked out for you. They're especially useful when trying to register standard library types. And, sometimes, Angelscript just won't let you register a behavior with anything but thiscall or obj_last, even when they can be implemented in C++ as free function.

This topic is closed to new replies.

Advertisement