How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Forum for discussion about the documentation project.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by MrSwiss »

[header]
Audience: beginners (with some experience) / advanced
Skills required: understands procedures (sub/function), with one or more parameter's and (maybe) overload(ing)
Terms used:
  • UDT = user defined type
    overload(ed) constructor (superseeds default procedure, in the chain of events)
    ctor = constructor (default or overloaded)
    dtor = destructor (default)
[edit]updated code and some comments: 2018-06-07 (based on feedback by: fxm/badidea)[/edit]
[/header]

Object Encapsulation
WHY:
To allow the programmer of a Type/Class or even a whole library, to:
- actively control the access to its internals (pointers/variables etc.), none/read only/write only/read & write
- secure operations, by denying certain destructive user actions (like pointer overwriting, deallocating etc.)

HOW:
In order to "get" control, we have to first deny all external access, by labeling all internals as *PRIVATE*.
Excerpt from below code:

Code: Select all

Type image      ' takes ImageCreate()/-Destroy()/ImageInfo() from the user (automatic handling)
  Private:                              ' prevent direct user access = IMPORTANT!
    As Any Ptr  pimg                    ' image ptr (user: read only!)
    As String   descr                   ' image description, title or name (user: read/write)
  Public:
    ' Abstraction starts here ...
End Type
Since above (at this stage) is absolutely useless, we have to use Abstraction, to achieve desired results.

Abstraction

In order to *selectively* return some access rights to user, we have to use Abstraction (remember: user has none rights, any longer).
Abstraction, also referred to as Interface, in its simplest form, has usually three/four distinctive types of methods:
1) "generate a user Object" method: constructor (overloaded), more than one possible (user callable)

Code: Select all

Var pt = New image(100, 100, RndARGB)
2) "destroy a user Object" method: destructor (), only one per instance (only indirectly callable by user)

Code: Select all

Delete pt
3) some *handlers* the user can call, for "allowed" handing of the Type (aka: getter's/setter's/actor's)
  • getter examples:
    get_def() (Sub, ByRef return) retrieves: width/height/size of a image [changed name: 2018-06-07]
    get_pval() (Function, no parameter) returns ptr address (debug/test use mostly)
    setter example:
    newdesc() (Sub) sets: a new or diferent image description
    actor example:
    showimg() (Sub) puts image to screen (at user given position and user chosen method)

    Getter/Setter can also be achieved, by using "Property" statement, for more information on that, refer to the link, provided by fxm.
    (Since this is for beginners also, I didnt want to introduce more complexity, than absolutely needed!) [added: 2018-06-07]
4) Operator's (none used, in this example). Operators are typically used to overload (default) Operator's like:
  • = (assignement)
    let (copy & assign) and so on ... (see FB-Manual, for details)
There is one more thing to know: all images are exclusively 32-bit's color (hard coded in type, bypp = 4 always).

Additional info can be gotten from comments in below code (or ask in this thread):

Code: Select all

' Image-Type_test.bas -- 2018-06-04, MrSwiss (Rev. 1 -- 2018-06-07)
'
' compile: -s gui
'

Type image      ' takes ImageCreate()/-Destroy()/ImageInfo() from the user (automatic handling)
  Private:                              ' prevent direct user access = IMPORTANT!
    As Any Ptr  pimg                    ' image ptr (user: read only!)
    As String   descr                   ' image description, title or name (user: read/write)
  Public:
    ' ctor / dtor
    Declare Constructor(ByVal iw As UShort, ByVal ih As UShort, ByVal clr As ULong=&h00FF00FF00, ByRef n As Const String="")
    Declare Destructor()
    ' subroutines
    Declare Sub newdesc(ByRef nd As Const String)       ' set or change description (e.g. if not set in ctor)
    Declare Sub get_def(ByRef iw As UShort=0, ByRef ih As UShort=0, ByRef siz As ULong=0)   ' get header info (w, h, size)
    Declare Sub showimg(ByVal ix As Short, ByVal iy As Short, ByVal tr As Boolean=TRUE) ' display image (at pos. ix/iy)
    ' functions
    Declare Function get_pval() ByRef As Const Any Ptr  ' probably only used for debugging/testing
    Declare Function get_desc() ByRef As Const String   ' read image description
End Type

Constructor image(ByVal iw As UShort, ByVal ih As UShort, ByVal clr As ULong=&h00FF00FF00, ByRef n As Const String="")
    This.pimg   = ImageCreate(iw, ih, clr, 32)  ' exlcusively 32-bit color! default: transparent
    This.descr  = n                     ' assign description (can be left empty)
