Game Development Community

Torquescript inheritance (foiled again)

by Alex Rice · in Torque Game Builder · 05/23/2006 (10:20 pm) · 18 replies

GetClassname() and isMemberOfClass() don't seem to have any notion of the class and superclass fields in a ScriptObject. From a "user" point of view, torquescript seems inconsistent about what is really class. In some cases it seems to be referring to the so called "namespace" path. In other cases it seems to be referring to the C++ base class. I am writing some custom Assertion functions. I guess I'll forget inspecting the classname at runtime and just check isObject() and hope for the best.

Quote:
new ScriptObject()
{
class = "MLEggsEggsControllerStateSpawn";
superclass = "MLState";
};
Quote:
==>2477.nosuchFn();
(0): Unknown command nosuchFn.
Object (2477) MLEggsEggsControllerStateSpawn -> MLState -> ScriptObject -> SimObject
(Called a bogus function to get Torque to print namespace path. OK this looks as expected)

==>2477.dumpClassHierarchy();
ScriptObject ->
SimObject ->
(Whoa- what about my class and superclass?)

==>echo(2477.getClassname());
ScriptObject
(Class is what?)

==>echo(2477.class);
MLEggsEggsControllerStateSpawn
(Class is what?)

==>echo(2477.superclass);
MLState
(Whew)

==>echo(2477.isMemberOfClass("MLState"));
0
(Guess again)

#1
05/23/2006 (10:42 pm)
You can make your own function that checks the isMemberofClass as well as the .class and .superclass fields.

class and superclass is really just a namespace, but namespace and superNamespace not only are very long, but even non-coders tend to understand the "class" word and get confused by the "namespace" work.
#2
05/23/2006 (10:44 pm)
function ScriptObject::isOfClass(%this, %class)
{
   if(%this.class $= %class || %this.superClass $= %class || %this.isMemberOfClass(%class))
   {
      return true;
   } else
   {
      return false;
   }
}
#3
05/23/2006 (11:13 pm)
Sounds reasonable- thanks Matthew
#4
05/23/2006 (11:32 pm)
This seems like a bug I have run into before: having an underscore in the scriptobject class name sometimes, apparently, breaks inheritance:

Quote:
==>2477.getClassname();
(0): Unknown command getClassName.
Object (2477) MLEggsEggsController_StateSpawn -> MLState -> ScriptObject -> SimObject
==>echo(isObject(2477));
1

