Thank you for sure I will use this on my later post.
I’m glad you are talking about this, it is always good to have other developer perception of the way Clarion works, moreover, on this part of the language for which it is powerful. Just the reference to the generic &QUEUE, the generic can reference any TYPE QUEUE structure.
I’ve learned what you are explaining by reading the code of Gus Greece from Clarion Handy Tools, now I’m always doing the following in the construct and destruct and checking the NULL presence when needed.
`In the construct procedure, I'll do the following three statements.`
SELF.ValidateCodeSet &= NULL
SELF.ValidateCodeSet &= NEW jcWhenTaskService
? ASSERT(~SELF.ValidateCodeSet &= NULL,'SELF.ValidateCodeSet not initialized properly to jcWhenTaskService')
In the destruct procedure, it will be
IF ~SELF.ValidateCodeSet &= NULL THEN
DISPOSE(SELF.ValidateCodeSet)
SELF.ValidateCodeSet &= NULL
END
Thank you Mike for sharing
I would replace
with just
SELF.ValidateCodeSet &= NEW jcWhenTaskService
Even in the special case of an ANY in a queue.
Were you’d still want to rely on CLEAR( MyQueue )
As to the destruct, I would replace
with just
DISPOSE(SELF.ValidateCodeSet)
This is precisely how I was handling the reference and disposal before, just as you are demonstrating.
SELF.ValidateCodeSet &= NULL
SELF.ValidateCodeSet &= NEW jcWhenTaskService
? ASSERT(~SELF.ValidateCodeSet &= NULL,'SELF.ValidateCo
When I initially started using this, I found myself wondering why. To clarify, I encountered a situation where a reference to NULL was definitively established. This indicates that there’s no data in memory associated with the property in question. Before creating a new reference to this property, we employ this practice to avoid initializing a new reference in the same memory area. The ASSERT statement is then used to confirm the presence of the property in memory with the correct reference.
IF ~SELF.ValidateCodeSet &= NULL THEN
DISPOSE(SELF.ValidateCodeSet)
SELF.ValidateCodeSet &= NULL
END
Regarding the ‘dispose’ operation, it checks whether the property in the reference still exists in memory, essentially verifying if it is ‘NOT NULL.’ If it is still in memory (i.e., ‘NOT NULL’), we proceed with DISPOSE. Then, we ensure that the property is no longer in memory.
In the context of DISPOSE, this procedure prevents double disposal of the same property in memory. For example, I have also achieved this by implementing a ‘kill’ procedure within a class, which includes a boolean variable check to determine if it has not already been ‘killed.’ If the condition over the boolean variable checks out as ‘NOT,’ then the ‘Kill’ procedure is called, thus preventing the double invocation of the ‘Kill’ procedure."
IF ~SELF.Killed THEN
SELF.Kill()
END
At first, I thought that excessive referencing and NULL checks might not be necessary. However, I eventually realized their utility, especially when I’m dealing with numerous referenced properties in the automatic CONSTRUCT and DESTRUCT procedures of a class. Many of my classes heavily rely on properties like QUEUE, GROUP, and other CLASSES. Typically, initialization occurs in the CONSTRUCT procedure, and disposal in the DESTRUCT procedure of the class.
Since these procedures are automatically called at the beginning and end of an instance of the property, debugging can be challenging. Nonetheless, it is invaluable for ensuring that the required references are initialized and disposed of correctly. The drawback is that debugging in these areas can be tricky. To mitigate this, we also employ Init and Kill procedures within a class to initialize and dispose of referenced properties. This approach requires us to call the Init and Kill procedures for each instance.
The most common issues encountered with references made in the Construct and Destruct procedures are ‘Access Violation’ and ‘Stack Overflow’ errors. To tackle these issues, I use tools like the Clarion Debugger, DebugView, STOP statements, and sometimes MESSAGE statements.
I have successfully resolved those bugs. While working with object classes that share interdependencies, I must admit that I have encountered situations where debugging took a considerable amount of time. Here’s how I’ve found an effective way to address this issue. I’m not too cautious.
If your concern is about avoiding memory leaks then just assigning SELF.ValidateCodeSet &= NULL
will not help with that.
If it’s the case that SELF.ValidateCodeSet is the only reference to the data that NEW’d into it.
Then will you will loose the only reference to that instance once you &= it to something else (including NULL)
If that is your concern then I would replace
with
IF SELF.ValidateCodeSet &= NULL
SELF.ValidateCodeSet &= NEW jcWhenTaskService
END
or
DISPOSE( SELF.ValidateCodeSet )
SELF.ValidateCodeSet &= NEW jcWhenTaskService
Remember HELP[ DISPOSE ] says
“… This reference may be NULL and no ill effects will occur. …”
I want to point out something that is often missed.
Look at the following code,
SELF.ValidateCodeSet &= NEW jcWhenTaskService
! typically you'd do something with the property at this point
DISPOSE( SELF.ValidateCodeSet )
IF SELF.ValidateCodeSet &= NULL
MESSAGE('SELF.ValidateCodeSet is NULL')
ELSE
MESSAGE('SELF.ValidateCodeSet is NOT NULL')
END
Which message do you expect to see ?
The answer is… ( drumroll )…
SELF.ValidateCodeSet is NULL
As to debugging in .Destructs, for the most part it isn’t any harder (or easier) to debug a .Destruct as any other code. The lone issue is when using code to call a class object for the logging / outputDebugString calls on instances that are being constructed/destructed at program startup/closedown. The challenge there is to make sure that your debugging class has already been setup and not yet torn down. For that you can try changing where your debugger class appears in your code, and you might try using code like PRAGMA ('define(init_priority=>19)')
as seen in ABFile.clw. Note: I’m unclear as to what values to set in that define.
FWIW -
Checking “IF ~SELF.ValidateCodeSet &= NULL” does absolutely nothing to test whether or not the object is in memory. It just checks to see whether or not there’s an address that it’s pointing to. So, if your object was already disposed from another reference the test is not useful.
e.g.
Ref1 &STRING
Ref2 &STRING
CODE
Ref1 &= NEW STRING(20)
Ref2 &= Ref1
DISPOSE(Ref1)
So now, Ref2 still points to an address. It’s not valid for use, but it’s still pointing to it.
Which message do you expect to see ?
The answer is… ( drumroll )…
would be
MESSAGE(‘SELF.ValidateCodeSet is NULL’)
meaning absent from memory, the DISPOSE has done his job to remove the reference property from memory.
PRAGMA (‘define(init_priority=>19)’)`
Regarding the PRAGMA statement I’ve look the ABFILE.CLW called under MEMBER statement. It is a a good idea to use. I will add this in my Debug class. From what I’ve read about the “define(init_priority=>19)” parameter we need, there is no special value. I’ve read in the help define it default to 5.
#pragma define(init_priority => n) pw
Specifies a number (n) that is compatible with the C++module priority schema. Default is 5.
In my debug class, I aim to optimize the initialization process by calling it as early as possible within the construct procedure. Similarly, in the destruct procedure, I strive to delay the disposition step until as late as possible. Although I have attempted this approach before, I plan to refine it further by ensuring a comprehensive initialization at the start of the construct procedure and postponing disposition until the end of the destruct procedure.
Thank you Mark for your input.