BBC BASIC for Windows
« Object Class library - CLASSLIB »

Welcome Guest. Please Login or Register.
Apr 5th, 2018, 9:58pm



ATTENTION MEMBERS: Conforums will be closing it doors and discontinuing its service on April 15, 2018.
Ad-Free has been deactivated. Outstanding Ad-Free credits will be reimbursed to respective payment methods.

If you require a dump of the post on your message board, please come to the support board and request it.


Thank you Conforums members.

BBC BASIC for Windows Resources
Online BBC BASIC for Windows documentation
BBC BASIC for Windows Beginners' Tutorial
BBC BASIC Home Page
BBC BASIC on Rosetta Code
BBC BASIC discussion group
BBC BASIC for Windows Programmers' Reference

« Previous Topic | Next Topic »
Pages: 1 2  Notify Send Topic Print
 veryhotthread  Author  Topic: Object Class library - CLASSLIB  (Read 263 times)
admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Object Class library - CLASSLIB
« Thread started on: Apr 21st, 2011, 4:51pm »

From time to time I have been asked to provide better support for Object Orientated (OO) programming in BBC BASIC. There have also been a few discussions about this elsewhere, for example:

http://www.riscosopen.org/forum/forums/2/topics/71
http://xania.org/200711/irbasic

These have tended to assume changes to the language itself, and since I am no longer actively developing BB4W such an approach is not attractive. I therefore decided to see what could be achieved using the existing language in conjunction with a 'clever' library. I am pleased to announce the initial release of the Object Classes library CLASSLIB:

http://groups.yahoo.com/group/bb4w/files/Libraries/CLASSLIB.BBC

A key concept in OO languages is the Class. A Class is rather like a structure, except that as well as encapsulating several data items ('members') it also encapsulates the functions used to operate on those items ('methods'). Hence the data and the functions associated with those data are tightly bound together:

http://en.wikipedia.org/wiki/Class_%28computer_programming%29

The Class acts as a prototype from which 'instances' of the class ('objects') are created. Each object has an independent copy of the members.

CLASSLIB supports the main features of the Class/Object paradigm. The file includes details of how it is used, but here is a simple example. We will create a Class representing a circle and the operations one might want to perform on a circle such as calculating its area and its circumference. The first step is to create a regular BB4W structure:

Code:
      DIM Circle{radius, set_radius, get_area, get_circumference} 

Here 'radius' is the data associated with the Class and 'set_radius', 'get_area' and 'get_circumference' are methods which operate on that data. The user may wish to choose a naming convention to make it clear which are the 'members' and which are the 'methods', but CLASSLIB doesn't care.

We now define the various methods, which is done using a variant of the regular DEF statement:

Code:
      DEF Circle.set_radius (r) Circle.radius = r : ENDPROC
      DEF Circle.get_area = PI * Circle.radius^2 
      DEF Circle.get_circumference = 2 * PI * Circle.radius 

Having created the structure and defined the methods we now register it as a class:

Code:
      PROC_class(Circle{}) 

We could also have specified a 'constructor', but for simplicity this example doesn't use one. We can create one or more instances of the class as follows:

Code:
      PROC_new(mycircle{}, Circle{}) 

This creates an object 'mycircle{}' on which we can perform operations:

Code:
      PROC(mycircle.set_radius)(10.0)
      PRINT "Area is "; FN(mycircle.get_area)
      PRINT "Circumference is "; FN(mycircle.get_circumference) 

Note that something interesting is going on here. When the methods are called the member 'Circle.radius' is automatically substituted with the object data 'mycircle.radius'. This is what makes it possible to have multiple concurrent objects all sharing the same methods.

Finally when we've finished with the object we release the memory it occupied:

Code:
      PROC_discard(mycircle{}) 

Enjoy!

Richard.
User IP Logged

softweir
New Member
Image


member is offline

Avatar




PM


Posts: 12
xx Re: Object Class library - CLASSLIB
« Reply #1 on: Apr 22nd, 2011, 01:39am »

Very interesting and ingenious!

Richard Weir.
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Object Class library - CLASSLIB
« Reply #2 on: Apr 22nd, 2011, 9:17pm »

Another feature provided by most Object Orientated languages is that of 'inheritance'. This allows a subclass to inherit the members and methods from a parent class; this Java example explains it quite well:

http://download.oracle.com/javase/tutorial/java/concepts/inheritance.html

I have updated the CLASSLIB library (v0.5) to support inheritance:

http://tech.groups.yahoo.com/group/bb4w/files/Libraries/CLASSLIB.BBC

Here is a simple example of how inheritance can be used. Suppose we have already created a class to describe an ellipse, with a method to return its area:

Code:
    DIM Ellipse{a, b, set_size, get_area}
    DEF Ellipse.set_size (a,b) Ellipse.a = a : Ellipse.b = b : ENDPROC
    DEF Ellipse.get_area = PI * Ellipse.a * Ellipse.b
    PROC_class(Ellipse{}) 