[edit: apparently it has nothing to do with the underscore in the classname - something is just TOTALLY breaking inheritance and I can't even call the Simobject functions at all. ugh.]
#5
05/24/2006 (12:30 am)
God- I rediscovering the same bugs over and over. Somewhere there is an old thread talking about this. This is bug imho

This code works:

function MyClass_instance() 
{
 	if( ! $MyClass::singleton )
	{
		$MyClass::singleton = new ScriptObject() { class = "MyClass"; };
	}
	return $MyClass::singleton;
}
echo( MyClass_instance() );
echo( MyClass_instance().getClassName() );
echo( isObject( MyClass_instance() ));

This code (creating the object from a function in the same namespace) breaks the linkeage between the new object and it's parent class:

function MyClass::instance() 
{
 	if( ! $MyClass::singleton )
	{
		$MyClass::singleton = new ScriptObject() { class = "MyClass"; };
	}
	return $MyClass::singleton;
}
echo( MyClass::instance() );
echo( MyClass::instance().getClassName() );
echo( isObject( MyClass::instance() ));
#6
05/24/2006 (10:39 am)
Still see you using ScriptObjects...as I've mentioned before, for the most part they are no longer required to accomplish what you seem to be looking for.

Simply create them as the type of t2d object you need, and make sure that the .class and .superclass are assigned as part of the creation.
#7
05/24/2006 (10:46 am)
Thanks Stephen, but is that a workaround for the issues above? In this case I am using ScriptObjects because i'm writing a state machine so there is no real reason to inherit from a t2d object for these classes.
#8
05/24/2006 (2:28 pm)
Do you think that subclassing say t2dSceneObject instead of ScriptObject would be beneficial for other reasons? The class in question is a state for a finite state machine and doesn't have any on-screen representation?
#9
05/25/2006 (2:02 pm)
The two issues I have posted in this thread are bugs in the torquescript core, in my opinion. I hope they can be fixed for future release of torque. If you do not see why they are bugs, please let me know so I can post a more concise description of the defect.

@Matthew- thanks for that suggestion instead I just did SimObject::isOfClass(%this, %class) for convenience, and it's working well as a way to reliably tell if an object is a class or subclass.
#10
05/25/2006 (2:03 pm)
Could you give the outputs of the above code, both versions, as well as the script warning/error generated?
#11
05/25/2006 (2:16 pm)
@Stephen- you bet

Bug #1 "GetClassname() and isMemberOfClass() don't seem to have any notion of the class and superclass fields in a ScriptObject" I wrote the script and the console output in the first post of this thread. So that one should be good.

Bug # 2 "creating an object from a function in the same namespace breaks the linkeage between the new object and it's parent class" I will write a more concise script and show the console output and post it here later tonight.

Thanks as always for your suggestions and workarounds. I know I am more anal about OOP stuff than most torquescript developers. After all I think Eiffel is cool. It's arguably the most anal OOP language out there.
#12
05/25/2006 (2:17 pm)
Bug #1 also applies to t2dSceneObject, I believe I saw that in my testing.
#13
05/25/2006 (9:44 pm)
OK here is the deal with Bug #2. I made it as concise as possible. I think there is some corruption of the namespace lookup table, for lack of better description. As you will see it depends on the order in which the objects are created, and the name of the function that is creating the object. Here is the code, followed by replication.
Quote:function MyClass::construct()
{
return new ScriptObject() { class = "MyClass"; };
}

function MyClass_construct()
{
return new ScriptObject() { class = "MyClass"; };
}

function TestMyClass( %pId )
{
echo( "getID()" SPC %pId.getId() );
echo( "getClassname()" SPC %pId.getClassname() );
echo( "isObject()" SPC isObject(%pId) );
}
TO REPLICATE
1. Launch Torque (I am using TGB b4) and into console put
Quote:TestMyClass( MyClass::construct() ); ENTER
TestMyClass( MyClass_construct() ); ENTER
Output by console:
Quote:==>TestMyClass( MyClass::construct() );
MLEggs/gameScripts/Test.cs (14): Unknown command getId.
Object (2446) MyClass -> ScriptObject -> SimObject
getID()
MLEggs/gameScripts/Test.cs (15): Unknown command getClassName.
Object (2446) MyClass -> ScriptObject -> SimObject
getClassname()
isObject() 1
==>TestMyClass( MyClass_construct() );
MLEggs/gameScripts/Test.cs (14): Unknown command getId.
Object (2447) MyClass -> ScriptObject -> SimObject
getID()
MLEggs/gameScripts/Test.cs (15): Unknown command getClassName.
Object (2447) MyClass -> ScriptObject -> SimObject
getClassname()
isObject() 1
So clearly the object being returned is corrupted. It does not respond to members from SimObject (GetId() and getClassname() ) .
Now here is the really weird part. QUIT and restart Torque, then do the following (reverse order of function calls) . Et voila no more corruption of the namespace lookup.
Quote:
==>TestMyClass( MyClass_construct() );
getID() 2446
getClassname() ScriptObject
isObject() 1
==>TestMyClass( MyClass::construct() );
getID() 2447
getClassname() ScriptObject
isObject() 1
The problem is triggered by the creation of an object of a class, in a function with the same namespace (class). So if you changed MyClass::construct() to MyClassXXXXXXconstruct() the corruption would not occur.
#14
05/26/2006 (11:22 am)
Which is I think the root issue here...remember that TorqueScript is not c++, and it's not even truly "oop". I think you are simply using a technique that isn't fully supported in TorqueScript, but I've bumped this thread up to "higher authorities" to see if they have any additional thoughts.
#15
05/26/2006 (1:29 pm)
Thanks for doing that Stephen. I am getting to understand the scope & limitations of torquescript pretty well now, but the 2 issues listed here are well... still bugs in my eyes. Even if the Torque gods don't agree- at least I have the workarounds memorized now. Cheers
#16
05/27/2006 (12:15 am)
I don't think the second part is as much a bug as you are confusing the script compiler...


If I script either this

new ScriptObject(MyClass);

function MyClass::construct()
{
return new ScriptObject() { class = "MyClass"; };
}

function MyClass_construct()
{
return new ScriptObject() { class = "MyClass"; };
}

or this

new ScriptObject() { class = "MyClass"; };

function MyClass::construct()
{
return new ScriptObject() { class = "MyClass"; };
}

function MyClass_construct()
{
return new ScriptObject() { class = "MyClass"; };
}

It works fine...

I think the compiler is getting confused because the class hasn't been initialized (or namespace)... so when you first enter

"MyClass::construct()"

It's not calling the namespaced function of construct() on the class MyClass... its calling a function named "MyClass::construct()"...

then when it hits this line

"return new ScriptObject() { class = "MyClass"; };"

it borks out on the object creation/initialization since it's already identified "MyClass" as 'not' a namespace.

Now this is purely what I imagine is happening, though the responses seem to justify it...

now the reason why I don't think this is a bug in this context is because your calling a "namespaced" function before it's a namespace...

then again others may consider it a bug :) Good catch in any case, haven't run into this ever before.
#17
05/27/2006 (12:58 am)
Alex,

