On method level you can check the prerequisites before the method is executed (requires) and the results after the method has executed (ensure).
On Class level, you can only check the results after a method has executed (invariants).
It would be nice to have the possibility to check the prerequisites on class level too (private / public requires).
Example usage scenarios:
check if the object has been disposed before executing any method.
check if the object is ready to respond to any action (b.e. state not correct because of still executing async code)
That is not the case when async code is still running.
A simple example:
call method1 (normal method) - everything is ok
call async method2 - long running method, that sets a flag halfway the execution - as soon as this flag is set, the object is not in a state that it can execute any other method, until this flag has been reset.
call method3 (normal method) while method2 is still running.
With invariants, the flag is only checked when method3 finishes, not before it starts. If the flag has been reset when method3 finishes, there won’t be any assertion. The check should have been done before method3 executes.
And with the case of Dispose; the dispose method sets the IsDisposed flag. When I check on it with an invariant, it means that the dispose method itself will fail instead of any method executed after Dispose.
Ok, but methods are allowed (and often required) to invalidate invariants while they are running. So anything with threading and overlapped unprotected access to methods that mess with data protected by invariants is probably taking you down a path of pain anyways…
What I could see is, simply enforcing invariants before and after method calls (rather than just after?). how does that sound?
That’s correct, and that’s why I want to check for it using the class contracts - which are only available in the Debug builds. As I see it, class contracts are there to find the flaws.
Invariants are still only enforced after a method exists AFAIK. 80830 is still open, as this was a low priority feature request (and frankly hasn’t been requested again since 2018, so…)
Currently near the work I do for Island at the evening mostly, I m working with the pascal based engine called “CGE” and im trying to port critical render and physics code over to oxygene and write them as modules I can import to fpc (sadly the entire thing has to much LoC and is to heavy to refactor to oxygene and thus I need to make a bridge between fpc and oxygene here (maybe I can use hydra for this?) however, I have encournted that the main dev of CGE uses alot of custom types which are used inconjunction with defered execution which changes rendering and physics state and hence decides what shall be drawn in which state, so he has some code for instance:
TRenderCycle = 0..FrameCount;; //mostly 60FPS
and his code is cluttered (in respective proceudres) with if-else to check if a specific renderCycleCount has been reached (think it was 30 or so…)in asynchronous code, so the physicsengine can kick in and do its part and give controlback to the main-render update loop and so on, and this could be much much easier made with the suggestion @Theo69 gave in that case, since you define only one time in the TRenderer class that renderCycleCount <= countToCallIntoPhysicsCycle so this is actually a very neat Idea here. As always, would love to hear from you !
PS:
And based on the async state of the physic-engine, it will notify the renderengine when its done to give controlback and there are no other things which shall occur at that time so there is flawless rendering happening as i could see, so the phyiscsengine must get the state_flag from the render() call for it to proceed right after and before, so a flawless rrythmic can occur between render() and collision/mesh checking stuff, and his code kinda uglifies those checks over and over to gurantee a flawless sync between render and collisions and stuff, so I could definitely see a big benefit of this feature
Not tested yet, but the following aspect code should enable the pre-check:
Imports RemObjects.Elements.Cirrus
Imports RemObjects.Elements.Cirrus.Values
Imports RemObjects.Elements.Cirrus.Statements
Public Class PreCheckInvariantAspect
Inherits Attribute
Implements IMethodInterfaceDecorator
Public Sub HandleInterface(Services As IServices, aMethod As IMethodDefinition)
If defined("Debug") Then
//get all methods
For Each m In aMethod.Owner.GetMembers
For Each m1 In aMethod.Owner.GetMethods(m.Name)
If m1.Name <> aMethod.Name Then
Dim b = new BeginStatement
b.Add(new StandaloneStatement(new ProcValue(new TypeValue(aMethod.Owner), aMethod.Name, {})))
b.Add(new PlaceHolderStatement())
CType(m1, IMethodDefinition).ReplaceMethodBody(b)
End If
Next
Next
End If
End Sub
End Class
Usage:
//This method is (only in debug mode) called before the execution of each method within the class
<PreCheckInvariant>
Private Sub PreCheckInvariant
assert(x < 5, 'assertion test');
End Sub