Hello fxm our FB OOP guru :-)

General FreeBASIC programming questions.
Post Reply
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Hello fxm our FB OOP guru :-)

Post by D.J.Peters »

Hello fxm
only if you have time and fun to help i need two short oop examples.

First problem I need a kind of singleton:

pseudo code

Code: Select all

type singleton exends object
  ...
end type
constructor singleton
  if static_instance_counter>0 then
    delete @this
  else
    static_instance_counter+=1
  endif
end constructor

type thing extends singleton
  ...
end type
' main
dim as thing ptr thing1 = new thing 
dim as thing ptr thing2 = new thing ' this should return NULL
second problem are reference counted:

For example i have a OpenGL texture class (and all this texture objects used GPU memory)

Many 3d modells with the same material used a refence to the same texture object.
If no more references to an texture object exists it should be deleted (than I can free the GPU memory)

Of course this kind of base class can be used of for any other reference counted classes also.

after "delete last_object_that_use_a_referencecounted_class"

it should trigger any event/sub to the base class that implement the reference counted class
In the case of the texture class it must know when it can remove the texture from GPU memory.

Any idea how to implement this two OOP things with FreeBASIC ?

I'm sure I can get it self after many try and error
but looks like you are a little fan of oop and has fun to solve this things with FreeBASIC.

Joshy
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Hello fxm our FB OOP guru :-)

Post by fxm »

Obviously, everyone can speak on this topic!

Point 1

I think I understood that your goal is to create a generic singleton base class ("singleton") so that any derived class ("thingA", "thingB", ...) works as a singleton (each derived class, independently of one another, may have 0 or 1 object at most created at same time).
Each derived class must have no (or a minimal) specific code to operate as singleton. All (or the more possible) must be inherited!

Is that correct?
If so, this project at first sight seems to me too ambitious given the present OOP features of FreeBASIC (there may be a solution by reviewing your aim down!).

As simplest example, if you want this behavior for the singleton creation:
dim as thingA ptr thingA1 = new thingA
dim as thingA ptr thingA2 = new thingA ' this should return NULL

you should overload the "new" operator in "thinkA" in order to, either create one object returning its pointer, or not create and return 0 but, return 0 crashes.
If you create the singleton by means of a static method, the first constraint is to define a static method in "thingA":
dim as thingA ptr thingA1 = thingA.createInstance()
dim as thingA ptr thingA2 = thingA.createInstance() ' this should return NULL


Point 2

I do not quite understand what exactly you want.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Hello fxm our FB OOP guru :-)

Post by fxm »

Rustic workaround for point 1 : Macros for singleton

In each type where singleton behavior is needed, a workaround is to insert the matching singleton code for declaration and body.

To simplify the syntax, 2 macros are called for each type where singleton behavior is needed:
- One macro for the declaration ('singletonDeclaration()') and another macro for the body ('singletonBody()').
- The two macros have a same parameter ('typeName') which is the name of the type which uses these.
- The two macros bodies are encapsulated in a file to include ('singleton.bi') and the file is included once at the main file header.
- The singleton creation is called by the syntax: 'pointer = typeName.createSingleton()'.
- The singleton suppression is called by the syntax: 'typeName.deleteSingleton()'.
- The user must always provide a default constructor body and a destructor body, even empty (code can be insert within each one), and at opposite no constructror declaration neither destructor declaration).

singleton.bi:

Code: Select all