Now suppose we want to create a class for a circle. We could start from scratch, but a circle is a special case of an ellipse so instead we can create a Circle class which inherits from the Ellipse class:

Code:
    DIM Circle{get_circumference}
    PROC_inherit(Circle{}, Ellipse{})
    PROC_class(Circle{}) 

The class inherits the get_area method from its parent, but the Circle class has an extra get_circumference method. For convenience we can override the inherited set_size method with a new version which takes only one parameter (the radius):

Code:
    DEF Circle.get_circumference = 2 * PI * Circle.a
    DEF Circle.set_size (r) Circle.a = r : Circle.b = r : ENDPROC 

Now we can use the Circle class:

Code:
    PROC_new(mycircle{}, Circle{})
    PROC(mycircle.set_size)(10.0)
    PRINT "Area is "; FN(mycircle.get_area)
    PRINT "Circumference is "; FN(mycircle.get_circumference)
    PROC_discard(mycircle{}) 

I hope I have whetted your appetite for Object Orientated programming in BBC BASIC, and look forward to seeing some practical applications.

Richard.
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Object Class library - CLASSLIB
« Reply #3 on: May 1st, 2011, 4:10pm »

Has anybody tried using CLASSLIB yet?

Those with some previous experience of Object Oriented Programming may have been a little surprised at the syntax I adopted for what in other OO languages is called 'This' (e.g. Java), 'Self' (e.g. Python) or 'Me' (e.g. Visual Basic):

http://en.wikipedia.org/wiki/This_%28computer_programming%29

With CLASSLIB you use the name of the class itself rather than 'This', 'Me' etc. For example to calculate a factorial using a recursive method you could do:

Code:
      INSTALL @lib$+"CLASSLIB"
      
      DIM Factorial{calculate}
      DEF Factorial.calculate (n) IF n<=1 THEN =n ELSE = n*FN(Factorial.calculate)(n-1)
      PROC_class(Factorial{})
      
      PROC_new(myfact{}, Factorial{})
      PRINT "Factorial 6 = ";FN(myfact.calculate)(6)
      PRINT "Factorial 7 = ";FN(myfact.calculate)(7)
      PROC_discard(myfact{}) 


I initially tried to use a special name such as '@This' but it wasn't compatible with being compiled (with crunching enabled).

Below I've listed a simple Object Oriented 'Towers of Hanoi' program (you will need the latest CLASSLIB, version 0.6, for it to run successfully); it uses recursive method calls.

Richard.

Code:
      INSTALL @lib$+"CLASSLIB"
      OFF
      
      DIM Hanoi{peg(3), @, solve, take, put, show}
      PROC_class(Hanoi{})
      
      PROC_new(puzzle{}, Hanoi{})
      WAIT 100
      PROC(puzzle.solve)(12,1,2,3)
      PROC_discard(puzzle{})
      REPEAT UNTIL INKEY(1)=0
      
      DEF Hanoi.@ LOCAL n : FOR n=12 TO 1 STEP -1 : PROC(Hanoi.put)(n,1) : NEXT : ENDPROC
      DEF Hanoi.take (n,p) Hanoi.peg(p)-=1 : COLOUR 143 : PROC(Hanoi.show)(n,p) : ENDPROC
      DEF Hanoi.put (n,p) COLOUR 128+n : PROC(Hanoi.show)(n,p) : Hanoi.peg(p)+=1 : ENDPROC
      DEF Hanoi.show (n,p) PRINT TAB(26*p-n-10, 20-Hanoi.peg(p)) SPC(2*n); : ENDPROC
      DEF Hanoi.solve (a,b,c,d) IF a=0 THEN ENDPROC
      PROC(Hanoi.solve)(a-1,b,d,c)
      PROC(Hanoi.take)(a,b)
      PROC(Hanoi.put)(a,c)
      PROC(Hanoi.solve)(a-1,d,c,b)
      ENDPROC 
« Last Edit: May 1st, 2011, 4:18pm by admin » User IP Logged

Michael Hutton
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 248
xx Re: Object Class library - CLASSLIB
« Reply #4 on: May 9th, 2011, 02:31am »

Quote:
Has anybody tried using CLASSLIB yet?

here goes...

Functions to duplicate objects would be good... Could using structure arrays be possible <thinking aloud before going to bed> to create multiple objects of the same type but easily referenced.. but I feel that is not in the 'spirit' of OOP...rather than having to type in the name for each... hmm.. yawn.. tired... making no sense... ZZzzzzzzzzzzzzzz

I am sure there is a better way of writing this...