End Constructor

Destructor image()
    ImageDestroy(This.pimg)             ' deallocate ptr (before type destruction) test only: _
End Destructor                          ' comment out above: to see a massive memory leak!!!

Sub image.newdesc(ByRef nd As Const String)
    This.descr = nd
End Sub

Sub image.get_def(ByRef iw As UShort=0, ByRef ih As UShort=0, ByRef siz As ULong=0)
    Dim As Integer w, h, s              ' solely to satisfy: ImageInfo()

    ImageInfo(This.pimg, w, h,,,, s)    ' only get the important parts: width/height/size
    iw  = CUShort(w)                    ' convert & assign
    ih  = CUShort(h)                    ' dito
    siz = CULng(s)                      ' dito
End Sub                                 ' local vars. destroyed here (going out of scope)

Sub image.showimg(ByVal ix As Short, ByVal iy As Short, ByVal tr As Boolean=TRUE)
    If tr Then
        Put (ix, iy), This.pimg, Trans  ' show the image with transparency
    Else
        Put (ix, iy), This.pimg, PSet   ' show the image opaque (tr = FALSE)
    End If
End Sub

Function image.get_pval() ByRef As Const Any Ptr
    Return This.pimg                    ' provide read access to user
End Function

Function image.get_desc() ByRef As Const String
    Return This.descr                   ' provide read access to user
End Function
' ----- end type -----


' preparation: define color randomizer & heat rnd() up
#Define RndARGB     ( CULng(Rnd() * &hFFFFFFFFul) ) ' any 32-bit color (any alpha value)

Randomize( , 5)                         ' initialize randomizer (OS specific method)
for i As UInteger = 1 To 50             ' heat up randomizer (300 call's)
    Rnd() : Rnd() : Rnd() : Rnd() : Rnd() : Rnd()
Next

' testing code, purpose: detect memory leaks (if any)
ScreenRes(800, 800, 32,, 64)            ' using: GFX_ALPHA_PRIMITIVES (see fbgfx.bi)
Color(&hFF000000, &hFFFFFFFF) : Cls     ' black on white bg
Width 800\8, 800\16                     ' 8 x 16 Font

Dim As ULong    cnt = 1, siz
Dim As UShort   wid, hei

Do
    Var pt = New image(100, 100, RndARGB)   ' partly init (ctor without description)
    pt->newdesc("Image" + Str(cnt))     ' add description (just for examples sake!)
    'Var pt = New image(100, 100, RndARGB, "Image" + Str(cnt))   ' complete ctor call
    pt->get_def(wid, hei, siz)          ' get image definitions
    If CsrLin = 50 Then Cls             ' clear screen when getting full
    Print Str(pt->get_pval), pt->get_desc, "w: "; wid; "  h: "; hei; "  size: "; siz
    pt->showimg(650, 350)               ' display image (fixed pos.)
    Delete pt                           ' hidden dtor call (aka: indirect)
    Sleep(20, 1)                        ' free some CPU resources
    cnt += 1 : If cnt > 10000 Then cnt = 1  ' counter handling
Loop Until Len(InKey()) > 0             ' run, until user interrupts (=prog. end)
' ----- EOF -----
Since the test code contained, performs actually a memory leak test, you can check that yourself, by running it.
As noted in comments: commenting out "ImageDestroy()" in destructor, creates a massive *memory leak* (uncomment after test!!!).
Last edited by MrSwiss on Jun 07, 2018 11:41, edited 1 time in total.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by fxm »

Just a small remark on the code:
In my opinion, use screen locking in the main loop is useless because there is no redrawing of screen to mask in that case.
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by badidea »

Another great tutorial. Some questions / remarks:

1) Heating up the randomizer? I have not seen that before. What is reason for that?
2) You don't use the property statement. Seems that the property statement does not really add anything to freebasic?
3) "local's" confused me for a second, suggestion "local variables"
4) "showdef" does not "show" anything (unlike showimg), suggestion "getshowdef"
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by fxm »

badidea wrote:Seems that the property statement does not really add anything to freebasic?
The interest, from point of view of user syntax, to use properties getter/setter instead of function/sub is quite well explained here:
Properties
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by MrSwiss »