#Macro singletonDeclaration (typeName)
  Public:
    Declare Static Function createSingleton () As ##typeName Ptr
    Declare Static Sub deleteSingleton ()
  Private:
    Static As ##typeName Ptr ref
    Declare Constructor ()
    Declare Constructor (Byref rhs As ##typeName)
    Declare Destructor ()
  Public:
#Endmacro

#Macro singletonBody (typeName)
  Dim As ##typeName Ptr ##typeName.ref = 0
  Static Function ##typeName.createSingleton () As ##typeName Ptr
    If ##typeName.ref = 0 Then
      ##typeName.ref = New ##typeName
      Return ##typeName.ref
    Else
      Return 0
    End If
  End Function
  Static Sub ##typeName.deleteSingleton ()
    If ##typeName.ref > 0 Then
      Delete ##typeName.ref
      ##typeName.ref = 0
    End If
  End Sub
#Endmacro
The singleton code is simple:
- The default constructor and copy constructor are declared private in order to forbid any object user creation by 'Dim' or 'New'.
- Instead, a public static member function allows creation of a single object by using a private static member pointer to store the unique object address when exists.
- The destructor is declared private in order to forbid any object user suppression by 'Delete'.
- Instead, a public static member sub allows a single suppression of the object by using the private static member pointer to check the unique object address and clear it if exists.
(user must always provide a default constructor body and a destructor body, even empty (some code can be insert within each one), and obviously no constructror declaration neither destructor declaration)

main.bas:

Code: Select all

#Include "singleton.bi"

Type thing
  singletonDeclaration(thing)
  Dim i As Integer ''for example
  '.....
End Type
singletonBody(thing)
'.....

Constructor thing () ''mandatory, even if empty
  '.....
End Constructor

Destructor thing () ''mandatory, even if empty
  '.....
End Destructor

'---------------------------------------------

Dim As thing Ptr ps1 = thing.createSingleton()
ps1->i = 1234
Print ps1, ps1->i

Dim As thing Ptr ps2 = thing.createSingleton()
Print ps2

thing.deleteSingleton()

Dim As thing Ptr ps3 = thing.createSingleton()
Print ps3, ps3->i

thing.deleteSingleton()

Sleep

Code: Select all

3354584        1234
0
3354584        0
[Edit]
- Added a static sub for singleton suppression to avoid multi suppressions by user (the destructor becomes private).
- the default constructor body and the destructor body are removed from the 'singletonBody()' and must be provided even empty by the user (user code can be insert within each one).
Last edited by fxm on Nov 16, 2013 9:35, edited 12 times in total.
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: Hello fxm our FB OOP guru :-)

Post by D.J.Peters »

Thank you for the explanation.

Joshy
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Hello fxm our FB OOP guru :-)

Post by fxm »

I just have to update my program above to be more rigorous for the suppression of the singleton, and also allow the user to insert his own code in the constructor body and destructor body.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Hello fxm our FB OOP guru :-)

Post by fxm »

Some ideas for point 2 : Reference counting base of all derived-types in inheritance hierarchy

Main principles:
- For reference creation, the base constructor itself cannot know the real type of the object during construction (provisional type like base-type). The most derived constructor must pass as parameter its typename to the base constructor (by chaining the constructors by means of 'Base()' used at the first line of the constructors).
- For reference suppression, the base destructor can know the real type of the object during destruction because its typename has been stored in a base string member by the constructor.
- Two private dynamic arrays (array of typenames, array of numbers of references) are updated and resized by the base constructor and the base destructor (public static functions allow to read these arrays for debug at least).
- Two public static pointers to procedures (defined As Sub (Byref typeName As String)) can be initialized by user in order to execute two user procedures (two callbacks), the first when is created the first reference for a new typename (bornedDerivedType) and the second when is suppressed the last reference of a typename (expiredDerivedType).
- Constructor, copy-constructor and destructor are protected in order to forbid any base construction or any base destruction directly by user.

reference_counting_base.bi:

Code: Select all

'reference counting base of all derived-types in inheritance hierarchy

Type referenceCountingBase 'or Type referenceCountingBas Extends Object
  Public:
    Declare Static Function numberDerivedType () As Integer
    Declare Static Function DerivedTypeName (Byval derivedTypeNumber As Integer) As String
    Declare Static Function numberOfReference (Byval derivedTypeNumber As Integer) As Integer
    Static As Sub (Byref typeName As String) bornedDerivedType
    Static As Sub (Byref typeName As String) expiredDerivedType
  Protected:
    Declare Constructor ()
    Declare Constructor (Byref typeName As String)
    Declare Destructor ()
  Private:
    Dim As String _myTypeName
    Static As String _typeName()   '_typeName(0) unused, the active typnames are stored from _typeName(1)
    Static As Integer _typeCount() '_typeCount(0) = number of active typenames