Code:
      REM A random Walk through Object Biomorph land...
      REM Michael Hutton May 2011
      REM based on Biomorphs by Mike Cook (c) Micro user
      REM Works in demo version of BB4W

      MODE 8
      INSTALL @lib$+"CLASSLIB"
      OFF
      
      g_Index% = -1
      
      DIM Biomorph{index, gene(5), generation, x, y, @, _draw, _grow, _mutate, _givegenes}
      PROC_class(Biomorph{})
      
      PROC_new(p{}, Biomorph{})
      PROC_new(a{}, Biomorph{})
      PROC_new(b{}, Biomorph{})
      PROC_new(c{}, Biomorph{})
      PROC_new(d{}, Biomorph{})
      PROC_new(e{}, Biomorph{})
      PROC_new(f{}, Biomorph{})
      PROC_new(g{}, Biomorph{})
      PROC_new(h{}, Biomorph{})
      PROC_new(i{}, Biomorph{})
      PROC_new(j{}, Biomorph{})
      PROC_new(k{}, Biomorph{})
      PROC_new(l{}, Biomorph{})
      
      *REFRESH OFF
      ON ERROR OSCLI"REFRESH ON" : PRINT REPORT$ : ON : PROC0
      
      Stop% = FALSE
      REPEAT
        K$ = INKEY$(1)
        CASE K$ OF
          WHEN "s","S" : Stop% = TRUE
          WHEN "r","R" : REM erm.. to do : reset objects
          WHEN " " : REPEAT: UNTIL INKEY(1) = 32
        ENDCASE
        
        REM Mutate the children (!)
        PROC(a._mutate):PROC(b._mutate):PROC(c._mutate):PROC(d._mutate):PROC(e._mutate):PROC(f._mutate)
        PROC(g._mutate):PROC(h._mutate):PROC(i._mutate):PROC(j._mutate):PROC(k._mutate):PROC(l._mutate)
        
        CLS
        REM Draw Biomorphs
        PROC(p._draw)
        PROC(a._draw):PROC(b._draw):PROC(c._draw):PROC(d._draw):PROC(e._draw):PROC(f._draw)
        PROC(g._draw):PROC(h._draw):PROC(i._draw):PROC(j._draw):PROC(k._draw):PROC(l._draw)
        *REFRESH
        
        REM Choose a random Parent from the children
        CASE RND(12) OF
          WHEN 1: PROC(a._givegenes)(p{})
          WHEN 2: PROC(b._givegenes)(p{})
          WHEN 3: PROC(c._givegenes)(p{})
          WHEN 4: PROC(d._givegenes)(p{})
          WHEN 5: PROC(e._givegenes)(p{})
          WHEN 6: PROC(f._givegenes)(p{})
          WHEN 7: PROC(g._givegenes)(p{})
          WHEN 8: PROC(h._givegenes)(p{})
          WHEN 9: PROC(i._givegenes)(p{})
          WHEN 10: PROC(j._givegenes)(p{})
          WHEN 11: PROC(k._givegenes)(p{})
          WHEN 12: PROC(l._givegenes)(p{})
        ENDCASE
        
        PROC(p._givegenes)(a{})
        PROC(p._givegenes)(b{})
        PROC(p._givegenes)(c{})
        PROC(p._givegenes)(d{})
        PROC(p._givegenes)(e{})
        PROC(p._givegenes)(f{})
        PROC(p._givegenes)(g{})
        PROC(p._givegenes)(h{})
        PROC(p._givegenes)(i{})
        PROC(p._givegenes)(j{})
        PROC(p._givegenes)(k{})
        PROC(p._givegenes)(l{})
        
      UNTIL Stop%
      
      *REFRESH ON
      ON
      
      DEF PROC0
      
      PROC_discard(p{})
      PROC_discard(a{})
      PROC_discard(b{})
      PROC_discard(c{})
      PROC_discard(d{})
      PROC_discard(e{})
      PROC_discard(f{})
      PROC_discard(g{})
      PROC_discard(h{})
      PROC_discard(i{})
      PROC_discard(j{})
      PROC_discard(k{})
      PROC_discard(l{})
      
      END
      
      DEF Biomorph.@
      g_Index% += 1
      Biomorph.index   = g_Index%
      Biomorph.gene(0) = 5
      Biomorph.gene(1) = 20
      Biomorph.gene(2) = .785
      Biomorph.gene(3) = 1
      Biomorph.gene(4) = 0
      Biomorph.gene(5) = 0
      Biomorph.generation = 0
      IF g_Index% THEN
        Biomorph.x = 640 + (400*SIN(g_Index%*PI/6))
        Biomorph.y = 512 + (400*COS(g_Index%*PI/6))
      ELSE
        Biomorph.x = 640
        Biomorph.y = 512
      ENDIF
      ENDPROC
      
      DEF Biomorph._draw
      D = Biomorph.gene(0)
      L = Biomorph.gene(1)
      dA = Biomorph.gene(2)
      AR = Biomorph.gene(3)
      DT = Biomorph.gene(4)
      DS = Biomorph.gene(5)
      X = Biomorph.x
      Y = Biomorph.y
      MOVE X,Y
      DRAW X,Y-L
      PROC(Biomorph._grow)(PI/2,L,X,Y,D)
      ENDPROC
      
      DEF Biomorph._grow (TH,L,X,Y,D)
      IF D MOVE X,Y ELSE ENDPROC
      dX=L*COS(TH+dA)*(1/AR)
      dY=L*SIN(TH+dA)*AR
      PLOT 1,dX,dY
      PROC(Biomorph._grow)(TH+dA+DT,L-DS,X+dX,Y+dY,D-1)
      MOVE X,Y
      dX=L*COS(TH-dA)*(1/AR)
      dY=L*SIN(TH-dA)*AR
      PLOT 1,dX,dY:MOVE X,Y
      PROC(Biomorph._grow)(TH-dA-DT,L-DS,X+dX,Y+dY,D-1)
      ENDPROC
      
      DEF Biomorph._mutate
      LOCAL I%, S%, f
      I% = (Biomorph.index-1)DIV2
      IF (Biomorph.index MOD 2) THEN S% = 1 ELSE S% = -1
      f = RND(1)
      CASE Biomorph.index OF
        WHEN 1:
          Biomorph.gene(0) -= 1
          IF Biomorph.gene(0) =0 THEN Biomorph.gene(0) = 1
        WHEN 2:
          Biomorph.gene(0) += 1
          IF Biomorph.gene(0) > 8 THEN Biomorph.gene(0) = 8
        WHEN 3,4:
          Biomorph.gene(1) += RND(1)-0.5
          IF Biomorph.gene(1)<1 THEN Biomorph.gene(1) = 1
          IF Biomorph.gene(1)>40 THEN Biomorph.gene(1) = 40
        WHEN 5,6:
          Biomorph.gene(2) += (RND(1)-0.5)*0.5
          IF Biomorph.gene(2)>2*PI THEN Biomorph.gene(2) -= 2*PI
          IF Biomorph.gene(2)<0 THEN Biomorph.gene(2) += 2*PI
        WHEN 7,8
          Biomorph.gene(3) += (RND(1)-0.5)*0.5
          IF Biomorph.gene(3)<0.1 THEN Biomorph.gene(3) = 0.1
          IF Biomorph.gene(3)>10 THEN Biomorph.gene(3) = 10
        WHEN 9,10:
          Biomorph.gene(4) += (RND(1)-0.5)*0.5
          IF Biomorph.gene(4)>2*PI THEN Bimorph.gene(2) -= 2*PI
          IF Biomorph.gene(4)<0 THEN Biomorph.gene(2) += 2*PI
        WHEN 11,12:
          Biomorph.gene(5) += (RND(1)-0.5)*0.5
          IF Biomorph.gene(5)<-18 THEN Biomorph.gene(5) = -18
          IF Biomorph.gene(5)>18 THEN Biomorph.gene(5) = 18
      ENDCASE
      
      ENDPROC
      
      DEF Biomorph._givegenes (t{})
      LOCAL I%
      FOR I%=0TO5
        t.gene(I%) = Biomorph.gene(I%)
      NEXT
      ENDPROC

 
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Object Class library - CLASSLIB
« Reply #5 on: May 9th, 2011, 2:58pm »