@fxm,
are you absolutely certain (the image is new, on every run, therefore "overwriting" previous ...)
Secondly, put it down to: "good coding practice" (we don't want to teach, bad habits!)

@badidea,
1) courtesy of deltarho[], in one of his randomizer's posts (can't remember which one, as well as the reasons he gave).
They must have made sense to me at the time, so I simply took 'em over.
2) fxm was faster on this one (see separate post) ...
3) you've figured it out too (just a shortcut)
4) well, that one could be called: "get_def", to be a bit clearer on its purpose ... (nobody is perfect).
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by fxm »

If you are really sure of the usefulness of screen locking, at least put the 'cls' instruction within the [screenlock...screenunlock] block!
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by MrSwiss »

??? I fail to see the logical connection ???
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by fxm »

Since I can not convince you, have you noticed a difference with or without screen locking?
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by MrSwiss »

Answering a question, with one of your own, goes into the category: "beliving"
which is, by my definition: the opposite of "convincing", because that would
require, some facts and figures (opposite of: "belive me, I know").

Btw: updated code and added some remarks, today ...
<fun>

Code: Select all

Const beliving_capacity_MrSwiss = 0.0  ' ;-)
</fun>
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by dodicat »

belive -- (believe)

Aside:
Between Abstraction layers, Mr Swiss and fxm pending, here, encapsulate raw pixels via pointer with custom image texturing.
Also the two levels of images encapsulate the screen and image ratios

Code: Select all

 
 
Type image
    private:
    As Any Ptr i         'image
    Dim As integer xx,yy 'graphics screen
    public:
    Declare  Function create(w As Long,h As Long,Byval As Function(As Long=0) As Ulong,flag As Long=300) As image
    Declare Sub destroy()
    Declare Sub Put(As Long,As Long)
End Type

Function image.create(w As Long,h As Long,Byval fn As Function(As Long=0) As Ulong,flag As Long) As image
    Screeninfo xx,yy
    #define pad(n) (n)+(n) Mod 16
    Dim As Ulong Ptr  p = Callocate(w * h +8 , SizeOf(ULong)) 'modify via mrSwiss
    p[0]=7              'always
    p[1]=4              'pixelsize
    p[2]=w              'width
    p[3]=h              'height
    p[4]=pad(w*p[1])    'pitch -- padded to a multiple of 16
    For a As Long=5 To 7
        p[a]=0          'reserved
    Next
    For a As Long=8 To h*w +8 -1 'keep within allocation and outside header info
        p[a]=fn(a\flag)        'colour (custom)
    Next
    this.i=p
    Return This
End Function

Sub image.destroy
    Deallocate i
    i=0
End Sub

Sub image.Put(xpos As Long,ypos As Long)
    #define ppset32(_x,_y,colour)    *Cptr(Ulong Ptr,row+ (_y)*pitch+ (_x) Shl 2)  =(colour)
    Dim As Ulong Ptr p=Cast(Ulong Ptr,this.i)
    Dim As Any Ptr row=Screenptr
    Dim As Long pitch=xx shl 2 'xx*4
    Dim As Long inc=8,lim=p[3]*p[2] +8 -1
    For y As Long=0 To p[3]-1
        If (y+ypos)>=0 Andalso (y+ypos) < (yy) Then        'ciip
            For x As Long=0 To p[2]-1
                If (x+xpos)>=0 Andalso (x+xpos) <(xx) Then 'clip
                   ' If inc >lim Then  Return   'no over run on pointer 
                    ppset32((x+xpos),(y+ypos),p[inc])
                End If
                inc+=1
            Next
        Else
            inc+=1 'if clip missed
        End If
    Next
End Sub

'custom image texture
Function rainbow(deg As Long) As Ulong  'from blutiagro
    Return Rgb(Sin(.01745329252*(deg))*127+128, _
               Sin(.01745329252*(deg-120))*127+128,_
               Sin(.01745329252*(deg+120))*127+128)
End Function 

#define map(a,b,x,c,d) ((d)-(c))*((x)-(a))/((b)-(a))+(c)

Screen 19,32
screenset 0,1
Dim As Integer xres,yres
Screeninfo xres,yres

Dim As image i,bx,back
Dim As Single x,y,kx=.8,ky=1                      'motion parts
Dim As Single iw=400/(xres/400),ih=300/(yres/300) 'small box size (in ratio)
    i.create(400,300,@rainbow,200)                'large box
    bx.create(iw,ih,@rainbow,200*iw/xres)         'small box
    back.create(xres,yres,@rainbow,200*xres/iw)   'background
