Type of..

BlitzMax Forums/BlitzMax Programming/Type of..

Brendane(Posted 2006) [#1]
Is there, or can we have, built-in 'type of'/'type is' type functions so I could do something like this :-

Function DoSomething( o:Object )
   If o.TypeOf( TShip )  ' this will return true if my object is a TShip or derived from that.
   ElseIf o.TypeIs( TPlane ) ' returns true only if it is a TPlane
   EndIf
EndFunction


I can write this myself, (if it doesn't exist) but it requires me to create an instance to compare against, I'd rather the compiler generate it automagically.

Perhaps we have it but I can't find anything like this in the docs.

[Edit] Ok, I can downcast a derived type to see if it is a TypeOf, but what about detecting whether a type IS...


ziggy(Posted 2006) [#2]
it has to be this way:
Function DoSomething( o:Object )
   If Tship(o)<>null  ' this will return true if my object is a TShip or derived from that.
   ElseIf Tplane(o)<>null ' returns true only if it is a TPlane
   EndIf
EndFunction


each type is a type cast sencente.
Example:
Type a
method Play()
end Type

Type B extends A
Field X
End Type

Local Var1:B = new B
Local Var2:B = B(Var1)


Var1 and Var2 are pointing to the same instance of B


Brendane(Posted 2006) [#3]
Hi, thanks. Yes, I forgot you can downcast to detect whether is it a TypeOf something. But knowing whether an object IS a particular type is something else I need.

I can do this by checking the 'class' member in the object structure, but this requires that I create an instance to compare against. I had hoped there was a built in method.


Brendane(Posted 2006) [#4]
- and I think you meant..

Local Var1:B = new B  
Local Var2:A = A(Var1)


;)


Dreamora(Posted 2006) [#5]
Brendane: If you create the object yourself, I would suggest that you add a field class:string to them in which you define the class (or class:int if you only have a few for faster select-ing) and that you create a core class which only has this field which you extend.

then its a fast and easy thing to retrieve the class :)


Brendane(Posted 2006) [#6]
Thanks Dreamora, that is probably the cleanest solution for me given that there is no built-in method and these are my own types.

For those interested, here is another method which checks the class pointer in the object itself.

SuperStrict

Type typea
EndType

Type typeb Extends typea
EndType


' Returns True if the 2 types are objects of exactly the same type
' Expects 2 parameters to be objects derived from 'Object'
Function AreSameType:Int( o1:Byte Ptr, o2:Byte Ptr )
	Local _op1:Int Ptr = Int Ptr(o1)
	Local _op2:Int Ptr = Int Ptr(o2)

	' The byte ptr cast returns a pointer to the first type field - and not to the object itself
	' The object contains a pointer to a class type followed by a ref count followed by the 
	' type's fields.
	Return _op1[-2] = _op2[-2]
EndFunction


Local a:typea = New typea
Local b:typeb = New typeb

If AreSameType( New typea, a )
	Print "A is a typea"
Else
	Print "A is not a typea"
EndIf

If AreSameType( New typea, b )
	Print "B is a typea"
Else
	Print "B is not a typea"
EndIf



Fabian.(Posted 2006) [#7]
For those interested
Well, I've read this and really got interested. I've put it into a module and added some more features; for those who are very interested: http://www.blitzbasic.com/codearcs/codearcs.php?code=1786


Dreamora(Posted 2006) [#8]
Definitive nice collection of functions mentioned here on the boards in the past :-)

*although, type size isn't needed, thats wherefor sizeof() already exists ^^*


Fabian.(Posted 2006) [#9]
There's a difference between the variable's type and the runtime type.
SizeOf returns the size of the variable's type and TypeSize returns the size of the runtime type:
Strict
Framework brl.blitz

Import brl.standardio
Import pub.TypeSystem

Local O:Object = New T
Print SizeOf O ' get the size of the variable's type (this is 0 because Object has no fields)
Print TypeSize ( ObjType ( O ) ) ' get the size of the runtime type (this is 12 because the type contains a Long and a Int)

Type T
  Field L:Long
  Field I
EndType



skidracer(Posted 2006) [#10]
Fabian, I would rather people didn't post code in the archives that will work differently in debug/release, completeley break the garbage collector and be specific to a particular version of BlitzMax as it doesn't exactly make our jobs easier if people actually use such functions.


Fabian.(Posted 2006) [#11]
work differently in debug/release
Well, if you'd like me to do so I'll add the "?Debug"/"?" syntax around the TypeName function. Then the TypeName function would be like the DebugStop function: normal use in debug mode - ignored in release mode.
completeley break the garbage collector
Except the ObjCopy function they surely don't.
be specific to a particular version of BlitzMax
Yes, you're right. I'd like to start my code with "Strict V1.20" so that the compiler gives an compile-time error if it doesn't support V1.20. Then there'd be no runtime errors. But unfortunately BMX doesn't support it.


Brendane(Posted 2006) [#12]
Nice one Fabian. Here's another one ;)

If you want to write some module code which reads a BMax string but don't want to create a WString or a CString (for efficiency sake) - you can get the pointer to the start of the string data (it's not Null terminated in a bmax string remember so you have to depend on the length value. And it's unicode of course) :-

' Return the start of the string text (expects a string argument)
Function _textPtr:Short Ptr( o:Object )
	Local p:Byte Ptr = Byte Ptr(o)
	Return Short Ptr( p+4 )
EndFunction


I used this in my regular expression mod since it's much more efficient to use the source string instead of creating/destroying a copy to pass to the c functions.

I do have a query about this though. I wrote it like this because I can't perform an Int Ptr() or an Object() cast on a String type. Yet I can pass a string through to a function which expects an Object... why?


Dreamora(Posted 2006) [#13]
Because String is extended from Object (while numberics aren't).

That Int Ptr to something does not work is normal. It is not typesafe and BM will refuse it with any kind of ptr.

That object -> string does not work sounds strange. Why? Because you couldn't load anything if it didn't work as all bm own functions for loading use it ...

BUT: You can't do it String(object). The class object has a method called "toString()" which you have to use ... or simply assign the object to a string as it is called implicitely normally.
You can implement this method on your own types as well if you want them to have a "string return" for example if you try to print/debuglog the object :-)


Fabian.(Posted 2006) [#14]
Thanks a lot; I've done some changes: TypeName never throws an exception (except you passed an invalid type ptr); it returns the type pointer converted to a string marked with "*" if not in debug mode. Also I've added a GetStringPtr function(same as Brendane's _textPtr function), but the returned pointer should be only for READ ONLY access. For exmaple:
Strict
Framework brl.blitz

Import brl.standardio
Import pub.TypeSystem

Local Text$ = "Hello, world!"
Print String.FromShorts ( GetStringPtr ( Text ) , Len Text ) ' simply copies a string to test whether the new function works properly
Thanks a lot for the new ideas!


Brendane(Posted 2006) [#15]
Nice work Fabian, thanks.

@Dreamora : Sorry, I meant I can't cast String to Byte Ptr, nor to Object. This is strange for two reasons. First, String is an extension of Object so an Object cast should be allowed (in fact it is via a function parameter). Second, all other Objects can be cast to Byte Ptr, so why not String? I'm not at my PC right now but I'll check whether String really is an Object when I next have chance.


Fabian.(Posted 2006) [#16]
You can upcast a string to an object and then get the byte ptr. Upcasting String to Object is allowed so String is really an Object. But it looks strange to me, too, that you have to convert to Object before getting the byte ptr.
However I wouldn't call this a bug because we can still get this value:
Local P:Byte Ptr = Byte Ptr Object "Test"



Brendane(Posted 2006) [#17]
Ah, thanks my mistake. I was sure I'd tried a cast to Object and it threw an exception, but clearly it works fine.

Thanks for that.


Dreamora(Posted 2006) [#18]
I would assume that the straight Byte Ptr does not work because Strings work a little different from regular objects.

If you do String = String + "a", you get a new string, not a modified version of the string (ie behaves as java strings)
For that reason a pointer is a little critical to a string as the string just won't be there anymore after the next change and you better use ToCString / ToWString if you need a pointer as this creates an unmanaged copy of the current string.

Or create yourself a StringBuffer object (think someone did that already but don't nail me on it) a la java


Brendane(Posted 2006) [#19]
Yes, all true, but doing this:-

string = string + "a"

is no different to doing this:-

ptr = Byte Ptr( obj )
obj = anotherObj

Now the original object is due for garbage collection and ptr is going to be pointing at something that may or may not be there. It's just about being careful when you use pointers.

I think that Byte Ptr( string ) should be allowed just like any other object. It's about flexibility and most of all consistency.


Dreamora(Posted 2006) [#20]
The problem is, it is not the same.

On a regular object, lets say a TVector, you can change, x,y,z without breaking the reference. On a string, a modification will destroy the pointer.
For that reason if you want to have pointer to string, better implement StringBuffer or just use CString until you need it as BM string for some reason.


You shouldn't be using Byte ptr within BM anyway, they aren't meant for that after all (why Object Ptr already was removed and such hack arounds like posted on the other thread will be disabled at some point. I think Byte Ptr should only be accepted as type within Extern - EndExtern block, nowhere else in a BM source to "encapsulate" it as C# does with old code).
They are for interfacing with C / ASM imports.

If you like pointer hick hack, C++ is the far better way to go.


Brendane(Posted 2006) [#21]
It isn't exactly the same no, in the sense that calling a method of a string which modifies the string will recreate a new string, that's the compilers doing and it's frankly irrelevant.

There's nothing hick-hack about pointers - what is this thing you have with C++? I program in C++ every day, that's my day job. I can write buggy code in BMax FAR easier than in C++ - why? Because the documentation SUCKS and it's left to me to analyse the compiler output or the module code to find out how the bleeding thing works.

I need these pointers in order to make an efficient job of calling c-functions which use them. I don't want my code to be creating/destroying a peice of data 1000 times when it already exists just because some other people are afraid of pointers.

Shouldn't be using a pointer in BM? Why not? I am perfectly able to understand the concept of the lifetime of a peice of data and its structure and write my (bug free) code accordingly. If BMax was meant to cater solely for programming novices it would have been called Dark Basic.


tonyg(Posted 2006) [#22]
You shouldn't be using Byte ptr within BM anyway
Not use byte ptr in BM?
How would we use CreateStaticPixmap, BankBuf, CreateRamStream, CreateStaticBank, IncBinPtr, LockBank, MemAlloc (Mem<anything> for that matter) to name but a few?
<edit>
I think Byte Ptr should only be accepted as type within Extern - EndExtern block, nowhere else in a BM source to "encapsulate" it as C# does with old code

Taken from 1.18 release notes...
It is still legal to convert an object reference to a 'Byte Ptr', but you should be aware the pointer will only be valid while the object remains 'in scope'.
So Mark even advocates using byte ptrs.


Dreamora(Posted 2006) [#23]
Erm tonyg, all those commands you mention ARE for interfacing with C or ASM.

MemAlloc as well as BankBuf are very good examples for stuff thats main target is to access data from within BM outside of BM source (sending vertex arrays to something external or what do I know)

Show me just one reason to use them within BM.

MemAlloc -> TBank
Stream -> TStream

I don't say that Ptr have no use, how would you use C imports otherwise on a usefull level for example.
But I would try to get around them within BM and use the GC based stuff as most operations are optimized for that way.


tonyg(Posted 2006) [#24]
I must be misunderstanding
I think Byte Ptr should only be accepted as type within Extern - EndExtern block, nowhere else in a BM source to "encapsulate" it as C# does with old code).


How is the use of Byte Ptr in the commands I listed related to your suggested use? They're certainly not within extern/endextern.
How is CreateStaticPixmap, for example, 'interfacing with C or ASM' (unless you mean at some point ALL the commands 'interface' with C/ASM)
How do Mark's comments from the release notes equate to your suggestion?
Where did you get the information to support the statement

You shouldn't be using Byte ptr within BM anyway, they aren't meant for that after all (why Object Ptr already was removed and such hack arounds like posted on the other thread will be disabled at some point.




Dreamora(Posted 2006) [#25]
Just forget it.

If you don't want understand the importance of using only managed within a managed environment, ok. Be happy with it but please NEVER ask questions why something crashes, why you get funny memory leaks and the like if you are using it.

Interfacing with C means that you use Byte Ptr just on the C call you externed, not within your BM only code. Alternative would be something to $z / $w which is a "safe" bridge to extern with C strings instead of ToCString which creates a copy you have to free manually *and that most forget as they think it is only a pointer to the actual string which isn't possible due to changing ref as String != StringBuffer*

And CreateSTaticPixmap as a mather of fact is for interfacing with C. You use a pixeldata memory block of which you have the byte ptr to create a pixmap out of it.
Creating that memory block within BM is only possible through unmanaged (TBanks bankptr not counted) normally memalloc which is C.


Brendane(Posted 2006) [#26]
I think Dreamora is forgetting that BlitzMax is targetted at games programmers, who, historically are keen on getting down to the low level, interfacing to devices etc. and getting the best out of the hardware.

We don't want to be limited by language constraints; quite the opposite.

My original question was to query the runtime object type, something apparently lacking in the language. If I want to do this (especially with types I do not have control over) I have to get at the underlying structure somehow, or write higher level wrappers around everything - I know which I prefer.


Brendane(Posted 2006) [#27]
Where is this $z / $w actually documented please?


Brendane(Posted 2006) [#28]
Ok, I have checked the compiler output to see exactly what it does.

$z simply inserts a ToCString() and pushes the result as the parameter to the function call, then calls MemFree after the call. $w performs the same operation with ToWString().

Do it yourself or use the built-in method, either way it's inefficient if your string is large and you call the same function hundreds of times on the same string.

Isn't this documented at all? I can't find it.