on May 9th, 2011, 02:31am, Michael Hutton wrote:
Functions to duplicate objects would be good...

I'm not too sure why you would want to, but I think you can duplicate an object as follows:

Code:
      DIM Copy{} = Object{}
      Copy{} = Object{}
      PROC_class(Copy{}) 

Richard.
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Object Class library - CLASSLIB
« Reply #6 on: May 9th, 2011, 5:08pm »

on May 9th, 2011, 02:31am, Michael Hutton wrote:
Could using structure arrays be possible

Again, I don't think you need any modifications to the library to achieve that. The only issue is how to create the structure array in the first place; one rather inelegant way is this:

Code:
      REM A random Walk through Object Biomorph land...
      REM Michael Hutton May 2011
      REM based on Biomorphs by Mike Cook (c) Micro user
      REM Works in demo version of BB4W
      
      MODE 8
      INSTALL @lib$+"CLASSLIB"
      OFF
      
      g_Index% = -1
      
      DIM Biomorph{index, gene(5), generation, x, y, @, _draw, _grow, _mutate, _givegenes}
      PROC_class(Biomorph{})
      
      DIM morph{(12) dummy&}
      
      FOR I% = 0 TO 12
        PROC_new(morph{(I%)}, Biomorph{})
      NEXT
      
      *REFRESH OFF
      ON ERROR OSCLI"REFRESH ON" : PRINT REPORT$ : ON : PROC0
      
      Stop% = FALSE
      REPEAT
        K$ = INKEY$(1)
        CASE K$ OF
          WHEN "s","S" : Stop% = TRUE
          WHEN "r","R" : REM erm.. to do : reset objects
          WHEN " " : REPEAT: UNTIL INKEY(1) = 32
        ENDCASE
        
        REM Mutate the children (!)
        FOR I% = 1 TO 12
          PROC(morph{(I%)}._mutate)
        NEXT
        
        CLS
        REM Draw Biomorphs
        FOR I% = 0 TO 12
          PROC(morph{(I%)}._draw)
        NEXT
        *REFRESH
        
        REM Choose a random Parent from the children
        PROC(morph{(RND(12))}._givegenes)(morph{(0)})
        
        FOR I% = 1 TO 12
          PROC(morph{(0)}._givegenes)(morph{(I%)})
        NEXT
        
      UNTIL Stop%
      
      *REFRESH ON
      ON
      
      DEF PROC0
      
      FOR I% = 0 TO 12
        PROC_discard(morph{(I%)})
      NEXT
      
      END
      
      DEF Biomorph.@
      g_Index% += 1
      Biomorph.index   = g_Index%
      Biomorph.gene(0) = 5
      Biomorph.gene(1) = 20
      Biomorph.gene(2) = .785
      Biomorph.gene(3) = 1
      Biomorph.gene(4) = 0
      Biomorph.gene(5) = 0
      Biomorph.generation = 0
      IF g_Index% THEN
        Biomorph.x = 640 + (400*SIN(g_Index%*PI/6))
        Biomorph.y = 512 + (400*COS(g_Index%*PI/6))
      ELSE
        Biomorph.x = 640
        Biomorph.y = 512
      ENDIF
      ENDPROC
      
      DEF Biomorph._draw
      D = Biomorph.gene(0)
      L = Biomorph.gene(1)
      dA = Biomorph.gene(2)
      AR = Biomorph.gene(3)
      DT = Biomorph.gene(4)
      DS = Biomorph.gene(5)
      X = Biomorph.x
      Y = Biomorph.y
      MOVE X,Y
      DRAW X,Y-L
      PROC(Biomorph._grow)(PI/2,L,X,Y,D)
      ENDPROC
      
      DEF Biomorph._grow (TH,L,X,Y,D)
      IF D MOVE X,Y ELSE ENDPROC
      dX=L*COS(TH+dA)*(1/AR)
      dY=L*SIN(TH+dA)*AR
      PLOT 1,dX,dY
      PROC(Biomorph._grow)(TH+dA+DT,L-DS,X+dX,Y+dY,D-1)
      MOVE X,Y
      dX=L*COS(TH-dA)*(1/AR)
      dY=L*SIN(TH-dA)*AR
      PLOT 1,dX,dY:MOVE X,Y
      PROC(Biomorph._grow)(TH-dA-DT,L-DS,X+dX,Y+dY,D-1)
      ENDPROC
      
      DEF Biomorph._mutate
      LOCAL I%, S%, f
      I% = (Biomorph.index-1)DIV2
      IF (Biomorph.index MOD 2) THEN S% = 1 ELSE S% = -1
      f = RND(1)
      CASE Biomorph.index OF
        WHEN 1:
          Biomorph.gene(0) -= 1
          IF Biomorph.gene(0) =0 THEN Biomorph.gene(0) = 1
        WHEN 2:
          Biomorph.gene(0) += 1
          IF Biomorph.gene(0) > 8 THEN Biomorph.gene(0) = 8
        WHEN 3,4:
          Biomorph.gene(1) += RND(1)-0.5
          IF Biomorph.gene(1)<1 THEN Biomorph.gene(1) = 1
          IF Biomorph.gene(1)>40 THEN Biomorph.gene(1) = 40
        WHEN 5,6:
          Biomorph.gene(2) += (RND(1)-0.5)*0.5
          IF Biomorph.gene(2)>2*PI THEN Biomorph.gene(2) -= 2*PI
          IF Biomorph.gene(2)<0 THEN Biomorph.gene(2) += 2*PI
        WHEN 7,8
          Biomorph.gene(3) += (RND(1)-0.5)*0.5
          IF Biomorph.gene(3)<0.1 THEN Biomorph.gene(3) = 0.1
          IF Biomorph.gene(3)>10 THEN Biomorph.gene(3) = 10
        WHEN 9,10:
          Biomorph.gene(4) += (RND(1)-0.5)*0.5
          IF Biomorph.gene(4)>2*PI THEN Bimorph.gene(2) -= 2*PI
          IF Biomorph.gene(4)<0 THEN Biomorph.gene(2) += 2*PI
        WHEN 11,12:
          Biomorph.gene(5) += (RND(1)-0.5)*0.5
          IF Biomorph.gene(5)<-18 THEN Biomorph.gene(5) = -18
          IF Biomorph.gene(5)>18 THEN Biomorph.gene(5) = 18
      ENDCASE
      
      ENDPROC
      
      DEF Biomorph._givegenes (t{})
      LOCAL I%
      FOR I%=0TO5
        t.gene(I%) = Biomorph.gene(I%)
      NEXT
      ENDPROC 