Do
    'motion
    x+=kx
    y+=ky
    'boundary
    If x<0 Or x>xres-400 Then kx=-kx
    If y<0 Or y>yres-300 Then ky=-ky 
    '=====   motion mapping ====== 
    Var xp=map(0,(xres-400),x,0,400-iw)
    Var yp=map(0,(yres-300),y,0,300-ih)
    '====================
    Screenlock 'required for direct pixel plotting
    Cls
    back.put(0,0)
    i.put(x,y)
    bx.put(x+xp,y+yp)
    Screenunlock
    Sleep 1,1
Loop Until Inkey=Chr(27)
Sleep
i.destroy
bx.destroy
back.destroy

Last edited by dodicat on Jun 08, 2018 14:30, edited 2 times in total.
Imortis
Moderator
Posts: 1923
Joined: Jun 02, 2005 15:10
Location: USA
Contact:

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by Imortis »

fxm wrote:...
In my opinion, use screen locking in the main loop is useless because there is no redrawing of screen to mask in that case.
...
MrSwiss wrote:...
put it down to: "good coding practice" (we don't want to teach, bad habits!)
...
fxm wrote:If you are really sure of the usefulness of screen locking, at least put the 'cls' instruction within the [screenlock...screenunlock] block!
Perhaps another tutorial should be made show how to make proper use of Screenlock/Screenunlock. I know I had trouble with that when I first started working with graphics screens in FB.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by fxm »

There is already this article: How to Solve the Flickering Issue when Refreshing a Window in FB Graphics Mode.
But if it is desirable to write an additional article more generally on "When must implement an anti-flickering process":
- Since I have not managed to convince MrSwiss, either I am wrong, or I am right.
- But in the two cases, I am not in the best position to write such an article!
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by MrSwiss »

dodicat, thanks for the correct spelling.

In your DIY "fb.image", in the Function "image.create", the CAllocate() amount is huge,
far to much memory is allocated:

Code: Select all

Dim As Ulong Ptr  p=Callocate(w*h,Sizeof(image))
first parmeter (number of elements): w * h <-- "... and, where goes the header?" + 32 bytes or 8 * SizeOf(ULong)

second parameter (element size in bytes): SizeOf(image) --> SizeOf(ULong) = 4
(SizeOf(image) + FBC32 = 12 bytes, SizeOf(image) + FBC64 = 24 bytes!)

my memory calculation used and tested, looks as follows:

Code: Select all

Dim As Ulong Ptr  p = Callocate(w * h + 8, 4)
alternative:

Code: Select all

Dim As Ulong Ptr  p = Callocate(w * h + 8, SizeOf(ULong))
where the: + 8 (8 * 4 = 32) = header-size (in bytes) ...

Why don't you write: ... (basics 2), describing the methods used, in this code example?
(looks cool, btw)
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by dodicat »

Thanks Mr Swiss, size of ulong should have been used.
The pointer indices stretch from 0 to w*h-1, each one occupying 4 bytes.
The first eight indices are reserved as a header, so, readable by fb native keywords (imageinfo, put ...)

Adding the 8 bytes, (32 bits)
Is this necessary?
The first 8 indices remain for information in the custom put sub, and are not looped through of course.
I could have used fb put instead, but this would mean making the image field public (unless I wrapped it into another method I suppose)
The little demo sits between your abstraction/ encapsulation and fxm's next, so has no value as such. Perhaps the most interesting thing about it is that screenlock/unlock or screenset flip must be used if direct screen pixels via pointer[] is used.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: How and Why to make Abstraction by Object Encapsulation, with FB Syntax in UDTs (basics)

Post by MrSwiss »

dodicat wrote:Adding the 8 bytes, (32 bits). Is this necessary?
First part: its 32 bytes or 8 * SizeOf(ULong), aka: the HEADER
Second part: No, it is MANDATORY! (except you want to, on purpose *blast* the mem-allocation!)
I'm talking about the amount of allocated memory (to image ptr), which
hast to hold: header memory 8 * SizeOf(ULong) + all of pixel-data.

Nothing to do with: pixel-data indexing ... (which starts after the header)
p + 32 position, aka: p[8] reference.
dodicat wrote:The little demo sits between your abstraction/ encapsulation and fxm's next, so has no value as such. Perhaps the most interesting thing about it is that screenlock/unlock or screenset flip must be used if direct screen pixels via pointer[] is used.
I disagree with the *striked out* part, of your statement. The more different points of view (on the same or related issue), the more users will be able to benefit, IMHO. (That is why I've proposed ... (basics 2), in the title.)
Post Reply