Working with User-Defined Functions

About this chapter

This chapter describes how to build and use user-defined functions.

About user-defined functions

The PowerScript language has many built-in functions, but you may find that you need to code the same procedure over and over again. For example, you may need to perform a certain calculation in several places in an application or in different applications. In such a situation, create a user-defined function to perform the processing.

A user-defined function is a collection of PowerScript statements that perform some processing. After you define a user-defined function and save it in a library, any application accessing that library can use the function.

There are two kinds of user-defined functions, global and object-level functions.

Global functions

Global functions are not associated with any object in your application and are always accessible anywhere in the application.

They correspond to the PowerBuilder built-in functions that are not associated with an object, such as the mathematical and string-handling functions. You define global functions in the Function painter.

Object-level functions

Object-level functions are defined for a window, menu, user object, or application object. These functions are part of the object's definition and can always be used in scripts for the object itself. You can choose to make these functions accessible to other scripts as well.

These functions correspond to built-in functions that are defined for specific PowerBuilder objects such as windows or controls. You define object-level functions in a Script view for the object.

Deciding which kind you want

When you design your application, you need to decide how you will use the functions you will define:

  • If a function is general purpose and applies throughout an application, make it a global function.

  • If a function applies only to a particular kind of object, make it an object-level function. You can still call the function from anywhere in the application, but the function acts only on a particular object type.

    For example, suppose you want a function that returns the contents of a SingleLineEdit control in one window to another window. Make it a window-level function, defined in the window containing the SingleLineEdit control. Then, anywhere in your application that you need this value, call the window-level function.

Multiple objects can have functions with the same name

Two or more objects can have functions with the same name that do different things. In object-oriented terms, this is called polymorphism. For example, each window type can have its own Initialize function that performs processing unique to that window type. There is never any ambiguity about which function is being called, because you always specify the object's name when you call an object-level function.

Object-level functions can also be overloaded—two or more functions can have the same name but different argument lists. Global functions cannot be overloaded.

Defining user-defined functions

Although you define global functions in the Function painter and object-level functions in the painter for a specific object, in both cases you define and code the function in a Script view.

When you add a new function, a Prototype window displays above the script area in the Script view. The fields in the Prototype window are in the same order as the function's signature:

  • The function's access level, return type, and name

  • For each parameter, how it is passed, its datatype, and its name

  • The exceptions the function can throw, if any


The following sections describe each of the steps required to define and code a new function:

Opening a Prototype window to add a new function

How you create a new function depends on whether you are defining a global function or an object-level function.

To create a new global function

  • Select File>New from the menu bar and select Function from the PB Object tab.

    The Function painter opens, displaying a Script view with an open Prototype window in which you define the function.

To create a new object-level function

  1. Open the object for which you want to declare a function.

    You can declare functions for windows, menus, user objects, or applications.

  2. Select Insert>Function from the menu bar, or, in the Function List view, select Add from the pop-up menu.

    The Prototype window opens in a Script view or, if no Script view is open, in a new Script view.

Defining the access level

In the Prototype window, use the drop-down list labeled Access to specify where you can call the function in the application.

For global functions

Global functions can always be called anywhere in the application. In PowerBuilder terms, they are public. When you are defining a global function, you cannot modify the access level; the field is read-only.

For object-level functions

You can restrict access to an object-level function by setting its access level.

Access

Means you can call the function

Public

In any script in the application.

Private

Only in scripts for events in the object in which the function is defined. You cannot call the function from descendants of the object.

Protected

Only in scripts for the object in which the function is defined and scripts for that object's descendants.


If a function is to be used only internally within an object, you should define its access as private or protected. This ensures that the function is never called inappropriately from outside the object. In object-oriented terms, defining a function as protected or private encapsulates the function within the object.

Defining a return type

Many functions perform some processing and then return a value. That value can be the result of the processing or a value that indicates whether the function executed successfully or not. To have your function return a value, you need to define its return type, which specifies the datatype of the returned value.

You must code a return statement in the function that specifies the value to return. See Returning a value. When you call the function in a script or another function, you can use an assignment statement to assign the returned value to a variable in the calling script or function. You can also use the returned value directly in an expression in place of a variable of the same type.

To define a function's return type

  • Select the return type from the Return Type drop-down list in the Prototype window, or type in the name of an object type you have defined.

    You can specify any PowerBuilder datatype, including the standard datatypes, such as integer and string, as well as objects and controls, such as DataStore or MultiLineEdit.

    You can also specify as the return type any object type that you have defined. For example, if you defined a window named w_calculator and want the function to process the window and return it, type w_calculator in the Return Type list. You cannot select w_calculator from the list, because the list shows only built-in datatypes.