Richard.
User IP Logged

Michael Hutton
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 248
xx Re: Object Class library - CLASSLIB
« Reply #7 on: May 9th, 2011, 5:48pm »

Quote:
DIM morph{(12) dummy&}


...but not that inelegant compared with what I have just been doing! I had thought of a dummy variable but using PROC_inherit rather than your method. I *think* (really not sure atm) I realised it wasn't the way to go. I then resorted to saving the pointers of the structure in their own list as in this code :

Code:
      REM Particles with CLASSLIB
      REM Won't be fast...proof of concept
      
      REM Just to test the idea of 'anonymous' objects
      REM ie all the objects are called the same
      REM we track them by saving their pointers
      MODE 8
      
      INSTALL @lib$+"CLASSLIB"
      
      MaxParticles% = 50
      NParticles% = 5
      
      REM Array of structures to hold Object pointers
      DIM pParticle{(MaxParticles%) fba%, dba%}
      
      DIM Particle{x,y,vx,vy,creationtime,life,@,_draw,_move,_check,_kill,_spawn}
      PROC_class(Particle{})
      
      
      REM Create particle objects and save their pointers
      FOR I%=1 TO NParticles%
        PROC_new(particle{}, Particle{})
        pParticle{(I%)}.fba% = !^particle{}
        pParticle{(I%)}.dba% = !(^particle{}+4)
      NEXT
      
      REM Set the particle{} pointers to null for a giggle
      !^particle{} = 0 : !(^particle{}+4) = 0
      
      REPEAT
        FOR I%=1 TO MaxParticles%
          IF pParticle{(I%)}.fba% THEN
            !^particle{} = pParticle{(I%)}.fba%
            !(^particle{}+4) = pParticle{(I%)}.dba%
            PROC(particle._draw)
            PROC(particle._move)
            PROC(particle._check)
          ENDIF
        NEXT
      UNTIL INKEY(1) = 32
      
      REM Destroy the particles
      FOR I%=1 TO MaxParticles%
        IF pParticle{(I%)}.fba% THEN
          !^particle{} = pParticle{(I%)}.fba%
          !(^particle{}+4) = pParticle{(I%)}.dba%
          PROC_discard(particle{})
        ENDIF
      NEXT
      
      END
      
      DEF Particle.@
      Particle.x = RND(600)+200 : Particle.y = RND(600)+200
      Particle.vx = (RND(1)-0.5) * 16
      Particle.vy = (RND(1)-0.5) * 16
      Particle.creationtime = TIME
      Particle.life = RND(200)+500
      ENDPROC
      
      DEF Particle._draw PLOT 69, Particle.x, Particle.y : ENDPROC
      
      DEF Particle._move
      Particle.x += Particle.vx : Particle.y += Particle.vy
      IF Particle.x < 200 THEN Particle.x = 200 : Particle.vx *=-1
      IF Particle.x > 800 THEN Particle.x = 800 : Particle.vx *=-1
      IF Particle.y < 200 THEN Particle.y = 200 : Particle.vy *=-1
      IF Particle.y > 800 THEN Particle.y = 800 : Particle.vy *=-1
      ENDPROC
      
      DEF Particle._check
      IF (TIME - Particle.creationtime) > Particle.life THEN
        REM IF RND(5) = 1 THEN PROC(Particle._spawn)(RND(5))
        PROC(Particle._kill)
      ENDIF
      ENDPROC
      
      DEF Particle._kill
      PROC_discard(Particle{})
      pParticle{(I%)}.fba% = 0
      pParticle{(I%)}.dba% = 0
      ENDPROC
      
      DEF Particle._spawn (n%)
      LOCAL I%, J%
      FOR I%=1 TO n%
        J% = 1
        WHILE pParticle{(J%)}.fba% AND J%<>0 : J% = (J%+1) MOD MaxParticles% : ENDWHILE
        PRINT J%
        IF J%=0 THEN EXIT FOR
        
        REM Doesn't like this..
        PROC_new(particle{}, Particle{})
        pParticle{(J%)}.fba% = !^particle{}
        pParticle{(J%)}.dba% = !(^particle{}+4)
      NEXT
      ENDPROC
 


