Constructor/Destructor identification

General FreeBASIC programming questions.
deltarho[1859]
Posts: 4310
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Constructor/Destructor identification

Post by deltarho[1859] »

Last August I had a problem and fxm came up with a very clever solution. In the end I decided against the idea I was looking at so did not use fxm's solution. The idea has cropped up again, so I revisited fxm's solution. The idea was to be able to identify which Constructor/Destructor was being executed where we have more than one instance of a UDT being employed.

I tweaked fxm's solution to make it more flexible and, hopefully, easier to understand. The crucial statement in fxm's original code, here, is 'ct += 1' and in the tweaked code 'temp += 1'.

For the UDT instances I have used instance0, instance1, and so on but, obviously, they can be any names you like. The instance ordering in the Scope statements needs to be the same as the ordering in the ID() array in Type MyParams. If not, we will be in serious trouble.

Run the following:

Code: Select all

'#include "windows.bi"
 
Type MyParams
  Public:
    Declare Constructor()
    Declare Destructor()
  Private:
    As UByte ct, dt
    As String ID(3) = { "instance0", "instance1", "instance2", "instance3" }
End Type
 
Constructor MyParams()
  Static As Byte temp = -1
  temp += 1
  ct = temp
  Print "Inside Constructor ";ID(ct)
'  if ID(ct) = "instance1" then messagebox(0,"Do you want me to load '" + ID(ct)+"SavedData.dat'", "Constructor "+ID(ct), MB_YESNO)
End Constructor
 
Destructor MyParams()
  Static As Byte temp = -1
  temp += 1
  dt = temp
  Print "Inside Destructor ";ID(dt)
'  if ID(dt) = "instance1" then messagebox(0,"Do you want me to create '" + ID(dt) + "SavedData.dat'", "Destructor "+ID(dt), MB_YESNO)
End Destructor
 
Scope
  Dim As MyParams instance0
  Dim As MyParams instance1
  Dim As MyParams instance2
  Dim As MyParams instance3
  Print
End Scope
 
Sleep
I get this:

Code: Select all

Inside Constructor instance0
Inside Constructor instance1
Inside Constructor instance2
Inside Constructor instance3
 
Inside Destructor instance0
Inside Destructor instance1
Inside Destructor instance2
Inside Destructor instance3

However, we are not making use of the identification.

If we remove the comment in both the Constructor and Destructor, and the “windows.bi” include, we get some additional work with instance1. For illustration purposes, I have used the Windows API MessageBox and do not respond to the selected choice.

Needless to say, there could be a myriad of types of additional work for the instance1 Constructor/Destructor or any other instance we decide to add more work to.

I am undecided whether to use the above in a particular application because it requires the user to get their hands dirty, and I don't really want to do that.

Anyway, I figured it worth mentioning as it may be food for thought in some of your applications.
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Constructor/Destructor identification

Post by fxm »

Why renumber the instances on destruction?
Why limit the number of instances?

Code: Select all

Type MyParams
  Public:
    Declare Constructor()
    Declare Destructor()
  Private:
    As UByte n
End Type
 
Constructor MyParams()
  Static As Byte temp = -1
  temp += 1
  n = temp
  Print "Inside Constructor instance";n
End Constructor
 
Destructor MyParams()
  Print "Inside Destructor instance";n
End Destructor
 
Scope
  Dim As MyParams instance0
  Dim As MyParams instance1
  Dim As MyParams instance2
  Dim As MyParams instance3
  Print
End Scope
 
Sleep
deltarho[1859]
Posts: 4310
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Constructor/Destructor identification

Post by deltarho[1859] »

Thanks, fxm.
fxm wrote:Why renumber the instances on destruction?
I wanted a correlation between Constructor and Destructor – the additional work may be polar opposites, as with the illustration, when the comments are removed giving a Save/Load environment. If we don't renumber the instances on destruction, the destructors are 'unrolled' 3 to 0 instead of 0 to 3. I need to check that out to see if any issues occur. There does not seem to be any issues with the OP as a 'First in, first out' scenario.

Using just 'n' oversimplifies and requires the user to remember what n=0 does, what n=1 does, and so on. I wanted to be able to use any name that I wanted, and hence the ID() array in MyParams. Having said that, some may prefer your 'stripped down' version, especially if they only have a couple of instances, for example. With many instances, some may prefer meaningful names. With a PRNG, like MsWsII, we could have an instance dedicated to a range float, for example, and instance6, say, would not be very readable.
Why limit the number of instances?
I'm not – I just chose four as an example.
deltarho[1859]
Posts: 4310
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Constructor/Destructor identification

Post by deltarho[1859] »

The question is: How does FB actually 'unroll' the following.

Code: Select all

Scope
  Dim As MyParams instance0
  Dim As MyParams instance1
  Dim As MyParams instance2
  Dim As MyParams instance3
  Print
End Scope
.
Logically, I would reckon 3 to 0 - 'First in, last out'

