gmplOverGenericStatement

 

OverGeneric Statement Summary

OverGeneric is a terminal, refactoring statement that occurs within Refactor statements. It is used to specify that individual subprogram parameters be implemented as being generic via an overloaded wrapper to accept a list of different types.

 

The attributes of the Generic statement are as follows:

 

Attribute Description
IdentifierThis required attribute specifies the symbol, the actual parameter to be made generic. If the containing Refactor statement had a FileFilter specified then this identifier should be specified relative to it; else, it should be specified relative to the root of the symbol table
TypesThis optional attribute contains a comma-delimited list of the types to be allowed for the generic parameter.
ConstraintThis optional parameter specifies a constraint type that can be applied to the declaration of the generic. The constaint types are as follows:

 

Constraint Description
structThe type argument must be a value type. Any value type except Nullable can be specified.
classThe type argument must be a reference type; this applies also to any class, interface, delegate, or array type.
newThe type argument must have a public parameterless constructor. When used together with other constraints, the new() constraint must be specified last.
baseclassThe type argument must be or derive from the specified base class.
interfaceThe type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic.
UThe type argument supplied for T must be or derive from the class U
 

The OverGeneric statement is especially important for dealing with ByRef parameters that have Variant or Object types. Note that this statement is closely related to the Overload and Generic refactoring statements which also deal with these types of parameters.

 

Consider the following simple VB6 code

 

Private Sub Form_Load()
   Dim fiveString As String
   Dim fiveNumber As Integer
   MySub False, fiveString
   MySub True, fiveNumber

   Dim sixNumber As Integer
   sixNumber = 1 + fiveNumber
   MsgBox "Five plus one is " & sixNumber

   Dim sixString As String
   sixString = "one plus " & fiveString
  MsgBox "In other words six is equal to " & sixString
End Sub

Private Sub MySub(ByVal i_Param1 As Boolean, ByRef o_Var As Variant)
   If i_Param1 Then
       o_Var = 5
   Else
       o_Var = "five"
   End If
End Sub
Though this is clearly a contrived example such uses of Variant ByRef parameters are quite common in VB6 code. The important thing to note is that the parameter returns either an integer value or a string value to an argument of the corresponding binary type. Using this default translation script

 

<gmBasic>
   <Storage Action="Create" Identifier="Overload" />
   <Select DevEnv="VS2013" />
   <Select Dialect="csh" />
   <Select BuildFile="local" />
   <Select DeployLocation="C:\Temp" />
   <Select System="..\..\FromIdl" />
   <Compile Project="..\vb6\Project1.vbp" >
   </Compile>
   <Analyse />
   <Output Status="New" Filename="..\csh\Overload.bnd" />
   <Author />
   <Storage Action="Close" />
</gmBasic>
produces this translation

 

private void Form_Load(object sender, EventArgs e)
{
   string fiveString = "";
   int fiveNumber = 0;
   int refTemp1 = Convert.ToInt32(fiveString);  <=======
   MySub(false,ref refTemp1);
   MySub(true,ref fiveNumber);

   int sixNumber = 0;
   sixNumber = 1 + fiveNumber;
   System.Windows.Forms.MessageBox.Show("Five plus one is " + sixNumber, "", MessageBoxButtons.OK);

   string sixString = "";
   sixString = "one plus " + fiveString;
  System.Windows.Forms.MessageBox.Show("In other words six is equal to " + sixString, "", MessageBoxButtons.OK);
}
private void MySub(bool i_Param1,ref int o_Var)   <======
{
   if (i_Param1)
   {
      o_Var = 5;  <======
   }
   else
   {
      o_Var = Convert.ToInt32("five");
   }
}
Since the tool has no consistent typing in the the arguments being passed to the parameter o_Var, it takes the first assignment, which is an integer assignment, and types the parameter accordingly. The consequences of this is that the string return case will either throw an exception or return zero. Regardless of this, even if a value is returned to the Form_Load method it will end up being stored in the refTemp1 variable and not in fiveString. Using a Refactor.Fixtype statement like the following

 

<FixType identifier="Project1.Form1.MySub.o_Var" Type="Variant" status="ObjectOnly" />
produces this improved translation of MySub

 

private void Form_Load(object sender, EventArgs e)
 {
   string fiveString = "";
   int fiveNumber = 0;
   object argTemp1 = fiveString;
   MySub(false,ref argTemp1);
   object argTemp2 = fiveNumber;
   MySub(true,ref argTemp2);

   int sixNumber = 0;
   sixNumber = 1 + fiveNumber;
   System.Windows.Forms.MessageBox.Show("Five plus one is " + sixNumber, "", MessageBoxButtons.OK);

   string sixString = "";
   sixString = "one plus " + fiveString;
   System.Windows.Forms.MessageBox.Show("In other words six is equal to " + sixString, "", MessageBoxButtons.OK);
}
private void MySub(bool i_Param1,ref object o_Var)
{
   if (i_Param1)
   {
      o_Var = 5;
   }
   else
   {
      o_Var = "five";
   }
}
but makes the calling code in Form_Load even worse. Now neither source argument actually ends up receiving its intended value. The OverGeneric statement resolves this problem by treating the parameter as though it had been marked as ObjectOnly and defining the parameter as being generic in an overloaded wrapper.

 

<gmBasic>
   <Storage Action="Create" Identifier="Overload3" />
   <Select DevEnv="VS2013" />
   <Select Dialect="csh" />
   <Select BuildFile="local" />
   <Select DeployLocation="C:\Temp" />
   <Select System="..\..\FromIdl" />
   <Compile Project="..\vb6\Project1.vbp" >
      <Refactor>
         <OverGeneric identifier="Project1.Form1.MySub.o_Var" Types="Integer,String" />
      </Refactor>
   </Compile>
   <Analyse />
   <Output Status="New" Filename="..\csh\Overload3.bnd" />
   <Author />
   <Storage Action="Close" />
 </gmBasic>
In the Generic statement the Identifier attribute contains the fully qualified identifier of the parameter and the Types attribute specifies that there are to be two types of arguments expected in the calls to this method. These types are not actually used in the translated code, but are used to check argument types passed to the parameter. If the Types attribute is nor specified then any type argument may be passed.

 

The resultant translation is as follows.

 

private void Form_Load(object sender, EventArgs e)
{
   string fiveString = "";
   int fiveNumber = 0;
   MySub(false,ref fiveString);
   MySub(true,ref fiveNumber);

   int sixNumber = 0;
   sixNumber = 1 + fiveNumber;
   System.Windows.Forms.MessageBox.Show("Five plus one is " + sixNumber, "", MessageBoxButtons.OK);

   string sixString = "";
   sixString = "one plus " + fiveString;
   System.Windows.Forms.MessageBox.Show("In other words six is equal to " + sixString, "", MessageBoxButtons.OK);
}
private void MySub(bool i_Param1,ref object o_Var)
{
   if (i_Param1)
   {
      o_Var = 5;
   }
   else
   {
      o_Var = "five";
   }
}
public void MySub<T>(bool i_Param1,ref T o_Var)
{
   object TemporaryArg1 = o_Var;
   MySub(i_Param1,ref TemporaryArg1);
   o_Var = (T)(TemporaryArg1);
}
In this overgeneric translation the calling code is able to pass its local variables directly ByRef; so those variables will receive their intended values. The actual method code does not force any typing or conversion operations on the variant parameter; but rather the overloaded wrapper method transfers the parameter instance to a temporary which is then passed to the actual method. Upon return the temporary value is stored back into the original parameter value.

 

Table of Contents