which, although runs, doesn't like me trying to 'spawn' more objects within Particle._spawn - which I don't see why just at the moment. I think I could see this maybe working more elegantly with the {()} method.

Michael
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Object Class library - CLASSLIB
« Reply #8 on: May 11th, 2011, 08:55am »

on May 9th, 2011, 5:48pm, Michael Hutton wrote:
...but not that inelegant compared with what I have just been doing!

Indeed! In fact the more I think about it the more I'm convinced there is no better way of creating the structure array than the one I listed (using a 'dummy' member).

The way DIM is coded means (reasonably enough I think) that you can't create a structure array in which the structures are 'empty'. So the best you can do is to make the structures as small as possible (one byte) so that when they are re-used as objects only the minimum amount of memory is wasted.

Richard.
User IP Logged

Andy Parkes
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 25
xx Re: Object Class library - CLASSLIB
« Reply #9 on: Feb 10th, 2014, 09:48am »

Hi Folks,

I'm a little confused about CLASSLIBs implementation of containment. CLASSLIB seems to get confused when multiple constructors are involved. In the example below, an instance of Class1 is contained in Class2. Both classes have a user-defined constructor. When creating an new instance of Class2, I would have expected the constructor for Class2 to be called, regardless of whether the contained class has a constructor.

Code:
      INSTALL @lib$ + "CLASSLIB"

      PRINT "Contained class has user-defined constructor"'

      DIM Class1{member1%, @Class1,@@Class1}
      PROC_class(Class1{})

      DIM Class2{member2%,Contained{}=Class1{}, @Class2,@@Class2}
      PROC_class(Class2{})

      PROC_new(Obj{},Class2{})
      PROC_discard(Obj{})

      PRINT '"Contained class noes not have user-defined constructor"'

      DIM Class3{member1%}
      PROC_class(Class3{})

      DIM Class4{member2%,Contained{}=Class3{}, @Class4,@@Class4}
      PROC_class(Class4{})

      PROC_new(Obj{},Class4{})
      PROC_discard(Obj{})

      END

      DEF Class1.@Class1
      PRINT "In Class1 Constructor"
      ENDPROC

      DEF Class1.@@Class1
      PRINT "In Class2 Destructor"
      ENDPROC

      DEF Class2.@Class2
      PRINT "In Class2 Constructor"
      ENDPROC

      DEF Class2.@@Class2
      PRINT "In Class2 Destructor"
      ENDPROC

      DEF Class4.@Class4
      PRINT "In Class4 Constructor"
      ENDPROC

      DEF Class4.@@Class4
      PRINT "In Class4 Destructor"
      ENDPROC
 


