Knowledge Base

Procedural Types

Understanding Procedural Types in Delphi

In the world of programming, flexibility and modularity are critical for building maintainable and efficient applications. Procedural types in Delphi provide an elegant way to work with procedures and functions as first-class citizens. This capability empowers developers to assign procedures and functions to variables, pass them as parameters, and even store them in data structures.

In this post, we’ll dive into the concept of procedural types, explore their variations, and understand how they can simplify and enhance your Delphi applications.

What are Procedural Types?

At its core, a procedural type defines the “signature” of a procedure or function—essentially its parameter list, calling convention, and return type (if any). Doing so allows you to use procedures and functions dynamically, treating them as values that can be manipulated at runtime.

Procedural types can be categorized into three main types:

  • Procedure Pointers: Standalone procedures or functions that are not tied to any particular object.
  • Method Pointers: Procedures or functions that are bound to a specific instance of an object.
  • Anonymous Methods: Inline procedures or functions that can be declared and used directly where needed.

Each type has its own use cases and advantages, which we’ll discuss in detail below.

Declaring Procedural Types

To declare a procedural type, you start by defining the expected signature. This involves specifying the parameter list and return type. For example:

type 
  TSimpleProc = procedure; 
  TAddFunc = function(const Input1, Input2: Integer): Integer;

Here’s a more detailed example showcasing all three types:

type
  // Procedure pointer
  TCalculaterProc = procedure(const Amount, Perc: Currency);
  TCalculatorFunc = function(const Amount, Perc: Currency): Currency;

  // Method Pointer
  TMethodCalcProc = procedure(const Amount, Perc: Currency) of object;
  TMethodCalcFunc = function(const Amount, Perc: Currency): Currency of object;

  // Anonymous Method
  TAnonymousCalcProc = reference to procedure(const Amount, Perc: Currency);
  TAnonymousCalcFunc = reference to function(const Amount, Perc: Currency): Currency;

Notice how the of object keyword distinguishes a method pointer from a procedure pointer, and the reference to syntax defines anonymous methods.

How to use Procedural Types

Using procedural types effectively requires understanding their compatibility rules and how they interact with your code. Let’s explore each type in more detail with examples.

1. Procedure Pointers

A procedure pointer is a simple reference to a standalone procedure or function. It is not tied to any particular object. For example:

procedure TProcess.StartCalculate;
begin
  var Price := CalculatePrice(100.0, CalculateVat);
end;

function TProcess.CalculatePrice(const Amount: Currency; 
                                 const Calc: TCalculatorFunc): Currency;
begin 
  // Executes given calculator
  Result := Calc(Amount, 21);
end;

// Classic procedural function
function CalculateVat(const Amount, Perc: Currency): Currency;
begin
  Result := Amount * (1 + (Perc / 100));
end;

 

2. Method Pointers

A method pointer, on the other hand, references a method of a specific object instance. This is useful for object-oriented programming, where procedures and functions often operate on object state:

procedure TProcess.StartCalculate;
begin
  var Price := CalculatePrice(100.0, AddVat);
end;

function TProcess.CalculatePrice(const Amount: Currency; 
                                 const Calc: TMethodCalcFunc): Currency;
begin
  // Executes given calculator
  Result := Calc(Amount, 21);
end;

// Function of object
function TProcess.AddVat(const Amount, Percentage: Currency): Currency;
begin
  Result := CalculateVat(Amount, Percentage);
end;

 

3. Anonymous Methods

Anonymous methods provide the ultimate flexibility by allowing you to define a procedure or function directly where it’s needed. This can make your code cleaner and easier to read:

procedure TProcess.StartCalculate;
begin
  // Using an inline anonymous function
  var Price := CalculatePrice(100.0,
                  function(const Amount, Perc: Currency): Currency
                  begin
                    Result := Amount * (1 + (Perc / 100));
                  end);
end;

function TProcess.CalculatePrice(const Amount: Currency; 
                                 const Calc: TAnonymousCalcFunc): Currency;
begin
  // Executes given calculator
  Result := Calc(Amount, 21);
end;

 

Rules of compatibility

When working with procedural types, it’s essential to ensure compatibility. Two procedural types are compatible if they have:

  • The same calling convention.
  • The same return type (or no return type).
  • The same number and types of parameters in corresponding positions (parameter names do not matter).

However, procedure pointers are always incompatible with method pointers. This distinction is crucial when passing procedures to functions or assigning them to variables.

Advantages of using Procedural Types

Procedural types offer several advantages, including:

  • Flexibility: They allow you to dynamically assign and change the behavior of procedures and functions at runtime.
  • Code Reusability: They enable you to write generic functions and procedures that can operate on different operations.
  • Improved Readability: Anonymous methods, in particular, can make your code more concise and expressive.

Conclusion

Procedural types are a powerful feature in Delphi that enhances your ability to write modular, reusable, and readable code. By understanding the differences between procedure pointers, method pointers, and anonymous methods, you can choose the right tool for your specific use case and take your Delphi programming skills to the next level.

We hope this guide helps you harness the power of procedural types in your projects. Happy coding!

Written by Alan Bariani
Delphi developer

Contact

Let us help you to realise your ambitions

GDK Software UK

(+44) 20 3355 4470

GDK Software USA

+1 (575) 733-5744