clib-integration skill

v1.0.0 · Agent Skill · pascalai · registry.pascalai.org

Integrate C/C++ libraries into PascalAI via the clib bridge — DLL wrapping, MSVC builds, Delphi bridge, Int64 rules

clibdllcnativebridgeintegration

Install

ppm install clib-integration
ppm skill install-claude

The second command activates the skill in Claude Code (copies it into .claude/skills/).

Skill content (SKILL.md)

You are a clib integration expert for PascalAI. You know the full bridge pipeline from C source to PAI external declarations.

Architecture

src/pai_libX.c            — C source (or .cpp for C++)
    ↓  cl /LD /O2 /MT
libpai_X.dll              — Win64 DLL
    ↓
PAI.ClibBridge.pas        — Delphi native function registrations
    ↓
ppm/pkgs/X/main.pai       — PascalAI unit with external declarations
    ↓
tests/examples/NNN_clib_X.pai — Demo program

C Source Template

// src/pai_mylib.c
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#define PAI_EXPORT __declspec(dllexport)

// For C++ files: always add extern "C" to avoid name mangling
// PAI_EXPORT extern "C" int pai_func(...) { ... }

PAI_EXPORT int64_t pai_version(void) { return 100; }

PAI_EXPORT double pai_compute(double a, double b) { return a + b; }

// Strings: return malloc'd buffer, caller frees via pai_free
PAI_EXPORT const char* pai_process(const char* input) {
    char* result = (char*)malloc(strlen(input) + 64);
    sprintf(result, "Processed: %s", input);
    return result;
}

PAI_EXPORT void pai_free(void* ptr) { free(ptr); }

Build Script (build_win64.bat)

@echo off
set LIB_NAME=libpai_mylib
cl /nologo /LD /O2 /MT ^
   /I"include" ^
   src\pai_mylib.c ^
   /Fe%LIB_NAME%.dll ^
   /link mylib.lib
copy %LIB_NAME%.dll ..\..\..\compiler\PascalAI\host\bin\

For C++: add /EHsc and /DMSGPACK_NO_BOOST (if needed).

PAI Unit (main.pai)

unit mylib;

// CRITICAL: ALWAYS use Int64 for integer returns (NOT Integer — 4B vs 8B → stack corruption)
function pai_version: Int64; external 'libpai_mylib';
function pai_compute(A, B: Double): Double; external 'libpai_mylib';
function pai_process(Input: PChar): PChar; external 'libpai_mylib';
procedure pai_free(Ptr: Pointer); external 'libpai_mylib';

Delphi Bridge (PAI.ClibBridge.pas)

// Registration
procedure LoadMyLibPackage;
begin
  Lape.addGlobalFunc('function MyLib_Version: Int64', @LapeMyLib_Version);
  Lape.addGlobalFunc('function MyLib_Compute(A, B: Double): Double', @LapeMyLib_Compute);
  Lape.addGlobalFunc('function MyLib_Process(Input: string): string', @LapeMyLib_Process);
end;

// Native function — read ALL params FIRST, write Result LAST
procedure LapeMyLib_Compute(const Params: PParamArray; const Result: Pointer);
var A, B: Double;
begin
  A := PDouble(Params^[0])^;   // read param 0
  B := PDouble(Params^[1])^;   // read param 1
  PDouble(Result)^ := A + B;   // write result LAST
end;

Critical Rules

Rule 1: Int64 for integers — NEVER Integer

// WRONG (4B Integer vs 8B Lape slot → stack corruption)
Lape.addGlobalFunc('function Foo: Integer', @LapeFoo);
// CORRECT
Lape.addGlobalFunc('function Foo: Int64', @LapeFoo);

Rule 2: Read params before writing Result

// WRONG — overwrites Params^[0] when Result = Params (same address)
PDouble(Result)^ := 0.0;        // corrupts first param!
A := PDouble(Params^[0])^;      // reads garbage

// CORRECT
A := PDouble(Params^[0])^;
PDouble(Result)^ := A * 2;

Rule 3: extern "C" for all C++ DLLs

Without it, C++ name mangling makes functions unfindable at runtime.

Rule 4: Register return type in FFuncReturnTypes (for InferLapeType)

If IntToStr(myFunc()) gives "overloaded", the return type isn't registered:

FFuncReturnTypes.AddOrSetValue('mylib_version', 'Int64');

Common Patterns

Handle-based API (stateful resources)

PAI_EXPORT int64_t pai_db_open(const char* path) {
    DB* db = db_open(path); return (int64_t)db;
}
PAI_EXPORT void pai_db_close(int64_t handle) { db_close((DB*)handle); }
var H: Int64 := pai_db_open('mydb.db');
// ... use H ...
pai_db_close(H);

Serialize complex returns (avoid structs across DLL boundary)

// Return delimited string: "field1\x01field2\x01field3"
PAI_EXPORT const char* pai_get_info(int64_t handle) { ... }

Troubleshooting

Symptom Cause Fix
Function not found at runtime C++ name mangling Add extern "C" to exports
Stack corruption / wrong values Integer size mismatch Use Int64 not Integer
Wrong result from compute func Param read after Result write Read all params first
Access violation on first call Missing transitive DLL Copy dep DLL to host/bin/
IntToStr overloaded error Return type not in FFuncReturnTypes Register in InferLapeType
Linker error: unresolved external Wrong lib name in /link Check .lib name from vcpkg