Am I correct in thinking that containment is only supported for classes that do not have a user-defined constructor and destructor?

Thanks

Andy
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Object Class library - CLASSLIB
« Reply #10 on: Feb 10th, 2014, 9:36pm »

on Feb 10th, 2014, 09:48am, Andy Parkes wrote:
When creating an new instance of Class2, I would have expected the constructor for Class2 to be called, regardless of whether the contained class has a constructor.

It would be, had you defined Class2 like this:

Code:
      DIM Class2{member2%, @Class2, @@Class2, Contained{}=Class1{}}
 

Specifically, the constructor is the first method in the class definition which starts with an @, so if both the 'contained' class and the 'container' class have a constructor, which is called depends on the order in which they appear. There is no way to arrange for both constructors to be called, if that is what you would have preferred (you could always arrange for the 'container' class's constructor explicitly to call the 'contained' class's constructor).

Please note that I don't know the first thing about Object Oriented Programming. The above comments are based on my very limited understanding of the code in CLASSLIB. The person who would be in a better position to answer is Jon Ripley, but as far as I'm aware he is no longer a BBC BASIC user.

Richard.
« Last Edit: Feb 10th, 2014, 9:49pm by admin » User IP Logged

Andy Parkes
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 25
xx Re: Object Class library - CLASSLIB
« Reply #11 on: Feb 12th, 2014, 11:07am »

Thanks Richard,

Quote:
if both the 'contained' class and the 'container' class have a constructor, which is called depends on the order in which they appear.


I see that now. I have had a closer look at CLASSLIB and made some modifications and uploaded the resulting alternative version of the library:

http://wiggio.com/yui/folder/stream_file.php?doc_key=UPj0PFAQhZunxxI9QgkanItctTc9D7wqtcAF2cmYrtg=

I have made the following changes:

1...
It is no longer a requirement that the user write a constructor member before contained class members. This is because it ignores all contained class constructors and leaves their execution to the discretion of the class designer.

I was in two minds about this idea to begin with, as it seemed like the previous way of doing things (by choosing one constructor to call based upon the order of the class structure members) was more flexible. After a little more thought, I've concluded that it is probably less flexible, potentially the cause of an insidious bug (which is how I have come across it), and does not seem to have an analogue in OOP languages. The fact that the class designer would have to 'manually' call the contained-class-destructors within the container-class-destructor anyway, also added to my feeling that this new system is clearer by virtue of being consistent. In terms of flexibility, it allows for constructors that take parameters, and also a choice of initialisation routines (what in other languages might be implemented as overloaded constructors) for contained classes.

2...
It allows constructors to take parameters (as mentioned).

3...
It allows new objects to be assigned to LOCAL structure variables.

I've done this by asking the user to pass the address of the structure variable to which they want to assign the new object, I could not see a neater way of doing this.

Quote:
There is no way to arrange for both constructors to be called...


For once I have to disagree with you, I can see some ways of implementing that with some sort of stack, but it would eliminate the other advantages I mentioned above. It would also lead to reduced performance and even greater complexity, in what is already fairly esoteric code.

I've documented the changes at the beginning of the library. Only superficial modifications were needed to implement the ideas I've mentioned so I hope that I have not introduced any bugs.

CLASSLIB (and CLASSLIBA grin) is a really powerful addition to BB4W. I wish I had realised its potential sooner. Just as some problems lend themselves particularly well for example, to recursion (and so we might keep that discipline in our tool kit), so I'm learning, that some problems are solved just beautifully with objects.

Andy
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Object Class library - CLASSLIB
« Reply #12 on: Feb 12th, 2014, 1:46pm »

on Feb 12th, 2014, 11:07am, Andy Parkes wrote:
I have had a closer look at CLASSLIB and made some modifications

