Object Oriented Programming

IEC 61131-3 Edition 3 (2013) added object-oriented programming to the standard. It builds on the function block — the language’s existing unit of reusable, stateful behavior — and adds three ideas: inheritance, interfaces, and abstract types. This page explains those concepts and the terminology the standard uses for them.

Note

The object-oriented syntax described here is recognized only when it is enabled; otherwise the keywords are ordinary identifiers. See Enabling Dialects and Features for the flag and dialects reference. How far IronPLC takes each construct beyond parsing varies — Object Oriented Programming gives the per-keyword status, and what is not yet analyzed reports P9999. This page describes the language concepts; it is not a statement of what IronPLC executes today.

Why object-oriented programming?

A function block already bundles data and behavior: it has internal state and runs code that acts on that state. Object-oriented programming extends this in two directions. First, it lets one function block type build on another instead of copying it — so shared behavior is written once and reused. Second, it lets code work with an instance through a contract rather than a specific type — so the same code can drive many different implementations. The standard expresses these with a small set of keywords.

Inheritance

Inheritance lets a function block type build on an existing one. A type is derived from a base type using the EXTENDS keyword. The derived type inherits the base type’s variables and methods, and it can add its own or refine what it inherited.

FUNCTION_BLOCK FB_Motor
    VAR
        running : BOOL;
    END_VAR
END_FUNCTION_BLOCK

FUNCTION_BLOCK FB_AdvancedMotor EXTENDS FB_Motor
    VAR
        speed : INT;
    END_VAR
END_FUNCTION_BLOCK

Here FB_AdvancedMotor is the derived type and FB_Motor is its base type. A function block type has at most one direct base type — the standard provides single inheritance for function block types. An instance of a derived type is a kind of its base type, so it can be used wherever the base type is expected.

Inherited names cannot be redeclared

A derived type inherits every variable of its base type, and it cannot declare a variable of its own with one of those names. The following is rejected with P4044, whatever the type of the second declaration:

FUNCTION_BLOCK FB_Base
    VAR
        state : INT;
    END_VAR
END_FUNCTION_BLOCK

FUNCTION_BLOCK FB_Derived EXTENDS FB_Base
    VAR
        state : BOOL;   (* Error: state is inherited from FB_Base *)
    END_VAR
END_FUNCTION_BLOCK

An instance of FB_Derived has exactly one state, the INT it inherits, and the derived type’s body and methods use that one. A derived type that needs another value declares it under a new name. Edition 3 inheritance gives a derived type a single set of variables and no way to hold a second one of an inherited name; CODESYS and TwinCAT reject the same code as a duplicate definition.

This is the same rule as declaring a name twice in one place, applied across the EXTENDS chain: two declarations of one name in a single VAR block are rejected with P4014, and a derived type’s declarations are checked against everything it inherits as though they shared that block.

Methods are different: a derived type may declare a method with the name of one it inherits. That overrides the inherited method — the derived type has one method of that name, and a call on the derived type runs it.

Hiding does exist in one place: a method’s own parameters and local variables. A method local with the same name as a variable of the function block hides that variable within the method, and the method reaches the hidden one through THIS^.

Referring to the instance and to the base type

Hiding raises a question: once a method local has hidden a member of the instance, how does the method reach the hidden one? The standard answers with two names for the instance a method is running on — THIS and SUPER.

THIS^ is the instance itself. It is what makes an instance variable reachable when a nearer name — a method parameter, say — hides it.

SUPER^ is the same instance seen as its base type. A derived type that overrides an inherited method uses it to call the implementation it overrode, so the derived behavior can add to the base behavior rather than replace it:

FUNCTION_BLOCK FB_LoggingMotor EXTENDS FB_Motor
    METHOD Stop
        SUPER^.Stop();
    END_METHOD
END_FUNCTION_BLOCK

Both are pointers to an instance, which is why each is written with the dereference operator ^. SUPER^ is resolved when the program is compiled, not while it runs: “the base type’s implementation” is decided by the declaration, not by what the instance turns out to be.

Interfaces

An interface is a named contract: a set of method signatures — the name, parameters, and return type of each method — with no implementation. An interface is declared with INTERFACE and closed with END_INTERFACE. A function block type states that it fulfils the contract with the IMPLEMENTS keyword, which obliges it to supply a body for every method the interface declares.

INTERFACE I_Drivable
END_INTERFACE

FUNCTION_BLOCK FB_Motor IMPLEMENTS I_Drivable
    VAR
        running : BOOL;
    END_VAR
END_FUNCTION_BLOCK

Unlike inheritance, interfaces are not limited to one: a function block type may implement several interfaces, and interfaces themselves may extend other interfaces to combine their contracts. Because any implementing type satisfies the same contract, code written against an interface can operate on instances of unrelated types — this is polymorphism, and selecting the right method implementation for the actual instance at run time is called dynamic binding (dynamic dispatch).

Interfaces and inheritance are complementary. Inheritance shares an implementation down a single line of descent; an interface shares a contract across any number of otherwise unrelated types. A declaration can use both at once — extend a base type and implement one or more interfaces.

Abstract types

A type marked ABSTRACT is one that is intended to be extended rather than instantiated on its own. An abstract function block type defines the shared shape of a family of types — common variables and method signatures — while leaving some behavior for derived types to complete. Because it is incomplete, an instance of an abstract type cannot be created directly; only a concrete type that extends it can be instantiated.

FUNCTION_BLOCK ABSTRACT FB_BaseAxis
    VAR
        enabled : BOOL;
    END_VAR
END_FUNCTION_BLOCK

FUNCTION_BLOCK FB_LinearAxis EXTENDS FB_BaseAxis
    VAR
        position : REAL;
    END_VAR
END_FUNCTION_BLOCK

FB_BaseAxis establishes what every axis has in common; FB_LinearAxis extends it into a concrete type that can be instantiated.

Terminology

Term

Meaning

Base type

The type another type is derived from.

Derived type

A type that extends a base type and inherits its members.

Inheritance

Deriving one type from another so it reuses the base type’s members.

Hiding (shadowing)

A method’s parameter or local variable takes precedence, within that method, over a member of the instance with the same name. A derived type cannot hide an inherited variable; see above.

Interface

A named set of method signatures with no implementation.

Implement

Supply a body for every method an interface declares.

Abstract type

A type that cannot be instantiated directly and is meant to be extended.

Polymorphism

Using instances of different types through a shared base type or interface.

Dynamic binding

Choosing the method implementation for the actual instance at run time.

Beyond EXTENDS, IMPLEMENTS, ABSTRACT, and INTERFACE, Edition 3 also defines METHOD, THIS and SUPER, PROPERTY, OVERRIDE, and FINAL for writing and refining methods. IronPLC parses METHOD, THIS, and SUPER; it does not parse PROPERTY, OVERRIDE, or FINAL yet.

See Also

References