A while back I came across @npalardy’s blog post Code for safety (best read that post first before you read on). And because I’ve made the error of not clearing flags a few times myself, I adopted his code for my own projects.
Yesterday I needed to create some new flags in a window, and I thought “Really, do I have to do the whole setup again?”. To create a
SetFlag and a
ClearFlag method for each flag and then pass these as delegates to that flipper class becomes a bit cumbersome with time. And clutters your code with all those set and clear methods. So I thought about how I could improve the whole setup.
What I came up with is a class called
CodeBlocker, which serves as the flipper and the actual flag. All you have to do to set up a flag with this class is:
- In the window/class/module create a flag property, e.g.
There is no need to initialize this property as this is done automatically as needed by the flipper.
myFlag as CodeBlocker
- In whichever method(s)/event(s) you want to activate the flag, type something like
This will set the flag and automatically clear it when
dim flipper as new CodeBlocker(myFlag) #pragma Unused flipper
- Wherever you need to query the status of the flag type
if CodeBlocker.IsActive(myFlag) then ... end if
How the class works
The class contains a private property for the actual flag. I don’t use a Boolean property here, because in some weird circumstances—when a flag gets set in more than one method coupled with some bad design or an unfortunate chain of events on Xojo’s part—the flag may get cleared prematurely. So instead of a Boolean I use an Integer counter which will be increased when the flag is “set”, and decreased when the flag is “cleared”.
To manipulate the counter I use the fact that a class can access private properties of other instances of the same class. This is done when you instantiate a new flipper or when it is destroyed. This is also the point where the flag is instantiated (if it isn’t already).
The shared method
IsActive checks if a) the flag is
not nil and b) the counter is
>0. Only if both checks are
true the flag is considered set.
Class CodeBlocker Private Sub Constructor() // This constructor is made private so that we can't instantiate a flipper // without a reference to an actual flag End Sub Sub Constructor(ByRef flag as CodeBlocker) if (flag Is nil) then // Because the "flag" parameter is defined as "ByRef", // the flag property of the class/module is instantiated // by this flag = new CodeBlocker end if flag.mCounter = flag.mCounter + 1 self.mFlag = new WeakRef(flag) End Sub Private Sub Destructor() if (self.mFlag Is nil) or (self.mFlag.Value Is nil) then return end if dim flag as CodeBlocker = CodeBlocker(self.mFlag.Value) flag.mCounter = flag.mCounter - 1 End Sub Shared Function IsActive(flag as CodeBlocker) as Boolean return not (flag Is nil) and (flag.mCounter > 0) End Function Private Property mCounter as Integer Private Property mFlag as WeakRef End Class
Let me know what you think. Useful or not, you decide.
This code is free for all to use, share and modify.
This code is given “as is”. No warranty is given in any form.