| name | horse-lazarus-compatibility |
| description | Guide for ensuring Lazarus and Free Pascal (FPC) compatibility, addressing anonymous methods differences, JSON units, and compiler directives. |
Horse Lazarus & FPC Compatibility
To write cross-platform Horse applications that compile flawlessly in both Delphi and Lazarus/FPC, you must adhere to Free Pascal's compiler rules and syntax limitations.
1. No Anonymous Methods in FPC (The Core Rule)
While Delphi supports registering routes using inline anonymous procedures (methods) directly, Free Pascal (FPC) does not support anonymous methods with variable capturing in the same syntax.
Delphi Syntax (Will Fail to Compile on Lazarus)
// Do NOT use this inline syntax if targeting Lazarus/FPC
THorse.Get('/ping',
procedure(Req: THorseRequest; Res: THorseResponse; Next: TProc)
begin
Res.Send('pong');
end);
Lazarus/FPC Correct Syntax
Always declare handlers as standard standalone procedures or method pointers, and pass them as callback parameters:
{$MODE DELPHI}{$H+} // CRITICAL compiler directive for Lazarus
uses
Horse;
// 1. Declare handler as a standard procedure
procedure GetPingHandler(Req: THorseRequest; Res: THorseResponse);
begin
Res.Send('pong');
end;
begin
// 2. Register the procedure callback
THorse.Get('/ping', GetPingHandler);
THorse.Listen(9000);
end.
2. Standard Compiler Directives for Lazarus
Always include these two compiler directives at the absolute top of your Lazarus .pas or .dpr (program) files:
{$MODE DELPHI}{$H+}
{$MODE DELPHI}: Instructs FPC to emulate Delphi's object-oriented syntax and generic type resolutions.
{$H+}: Enforces the use of Long Strings (AnsiString) by default instead of short strings (which are limited to 255 characters).
3. JSON Framework Differences
Delphi and Lazarus use entirely different built-in JSON serialization libraries.
- Delphi: Uses
System.JSON (TJSONObject, TJSONArray).
- Lazarus/FPC: Uses
fpjson and jsonparser (TJSONObject, TJSONArray).
Writing Cross-Compiler JSON Code
If your middleware or controllers need to compile on both platforms, use conditional compilation defines ($IFDEF):
uses
{$IFDEF FPC}
fpjson, jsonparser,
{$ELSE}
System.JSON,
{$ENDIF}
Horse;
procedure GetStatusHandler(Req: THorseRequest; Res: THorseResponse);
var
LJson: TJSONObject;
begin
LJson := TJSONObject.Create;
try
{$IFDEF FPC}
LJson.Add('status', 'healthy');
{$ELSE}
LJson.AddPair('status', 'healthy');
{$ENDIF}
Res.Send(LJson);
finally
// Johnson middleware takes ownership and frees this object.
// Refer to horse-middlewares skill.
end;
end;
4. Best Practices for Cross-Compiler Apps
- Prefer standalone controller procedures: Keep all route handlers in a separate unit as standard procedures, avoiding inline code in the bootstrap project file.
- Avoid Delphi-specific RTL: Do not use units like
System.NetEncoding or System.Hash without providing an alternative like LclIntf or FPC's built-in cryptographic units when compiling under FPC.