Re : Namespace Problems
The long and short of it is, as some people have mentioned, TorqueScript is not truly OOP. To help you better understand it, think of a namespace in c++, you might use a namespace to define a set of static functions that you could call like 'MyNS::getInstance()' etc. You however wouldn't however try to instantiate an object of that namespace, would you? It's the same principle here. Basically what it comes down to is that if you want to do namespace stuff like that, make sure it's either all on an object or all off an object. for example

function MyClass::Initialize() {};  -  Missing %this param - Not OK to call on an object

function MyClass::Initialize( %this ) {} - %this param - OK to call on an Object

in the first function there is no %this parameter, which means that if an object with a namespace link to that function calls it, the %this pointer is lost and bad things will happen. So just be consistent and use different namespaces to separate out your object functionality 'classes' and your singleton type 'namespace' stuff! you could do what I do and just repeat 'on object %this, static namespace functionality no %this' all day, but you'd probably get some weird looks like i did.

Re : getClassName and isMemberOfClass functions.
These functions are exposed by the engine (which is OOP) and as such have been exposed with these names. However, they are deceptive if you think of TorqueScript namespaces as being the same as true OOP classes. They are not, they are merely named .class and .superClass to be less confusing (that worked well!). getClassName returns the C++ class of the console object that you call it on. isMemberOfClass iterates up the C++ class hierarchy of an object trying to match the parameter class to one of the C++ classes.

That said, for your singleton problem i'd suggest you do something more like having a global named object to represent a singleton, then you can do isObject( EventManager ) checks to validate it. I've used this technique for a while in various projects and it's worked rather well for me. Something like

[b]//..game startup code..[/b]
if( !isObject( EventManager ) )
    new ScriptObject( EventManager ) { class="EventManagerClass"; };

[b]//..game shutdown code...[/b]
if( isObject( EventManager ) )
   EventManager.delete();

Hope this helps and remember to be kind to TorqueScript and it's namespaces, they're not truly OOP!

Best Regards,
-Justin
#18
05/27/2006 (10:53 am)
Matt and Justin- thanks for the suggestions it is helpful.