Mr. Cluey
: How To ...? : Scope ResolutionA self-referencing method conflict occurs when a method, attempting to call a different method which has the same name, instead calls itself, falling into a recursive spiral.
winclass CMenu : MenuItem
{
Pick ()
{
Print( "Stand by to Click!" ) ;
Pick () ; //the problem child
Print( "Helm reports Click Complete" ) ;
}
}
What's the problem here? That Pick (), instead of calling MenuItem::Pick(), calls CMenu::Pick(). CMenu::Pick() then calls CMenu::Pick(), which calls CMenu::Pick()... the technical term is "Infinite Recursion" although, in this case, the recursion isn't really infinite - your stack space evaporates in a finite amount of time.
If we need to code this way, and we don't want to keep inventing new names to do the same old things, we need to tell QAP that we mean the original Pick() method. We need a magic wand, and the magic wand that we need is called the Scope Resolution Operator. It is described in appendix A of the 4Test Language Reference - but it is a very short read.
The syntax for using the scope resolution operator is scope::Method(...).
When the scope operator is absent, as in the example above, the Method definition for the
local scope is automatically used. If we want to use some other definition, we must
specify explicitly which scope defines the method we want to use.
Here is an example of the code above, with using the scope operator
winclass CMenu : MenuItem
{
Pick ()
{
Print( "Stand by to Click!" ) ;
MenuItem::Pick () ; //this calls the Pick() method as defined for the menu item.
Print( "Helm reports Click Complete" ) ;
}
}
In general, you won't want to worry about precisely which scope you want to use, so long as it isn't the one you are overloading at the moment. The keyword derived protects you against changes in your inheritance model. The derived keyword, documented in the User's Guide instructs QAP to use the method of the parent class. The derived keyword should be preferred over a classname whenever possible.
winclass CMenu : MenuItem
{
Pick ()
{
Print( "Stand by to Click!" ) ;
derived::Pick () ; //this works, even when we change the class heirarchy.
Print( "Helm reports Click Complete" ) ;
}
}
winclass CMenu2 : CMenu
{
Pick ()
{
Print( "Inside CMenu2::Pick" ) ;
derived::Pick () ; //calls CMenu::Pick()
}
}
Of course, CMenu may be inheriting its implementation of the method from somewhere else - it doesn't matter. The derived::Pick() call in the CMenu2 class will automatically call the Pick() in the CMenu scope, regardless of whether it is declared there, or somewhere further up the inheritance chain.
This model breaks down when what you are trying to do is call a function. Functions live in the global scope, not in any of the classes, so this won't work.
winclass CMenu : MenuItem
{
Print ( string sMsg)
{
derived::Print( "CMenu Reports:" + sMsg) ; //doesn't work MenuItem::Print() not defined.
}
}
You fix this by using the scope resolution operator to specify the global scope. The global scope is the default scope, so all you need do is put the operator in without a prefix.
winclass CMenu : MenuItem
{
Print ( string sMsg)
{
::Print( "CMenu Reports:" + sMsg) ;
}
}
Works like a champ.
The scope resolution operator is not commonly used, and the documentation available could not be described as thorough. Every time you use this operator, you should be sure to add a comment explaining why it is necessary.
I like using /*Global*/::Function() when using the scope resolution operator to access global functions - it makes it clear that you didn't forget to specify the scope. Unfortunately, converting the source to Visual 4Test will cause the comment to jump to the end of the line, where it loses its effectiveness.
| Mr. Cluey : How To ...? : Scope Resolution |