Issue with cyclic Import statements

BlitzMax Forums/BlitzMax Programming/Issue with cyclic Import statements

PGF(Posted 2005) [#1]
I can't find a 'clean' way around a problem where types need to refer to each other. BlitzMax complains about duplicate type definitions.

In a space simulation program, I have created T_Thing which is the base of all objects that can exist in the universe. One of its methods is Draw which takes a T_Viewport parameter. The T_Viewport type provides scaling functions that T_Thing needs in order to know how and where to draw itself.

T_Thing and T_Viewport are definied in separate files. Within T_Thing it is necessary to Import "T_Viewport.bmx" so that the type is available.

All is OK so far.

Now I want T_Viewport to be able to track a given T_Thing so that the object is at the centre of the display and this automatically updates to the latest object position each draw cycle.

I attempt to do this by adding a Field to T_Viewport called TrackThing which is of type T_Thing. Now T_Viewport must contain Import "T_Thing.bmx". But then BlitzMax complains about a duplicate definition of T_Viewport.

To simplify the example, consider the following which compiles without error as the two types are definined in the one file:

Version1.bmx
Strict

Type A

	Field Name:String
	Field Income:Int
	
	Field Friend:B
	
End Type

Type B

	Field Name:String
	Field Height:Float
	
	Field Associate:A
	
End Type


Now separate the types:

Version2A.bmx
Strict

Import "Version2B.bmx"

Type A

	Field Name:String
	Field Income:Int
	
	Field Friend:B
	
End Type


Version2B.bmx
Strict

Import "Version2A.bmx"

Type B

	Field Name:String
	Field Height:Float
	
	Field Associate:A
	
End Type


Trying to build this starting with file Version2A.bmx gives "Compile Error - Duplicate identifier "B".

In general, it appears that BlitzMax cannot provide two types defined in separate files that make reference to each other.

I can of course work around the problem by having something outside of the viewport set the centre X, Y from the object but it would be so much nicer for the viewport to be able to know what it is centred upon.

Has anybody seen a similar problem and either solved it or determined that this is a language limitation ?


Robert(Posted 2005) [#2]
Create abstract parent types and stick them in a separate file which T_Thing.bmx and T_ViewPort.bmx both import.


eg:

types.bmx:
Type ViewPort Abstract
  Method viewportMethod() Abstract
End Type
Type Thing Abstract
  Method thingMethod() Abstract
End Type


viewport.bmx:
import "types.bmx"

Type MyViewPort Extends ViewPort
'viewport code here - can include references to Thing
'objects.
End Type


thing.bmx:
import "types.bmx"

Type MyThing Extends Thing
'thing code here - can include references to ViewPort types
End Type



PGF(Posted 2005) [#3]
Are you saying to define all methods that are required to be accessed by the other type as abstract in types.bmx ?

Not sure how it will work with the quite deep hierarchy of objects that are derived from T_Thing but I'll give it a try.

Seems non-sensical compared to C# Using which can deal with cycles.

Thanks.


rdodson41(Posted 2005) [#4]
I had a problem like this recently. You can use Include instead of Import, it works fine, except it doesn't document the files correctly. It only documented the source file. Or at least this was true as of version 1.03, they might have fixed it in 1.04 with this new documenting system.


Robert(Posted 2005) [#5]
I don't know exactly how C# deals with cyclical imports, I'm still thinking in terms of C++ here.


Arowx(Posted 2005) [#6]
I've had a similar problem with a branching heirarchy where lower classes are duplicated, e.g.

A -> B -> C
A -> D -> C

Class A uses class B which uses class C
Class A also uses class D which also uses class C

When I used Include this produced the Duplicate Identity problem, however once I converted to Import it worked fine!

So make sure you try Import before you try the Abstract base class approach, it worked for me!


marksibly(Posted 2005) [#7]
Hi,

Import's can not be cyclic, so you are correct in saying that 2 types cannot mutually refer to each other via imports.

The solution to this is simple, if initially a little scary: 2 types which mutually refer to each other must appear in the same source file. You can do this either by physically placing the source code for the types into the same source file, or by using Include to produce a logically 'large' source file from several smaller source files. Indeed, this is the *real* reason Include is in there!

In my opinion - and I know this will be contentious - not allowing Import cycles is actually a very good thing. It gives you a very clear dependancy tree and allows for perfectly predictable startup/initialization behaviour, as any Imports are *guaranteed* to be fully initialized (ie: their 'main' code has run and returned) by the time you're initializing.

I have also found that it almost forces you into writer cleaner code, making you think about your file/object dependancies a bit more carefully and occasionally prodding you into a better design - but that may be my imagination!


PGF(Posted 2005) [#8]
Mark,

Yes - I long ago switched over to using Include rather than Import and it not only solves the reference problem but (surprisingly to me) compiles several times faster. I had thought that Import'ed modules would only be recompiled if need be thus saving time.

With due respect to your guru status vs my neophyte status, I'll disagree on the 'better design' bit. Compared to say C#, BlitzMax as it is today -forces- certain approaches and work-arounds that may be contrary to good design. Having to include cross-referenced but otherwise unrelated objects in one source file is an example.

While I'm making a fool of myself in front of the master I'll also disagree with your stated view against get/set accessors (aka properties).

These are so useful that it feels antiquated to work in an OO styled language that does not provide them. I think that you said something like 'linear execution time should be guaranteed' but surely that is up to the type implementor. And the alternative is to create GetXXX/SetXXX methods which can execute as much code as they want anyway if that is what getting or setting a property requires. So the work-around has the same performance implications.

Interfaces are another OO feature that I find highly desirable.

Ditto method overloading.

Ditto (and particularly) constructors with parameters.

I haven't seen anything recently about which if any of the above you plan to address and when. Perhaps some are in the pipeline and others you've considered and don't agree with the need for them.

Do you have any stated plans for the above ? Are you open to discussion about the relative merits ? I'd be more than happy to provide cases where I think they would be of great benefit. Of course you or some other wise head may show me why they are not useful at all in which case I and I suspect many other learners will benefit immensely.

BM is such an advance over B2D but being 'kind of OO' is like being 'kind of pregnant' - You want the other half !

PGF


Dreamora(Posted 2005) [#9]
What kind of interfaces are you missing?

The following is actually possible:
(my particle module does that to prevent users from playing around with values they should not)

strict

public

Type Test

 function create:Test ()
   return Test( TEST_IMP.create())
 end function

end type

private

Type TEST_IMP extends Test

 function create:TEST_IMP () ' <--- constructor with param can be done this way
  return new TEST_IMP
 end function
End type



This can even be extended with FINAL and ABSTRACT


PGF(Posted 2005) [#10]
Dreamora,

An interface is a formal way of declaring that a class provides a certain set of properties and methods. Instances of the class can then be passed to a method or function that expect something that supports the interface.

This is in addition to the class being able to inherit from a different class. One way to think of it is that it solves the problem of not having multiple inheritance by instead allowing a class to give a contract that it can do certain things. The concept of an interface is not supported in all OO languages but it is extremely useful.

In BM the syntax could be something like:
Interface I_Mappable
	...
End Interface

Type T_Place Implements I_Mappable
	...
End Type


Your example does show a constructor like approach with inheritance. Try adding parameters to the constructor and allowing for say TEST_IMP to have parameters that TEST does not.

Separate issue about constructors:

Here is an example of what I'd like to do:
Strict

Public

Type T_Place

	Field Name:String

	Function Create:T_Place (name:String)
		Local obj:T_Place = New T_Place
		
		obj.Name = name
		
		Return obj
	End Function

End Type

Private

Type T_City Extends T_Place

	Field Population:Int
	
	Function Create:T_City (name:String, population:Int)
		Local obj:T_City = New T_City
		
		obj.Name = name
		obj.population = population
		
		Return obj
	End Function
	
End Type

But you cannot have a method with the same same but different parameters and return type. So you end up changing the T_City constructor to be called say CreateCity. This gets ugly and unwieldy with a deep object hierarchy.

Also the derived type is having to initialise fields that belong to the base type. This again is more work and an opportunity for mistakes to happen.

Better would be something like:

Strict

Public

Type T_Place

	Field Name:String

	Function T_Place (name:String)
		
		this.Name = name
		
	End Function

End Type

Private

Type T_City Extends T_Place

	Field Population:Int
	
	Function T_City (name:String, population:Int) : base (name)
		
		this.population = population
		
	End Function
	
End Type

Here the constructors are just methods with the same name as the type. The object is implicity created and returned. Note also that the T_City constructor invokes its parent type constructor ': base(name)' passing the parameter that it requires. The derived type then does not need to set that field itself.

You could still have a different function that creates and returns a type if you want. Constructors are a special case.


Dreamora(Posted 2005) [#11]
The first sample is no problem. That is wherefore abstract is. Abstract are only for declaration, they can not be created.

type test abstract
...
end type

type test_imp extends test
' working implementation of test
...
end type


the second comes from missing overloading and is a really bad thing for inheritance, can only second that.

3rd example:
use method new for initialisation things. They are called for any of their base types.

type test
 method new ()
  print "test"
 end method
end type

type test_ex extends test
 method new ()
  print "trala"
 end method
end type


if you create an instance of test_ex you will see that both new will be called as the creation of test_ex needs an initialisation of test for extension.


btw: I found something very strange today in one of BRLs modules.

There you see that he calls a Typename with a value (TMax2DGraphicsDriver (g)) to create a type. But so far I'm not able to understand what it does there or how to use. If I try it, I always get the message "Illegal subexpression for object cast" ...

So somehow constructor with values seem to be possible.


PGF(Posted 2005) [#12]
No. You are missing the point with interfaces. They don't come from the class that you inherit from. They are a definition of an additional set of methods and fields that you can implement in any class irrespective of what it is derived from.

As I said, this allows you to overcome the fact that multiple inheritance is not available. It is a clean and natural way of doing what multiple inheritance would be useful for without the ugly issues.

In shorthand:

Interface I

Type A

Type B Extends A Implements I

Type C Extends A (but does not implement I)

Type P

Type Q Extends P Implements I

Type R Extends Q (and therefore also implements I)

So types B, Q and R all implement interface I and could therefore be passed to a method expecting something that implements I.

And don't tell me that P should de derived from A because that may not be possible or desirable. Accept that P and A may be completely unrelated but that you want them to be able to perform some set of things in common.

Examples of what an interface could be providing:

- Methods to save and load a type in a standard way where the types may be as unrelated as game settings, player and enemy units, window positions, high score table. Call this IPersist for example.

- Methods to update objects (say with gravity in the case of masses) and draw them to the screen where the objects may vary from ships to waypoints to information dialogs. Call this IUpdate.

- AI methods to say navigate to a position (INavigate) or control weapons (IWeaponControl) for anything from ships and missiles to ground bases.

And these could be different interfaces that objects implement zero or more of. Of course if a class implements an interface then so do the descendents of the class.

Try to build all this using a single inheritance hierarchy and you end up with an unwieldy and fragile hierarchy with nonsensical possibilities (like a window that can navigate itself or could be asked to fire upon a target). Build it as separate hierarchies but with common sounding methods but the same named methods are not in fact type compatible. You could not pass unrelated types as a parameter since they are incompatible.

Where it really comes into play is if somebody produces a library and publishes the interfaces. There will be lots of routines that they provide that accept parameters that implement various interfaces. Then YOU can implement the same interfaces in YOUR types and bingo the library can be applied to them as well. No need to derive your types from their base types.

The concept of interfaces is massively useful but if you haven't come across it before you'll think it is all solvable with Extends and Abstract.

At a guess, the reference to TMax2DGraphicsDriver(g) that you saw would be a cast or a test to see if g is of that type. The BM syntax for this is confusingly different to other languages that I use.

We agree on the lack of overloading being bad.

I know that the new method is called up the chain. My gripe is that since new does not allow parameters you can't set parameters like in my third code example but have to laboriously write CreateXYZ methods all the way up the chain. Have another look at the wishlist code. I think it is the most concise way of doing this. It is based on how C# works.


Dreamora(Posted 2005) [#13]
No it is not, TMax2DGraphicsDriver (g) is definitely a creator.

And your way of interfaces can be done with a little more work of "inbetween classes" if you have "a extends b implements i" you would just create a class x which is similar to be but extends i

its actually not really hard, just a little more work ...
everything else does not work due to missing multi-inheritance. otherwise it would be no problem with extending of real and abstract (interface) types

I know where single inheritence ends up here, was facing this problems with my little physics fun game that I will show somewhen in the near future ...


PGF(Posted 2005) [#14]
I give up.

Either I can't explain the benefits of interfaces or you can't understand them. Probably a mixture of both.

It would be a different matter if you understood them but could mount an argument that they are not necessary or desirable in BM.

Like trying to explain the concept of Blue to somebody who can only see in Red and Green. You keep thinking that Blue and all of the colours with a component of Blue are just combinations of Red and Green.

In max2d.bmx the only reference that I can see to what you refer to as 'TMax2DGraphicsDriver (g)' is 'TMax2DGraphics(g)' in the Graphics function partially shown below.

It is not a creator.

It is a cast of variable g which is declared and initialised in the first statement shown. If in the inheritence path of g there is a type TMax2dGraphics then the cast returns the instance of that type, otherwise it returns Null.

Function Graphics( width,height,depth=16,hertz=60 )
	...
	Local g:TGraphics=CreateDisplayGraphics( width,height,depth,hertz,0 )
	If Not g
		Throw "Unable to create graphics"
	EndIf
	
	displayGraphics=TMax2DGraphics(g)
	If Not displayGraphics 
		g.Close
		Throw "Current graphics driver is not a Max2D driver"
	EndIf
	...
End Function

So unless you have another bit of code in mind I think you'll have to agree that it is a cast ?

First time I saw somthing like this I did a double take myself as I'm more familiar with a cast such as '(TMax2DGraphics)g' which is the inverse syntax or a compatibility test such as 'if (g is TMax2DGraphics) ...'.

BTW - You can see some of the internals of BM dealing with types and how this cast works in file BlitzMax\mod\brl.mod\blitz.mod\blitz_object.c

...
BBObject *bbObjectDowncast( BBObject *o,BBClass *t ){
	BBClass *p=o->clas;
	while( p && p!=t ) p=p->super;
	return p ? o : &bbNullObject;
}
...

A very neat walk up the inheritence chain.

I find this bit of BM syntax a bit strange but hey it is consistent and it works. If you learnt BM first then tried say C# you'd wonder why it was so strange !

Peace Dreamora.


Tibit(Posted 2005) [#15]
PGF you really made me want intefaces! Seems like the best thing ever! Never knew what it was before. Great explination there.

So now that we don't have them the only work-around I can see it to use Var? Like this: AIUpdate(X Var, Y Var). I don't know but it kinda feels like an allmighty interface. Now I confused myself =) So what is the difference or benefits from interfaces now? I'mean I know nothing of interfaces than what you said above (very interresting btw) so..

And the blitz_object example = ?Err is that max or basic?

your way of interfaces can be done with a little more work of "inbetween classes"

Dreamora can you explain (with an simple example) how? Because I really want to know.

Another thing, why can't the new method accept arguments?


Dreamora(Posted 2005) [#16]
Has not much use if I try to show you a kind of creator if you don't want to look in the module I pointed out to see that it is actually some kind of constructor.


PGF(Posted 2005) [#17]
Wave,

It is much more than passing a parameter. You can pass an object that the receiver can expect will provide fields and methods according to the interface definition.

Ordinarily a function or method parameter will be of some type and the receiver can operate on that type or any type that is derived from it.

With an interface this changes to the receiver being able to operate on any type that implements the interface.

'  Define an interface which gives the method signatures but does not provide their implementation
interface INavigate
--method TakeShortestRoute(from:Place, to:Place)
--method TakeSafestRoute(from:Place, to:Place)
--method TakeScenicRoute(from:Place, via:Place, to:Place)

' Define types that implement the interface
type TShip implements INavigate
--method ShortestRoute(from:Place, to:Place)
{ ' Some clever code appropriate to ship navigation}
etc

type TBird implements INavigate
--method ShortestRoute(from:Place, to:Place)
{ ' Some clever code appropriate to bird navigation}
etc

type TTank implements INavigate
--method ShortestRoute(from:Place, to:Place)
{ ' Some clever code appropriate to tank navigation}
etc

type TRoadBuilder INavigate
--method ShortestRoute(from:Place, to:Place)
{ ' Some clever code appropriate to a machine and crew building a road}
etc

type TPilot
--method PlanCourse(INavigate ride)
' Expects something that can navigate by one of several means and applies logic to decide which route is best

And yes, in this example you could derive everything from a single type that defines virtual methods then override with the actual methods but the point with interfaces is that you don't have to. There is no good design reason that I can see to have a tank, a bird and a road laying machine all derive from some super type when just about everything else that they do will be different.

Add other capabilities like refueling, weapons control and saving/loading object state. It becomes difficult to impossible to come up with a sensible hierarchy but remains manageable to design it using interfaces. Remember that you may want to give non-navigable types like a fixed gun-turret weapons control and just about everything including game settings and windows will need saving/loading object state.

Then you want to add stealth mode for some types and allegiance and build cost and experience etc. And pass all of these types around to common draw, file and AI routines.

Then somebody develops a custom skinning manager that can take an object if it supports the ISkinnable interface.

The blitz_object code fragment is C or C++ which is the language in which a lot of the internals of BM is written. Amazingly and quite helpfully a lot of these internals are exposed to you if you go looking.

This particular bit is how the 'down cast' operates.

Suppose the the type hierarchy is:

type A
type B extends A
type C extends B
type D extends C

You have an instance of D called myD.

You want to cast myD to type B for example. Or even just see if myD is derived from it.

What the code does is take your instance as parameter o and the desired type as parameter t. It checks the type of o to see if it is the same as t (it isn't but if it was it would return the instance).

If the type is not the same then it walks up the hierarchy to check the next higher object/type (the 'super'). Eventually it hits the matching type B and returns the object.

If it ran right up the hierarchy without finding a match then it returns a null.

---------------------------------------------------------
Dreamora,

Originally you said:


btw: I found something very strange today in one of BRLs modules.

There you see that he calls a Typename with a value (TMax2DGraphicsDriver (g)) to create a type. But so far I'm not able to understand what it does there or how to use. If I try it, I always get the message "Illegal subexpression for object cast" ...

So somehow constructor with values seem to be possible.


You did not point to a module but quoted 'TMax2DGraphicsDriver (g)' so I went looking. There is no such text but I did find 'TMax2DGraphics(g)' in module max2d.bmx and showed the relevant section then explained that it is a cast not a constructor.

Perhaps I didn't find the right bit of code ?

If you can really provide the module and function name for this bit of code that shows a constructor with a parameter then please do so. I'd be very interested to see it.

It is Ok to make a mistake but pretty immature to get snotty and attempt to misrepresent the situation when your little misunderstanding gets corrected.


Tibit(Posted 2005) [#18]
Dreamora, The first thing I did was to go look for it but unfortunatle I didn't find it.Tell me where or post, and I'll look at it.

PGF, ok I'm starting to get it. I do really want interfaces in Bmax. I especially see the gain when using external modules. And to extend a type is not an option in many cases because a type can only extend one other type. And that's the limit I don't like. Because wouldn't an abstract type (if a type could extend several types) be like an interface then? Any type could implement it and it's functions? Wouldn't "extend multiple" be better? Because then I could do the INavigate interface from an non-abstract type and by that have default-methods and I would only need to override those that I want to be unique for my Tank,Car,Ship. I assume an Interface is like an blueprint your type can use.


Now I got an idea for an workaround considering your last example =) and I hope what it will give the same result with a slightly longer implentation time. Let's say you have an abstract Type INavigate with some methods and variables and such.

Any type could incorporate it in a field. Type TBird Field Nav:INavigate.

When you got that and you want to pass the object to the pilot: Function Pilot( N:INavigate ) then you go
Pilot(Bird.Nav) and the pilot has access to all Navigation functions for Bird. The downside is that you have to make several derivied versions of navigate, one for TBird, one for TTank, one for TShip... Which would result in one navigate function for each type that implements the navigate protocol.
Now it this case the INavigation Type field Nav in Bird would not reach the birds X,Y and such. Therefore you have to add a Get Method to Navigate which could in this case accept a TMoveObject or using Var for X,Y. It would be longer but it seems doable and the result seems to me to be quite similar, wouldn't it?

I'll try to put together an example on this.. brb [EDIT - seems liek I don't ;) ]


PGF(Posted 2005) [#19]
Wave,

Your idea can no doubt be made to work after a fashion. One complication is that the INavigate type held as a field can't access other fields and methods of the host type except by getting it as a parameter and again having to incorporate code to determine the type and treat it specially.

You should want all of the type specific code to be implemented within the type rather than in other types.

Another approach is to type the parameter as just an object. Then have a big test for each type that you want to implement the psuedo interface, cast to that type and then invoke the method.

Type TPilot

Method PlanCourse:TCourse(object ride)

  Local ship:TShip = TShip(ride)
  if (ship)
    if (ship.Aggressive) return ship.TakeShortestRoute(from, to)
    return ship.TakeSafestRoute(from, to)
  endif

  Local tank:TTank = TTank(ride)
  if (tank) return tank.TakeShortestRoute(from, to)

  Local road:TRoadBuilder = TRoadBuilder(ride)
  if (road) return road.TakeScenicRoute(from, via, to)

  throw "Type not supported by TPilot"

end method

Although the types implement methods with the same name and parameter types they are not the same in terms of the method signature because they belong to unrelated types.

Can sort of work but has a lot of drawbacks compared to properly supported interfaces:

- More code to write
- Less 'type safety'. With an interface you don't need the testing and default exception because only types that implement the interface can be passed as a parameter. The checking is at compile time rather than run time.
- Doesn't help with external modules where you can't add code to support your additional types

As complexity rises in a project, I've found that interfaces become increasingly important to keep the design clean and flexible while also keeping code size down immensely.

There are basic OO things that I'd give higher priority to than interfaces as BM matures (see my reply to Mark on 2005-06-18). I do hope though that interfaces could be implemented somewhere along the track.

TNet sounds good. At a guess, something like the Smooth Sync feature would be ideal to implement as an interface. You might say have to implement a method in objects to predict their near term position and another to resolve differences between the local and remote copies.


Tibit(Posted 2005) [#20]
I'll look more into a good work-around that I can use in TNet because interfaces would be quite on the spot. But I would like one type to be able to extend more than one, I don't see why this should be limited? Is it possible to use pointers to make interfaces from scratch?


PGF(Posted 2005) [#21]
Pointers - Not really although you might use them to simulate or approximate something like interfaces as discussed before.

Interfaces are really a language feature needing support of the compiler and runtime.

Multiple inheritence (MI) is allowed in some languages - C++ being one example. Many people regard it as dangerous, mainly because of the difficulty it can lead to in determining exactly which parent class will provide a given property or method. The capability no doubt has its uses but can lead to 'spaghetti inheritence' if poorly implemented.

I'm going from hearsay as I avoid C++ in favour of C#. But people who I know who are very experienced with C++ agree that it is a potential minefield and instead use interfaces to resolve the issues that MI can also address while avoiding its danger. This is on large commercial projects and they pretty much ban any use of MI.

In contrast Java and C# which are (IMHO) more evolved and rational than C++ do not allow MI but provide interfaces as a cleaner and safer solution.

OOP (especially with interfaces), garbage collection, strong typing and a decent IDE is such a powerful programming environment - productive and enjoyable.


Tibit(Posted 2005) [#22]
Now I see why java doesn't have MI. That has always puzzled me =) Very good to know.


Dreamora(Posted 2005) [#23]
Its in Max2D (as anyone would assume for graphics), function graphics ()
and G is of type TGraphics, not TGraphicsDriver as you will see there.

Perhaps someone is able to help figuring out how this thing works :)


PGF(Posted 2005) [#24]
Yep

max2d.bmx is where I found it as shown and explained in my post of 2005-06-20.

Once more in a bit more detail.

g is a Local variable of type TGraphics. It is declared and set to the value returned by CreateDisplayGraphics(...) in the line:

Local g:TGraphics=CreateDisplayGraphics( width,height,depth,hertz,0 )

If the value returned is null then the next little test will throw an exception
If Not g
	Throw "Unable to create graphics"
EndIf

displayGraphics is a Global variable declared earlier in this module. It is of type TMax2dGraphics. Importantly TMax2dGraphics is derived from type TGraphics.
Type TMax2dGraphics Extends TGraphics

You can see the definition of TMax2dGraphics earlier in this module while the definition of TGraphics is in graphics.bmx.

Now comes the bit that apparently confuses you.
displayGraphics=TMax2DGraphics(g)

Here TMax2DGraphics() is a function call. Each type has an implicitly defined function like this that performs type casting. It tries to find a type in the inheritence path of the given parameter that matches the type in question.

If successful then the object will be returned. If not the function returns null.

So then displayGraphics now either refers to an object of type TMax2dGraphics or it is null.

Note that this is not a constructor because no object has been creeated. The object will have been created previously as the object of the derived type was itself created.

The code continues with testing whether the cast was sucessful and if so using it to prepare the graphics.

One final bit - the fact that CreateDisplayGraphics can return a value that is compatible with TGraphics but is actually of the more derived type TMax2dGraphics is just part of the basic mechanics of OO.

And that is it. Just proper and quite clever OO code. Not a constructor with a parameter although we can all hope for that to be introduced one day in a future version.


Charles(Posted 2005) [#25]
Decorator and Visitor patterns, for composition and examples as to inject behavior into objects.. if I ever find time I had thought to try and write examples using Design Patterns in BlitzMax - that would also help explain some of what's being discussed.


Charles(Posted 2005) [#26]
Just out of curiosity, PGF, what brought you to use BlitzMax as opposed to Java with JoGL or LWJGL, or C++, or C#.. ?


PGF(Posted 2005) [#27]
Charles,

Mainly the chronology.

A few years ago I wanted to start some graphics oriented 'hobby' programming and found Blitz2D which was just about ideal. Easy, quick and powerful.

Later on I learned C# for a commercial project.

I bought BlitzMax because it overcomes some major limits of Blitz2D (realtime image scaling and rotation, alpha blending) and it is heading in the OO direction. Thankfully it retains the easy startup and sort of safety net feeling that BASIC languages should have.

Yes - I have considered whether to just go with C# for graphics and games stuff. You lose a bit of 'ease' but gain a lot of power and third party tools. For now though I enjoy the contrast between the languages.

A lot of impressive tools and libraries have been released already for BlitzMax and I think and hope that it will quickly close some of the gaps that might lean me towards C#. Hence my interest in the core language adopting some of the missing OO concepts.

Regards.


Charles(Posted 2005) [#28]
An option I am starting to like (as another additional language) is "D" :

http://www.digitalmars.com/d/


PGF(Posted 2005) [#29]
I've heard good things about D but have not tried it myself.

What are the key differences to C# ?

Apart from the language itself, the available class libraries and third party tools really determine how useful a language is for a given application.


Charles(Posted 2005) [#30]
There is a comparison : http://www.digitalmars.com/d/comparison.html


PGF(Posted 2005) [#31]
Good info thanks.


Jay Kyburz(Posted 2005) [#32]
I'd like to see that list with a BMax colum :)