Skip to content
HN On Hacker News ↗

Modern Object Pascal Introduction for Programmers | Castle Game Engine

▲ 204 points • 90 comments • by birdculture • 2w ago • HN discussion ↗

Pangram verdict · v3.3

We believe that this entire text is human-written.

0 %

AI likelihood · overall

Human
100% human-written 0% AI-generated
SEGMENTS · HUMAN 1 of 1
SEGMENTS · AI 0 of 1
WORD COUNT 1,567
PEAK AI % 0% · §1
Analyzed
Sep 26
backend: pangram/v3.3
Segments scanned
1 windows
avg 1567 words each
Distribution
100 / 0%
human / AI fraction
Verdict
Human
Pangram v3.3

Article text · 1,567 words · 1 segments analyzed

Human AI-generated
§1 Human · 0%

Table of Contents 1. Why this book 2. Basics 2.1. "Hello world" program 2.2. Compilers and FPC "syntax modes" 2.3. Functions, procedures, primitive types 2.4. Testing (if) 2.5. Logical, relational and bit-wise operators 2.6. Testing single expression for multiple values (case) 2.7. Enumerated and ordinal types and sets and constant-length arrays 2.8. Loops (for, while, repeat, for .. in) 2.9. Output, logging 2.10. Converting to a string 3. Units 3.1. Overview 3.2. Extensions used for units and programs 3.3. Initialization and finalization 3.4. Units using each other 3.5. Qualifying identifiers with unit name 3.6. Exposing one unit identifiers from another 4. Classes 4.1. Basics 4.2. Inheritance, virtual methods, override, reintroduce 4.3. Classes and class instances, constructors, destructors 4.4. Testing class (is), typecasting (as, TMyClass(X)) 4.5. Properties 4.5.1. Serialization of properties 4.6. Exceptions - Quick Example 4.7. Visibility specifiers 4.8. Default ancestor 4.9. Self 4.10. Calling inherited method 4.11. Virtual methods, override and reintroduce 5. Freeing classes 5.1. Remember to free the class instances 5.2. How to free 5.3. Manual and automatic freeing 5.4. The virtual destructor called Destroy 5.5. Free notification 5.6. Free notification observer (Castle Game Engine) 6. Exceptions 6.1. Overview 6.2. Raising 6.3. Catching 6.4. Finally (doing things regardless of whether an exception occurred) 6.5. How the exceptions are displayed by various libraries 7. Run-time library 7.1. Input/output using streams 7.2. Containers (lists, dictionaries) using generics 7.3. Cloning: TPersistent.Assign 8. Various language features 8.1. Local (nested) routines 8.2. Callbacks (aka events, aka pointers to functions, aka procedural variables) 8.3. Anonymous functions 8.4. Generics 8.5. Overloading 8.6. Preprocessor 8.7. Records 8.8. Variant records and related concepts 8.8.1. Variant records 8.8.2. Variant type 8.8.3. TVarRec in array of const 8.9. Old-style objects 8.10. Pointers 8.11. Operator overloading 9. Advanced classes features 9.1. Private and strict private 9.2. More stuff inside classes and nested classes 9.3. Class methods 9.4. Class references 9.5. Static class methods 9.6. Class properties and variables 9.7. Class helpers 9.8. Should constructors and destructors be virtual? 9.8.1. Destructors 9.8.2. Constructors 9.9. An exception in constructor 10. Interfaces 10.1. Interfaces GUIDs 10.2. Typecasting interfaces 10.3. CORBA and COM types of interfaces 10.4. Reference-counted (COM) interfaces 10.5. Using COM interfaces with reference-counting disabled 11. About this document 1. Why this book I wanted to describe the modern Object Pascal: programming language with classes, units, generics, interfaces and other modern features you expect. I wanted to show how all the language features, basic and advanced, connect together into a consistent whole. I also wanted this book to be practical and concise to fellow developers. As such, I assume you already have some programming experience, and we can talk about things like "how to declare a variable" and avoid a lengthy explanation "what even is a variable and what is its purpose". When covering the basics, I will give a brief description, and then move on, like this: a variable is a container for some value; the container has a name; the value it holds may change over time. I emphasize the word modern in modern Object Pascal. That’s because Pascal has evolved a lot, and it’s quite different from e.g. Turbo Pascal that many people learned in schools long time ago. Feature-wise, modern Pascal is quite similar to C++ or Java or C#. It has all the modern features you expect — classes, units, interfaces, generics… It’s compiled to a fast, native code, It’s very type safe, High-level but can also be low-level if you need it to be. We also have an active ecosystem of tools and libraries. To name just a few: Pascal has an excellent, portable and open-source compiler called the Free Pascal Compiler, http://freepascal.org/ . And an accompanying IDE (editor, debugger, a library of visual components, form designer) called Lazarus http://lazarus.freepascal.org/ . There’s also a proprietary and commercial compiler and IDE Delphi https://www.embarcadero.com/products/Delphi . There’s a lot of libraries (for both FPC and Delphi) available, see https://github.com/Fr0sT-Brutal/awesome-pascal . We also support existing editors like VS Code, see https://castle-engine.io/vscode . Myself, I’m the creator of Castle Game Engine, https://castle-engine.io/ , which is an open-source 3D and 2D game engine using modern Pascal to create games on many platforms (Windows, Linux, FreeBSD, macOS, Android, iOS, Nintendo Switch, WebGL). 2. Basics 2.1. "Hello world" program // Just use this line in all modern FPC sources. {$ifdef FPC} {$mode objfpc}{$H+}{$J-} {$endif} // Below is needed for console programs on Windows, // otherwise (with Delphi) the default is GUI program without console. {$ifdef MSWINDOWS} {$apptype CONSOLE} {$endif} program MyProgram; begin WriteLn('Hello world!'); end. This is a complete program that you can compile and run. If you use the command-line FPC, just create a new file myprogram.dpr and execute fpc myprogram.dpr. If you use Lazarus, create a new project (menu "Project → New Project → Simple Program"). Paste this as the program source code. Compile using the menu item "Run → Compile" (or use shortcut Ctrl + F9). If you use Delphi, also create a new project (menu "File → New → Console Application - Delphi"). Paste this as the program source code. Compile using the menu item "Project → Compile" (or use shortcut Ctrl + F9). This is a command-line program, so just run the compiled executable from the command-line. Note You can also run it from Lazarus or Delphi IDE using the "Run" menu item (shortcut F9 in both IDEs). In this case, note that the console will appear and disappear quickly. The simplest way to avoid it is to add Readln (wait for Enter) at the end of the application. The rest of this article talks about the Object Pascal language, so don’t expect to see anything more fancy than the command-line stuff. If you want to see something cool, just create a new GUI project in Lazarus ("Project → New Project → Application") or Delphi ("File → New → Multi-Device Application"). Voila — a working GUI application, cross-platform, with native look everywhere, using a comfortable visual component library. The Pascal compilers come with lots of standard units for networking, GUI, database, file formats (XML, json, images…), threading and everything else you may need. I already mentioned my cool Castle Game Engine earlier:) 2.2. Compilers and FPC "syntax modes" This book, all the text and Pascal examples, has been written to support two modern Pascal compilers: Free Pascal Compiler (FPC), open-source Pascal compiler, used also by the Lazarus IDE. Delphi, a proprietary Pascal compiler from Embarcadero. In this book, we support fully both compilers. To complicate matters a bit, FPC compiler has multiple "syntax modes". In this book, we decided to show the ObjFpc syntax mode, which is recommended by the FPC developers and is the default for new Pascal projects created using Lazarus or Castle Game Engine. It’s a bit different from the Delphi syntax mode, which is most compatible with Pascal language as implemented by Delphi. We wrote a detailed comparison here. But you don’t want to read about these differences now, if you’re just starting to learn Pascal! The differences are minor, both between compilers and between FPC ObjFpc mode and Delphi mode. Just be aware you may see some {$ifdef FPC} … {$endif} clauses in the examples, that make the code valid for both FPC ObjFpc mode and Delphi. Using {$ifdef FPC_OBJFPC} … {$endif} in some of these cases would be more precise, but look even more complicated. If your project targets only one of these compilers, you can simplify your code, just pick the variant for your compiler and remove the {$ifdef …}, {$endif} stuff. 2.3. Functions, procedures, primitive types {$ifdef FPC} {$mode objfpc}{$H+}{$J-} {$endif} {$ifdef MSWINDOWS} {$apptype CONSOLE} {$endif} program MyProgram; procedure MyProcedure(const A: Integer); begin WriteLn('A + 10 is: ', A + 10); end; function MyFunction(const S: string): string; begin Result := S + 'strings are automatically managed'; end; var X: Single; begin WriteLn(MyFunction('Note: ')); MyProcedure(5); // Division using "/" always makes float result, use "div" for integer division X := 15 / 5; WriteLn('X is now: ', X); // scientific notation WriteLn('X is now: ', X:1:2); // 2 decimal places end. To return a value from a function, assign something to the magic Result variable. You can read and set the Result freely, just like a local variable. function MyFunction(const S: string): string; begin Result := S + 'something'; Result := Result + ' something more!'; Result := Result + ' and more!'; end; You can also treat the function name (like MyFunction in example above) as the variable, to which you can assign. But I would discourage it in new code, as it looks "fishy" when used on the right side of the assignment expression. Just use Result always when you want to read or set the function result. If you want to call the function itself recursively, you can of course do it. If you’re calling a parameter-less function recursively, be sure to specify the parenthesis () (even though in Pascal you can usually omit the parentheses for a parameter-less function), this makes a recursive call to a parameter-less function different from accessing this function’s current result. Like this: function SumIntegersUntilZero: Integer; var I: Integer; begin ReadLn(I); Result := I; if I <> 0 then Result := Result + SumIntegersUntilZero(); end; You can call Exit to end the execution of the procedure or function before it reaches the final end;. If you call parameter-less Exit in a function, it will return the last thing you set