End Type                           '_typeCount(I) = number of active instances of the _typeName(I)

Redim referenceCountingBase._typeName(0 To 1)  'pre-sized for one active typename
Redim referenceCountingBase._typeCount(0 To 1) 'pre-sized for one active typename
Dim As Sub (Byref typeName As String) referenceCountingBase.bornedDerivedType
Dim As Sub (Byref typeName As String) referenceCountingBase.expiredDerivedType

Static Function referenceCountingBase.numberDerivedType () As Integer
  Return referenceCountingBase._typeCount(0)
End Function

Static Function referenceCountingBase.DerivedTypeName (Byval derivedTypeNumber As Integer) As String
  If derivedTypeNumber >0 And derivedTypeNumber <= referenceCountingBase._typeCount(0) Then
    Return referenceCountingBase._typeName(derivedTypeNumber)
  Else
    Return ""
  End If
End Function

Static Function referenceCountingBase.numberOfReference (Byval derivedTypeNumber As Integer) As Integer
  If derivedTypeNumber >0 And derivedTypeNumber <= referenceCountingBase._typeCount(0) Then
    Return referenceCountingBase._typeCount(derivedTypeNumber)
  Else
    Return 0
  End If
End Function

Constructor referenceCountingBase ()
End Constructor

Constructor referenceCountingBase (Byref typeName As String)
  This._myTypeName = typeName
  For I As Integer = 1 To referenceCountingBase._typeCount(0) + 1
    If I <= referenceCountingBase._typeCount(0) Then
      If referenceCountingBase._typeName(I) = typeName Then
        referenceCountingBase._typeCount(I) += 1
        Exit For
      End If
    Else
      referenceCountingBase._typeName(I) = typeName
      If referenceCountingBase.bornedDerivedType > 0 Then
        referenceCountingBase.bornedDerivedType(referenceCountingBase._typeName(I))
      End If
      referenceCountingBase._typeCount(I) += 1
      referenceCountingBase._typeCount(0) = I
      Redim Preserve referenceCountingBase._typeName(0 To I + 1)
      Redim Preserve referenceCountingBase._typeCount(0 To I + 1)
    End If
  Next I
End Constructor

Destructor referenceCountingBase ()
  For I As Integer = 1 To referenceCountingBase._typeCount(0)
    If referenceCountingBase._typeName(I) = This._myTypeName Then
      referenceCountingBase._typeCount(I) -= 1
      If referenceCountingBase._typeCount(I) = 0 Then
        If referenceCountingBase.expiredDerivedType > 0 Then
          referenceCountingBase.expiredDerivedType(referenceCountingBase._typeName(I))
        End If
        For J As Integer = I + 1 To referenceCountingBase._typeCount(0) + 1
          referenceCountingBase._typeName(J - 1) = referenceCountingBase._typeName(J)
          referenceCountingBase._typeCount(J - 1) = referenceCountingBase._typeCount(J)
        Next J
        Redim Preserve referenceCountingBase._typeName(0 To referenceCountingBase._typeCount(0))
        Redim Preserve referenceCountingBase._typeCount(0 To referenceCountingBase._typeCount(0))
        referenceCountingBase._typeCount(0) -= 1
      End If
      Exit For
    End If
  Next I
End Destructor
main.bas (for testing the reference counting base):

Code: Select all

#Include "reference_counting_base.bi"

Type UDT1 Extends referenceCountingBase
  Declare Constructor ()
  Declare Constructor (Byref typeName As String)
  '.....
End type

Constructor UDT1 ()
  Base("UDT1")
  '.....
End Constructor

Constructor UDT1 (Byref typeName As String)
  Base(typeName)
  '.....
End Constructor