If that is true, then I cannot see the OP changing how FB works. So, I went on a mission to break the OP and did so.

With the uncommented code, I added 'et' to the statement 'As UByte ct, dt'. When inside Constructor instance1, I set 'et' to 99. When inside Destructor instance1 I read 'et' and got zero. Something was wrong.

I then used fxm's approach using 'As UByte n'. When inside Destructor instance1 I read 'et' and got 99; as expected.

So two things learnt for me anyway. FB 'unrolls' on a 'First in, last out' basis. Renumbering the instances on destruction actually screws things up.

I am going to stay with the ID() array because if I do use, this, I will probably have a few instances.

Here is the OP without renumbering the instances on destruction.

Code: Select all

'#include "windows.bi"
 
Type MyParams
  Public:
    Declare Constructor()
    Declare Destructor()
  Private:
    As UByte n
    As String ID(3) = { "instance0", "instance1", "instance2", "instance3" }
End Type
 
Constructor MyParams()
  Static As Byte temp = -1
  temp += 1
  n = temp
  Print "Inside Constructor ";ID(n)
'  if ID(n) = "instance1" then
'    messagebox(0,"Do you want me to load '" + ID(n)+"SavedData.dat'", "Constructor "+ID(n), MB_YESNO)
'  end if
End Constructor
 
Destructor MyParams()
  Print "Inside Destructor ";ID(n)
'  if ID(n) = "instance1" then
'    messagebox(0,"Do you want me to create '" + ID(n) + "SavedData.dat'", "Destructor "+ID(n), MB_YESNO)
'  end if
End Destructor
 
Scope
  Dim As MyParams instance0
  Dim As MyParams instance1
  Dim As MyParams instance2
  Dim As MyParams instance3
  Print
End Scope
 
Sleep
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Constructor/Destructor identification

Post by dodicat »

That looks right, last in first out.

Code: Select all

'#include "windows.bi"
 
Type MyParams
  Public:
    Declare Constructor()
    Declare Destructor()
  'Private:
  dim as ubyte dt
   static As UByte ct
End Type
dim MyParams.ct as ubyte

Constructor MyParams()
  print __function__,"instance";ct
  ct+=1
End Constructor
 
Destructor MyParams()
  ct-=1
  print __function__,"instance";ct,dt
 
End Destructor
 
Scope
  Dim As MyParams instance0
  Dim As MyParams instance1
  Dim As MyParams instance2
  Dim As MyParams instance3
  Print
End Scope
print
scope
dim as Myparams generalinst(7)
for n as long=0 to 7
with generalinst(n)
      .dt=n
end with
next
print
 end scope
Sleep 
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Constructor/Destructor identification

Post by fxm »

A simple monitoring of instance addresses is enough to demonstrate this well-known operation of construction/destruction according to the LIFO principle:

Code: Select all

Type MyParams
    Public:
        Declare Constructor()
        Declare Destructor()
    Private:
        Dim As Byte __
End Type
 
Constructor MyParams()
    Print "Constructor: address "; @This
End Constructor
 
Destructor MyParams()
    Print "Destructor: address "; @This
End Destructor
 
Scope
    Dim As MyParams instance0
    Dim As MyParams instance1
    Dim As MyParams instance2
    Dim As MyParams instance3
    Print
End Scope
 
Sleep
deltarho[1859]
Posts: 4310
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Constructor/Destructor identification

Post by deltarho[1859] »

Thanks, chaps. :)

Incorporating @This into the OP showed that Constructor instance1 and Destructor intance1 had differing addresses. The Save/Load would have failed. So, we would be saying 'Yes' to a save but having a different instance saved. Ouch. :o

Added: Doing the same with the latest version saw the 'books balance' so to speak. So saying 'Yes' to an instance1 save would see instance1 saved. :)

Saying 'Yes' to a save at instance1 on a Destructor run we would get 3, 2, 1 (Save), 0 and on a Constructor run saying 'Yes' to a load we would get 0, 1 (Load), 2, 3.
deltarho[1859]
Posts: 4310
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Constructor/Destructor identification

Post by deltarho[1859] »

It is worth noting that “well-known operation of construction/destruction according to the LIFO principle” is based upon how the Constructors were 'rolled out'.

The instances in the following can be in any order we like.

Code: Select all

Scope
  Dim As MyParams instance0
  Dim As MyParams instance1
  Dim As MyParams instance2
  Dim As MyParams instance3
  Print
End Scope
If the Constructors were 'rolled out' as 0,1,2,3 then the Destructors would be 'unrolled out' as 3,2,1,0 regardless of how the Scope block is ordered.

So,

Code: Select all

Scope
  Dim As MyParams instance2
  Dim As MyParams instance0
  Dim As MyParams instance3
  Dim As MyParams instance1
  Print
End Scope
would still be 'unrolled out' as 3,2,1,0 assuming that the Constructors were 'rolled out' as 0,1,2,3