To specify that a function does not return a value

  • Select (None) from the Return Type list.

    This tells PowerBuilder that the function does not return a value. This is similar to defining a procedure or a void function in some programming languages.

Examples of functions returning values

The following examples show the return type you would specify for some different functions:

If you are defining

Specify this return type

A mathematical function that does some processing and returns a real number

real

A function that takes a string as an argument and returns the string in reverse order

string

A function that is passed an instance of window w_calculator, does some processing (such as changing the window's color), then returns the modified window

w_calculator


Naming the function

Name the function in the Function Name box. Function names can have up to 40 characters. For valid characters, see the section called “Identifier names” in PowerScript Reference.

For object-level functions, the function is added to the Function List view when you tab off the Function Name box. It is saved as part of the object whenever you save the object.

Using a naming convention for user-defined functions makes them easy to recognize and distinguish from built-in PowerScript functions. A commonly used convention is to preface all global function names with f_ and object-level functions with of_, such as:

// global functions
f_calc
f_get_result
// object-level functions
of_refreshwindow
of_checkparent

Built-in functions do not usually have underscores in their names, so this convention makes it easy for you to identify functions as user defined.

Defining arguments

Like built-in functions, user-defined functions can have any number of arguments, including none. You declare the arguments and their types when you define a function.

Passing arguments

In user-defined functions, you can pass arguments by reference, by value, or read-only. You specify this for each argument in the Pass By list.

By reference. When you pass an argument by reference, the function has access to the original argument and can change it directly.

By value. When you pass by value, you are passing the function a temporary local copy of the argument. The function can alter the value of the local copy within the function, but the value of the argument is not changed in the calling script or function.

Read-only. When you pass as read-only, the variable's value is available to the function but it is treated as a constant. Read-only provides a performance advantage over passing by value for string, blob, date, time, and datetime arguments, because it does not create a copy of the data.

If the function takes no arguments

Leave the initial argument shown in the Prototype window blank.

To define arguments:

  1. Declare whether the first argument is passed by reference, by value, or read-only.

    The order in which you specify arguments here is the order you use when calling the function.

  2. Declare the argument's type. You can specify any datatype, including:

    • Built-in datatypes, such as integer and real

    • Object types, such as window, or specific objects, such as w_emp

    • User objects

    • Controls, such as CommandButtons

  3. Name the argument.

    If you want to add another argument, press the Tab key or select Add Parameter from the pop-up menu and repeat steps 1 to 3.

Passing arrays

You must include the square brackets in the array definition, for example, price[ ]or price[50], and the datatype of the array must be the datatype of the argument. For information on arrays, see the section called “Declaring arrays” in PowerScript Reference.

Defining a THROWS clause

If you are using user-defined exceptions, you must define what exceptions might be thrown from a user-defined function or event. You use the Throws box to do this.

When you need to add a THROWS clause

Any developers who call the function or event need to know what exceptions can be thrown from it so that their code can handle the exceptions. If a function contains a THROW statement that is not surrounded by a try-catch block that can deal with that type of exception, then the function must be declared to throw that type of an exception or some ancestor of that exception type.

There are two exception types that inherit from the Throwable object: Exception and RuntimeError. Typically, you add objects that inherit from Exception to the THROWS clause of a function. Exception objects are the parents of all checked exceptions, which are exceptions that must be dealt with when thrown and declared when throwing. You do not need to add Runtime error objects to the THROWS clause, because they can occur at any time. You can catch these errors in a try-catch block, but you are not required to.

Adding a THROWS clause

You can add a THROWS clause to any PowerBuilder function or to any user event that is not defined by an event ID. To do so, drag and drop it from the System Tree, or type the name of the object in the box. If you type the names of multiple user objects in the Throws box, use a comma to separate the object names. When you drag and drop multiple user objects, PowerBuilder automatically adds the comma separators.

The PowerBuilder compiler checks whether a user-defined exception thrown on a function call in a script matches an exception in the THROWS clause for that function. It prompts you if there is no matching exception in the THROWS clause.

You can define a user-defined exception object, and inherit from it to define more specific lower-level exceptions. If you add a high-level exception to the throws clause, you can throw any lower-level exception in the script, but you risk hiding any useful information obtainable from the lower-level exception.

For more information about exception handling, see the section called “Handling exceptions” in Application Techniques.

Coding the function

When you have finished defining the function prototype, you specify the code for the function just as you specify the script for an event in the Script view. For information about using the Script view, see Writing Scripts.

What functions can contain

User-defined functions can include PowerScript statements, embedded SQL statements, and calls to built-in, user-defined, and external functions.

You can type the statements in the Script view or use the buttons in the PainterBar or items on the Edit>Paste Special menu to insert them into the function. For more information, see Pasting information into scripts.

Returning a value

If you specified a return type for your function in the Prototype window, you must return a value in the body of the function.To return a value in a function, use the RETURN statement:

RETURN expression

where expression is the value you want returned by the function. The datatype of the expression must be the datatype you specified for the return value for the function.

Example

The following function returns the result of dividing arg1 by arg2 if arg2 does not equal zero. It returns –1 if arg2 equals zero:

IF arg2 <> 0 THEN 
 RETURN arg1 / arg2
ELSE 
 RETURN -1
END IF

Compiling and saving the function

When you finish building a function, compile it and save it in a library. Then you can use it in scripts or other user-defined functions in any application that includes the library containing the function in its library search path. You compile the script and handle errors as described in Compiling the script.

Modifying user-defined functions

You can change the definition of a user-defined function at any time. You change the processing performed by the function by modifying the statements in the Script view. You can also change the return type, argument list, or access level for a function.

To change a function's return type, arguments, or access level:

  1. Do one of the following:

    • In the Function painter, open the global function.

    • Open the object that contains the object-level function you want to edit and select the function from the Function list.

  2. Make the changes you want in the Prototype window.

  3. If the Prototype window is hidden, click the toggle button to display it.

  4. Select File>Save from the menu bar.

To change a function's name:

  1. If desired, modify the function's return type, arguments, or access level as described in the previous procedure.

  2. Do one of the following:

    • In the Function painter, select File>Save As from the menu bar and enter a name.

    • In the Script view, enter a new name in the Function Name box.

    When you tab off the box, the new function name displays in the Function List view.

Changing the arguments

You can change a function's arguments at any time using the pop-up menu in the Prototype window:

  • Add an argument by selecting Add Parameter. Boxes for defining the new argument display below the last argument in the list.

  • Insert an argument by moving the pointer to the argument before which you want to insert the argument and selecting Insert Parameter. Boxes for defining the new argument display above the selected argument.

  • Delete an argument by selecting it and clicking the Delete button.

To change the position of an argument

To change the position of an argument, delete the argument and insert it as a new argument in the correct position.

Recompiling other scripts

Changing arguments and the return type of a function affect scripts and other functions that call the function. You should recompile any script in which the function is used. This guarantees that the scripts and functions work correctly during execution.

Seeing where a function is used

PowerBuilder provides browsing facilities to help you find where you have referenced your functions. In the System Tree or Library painter, select a target, library, or object and select Search from the pop-up menu. You can also search multiple entries in the Library painter:

To determine which functions and scripts call a user-defined function:

  1. Open the Library painter.

  2. In a List view, select all the entries you want to search for references to the user-defined function.

  3. Select Entry>Search from the menu bar.

  4. The Search Library Entries dialog box displays.

  5. Specify the user-defined function as the search text and specify the types of components you want to search.

  6. Click OK.

    PowerBuilder displays all specified components that reference the function in the Output window. You can double-click a listed component to open the appropriate painter.

For more about browsing library entities, see Searching targets, libraries, and objects.

Using your functions

You use user-defined functions the same way you use built-in functions. You can call them in event scripts or in other user-defined functions.

For complete information about calling functions, see Application Techniques.

Pasting user-defined functions

When you build a script in the Script view, you can type the call to the user-defined function. You can also paste the function into the script. There are four ways to paste a user-defined function into a script:

  • Drag the function from the System Tree to the Script view.

  • Select Edit>Paste Special>Function>User-defined from the menu bar.

  • Enable AutoScript, select the function's signature in the list that displays when you pause, and press Tab or Enter.

  • Select the function in the Browser and copy and paste it into the script.

Using the System Tree, AutoScript, or the Browser pastes the function's prototype arguments as well as its name into the script.

For more information about AutoScript, see Using AutoScript.

To paste a user-defined function into a script from the Browser:

  1. Select Tools>Browser from the menu bar.

  2. Do one of the following:

    • Select a global function from the Function page.

    • Select the object that contains the object-level function you want to paste from the corresponding page (such as the Window page).

    • Double-click the Functions category in the right pane.

    • Select the function you want to paste and select Copy from its pop-up menu.

  3. In the Script view, move the insertion point to where you want to paste the function and select Paste from the pop-up menu.

    The function and its prototype parameters display at the insertion point in your script.

  4. Specify the required arguments.