Type UDT2 Extends referenceCountingBase
  Declare Constructor ()
  Declare Constructor (Byref typeName As String)
  '.....
End type

Constructor UDT2 ()
  Base("UDT2")
  '.....
End Constructor

Constructor UDT2 (Byref typeName As String)
  Base(typeName)
  '.....
End Constructor


Type UDT11 Extends UDT1
  Declare Constructor ()
  Declare Constructor (Byref typeName As String)
  '.....
End type

Constructor UDT11 ()
  Base("UDT11")
  '.....
End Constructor

Constructor UDT11 (Byref typeName As String)
  Base(typeName)
  '.....
End Constructor

'-------------------------------------------------------------------------------

Sub printBornedDerivedType (Byref typeName As String)
  Print "Borned derived type: " & typeName
End sub
referenceCountingBase.bornedDerivedType = @printBornedDerivedType

Sub printExpiredDerivedType (Byref typeName As String)
  Print "Expired derived type: " & typeName
End sub
referenceCountingBase.expiredDerivedType = @printExpiredDerivedType

Sub referenceCountingDump ()
  Print referenceCountingBase.numberDerivedType()
  For I As Integer = 1 To referenceCountingBase.numberDerivedType()
    Print referenceCountingBase.DerivedTypeName(I), referenceCountingBase.numberOfReference(I)
  Next I
  Print
End Sub


referenceCountingDump()

Dim As UDT1 Ptr p11 = New UDT1
referenceCountingDump()

Dim As UDT2 Ptr p21 = New UDT2
referenceCountingDump()

Dim As UDT1 Ptr p12 = New UDT1
referenceCountingDump()

Dim As UDT11 Ptr p111 = New UDT11
referenceCountingDump()

Delete p21
referenceCountingDump()

Delete p11
referenceCountingDump()

Delete p12
referenceCountingDump()

Delete p111
referenceCountingDump()

Sleep

Code: Select all

 0

Borned derived type: UDT1
 1
UDT1           1

Borned derived type: UDT2
 2
UDT1           1
UDT2           1

 2
UDT1           2
UDT2           1

Borned derived type: UDT11
 3
UDT1           2
UDT2           1
UDT11          1

Expired derived type: UDT2
 2
UDT1           2
UDT11          1

 2
UDT1           1
UDT11          1

Expired derived type: UDT1
 1
UDT11          1

Expired derived type: UDT11
 0
I can comment or modify on any part of code that seems obscure or badly coded.


[edit]
- Update for compatibility with any fbc version (supporting inheritance and static members: fbc >= 0.90.0).
Last edited by fxm on Jun 02, 2018 19:42, edited 11 times in total.
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: Hello fxm our FB OOP guru :-)

Post by D.J.Peters »

Thank you for the reference counting code I will test it if i'm at home.

Do you wrote a class example with an overloaded NEW operator in the past ?

Joshy
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Hello fxm our FB OOP guru :-)

Post by fxm »

D.J.Peters wrote:Do you wrote a class example with an overloaded NEW operator in the past ?
I wrote an example in documentation at KeyPgOperator (second example).
coderJeff
Site Admin
Posts: 4326
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: Hello fxm our FB OOP guru :-)

Post by coderJeff »

I know is an old post, but it was referenced in another discussion, and I don't know where to comment...

A simple kind of singleton, and it is going to be used for the duration of the program's lifetime, is to place all of the singleton's data in it's own module, create a single instance with DIM SHARED and expose it to other modules with EXTERN. This uses basic modules/includes to control access to the instance.

Reference counting a single object object or creating a singleton (a special kind of reference counting) require that there is some common global place that all the references know about (through the object). In fxm's example, I see using an array of names and counts.

I think fxm has answered the questions by DJ, however, the solutions seem complex. Maybe there is a simpler way to structure the problem or the types involved?

The singleton is to ensure that a resource (data) has 1 instance at most. Could be shared by multiple objects or not.

The reference counting is to ensure that some chuck of data remains valid while references are still held to it. Could be multiple instances of that kind of data, or one.
Post Reply