Home > Programmer's Guide > Advanced Issues
Polymorphic functions
A few predefined functions and methods in HB++ are polymorphic. This section covers the concept of polymorphism, and describes the inference rules the compiler uses to determine which version of a function to call according to the type of its parameters.
Note: polymorphism is reserved for predefined functions; you can take advantage of using these functions, but you cannot define your own polymorphic functions.
Polymorphism
In some circumstances, it is useful to write a function that can accept any data type for one of its parameters. For example, elements in a Collection can be referenced either by specifying a zero-based index (a numerical value), or by specifying a key (a character string). This can be easily achieved by using a type free parameter such as a Variant, but doing so leads to serious overheads:
HB++ is a strongly typed language. This means that the compiler can determine at compile time the exact type of any expression. Thus, a better and clever way of solving this problem would be to write as many versions of a function as needed, one for each data type it can handle, and let the compiler rely on type information gathered at compile time to decide which version to call.
Polymorphism precisely allows that. For example, consider the Format function. Its first parameter can be either a numerical value or a date. So, it comes with two flavors (optional parameters have been omitted for clarity):
Public Function Format(ByVal Value As Double, ByRef sFormat As String) As String Public Function Format(ByVal Value As Date, ByRef sFormat As String) As String
When the compiler encounters a call to this function in your code, it scrutinizes the expression you specified as first parameter and determines its exact type. It can then use inference rules to determine what version of the Format function best suit your intents. For example:
str = Format(Sgn(x),"mm/dd/yyyy")
In the code above, Sgn(x) returns an Integer. Since this type can be converted to a Double but cannot be converted to a Date, the compiler chooses to generate code to convert the first parameter to a Double and to call the first flavor of the function.
str = Format(Now(),"mm/dd/yyyy")
In the code above, Now() returns a Date. Since this type matches the second flavor of the function, the compiler choose to generate code to call it directly.
As you can see, polymorphic functions can greatly reduce execution overhead by performing type checking at compile time rather than runtime. The conterpart is that several copies of each function have to be provided, one for each type it can accept; in some circumstances, this can increase executable size.
Inference rules
Determining at compile time which version of a polymorphic function has to be called according to its arguments type is performed using inference rules. Those rules have to be strong enough to avoid programming errors; but they also have to be tolerant enough to save the programmer from using too many explicit type conversions.
Suppose that a polymorphic function comes with N flavors:
Public Sub Foo(ByVal arg as Type1) Public Sub Foo(ByVal arg as Type2) ... Public Sub Foo(ByVal arg as TypeN)
Suppose now that the compiler encounters a call to this function where the parameter has the type Type. To determine which call best suits the programmer's intent, the following rules are applied successively until a match is found:
| Type... | Set is... |
| Boolean | Empty |
| Byte | Integer, Long, Single, Double |
| Integer | Long, Single, Double |
| Long | Single, Double |
| Single | Double |
| Double | Empty |
| Date | Empty |
| String | Empty |
| A class | All the classes that derives from this class. |
Then, the compiler compares successively each of the types in this set, from the least to the most precise, with each of Type1, Type2,...TypeN. If a match is found, the relevant function is selected and the an implicit conversion is inserted.
Those rules have been carefully designed to provide an intuitive behavior. However, you should always pay attention when calling one of the predefined polymorphic functions. In the following example, the user is provided a text field where they can enter a numerical value. This value is used by the program to access an item in a Collection :
Private Sub Button1_Click() Dim v as Variant v = MyCollection.Item(Field1.Text) MsgBox CStr(v) End Sub
This code may not work as expected. Since Field1.Text returns a string, the compiler will call the flavor of Item that treats its argument as a key, not an index. Instead, you should write:
v = MyCollection.Item(CInt(Field1.Text))
Reference
The following predefined functions are polymorphic: