Modular Programs & Libraries

QB64-PE programs split code into files with the $INCLUDE metacommand (written inside a comment):

30.1 $INCLUDE

QB64-PE programs split code into files with the $INCLUDE metacommand (written inside a comment):

The convention: .bi (BASIC include) at the top for declarations, .bm (BASIC module) at the bottom for procedures — because QB64 requires SUBs after the main code.

' main.bas
'$INCLUDE: 'include/vector.bi'
OPTION _EXPLICIT
DIM a AS Vec2, b AS Vec2, c AS Vec2
a.x = 3: a.y = 4
b.x = 1: b.y = 2
VecAdd a, b, c
PRINT c.x, c.y, VecLen!(a)
END
'$INCLUDE: 'include/vector.bm'
' include/vector.bi — declarations: TYPEs, CONSTs, DIM SHARED
TYPE Vec2
    x AS SINGLE
    y AS SINGLE
END TYPE
' include/vector.bm — SUBs and FUNCTIONs
SUB VecAdd (a AS Vec2, b AS Vec2, result AS Vec2)
    result.x = a.x + b.x
    result.y = a.y + b.y
END SUB

FUNCTION VecLen! (v AS Vec2)
    VecLen! = SQR(v.x * v.x + v.y * v.y)
END FUNCTION

30.2 Metacommands

'$INCLUDE: 'file' insert another source file '$DYNAMIC / '$STATIC default array allocation $CONSOLE, $CONSOLE:ONLY console window / console program $RESIZE:ON / $RESIZE:STRETCH executable icon (Windows) executable version information (Windows) $SCREENHIDE / $SCREENSHOW start with the window hidden $CHECKING:OFF / ON disable/enable runtime checks

$LET, $IF, $ELSEIF, $ELSE, $END IF conditional compilation (e.g., $IF WIN THEN) use QB64 keywords without the leading underscore enable the IDE's interactive debugger enable _ASSERT checks

$IF WIN THEN
    CONST PATH_SEP$ = "\"
$ELSE
    CONST PATH_SEP$ = "/"
$END IF

30.3 DECLARE LIBRARY — calling C and system libraries

DECLARE LIBRARY (static, C/C++ headers or libraries), DECLARE DYNAMIC LIBRARY (.dll / .so / .dylib), DECLARE CUSTOMTYPE LIBRARY (pass custom types).

Use BYVAL for values; parameters without BYVAL are passed as pointers.

C strings must end with CHR$(0).

Recent QB64-PE versions include built-in dialogs (_MESSAGEBOX, _INPUTBOX$, _OPENFILEDIALOG$, _SAVEFILEDIALOG$), so check the wiki before reaching for the Windows API.

' Call a C function from a header file next to the program: mathhelpers.h
'   static inline int clampi(int v, int lo, int hi) { return v < lo ? lo : (v > hi ? hi : v); }
DECLARE LIBRARY "mathhelpers"
    FUNCTION clampi& (BYVAL v AS LONG, BYVAL lo AS LONG, BYVAL hi AS LONG)
END DECLARE
PRINT clampi&(150, 0, 100)
' Windows only: call MessageBoxA in user32.dll
DECLARE DYNAMIC LIBRARY "user32"
    FUNCTION MessageBoxA& (BYVAL hwnd AS _OFFSET, msg AS STRING, title AS STRING, BYVAL flags AS _UNSIGNED LONG)
END DECLARE
r& = MessageBoxA&(0, "Hello from QB64!" + CHR$(0), "DECLARE LIBRARY" + CHR$(0), 0)

30.4 Designing a reusable library

Prefix public names to avoid collisions: Vec_Add, UI_Button, Snd_Play.

Keep internal state in DIM SHARED variables with a library prefix, initialized by an Init SUB.

Document each procedure's parameters and side effects at the top of the .bm file.

Provide a small demo program per library.

30.5 Historical multi-module programs

QuickBASIC supported several .BAS modules per program, listed in a .MAK file; variables were shared between modules with COMMON SHARED; CHAIN "PROG2" loaded and ran another program (passing COMMON variables); .QLB Quick Libraries and .LIB files held compiled routines (often written in assembly). QB64 accepts COMMON and CHAIN for compatibility, but $INCLUDE is the modern approach.

FreeBASIC: #include "file.bi", #include once, separate compilation (fbc -c a.bas b.bas then link), static and dynamic libraries (fbc -lib, fbc -dll), Declare Function ... Lib "name", Namespace, #ifdef __FB_WIN32__. VB.NET: namespaces, assemblies (class library projects), NuGet packages, Imports System.IO, <DllImport("user32.dll")> for native calls.