|
|
[SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
-
10-08-2006, 8:59 PM |
-
John Schultz
-
-
-
Joined on 06-24-2005
-
Beverly Hills, CA
-
Posts 241
-
-
|
[SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
|
-
10-09-2006, 12:36 AM |
-
Kamaitati
-
-
-
Joined on 01-13-2006
-
-
Posts 47
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
Cool!
There's only issue with SqPlusConst.h - it doesn't compile with these changes.
To fix it it is needed to change SqClassDef to SQClassDefBase
and fix file parts incusion macros by changing
#ifdef SQPLUS_CALL_CONST_MFUNC_RET1
#undef SQ_REG_CONST_STATIC_VAR
to
#ifdef SQPLUS_CALL_CONST_MFUNC_RET1
#undef SQPLUS_CALL_CONST_MFUNC_RET1
and
#ifdef SQ_REG_CONST_STATIC_VAR
template
to
#ifdef SQ_REG_CONST_STATIC_VAR
#undef SQ_REG_CONST_STATIC_VAR
template
|
|
-
10-09-2006, 7:41 AM |
-
Project5
-
-
-
Joined on 12-15-2005
-
-
Posts 81
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
Nice :-) I have an implementation suggestion for SquirrelObjects construction using bound classes. In the test, there's a second parameter if you want to create by reference instead of value: SquirrelObject v(Vector3(1.f,2.f,3.f),true); // Pass in by reference instead of by copy ('true' arg). Only one temporary Vector3() is created and released.
When using SqPlus to bind functions, if a function argument isa pointer, SqPlus will use a reference to that object. In that vein, the second parameter to the constructor could be removed if you make the user pass a pointer rather than an object:
SquirrelObject v(Vector3(1.f,2.f,3.f)); // Pass in by value Vector3 vec3(1.f,2.f,3.f); SquirrelObject v(&vec3); // Pass in by reference
I suggest this approach because it more closely follows how SqClassDef.func() works. --Ben
|
|
-
10-09-2006, 11:25 AM |
-
John Schultz
-
-
-
Joined on 06-24-2005
-
Beverly Hills, CA
-
Posts 241
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
Kamaitati- thanks, fixed.
Ben- I changed the Get/Set methods to the following to allow pass-by-value (only required when using constant types), pass by reference (now the default, data will be copied (once) and be managed by Squirrel), and pass by pointer (pointer used directly: no data copying nor will Squirrel manage the memory).
void testSquirrelObjectSetGet(void) { // We can pass in arguments: // by value ('true' arg, required for constant float, int, etc., or when a copy is desired), // by reference (data will be copied to SquirrelObject and memory managed), // by pointer (no data copying: pointer is used directly in SquirrelObject; the memory will not be managed).
SquirrelObject tc(5.678f,true); // constant argument is passed by value ('true' arg), memory will be allocated and managed for the copy.
float val = 1.234f; SquirrelObject t(val); // val is passed by reference, memory will be allocated, and the value copied once.
float val2 = t.Get<float>();
scprintf(_T("Val2 is: %f\n"),val2);
if (1) { #if 0 SquirrelObject v(Vector3(1.f,2.f,3.f),true); // Pass in by value ('true' arg): note how many times the object is created, copied, and destroyed. #else SquirrelObject v(Vector3(1.f,2.f,3.f)); // Pass in by reference instead of by copy. One temporary Vector3() is created, copied, and released. #endif
Vector3 * pv = v.Get<Vector3 *>(); scprintf(_T("Vector3 is: %f %f %f\n"),pv->x,pv->y,pv->z); pv->z += 1.f;
if (1) { SquirrelObject v2p(pv); // This is a pointer to v's instance (passed in by pointer: see SquirrelObject.h). It will not get freed when v2p goes out of scope. pv = v2p.Get<Vector3 *>(); scprintf(_T("Vector3 is: %f %f %f\n"),pv->x,pv->y,pv->z); } // if
} // if
scprintf(_T("Vector3() instance has been released.\n\n"));
} // testSquirrelObjectSetGet
|
|
-
10-09-2006, 12:26 PM |
-
Project5
-
-
-
Joined on 12-15-2005
-
-
Posts 81
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
John- Thanks, having that more in line with SqClassDef is great :-)
EDIT: wow, pretty solid mistake on my part, I didn't see that it was only for constant objects. I'l leaving my original post here, and my relevant suggestion is after the ////////////////////////// below.
One other suggestion, looking at two of your three use cases:
// by value ('true' arg, required for constant float, int, etc., or when a copy is desired), // by reference (data will be copied to SquirrelObject and memory managed),
I'm hard pressed to really tell the difference between these two, as passing an object in by reference still copies it (by value) into Squirrel to be memory managed there. As their functionality is the same, presenting the user with two different functions that accomplish the same task seems a little combersome.
Looking into why the by-value case is required, it only seems to be for the integral types that you mentioned. Since there are only a few of those, template specialization seems to be a solution:
SquirrelObject { ... template<class T> Set(const T& t) { // Unchanged, this copies the referenced object into Squirrel. } template<>
Set<float>(const float& t) {
// Anything special to make floats work
} template<>
Set<int>(const int& t) {
// Anything special to make ints work
} //etc }
That way, SquirrelObject::Set() would just present the user with setting by reference or by pointer, instead of a third option for specific types.
////////////////////////// //////////////////////////
Now knowing that it's for constant objects, template specialization doesn't work at all in that case. My mistake :-)
There is a method to have only two functions though. Having these two functions side by side lets the compiler pick the proper one depending on the constness of the object being passed in:
template<class T> Set(T& t) { // Unchanged, this copies the referenced object into Squirrel. }
template<class T> Set(const T& t) { // A const object is passed in, so copy by value. }
Because the compiler recognises const T and T as two seperate types, function overloading solves the problem of choosing the proper Set method for you. This way, you're down to only two functions.
Let me know what you think :-)
--Ben
|
|
-
10-09-2006, 12:36 PM |
-
John Schultz
-
-
-
Joined on 06-24-2005
-
Beverly Hills, CA
-
Posts 241
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
Updated release 25:
void testSquirrelObjectSetGet(void) { // We can pass in arguments: // by value ('true' arg, required for constant float, int, etc., or when a copy is desired), // by reference (data will be copied to SquirrelObject and memory managed), // by pointer (no data copying: pointer is used directly in SquirrelObject; the memory will not be managed).
SquirrelObject tc(5.678f); // constant argument is passed by value (even though declaration is by ref: const & results in by-value in this case), memory will be allocated and managed for the copy. float valc = tc.Get<float>();
scprintf(_T("Valc is: %f\n"),valc);
float val = 1.234f; SquirrelObject t(val); // val is passed by reference, memory will be allocated, and the value copied once.
float val2 = t.Get<float>();
scprintf(_T("Val2 is: %f\n"),val2);
if (1) { SquirrelObject v(Vector3(1.f,2.f,3.f)); // Pass in by reference: will be copied once, with memory for new copy managed by Squirrel.
Vector3 * pv = v.Get<Vector3 *>(); scprintf(_T("Vector3 is: %f %f %f\n"),pv->x,pv->y,pv->z); pv->z += 1.f;
if (1) { SquirrelObject v2p(pv); // This is a pointer to v's instance (passed in by pointer: see SquirrelObject.h). // A new Squirrel Instance will be created, but the C++ instance pointer will not get freed when v2p goes out of scope (release hook will be null). pv = v2p.Get<Vector3 *>(); scprintf(_T("Vector3 is: %f %f %f\n"),pv->x,pv->y,pv->z); } // if
} // if
scprintf(_T("Vector3() instance has been released.\n\n"));
} // testSquirrelObjectSetGet
|
|
-
10-14-2006, 4:48 PM |
-
aeyr
-
-
-
Joined on 09-24-2006
-
-
Posts 101
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
Say, John, I've run into this problem a couple times, and actually had to work around it. Why does wrapping a C++ managed instance call the squirrel side constructor?
That is (using the new method above),
Vector3 v(1,2,3); Vector3 *pv = &v; SquirrelObject v2p(pv); // This is a pointer to v's instance (passed in by pointer: see SquirrelObject.h).
In this case, the squirrel-side instance uses the Vector3() constructor "call" to create a new instance. This isn't needed nor desired when wrapping a pointer. It should just call sq_newinstance() with the corresponding class name etc, which skips the constructor call.
And, on a related note, if you make this change, along with this new way of holding object values, it also "fixes" static class constants/enums:
I.e. change this: SQClassDef & constant(int constant,const SQChar * name) { RegisterInstanceConstant(newClass,ClassType<TClassType>::type(),constant,name); return *this; } // constant
into something like this: SQClassDef & constant(const SquirrelObject &constant,const SQChar * name) { HSQUIRRELVM vm = SquirrelVM::GetVMPtr(); int top = sq_gettop(vm); sq_pushobject(vm, newClass.GetObjectHandle()); sq_pushstring(vm, name, -1); sq_pushobject(vm, constant.GetObjectHandle()); sq_newslot(vm, -3, false); // Must be false here or it's treated as a method sq_settop(vm, top); return *this; } // constant
And some example bindings (assuming proper T -> SquirrelObject overrides are in place):
// classDef... .constant(1.0f, _SC("CONST_A")) .constant(MyClass::CONST_B, _SC("CONST_B")) .constant(&MyClass::INSTANCE_CONST, _SC("INST_CONST")) .etc... // (note, the last one is a pointer to an instance constant, which should Just Work since it's a C++ managed pointer)
This gets around having to have an instance to get at the static values. I.e. you could do MyClass.CONST_A instead of MyClass().CONST_A in squirrel. Savings in both performance and "proper" syntax.
|
|
-
10-14-2006, 5:39 PM |
-
John Schultz
-
-
-
Joined on 06-24-2005
-
Beverly Hills, CA
-
Posts 241
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
aeyr,
Place a breakpoint at SqurrelObject v2p(pv), as well as Vector3()'s constructor; trace the code with the debugger- you'll see that only a new Squirrel instance is created, and Vector3()'s constructor is not called. As for the Squirrel side constructor being called, according to the docs for sq_createinstance() (note that sq_newinstance() does not exist), call() must be used to call the constructor (trace the code: if the Squirrel side constructor was called, a new C++ instance would also be created, calling the C++ constructor). See SquirrelBindingsUtils.cpp::CreateNativeClassInstance().
re: new method for constants- have you tested creating new instances without any issues? What happens if you assign a new value to a (new style) constant?
|
|
-
10-17-2006, 12:00 PM |
-
aeyr
-
-
-
Joined on 09-24-2006
-
-
Posts 101
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
John Schultz:
See SquirrelBindingsUtils.cpp::CreateNativeClassInstance().
Hm, looking again, I see that you're correct. CreateNativeClassInstance doesn't call the squirrel class constructor. I distinctly remember seeing that same code with a commented out sq_createinstance() and a call to the constructor in its place, however. I must've been looking at an old version somewhere I guess, as I had to write my own version of CreateNativeClassInstance because of it. Oh well, looks good now! John Schultz: re: new method for constants- have you tested creating new instances
without any issues? What happens if you assign a new value to a (new
style) constant?
Well, further testing reveals the problem I had encountered before when binding instances of a class to the class being defined. Squirrel "locks" the class definition as soon as the first instance is created, thus there is one annoying restriction: you can't bind an instance of the class you're binding (nor even create an instance of it before completing the binding process). Which is kind of annoying, but otherwise the system works great (I'm using it currently). And in fact, the templatized version of the above looks like: template<typename ConstantType> SQClassDefBase & constant(ConstantType constant, const SQChar *name) { HSQUIRRELVM vm = SquirrelVM::GetVMPtr(); int top = sq_gettop(vm); sq_pushobject(vm, newClass.GetObjectHandle()); sq_pushstring(vm, name, -1); SqPlus::Push(vm, constant); sq_newslot(vm, -3, false); // Must be false here or it's treated as a method sq_settop(vm, top); return *this; } I think a check could be added that will check the constant's type (TypeInfo<ConstantType>().typeName) name against the newClass's type name and disallow matches (just to give a more informative error message than whatever runtime error is generated later due to the class being locked, such as "Could not create UserData").
|
|
-
10-17-2006, 5:42 PM |
-
aeyr
-
-
-
Joined on 09-24-2006
-
-
Posts 101
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
aeyr: John Schultz:
See SquirrelBindingsUtils.cpp::CreateNativeClassInstance().
Hm, looking again, I see that you're correct. CreateNativeClassInstance doesn't call the squirrel class constructor. I distinctly remember seeing that same code with a commented out sq_createinstance() and a call to the constructor in its place, however. I must've been looking at an old version somewhere I guess, as I had to write my own version of CreateNativeClassInstance because of it. Oh well, looks good now!
Ahah, I found the routine I was looking at before: CreateConstructNativeClassInstance. This bad boy is called in order to return an instance to squirrel. Here's where calling the constructor was/is a problem for me since it's entirely possible it has no constructor defined (or the constructor doesn't allow 0 arguments).
|
|
-
10-31-2006, 11:10 AM |
-
Project5
-
-
-
Joined on 12-15-2005
-
-
Posts 81
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
Hey John, I was just looking into the implementation of the SquirrelObject::Set() implementation, and I had a question for you in regards to simplification.
SquirrelObjects have three templated constructors: /////////////////////////////////////////// template <typename _ty> SquirrelObject(const _ty & val) { sq_resetobject(&_o); Set((_ty &)val); } template <typename _ty> SquirrelObject(_ty & val) { sq_resetobject(&_o); Set(val); } template <typename _ty> SquirrelObject(_ty * val) { sq_resetobject(&_o); SetByValue(val); } ///////////////////////////////////////////
While SquirrelObject::Set() has two implementations: /////////////////////////////////////////// // Set any bound type to this SquirrelObject. Note that Squirrel's handling of references and pointers still holds here. template<typename _ty> inline SquirrelObject SetByValue(_ty val) { // classes/structs should be passed by ref (below) to avoid an extra copy. SqPlus::Push(SquirrelVM::_VM,val); AttachToStackObject(-1); sq_poptop(SquirrelVM::_VM); return *this; }
// Set any bound type to this SquirrelObject. Note that Squirrel's handling of references and pointers still holds here. template<typename _ty> inline SquirrelObject Set(_ty & val) { SqPlus::Push(SquirrelVM::_VM,val); AttachToStackObject(-1); sq_poptop(SquirrelVM::_VM); return *this; } ///////////////////////////////////////////
For the use of SetByValue() in the constructor, you mention that Set() could also be used in its place, but isn't to avoid potential compiler overhead. Things can be simplified greatly were it removed:
It if could be removed, there could just be two constructors. Because both the non constant pointer and reference constructors would both use Set(), according to the comment, they'd look like this: /////////////////////////////////////////// template <typename _ty> SquirrelObject(const _ty & val) { sq_resetobject(&_o); Set((_ty &)val); } template <typename _ty> SquirrelObject(_ty & val) { sq_resetobject(&_o); Set(val); }
template <typename _ty> SquirrelObject(_ty * val) { sq_resetobject(&_o); Set(val); } ///////////////////////////////////////////
Note that Set's function signature looks like this: template<typename _ty> inline SquirrelObject Set(_ty & val) So effectively, calling the third constructor above passes Set() a reference to a pointer, and it still works correctly.
The key here is that when the compiler looks at <typename _ty>, _ty is an object pointer, making Set accept a reference to an object pointer.
If this works for Set(), it would also work for the constructors. This means that the third constructor is unnecessary, the second constructor can just do the work of handling pointers by handling references to pointers in the same way that Set() does. So now the constructors can just look like this: ///////////////////////////////////////////
template <typename _ty> SquirrelObject(const _ty & val) { sq_resetobject(&_o); Set((_ty &)val); }
template <typename _ty> SquirrelObject(_ty & val) { sq_resetobject(&_o); Set(val); }
///////////////////////////////////////////
I'm suggesting this because it removes two functions that are effectively duplicated elsewhere.
Additionally, looking at the first constructor, adding support for Set() for constant types is as simple as adding: /////////////////////////////////////////// template<typename _ty> inline SquirrelObject Set(const _ty & val) { return Set((_ty&)val); } ///////////////////////////////////////////
I've made these changes in my copy of SquirrelObject.h, and testSqPlus2 runs correctly.
Let me know what you think,
--Ben
|
|
-
11-05-2006, 10:43 PM |
-
John Schultz
-
-
-
Joined on 06-24-2005
-
Beverly Hills, CA
-
Posts 241
-
-
|
Re: [SqPlus] Abstract Class/Pure-virtual-interface support added, Squirrel Get/Set objects added
Hi Ben,
I just got my computer back online after moving. Given the many requests to update/change SqPlus and my limited time to work on it, it's probably a good time to put the source code on SourceForge and allow the community to update/manage the changes via SVN. It would also be a good idea to have two or three Squirrel+SqPlus experts to look over/manage community submitted changes.
If anyone is interested in setting up a SF SVN repo and/or managing SqPlus changes, please contact me via email (from my profile).
Thanks,
John
|
|
|
|