With my method if we used 'As String ID(3) = { "instance2", "instance3", "instance1", "instance0" }' then the Constructors would be forced to be 'rolled out' as 2,3,1,0 and the Destructors would then be 'unrolled out' as 0,1,3,2 again regardless of the Scope ordering.

If we did not use fxm's instance identification method then the Constructor 'roll out' would be as the Scope ordering. What else could it be? The Destructor's 'unroll out' would then be based upon the Constructor's LIFO ordering. :)
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Constructor/Destructor identification

Post by fxm »

The reverse order between allocation/[construction] and [destruction]/deallocation makes sense compared to the principle of using a stack (push/pull) to store allocations/[construction information.
Same reverse order for processing element instances of an array for construction/destruction.
deltarho[1859]
Posts: 4310
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Constructor/Destructor identification

Post by deltarho[1859] »

What I like about these types of exercises is that if we keep digging deeper, we get to a point where we can turn the fog lights off. :D
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Constructor/Destructor identification

Post by dodicat »

Deltarho
I don't think the constructor roll out affects the destructor roll out
A constructor washes everything to default values and sets up the new values (if you want it to). That's about all it does.
Here I bypass constructors altogether in the first run.

Code: Select all

Type MyParams
    Public:
        Declare Constructor()
        Declare Destructor()
    'Private:
        Dim As long b=800 'default
End Type
 
Constructor MyParams()
   b+=rnd*20
    Print __function__;" "; @This;" ";b
End Constructor
 
Destructor MyParams()
    Print __function__;" "; @This;" ";b
End Destructor
randomize
 print "No constructors:"
Scope
    Dim As MyParams instance0=any
    Dim As MyParams instance1=any
    Dim As MyParams instance2=any
    Dim As MyParams instance3=any
    print @instance0,instance0.b
    print @instance1,instance1.b
    print @instance2,instance2.b
    print @instance3,instance3.b
    Print
End Scope
print
print
print "Constructor:"
Scope
    Dim As MyParams instance0
    Dim As MyParams instance1
    Dim As MyParams instance2
    Dim As MyParams instance3
    Print
End Scope
 
Sleep 
deltarho[1859]
Posts: 4310
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Constructor/Destructor identification

Post by deltarho[1859] »

dodicat wrote:I don't think the constructor roll out affects the destructor roll out
That is contrary to fxm and me, and you confirmed that in the second run.

In the first run the Contructors have no affect on the Destructors but why would they – there aren't any?

It is not clear to me what you are proving.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Constructor/Destructor identification

Post by dodicat »

It was you who said it deltarho
"It is worth noting that “well-known operation of construction/destruction according to the LIFO principle” is based upon how the Constructors were 'rolled out'."
I just show that the LIFO principle is independent of constructors.
That's all.
deltarho[1859]
Posts: 4310
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: Constructor/Destructor identification

Post by deltarho[1859] »

dodicat wrote:It was you who said it deltarho
I was quoting fxm.
I just show that the LIFO principle is independent of constructors.
That is a nonsense statement. The LIFO principle is based upon the Conrtructor's roll out. The Destructors will unroll in reverse order.

From your code:

Code: Select all

Scope
    Dim As MyParams instance0=any
    Dim As MyParams instance1=any
    Dim As MyParams instance2=any
    Dim As MyParams instance3=any
    print @instance0,instance0.b
    print @instance1,instance1.b
    print @instance2,instance2.b
    print @instance3,instance3.b
    Print
End Scope
The @instance0, @instance1, and so on directly correlate with the Scope ordering of instance0, instance1 and so on.

Change the first four lines to

Code: Select all

Dim As MyParams instance1=any
Dim As MyParams instance0=any
Dim As MyParams instance3=any
Dim As MyParams instance2=any
and you will see that @instance0, @instance1, and so on have changed accordingly.

What we have here is Scope ordering dependency, which we get if we do “not use fxm's instance identification method.”

If we start to force issues, as we do with fxm's method, Scope dependency is pushed into the long grass.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Constructor/Destructor identification

Post by dodicat »

Of course firsts created last destroyed by a destructor.
It has nothing to do with
scope
end scope
or constructors.

Code: Select all


type udt extends object
      as double d
      declare destructor
end type



destructor udt
print @this,d;tab(60);__function__
end destructor

Dim As udt instance1=any:print @instance1,instance1.d;tab(60);__function__
Dim As udt instance0=any:print @instance0,instance0.d;tab(60);__function__
Dim As udt instance3=any:print @instance3,instance3.d;tab(60);__function__
Dim As udt instance2=any:print @instance2,instance2.d;tab(60);__function__

print
print "done"



sub fin destructor
      print "press a key"
      sleep
end sub
 
"I just show that the LIFO principle is independent of constructors"
Why do you say this is a nonsense statement?
There is no construction of any sort above, not even an implicit default constructor, as can be seen by the values of .d, they are just revenants at the address.
Post Reply