Function request: Class contracts - require on class level

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)

you can use invariants for that?

No, because invariants are executed after a method is executed instead of before.

right. but invariants are enforces to always be true. if they were true at the end of one call, they’ll be true before the next one, too?

I am not sure if I understand you right …

That is correct.

That is not the case when async code is still running.

A simple example:

  1. call method1 (normal method) - everything is ok
  2. 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.
  3. 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.

2 Likes

Good Point actually :face_with_monocle:

@mh
The discussion has stopped - does this mean No?
In that case: No problem but I like to have an answer.

No decision has been made.

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?

2 Likes

I agree with @mh regarding unprotected access. Have to check if an object is disposed or not appears to me like a design flaw.

Sounds good.

Thanks, logged as bugs://80830

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.

2 Likes

can you add the option precheck to the invariants?

private invariants
  MyField.Value > 10; precheck; //this check is done before (not after) method execution
  AnotherField = 0; //this check is done after method execution

This would solve all.

2 Likes

Does that actually work now? I had not have time to test that.

Since this is still in “logged as bugs” and not fixed … hence my question below

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 :slight_smile:

1 Like

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