I'm impressed that you were able to make modifications to what I often consider to be a 'read only' library, because its code is so incomprehensible!

Quote:
I have made the following changes:
1. It is no longer a requirement that the user write a constructor member before contained class members.

I think I understand the modification you have made, except that I can't figure out what your X% = 1 achieves. As far as I can see the functionality would not be affected if it was omitted.

Quote:
2. It allows constructors to take parameters (as mentioned).

I notice that you have achieved this in such a way as to make your modified library incompatible with the original. That was obviously deliberate, but it rules out your library replacing the current one.

Quote:
3. It allows new objects to be assigned to LOCAL structure variables. I've done this by asking the user to pass the address of the structure variable

The inelegant syntax seems a high price to pay for the ability to make a new object LOCAL.

In any case, making the object LOCAL is likely to result in a memory leak (that's precisely why it is prohibited using the conventional syntax). When the PROC or FN in which it is declared returns, the reference to the object will be lost but the memory occupied by the object won't automatically be freed. So every iteration will result in a new block of memory being allocated.

To avoid this problem you would need to PROC_discard the 'local' object prior to returning from the PROC/FN, in which case you might as well make it PRIVATE rather than LOCAL and thereby retain the more elegant syntax.

Quote:
For once I have to disagree with you, I can see some ways of implementing that...

Sorry if it wasn't obvious, but when saying there is "no way" I (naturally) meant using the existing CLASSLIB library. I wasn't considering the possibility of modifying the library.

Richard.
User IP Logged

Andy Parkes
Developer

member is offline

Avatar




PM

Gender: Male
Posts: 25
xx Re: Object Class library - CLASSLIB
« Reply #13 on: Feb 12th, 2014, 7:46pm »

Quote:
I think I understand the modification you have made, except that I can't figure out what your X% = 1 achieves. As far as I can see the functionality would not be affected if it was omitted.


Yes, your right its overkill, it might spare an IF statement, but it always creates an assignment, so I'll remove it.

Quote:
you might as well make it PRIVATE rather than LOCAL and thereby retain the more elegant syntax.


I take your point, but my motivation was to avoid the need for global and PRIVATE variables, which stems from the way I have picked up the warning given in the PRIVATE help topic:

http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin7.html#private

As a result, I try to avoid using PRIVATE variables as much as possible. I would favour the most robust solution, as it outweighs elegant syntax. But I admit that I don't fully understand the complications surrounding PRIVATE variables, so I wonder now if this is another example of overkill? I very much welcome being assured that it is.

In particular, I have never been entirely clear about the following statement:

“it is important to ensure that no errors occur whilst PRIVATE variables are in use”

I realise that it is still fine to use ON ERROR for exception handling, so what is meant by 'in use'?
Or have I taken this out of context, is the restriction specific to recursive functions with PRIVATE variables?

Quote:
Sorry if it wasn't obvious, but when saying there is "no way" I (naturally) meant using the existing CLASSLIB library. I wasn't considering the possibility of modifying the library.


Re-reading your message, I'm not sure how I misunderstood. Anyway, I should have guessed that you would be able to see the possible solutions if I can.

Andy
User IP Logged

admin
Administrator
ImageImageImageImageImage


member is offline

Avatar




PM


Posts: 1145
xx Re: Object Class library - CLASSLIB
« Reply #14 on: Feb 12th, 2014, 9:10pm »

on Feb 12th, 2014, 7:46pm, Andy Parkes wrote:
But I admit that I don't fully understand the complications surrounding PRIVATE variables, so I wonder now if this is another example of overkill?

Well, equally I don't fully understand what your anxiety is, so it's hard for me to comment. Given that PRIVATE was one of the more challenging features to add to BBC BASIC I would be pretty frustrated if people avoided its use!

Quote:
what is meant by 'in use'?

By 'in use' I mean after one or more PRIVATE statements have been executed but before the ENDPROC or end-of-function. In other words, whilst one or more PRIVATE variables are 'in scope'.

Quote:
is the restriction specific to recursive functions with PRIVATE variables?

The issue to which the warning refers is the possibility of a function containing PRIVATE variables being accidentally called re-entrantly, typically because an error occurred during its execution.

The combination of circumstances in which this might happen is pretty unlikely. Firstly errors must be trapped (otherwise there's no way the function can be called again after the error occurred). Secondly the error must occur within the scope of a PRIVATE variable, either because the code of the function itself fails or as the result of an asynchronous event such as pressing Escape.

And even if this unlikely situation can occur any untoward consequences can be avoided by a judicious use of 'ON ERROR LOCAL'. Many of my programs contain several of these unwieldy statements:

Code:
      ON ERROR LOCAL RESTORE LOCAL : ERROR ERR, REPORT$ 

Richard.

« Last Edit: Feb 12th, 2014, 9:41pm by admin » User IP Logged

Pages: 1 2  Notify Send Topic Print
« Previous Topic | Next Topic »

| |

This forum powered for FREE by Conforums ©
Terms of Service | Privacy Policy | Conforums Support | Parental Controls