FreeBASIC Manual
Welcome to FreeBASIC | Getting Help with FreeBASIC | Sound Library Reference | Graphics Keywords | Keyword Index

Language Documentation
Keywords - Alphabetical
Keywords - Functional
Graphics Keywords
Operator List

Variables and Data Types
Variable Declarations
User Defined Types
Standard Data Types
Standard Data Type Limits
Converting Between Data Types

Operators
Operator Overview
Operator Precedence
Bitwise Operators & Truth Tables

Statements
Control Flow
Procedures
Modularizing

Other
Preprocessor
Escape Sequences In String Literals
Meta-statements
Intrinsic Defines
Error Handling
Inline Asm

Runtime Library Reference
Array Functions
Bit Manipulation
Console Functions
Date and Time Functions
Error Handling Functions
File IO Functions
Mathematical Functions
Memory Functions
Operating System Functions
Sound Library Reference
String Functions
Threading Support Functions
User Input Functions

Graphics Library Reference
2D Drawing Functions
User Input Functions
Screen Functions

Supported graphics drivers (backends)
Keyboard Scan Codes
Default Palettes

Tutorials
Programmer's Guide
Community Tutorials
Community Code Library
External Libraries Index

Using the FreeBASIC compiler
Installing FreeBASIC | Requirements
Running FreeBASIC
Using the Command Line | Command Line Options

Debugging with FreeBASIC

Compiler Error Messages
Tools used by fbc
Installing gcc for -gen gcc

FreeBASIC dialects and QBASIC
FreeBASIC and Qbasic | Differences from QB
FreeBASIC Dialects

FAQs
Compiler FAQ
Graphics Library FAQ
Runtime Library FAQ
Xbox port FAQ
DOS related FAQ
Windows related FAQ
Linux related FAQ

Miscellaneous
Obsolete Keywords
Glossary
Miscellaneous Keywords
C Standard Library Functions
ASCII Character Codes
Runtime Error Codes
C/C++ vs. FreeBASIC syntax comparison
C/C++ vs. FreeBASIC integer data type comparison

Hacking on FreeBASIC
Developer's Table of Contents

This document last compiled : 2026-06-15 10:46:11 from https://www.freebasic.net/wiki/


Welcome to FreeBASIC
Welcome to our world! This page is an overview of our online warehouse of knowledge. Enjoy your surfing and we hope this will be the first of many visits.

Introduction
FreeBASIC is a free, BASIC compiler for Windows (32-bit and 64-bit), 32 bit protected-mode DOS (COFF executables, like DJGPP), and Linux (x86, x86_64, and ARM). It is open source and licensed under the GPL. It is designed to be syntax compatible with QuickBASIC, while expanding on the language and capabilities. It can create programs for MS-Windows, DOS and Linux, and is being ported to other platforms. See About FreeBASIC and Main Features.

Latest Version
FreeBASIC is a beta release compiler and development is ongoing. With each full update, many features are added, and bugs from previous releases are fixed. To see the latest version available, visit the News section on the forum.

Requirements and Installation
Minimum hardware is listed on the Requirements page. Visit our Installation page for setting up FreeBASIC on your computer.

Running
FreeBASIC is a compiler and as such is not packaged with an IDE (Integrated Development Editor), although there are a few IDE's available. For information on using FreeBASIC without an IDE, see Running.

Compatibility with QuickBASIC
FreeBASIC is designed to be syntax compatible with QuickBASIC. For best code-compatibility with QuickBASIC, the QB dialect can be used when compiling source code. See FreeBASIC Dialects and Differences from QB.

Documentation
All official documentation can be found online in the wiki at https://www.freebasic.net/wiki. The online documentation is the most up-to-date resource available. In all cases it can be regarded as the correct version. The downloadable versions of the manual are snapshots of the documentation available at a particular time and should be mostly correct for a specific released version of the compiler. However, we do not maintain multiple versions of the documentation so there may be some discrepancies.

Starting points in the Manual
  • Table of Contents
  • Getting Help with FreeBASIC
  • Programmer's Guide

Starting points on the Web
  • Official Website at https://www.freebasic.net
  • Official Forums at https://www.freebasic.net/forum

Thank you for using FreeBASIC. Happy coding!


Getting help with FreeBASIC
There are several options available for getting help with FreeBASIC.

The Manual
This huge user's manual is full of information that can help you learn to write programs using FreeBASIC.

The manual is available online at https://www.freebasic.net/wiki. There is a search box at the top of every page to help you find what you're looking for.

If you are unfamiliar with FreeBASIC or the documentation, you may find these pages a good place to start:
    • Table of Contents
    • Programmer's Guide
    • Library Headers Index
    • Glossary
    • Compiler FAQ
    • Graphics Library FAQ
    • Runtime Library FAQ

A downloadable manual (in CHM format) is available from the sourceforge project page at https://sourceforge.net/projects/fbc which features a full table of contents, searching capabilities, an index, plus all the same content as the online version.

Searching the manual on or offline is an excellent place to start finding help about how to write and use FreeBASIC programs.

Examples and Source Code
In the ./examples directory located where FreeBASIC was installed on your system are hundreds of examples to be compiled and run. Most of the external library examples will need additional libraries to be downloaded to allow them to work. See Library Headers Index for a full list.

Tutorials
Community created tutorials about FreeBASIC can be found at CommunityTutorials. Some selected tutorials are included in this manual.

FreeBASIC Forum
An active community forum can be found at https://www.freebasic.net/forum with several sub-forums. The forum has a search feature that can help you find answers to questions or problems that may have already been asked and solved. First do a search for your problem, if you can't find the answer then post a message in one of the sub-forums.

Chat
IRC or Internet Relay Chat is a great way to chat with the developers and other users, some of whom are very knowledgeable. There are several ways to connect to IRC, if you know what you're doing simply join #freebasic on FreeNode.

If you haven't the foggiest what IRC is and you have Java installed, you can simply go here.

If you're trying to get help, the most important thing is to be patient. Sometimes you won't get a reply right away. Stick around or check back and the Community will try and assist you.


Alphabetical Keywords List
Alphabetical listing of keywords, macros and procedures.

Operators & . _ # $ ? A B C D E F G H I K L M N O P R S T U V W X Y Z

Operators
  • See Operator List

&
  • &H, &B, &O (See Literals)

.
  • ... (Ellipsis)

_
  • __DATE__
  • __DATE_ISO__
  • __FB_64BIT__
  • __FB_ANDROID__
  • __FB_ARG_COUNT__
  • __FB_ARG_EXTRACT__
  • __FB_ARG_LEFTOF__
  • __Fb_Arg_Listexpand__
  • __FB_ARG_RIGHTOF__
  • __FB_ARGC__
  • __FB_ARGV__
  • __FB_ARM__
  • __FB_ASM__
  • __FB_BACKEND__
  • __FB_BIGENDIAN__
  • __FB_BUILD_DATE__
  • __FB_BUILD_DATE_ISO__
  • __FB_BUILD_FORK_ID__
  • __FB_BUILD_SHA1__
  • __FB_CYGWIN__
  • __FB_DARWIN__
  • __FB_DEBUG__
  • __FB_DOS__
  • __FB_ERR__
  • __FB_EVAL__
  • __FB_FPMODE__
  • __FB_FPU__
  • __FB_FREEBSD__
  • __FB_GCC__
  • __FB_GUI__
  • __FB_IIF__
  • __FB_JOIN__
  • __FB_JS__
  • __FB_LANG__
  • __FB_LINUX__
  • __Fb_Loongarch64__
  • __FB_MAIN__
  • __FB_MIN_VERSION__
  • __FB_MT__
  • __FB_NETBSD__
  • __FB_OPENBSD__
  • __FB_OPTIMIZE__
  • __FB_OPTION_BYVAL__
  • __FB_OPTION_DYNAMIC__
  • __FB_OPTION_ESCAPE__
  • __FB_OPTION_EXPLICIT__
  • __FB_OPTION_GOSUB__
  • __FB_OPTION_PRIVATE__
  • __FB_OPTION_PROFILE__
  • __FB_OUT_DLL__
  • __FB_OUT_EXE__
  • __FB_OUT_LIB__
  • __FB_OUT_OBJ__
  • __FB_PCOS__
  • __FB_PPC__
  • __FB_PROFILE__
  • __Fb_Query_Symbol__
  • __FB_QUOTE__
  • __Fb_Riscv64__
  • __Fb_S390X__
  • __FB_SIGNATURE__
  • __FB_SSE__
  • __FB_UNIQUEID__
  • __FB_UNIQUEID_POP__
  • __FB_UNIQUEID_PUSH__
  • __FB_UNIX__
  • __FB_UNQUOTE__
  • __FB_VECTORIZE__
  • __FB_VER_MAJOR__
  • __FB_VER_MINOR__
  • __FB_VER_PATCH__
  • __FB_VERSION__
  • __FB_WIN32__
  • __FB_X86__
  • __FB_XBOX__
  • __Fb_Wii__
  • __FILE__
  • __FILE_NQ__
  • __FUNCTION__
  • __FUNCTION_NQ__
  • __LINE__
  • __PATH__
  • __TIME__

#
  • #assert
  • #Cmdline
  • #define
  • #else
  • #elseif
  • #elseifdef
  • #elseifndef
  • #endif
  • #endmacro
  • #error
  • #if
  • #ifdef
  • #ifndef
  • #inclib
  • #include
  • #lang
  • #libpath
  • #line
  • #macro
  • #pragma
  • #Pragma Reserve
  • #print
  • #undef

$
  • $Dynamic
  • $Include
  • $Lang
  • $Static

?
  • ? (Shortcut For 'Print')
  • ? # (Shortcut For 'Print #')
  • ? Using (Shortcut For 'Print Using')

A
  • Abs
  • Abstract (Member)
  • Access
  • Acos
  • Add (Graphics Put)
  • Alias (Name)
  • Alias (Modifier)
  • Allocate
  • Alpha (Graphics Put)
  • And
  • And=
  • AndAlso
  • And (Graphics Put)
  • Any
  • Append
  • Arraylen
  • Arraysize
  • As
  • Asc
  • Asin
  • Asm
  • Assert
  • AssertWarn
  • Atan2
  • Atn
B
  • Balance
  • Base (Initializer)
  • Base (Member Access)
  • Beep
  • Bin
  • Binary
  • Bit
  • BitReset
  • BitSet
  • BLoad
  • Boolean
  • BSave
  • Byref (Parameters)
  • Byref (Function Results)
  • Byref (Variables)
  • Byte
  • ByVal

C
  • Call
  • CAllocate
  • Case
  • Cast
  • Capture
  • CBool
  • CByte
  • CDbl
  • cdecl
  • Chain
  • Channel
  • ChDir
  • Chr
  • CInt
  • Circle
  • Class
  • Clear
  • CLng
  • CLngInt
  • Close
  • Cls
  • Color
  • Command
  • Common
  • CondBroadcast
  • CondCreate
  • CondDestroy
  • CondSignal
  • CondWait
  • Const
  • Const (Member)
  • Const (Qualifier)
  • Constructor
  • Constructor (Module)
  • Continue
  • Cos
  • CPtr
  • CShort
  • CSign
  • CSng
  • CsrLin
  • CUByte
  • CUInt
  • CULng
  • CULngInt
  • CUnsg
  • CurDir
  • CUShort
  • Custom (Graphics Put)
  • Cva_Arg
  • Cva_Copy
  • Cva_End
  • Cva_List
  • Cva_Start
  • CVD
  • CVI
  • CVL
  • CVLongInt
  • CVS
  • CVShort

D
  • Data
  • Date
  • DateAdd
  • DateDiff
  • DatePart
  • DateSerial
  • DateValue
  • Day
  • Deallocate
  • Declare
  • DefByte
  • DefDbl
  • defined
  • DefInt
  • DefLng
  • DefLongInt
  • DefShort
  • DefSng
  • DefStr
  • DefUByte
  • DefUInt
  • Defulongint
  • DefUShort
  • Delete (Statement)
  • Destructor
  • Destructor (Module)
  • Device
  • Dim
  • Dir
  • Do
  • Do...Loop
  • Double
  • Draw
  • Draw String
  • DyLibFree
  • DyLibLoad
  • DyLibSymbol

E
  • Else
  • ElseIf
  • Encoding
  • End (Block)
  • End (Statement)
  • End If
  • Enum
  • Environ Statement
  • Environ
  • Envelope
  • Eoc
  • EOF
  • Eqv
  • Eqv=
  • Erase
  • Erfn
  • Erl
  • Ermn
  • Err
  • Error
  • Event (Message Data From Screenevent)
  • Exec
  • ExePath
  • Exit
  • Exp
  • Export
  • Extends
  • Extends Wstring
  • Extends Zstring
  • Extern
  • Extern...End Extern

F
  • False
  • Fastcall
  • Fb_Memcopy
  • fb_MemCopyClear
  • Fb_Memmove
  • Fbarray (Array Descriptor Structure And Access)
  • Fb_No_Gfxlib
  • Fb_No_Sfxlib
  • Field
  • FileAttr
  • FileCopy
  • FileDateTime
  • FileExists
  • FileFlush
  • FileLen
  • FileSetEof
  • Fix
  • Flip
  • For
  • For...Next
  • Format
  • Frac
  • Fre
  • FreeFile
  • Function
  • Function (Member)
  • Function (Pointer)

G
  • Get (Graphics)
  • Get # (File I/O)
  • GetJoystick
  • GetKey
  • GetMouse
  • GetTouch
  • GetTouchCount
  • GetTouchHit
  • Getxpad
  • Generated Sound Commands
  • GoSub
  • Goto

H
  • Hex
  • HiByte
  • HiWord
  • Hour

I
  • If...Then
  • IIf
  • ImageConvertRow
  • ImageCreate
  • ImageDestroy
  • ImageInfo
  • Imp
  • Implements
  • Import
  • Inkey
  • Inp
  • Imp=
  • Input (Statement)
  • Input (File I/O)
  • Input #
  • Input()
  • Instrument
  • InStr
  • InStrRev
  • Int
  • Integer
  • Is (Select Case)
  • Is (Run-Time Type Information Operator)
  • IsDate
  • IsRedirected

K
  • Kill

L
  • LBound
  • LCase
  • Left
  • Len
  • Let
  • Let (Assign)
  • Let() (Assignment)
  • Lib
  • Line
  • Line Input
  • Line Input #
  • LoByte
  • LOC
  • Local
  • Locate
  • Lock
  • LOF
  • Log
  • Long
  • LongInt
  • Loop
  • LoWord
  • LPos
  • LPrint
  • LSet
  • LTrim

M
  • Midi
  • Mid (Statement)
  • Mid (Function)
  • Minute
  • MKD
  • MkDir
  • MKI
  • MKL
  • MKLongInt
  • MKS
  • MKShort
  • Mod
  • Mod=
  • Month
  • MonthName
  • Music
  • MultiKey
  • MutexCreate
  • MutexDestroy
  • MutexLock
  • MutexUnlock

N
  • Naked
  • Name
  • Namespace
  • New (Expression)
  • New (Placement)
  • Next
  • Next (Resume)
  • Noise
  • Note
  • Not
  • Now

O
  • Object
  • Oct
  • Octave
  • OffsetOf
  • On Error
  • On...Gosub
  • On...Goto
  • Once
  • Open
  • Open Com
  • Open Cons
  • Open Err
  • Open Lpt
  • Open Pipe
  • Open Scrn
  • Open Tcp
  • Open Tcp Server
  • Operator
  • Option()
  • Option Base
  • Option ByVal
  • Option Dynamic
  • Option Escape
  • Option Explicit
  • Option Gosub
  • Option Nogosub
  • Option NoKeyword
  • Option Private
  • Option Static
  • Or
  • Or=
  • Or (Graphics Put)
  • OrElse
  • Out
  • Output
  • Overload
  • Override

P
  • Paint
  • Palette
  • Pan
  • pascal
  • PCopy
  • Peek
  • Play
  • PMap
  • Point
  • PointCoord
  • Pointer
  • Poke
  • Pos
  • Preserve
  • PReset
  • Print
  • Print #
  • Print Using
  • Private
  • Private: (Access Control)
  • ProcPtr
  • Property
  • Protected: (Access Control)
  • Pset (Statement)
  • Pset (Graphics Put)
  • Ptr (Shortcut For 'Pointer')
  • Public
  • Public: (Access Control)
  • Put (Graphics)
  • Put # (File I/O)

R
  • Random
  • Randomize
  • Read
  • Read (File Access)
  • Read Write (File Access)
  • Reallocate
  • ReDim
  • Rem
  • Reset
  • Restore
  • Resume
  • Resume Next
  • Return (From Procedure)
  • Return (From Gosub)
  • RGB
  • RGBA
  • Right
  • RmDir
  • Rnd
  • RSet
  • RTrim
  • Run

S
  • SAdd
  • Scope
  • Screen (Graphics)
  • Screen (Console)
  • ScreenCopy
  • ScreenControl
  • ScreenEvent
  • ScreenGLProc
  • ScreenInfo
  • ScreenList
  • ScreenLock
  • ScreenPtr
  • ScreenRes
  • ScreenSet
  • ScreenSync
  • ScreenUnlock
  • Second
  • Seek (Statement)
  • Seek (Function)
  • Select Case
  • SetDate
  • SetEnviron
  • SetMouse
  • SetTime
  • Sgn
  • Sfx
  • Shared
  • Shell
  • Shl
  • Shl=
  • Short
  • Shr
  • Shr=
  • Sin
  • Single
  • SizeOf
  • Sleep
  • Sound
  • Space
  • Spc
  • Sqr
  • Static
  • Static (Member)
  • stdcall
  • Step
  • Stick
  • Stop
  • Str
  • Strig
  • String (Function)
  • String
  • StrPtr
  • Sub
  • Sub (Member)
  • Sub (Pointer)
  • Swap
  • System

T
  • Tab
  • Tan
  • Tcp Accept
  • Tempo
  • Then
  • This
  • Thiscall
  • ThreadCall
  • ThreadCreate
  • ThreadDetach
  • ThreadSelf
  • ThreadWait
  • Time
  • Timer
  • TimeSerial
  • TimeValue
  • To
  • Tone
  • Trans (Graphics Put)
  • Trim
  • True
  • Type (Alias)
  • Type (Temporary)
  • Type (Udt)
  • TypeOf

U
  • UBound
  • UByte
  • UCase
  • UInteger
  • ULong
  • ULongInt
  • Union
  • Unlock
  • Unsigned
  • Until
  • UShort
  • Using (Print)
  • Using (Namespaces)

V
  • va_arg
  • va_first
  • va_next
  • Val
  • ValLng
  • ValInt
  • ValUInt
  • ValULng
  • Var
  • VarPtr
  • View Print
  • View (Graphics)
  • Virtual (Member)
  • Voice
  • Vol
  • Volume

W
  • Wait
  • Wave
  • WBin
  • WChr
  • Weekday
  • WeekdayName
  • Wend
  • While
  • While...Wend
  • WHex
  • Width
  • Window
  • WindowTitle
  • WInput
  • With
  • WOct
  • Write
  • Write #
  • Write (File Access)
  • WSpace
  • WStr
  • Wstring (Data Type)
  • Wstring (Function)

X
  • Xor
  • Xor=
  • Xor (Graphics Put)

Y
  • Year

Z
  • ZString


Literals
Non-variable compile-time string, numeric values and boolean values.

Literals are numbers, strings of characters or boolean truths specified directly in the source code. Literal values may be used by assigning them to a variable or constant, passing them to a procedure, or using them in an expression.

Numeric literals come in two forms - integer and floating-point.

Integer Literals

Decimal
Decimal digits ( 0 1 2 3 4 5 6 7 8 9 ).
Note: to get negative values, a "-" sign (Operator - (Negate)) can be placed before a numeric literal

Dim x As Integer = 123456
Dim b As Byte = -128

Hexadecimal
"&H", followed by hexadecimal digits ( 0 1 2 3 4 5 6 7 8 9 A B C D E F ).

Dim x As Integer = &h1E240
Dim b As Byte = &H80

Octal
"&O" (or "&"), followed by octal digits ( 0 1 2 3 4 5 6 7 )

Dim x As Integer = &O361100
Dim b As Byte = &O200

Binary
"&B", followed by binary digits ( 0 1 )

Dim x As Integer = &B11110001001000000
Dim b As Byte = &B10000000

Integer size suffixes

If an integer literal suffix is not given, the number field size required to hold the literal is automatically calculated. Specifying a size suffix guarantees that the compiler will consider a number as a specific integer size.

Integer literals ending with:
  • "%", are considered as signed 32/64 (depending on platform) bit integers. (Integer)
  • "L", "&", are considered as signed 32 bit long integers. (Long)
  • "U", are considered as unsigned 32/64 (depending on platform) bit integers. (UInteger)
  • "UL", are considered as unsigned 32 bit integers. (ULong)
  • "LL", are considered as signed 64 bit integers. (LongInt)
  • "ULL", are considered as unsigned 64 bit integers. (ULongInt)

Negative (-) and positive (+) signs that prefix a numeric literal, are not part of the numeric literal. expressions (like "-1L") are parsed as the negate operator applied to the value represented by the positive literal ("1L"), which may involve implicit type conversions. Same behavior when an explicit "+" sign precedes the integer literal.

The prefixes, suffixes, and hexadecimal letter digits are all case-insensitive.

Dim a As Long = 123L
Dim b As UInteger = &h1234u
Dim c As LongInt = 76543LL
Dim d As ULongInt = &b1010101ULL

Floating Point Literals

Floating point numbers are specified in decimal digits, may be positive or negative, have a fractional portion, and optionally an exponent. The format of a floating point literal is as follows (without space or parenthesis added):

number[.[fraction]][(D|E)[+|-][exponent]][suffix]
or
.fraction[(D|E)[+|-][exponent]][suffix]

By default, floating point numbers that do not have either an exponent or a suffix are considered as a double precision floating point value, except in the -lang qb dialect, where numbers of 7 digits or fewer are considered to be single precision.
Dim a As Double = 123.456
Dim b As Double = -123.0

The letter "D" or "E", placed after the number/fraction part, allows the number to be given an exponent. The exponent may be specified as either positive or negative with a plus ("+") or minus ("-") sign. Exponents that do not have a sign are positive.
An exponent value is not required after the letter (or even after the sign), so the letter can be used on its own just to specify the type. "D" specifies a double-precision floating-point number. "E" specifies a floating-point number using the default precision. When the letter is used on its own in combination with a suffix (see below) the type denoted by the suffix overrules the type specified by the letter.

Dim a As Double = -123.0d
Dim b As Double = -123e
Dim c As Double = 743.1e+13
Dim d As Double = 743.1D-13
Dim e As Double = 743.1E13
Dim f As Single = 743D!

A suffix of "!" or "F" on a number specifies a single precision (32 bit total) floating point value. A suffix of "#" or "D" specifies a double precision float.
Note that the letter suffixes and exponent specifiers are all case-insensitive.

Dim a As Single = 3.1!
Dim b As Single = -123.456e-7f
Dim c As Double = 0#
Dim d As Double = 3.141592653589e3#

String Literals

String literals are a sequence of characters contained between two double quotes. The sequence of characters escaped or non-escaped.

Double quotes can be specified in the string literal by using two double quotes together.
Print "Hello World!"
Print "That's right!"
Print "See the ""word"" contained in double quotes."

String literals can contain escape sequences if the string literal is prefixed by the ! Operator (Escaped String Literal). See Escape Sequences for a list of accepted escape sequences.
Print !"Hello\nWorld!"

By default, string literals are non-escaped unless Option Escape was used in the source in which case all string literals following are by default escaped.

A string may be explicitly specified as non-escaped when prefixed by the $ Operator (Non-Escaped String Literal).
Print $"C:\temp"

Besides ASCII files with Unicode escape sequences (\u), FreeBASIC can parse UTF-8, UTF-16LE, UTF-16BE, UTF-32LE and UTF-32BE source files as long as they were saved with Byte Order Mark (BOM), allowing unicode characters directly in the string literal.
Note: The most reliable cross-platform code is obtained by encoding without BOM in ASCII/UTF-8 characters.

String literals in an ASCII file are treated as ZString, while string literals in a Unicode file are treated as WString (regardless of the string literal's value).

Boolean Literals

The boolean type has two values, represented by literals True and False.

Dim a As Boolean = False
Dim b As Boolean = True

See also

  • TypeOf
  • #define
  • Const
  • Standard Data Types
  • Table with variable types overview, limits and suffixes


... (Ellipsis)
Used in place of procedure parameter to pass a variable number of arguments, or as the upper bound in an array declaration to denote that the number of elements will be determined by the initializer.

Syntax

Declare { Sub | Function } proc_name cdecl ( param_list, ... ) { | [ ByRef ] As return_type }

#define identifier( [ parameters, ] variadic_parameter... ) body

Dim array_symbol ([lbound To] ...) [As datatype] => { expression_list }

Description

Variadic Procedures
The ellipsis (three dots, ...) is used in procedure declarations and definitions to indicate a variable argument list.

A first argument (at least) must always be specified and the procedure must be called with the C calling convention cdecl.

In the procedure body, Cva_List data type, and Cva_Arg macro can be used to expand the ellipsis parameter (...) to obtain the values of the arguments passed to the variadic procedure. The argument list, once initialized with Cva_Start or copied with Cva_Copy, can be passed to another procedure taking a Cva_List parameter.

On some targets, for backwards compatibility, va_first, va_arg and va_next can still be used to handle the variable arguments.

Only numeric types and pointers are supported as variable arguments (all bytes and shorts passed on variable arguments are implicitly converted to integers, all singles passed on variable arguments are implicitly converted to doubles). Strings can be passed, in which case a ZString Ptr to the string data is taken.

A variadic procedure name can never be overloaded.

Variadic Macros
Using an ellipsis behind the last parameter in a #define or #macro declaration allows creation of a variadic macro. This means it is possible to pass any number of arguments to the variadic_parameter, which can be used in the body as if it was a normal macro parameter. The variadic_parameter will expand to the full list of arguments passed to it, including commas, and can also be completely empty.

Note: To distinguish between the different arguments passed by variadic_parameter, you can first convert variadic_parameter to a string using the Operator # (Preprocessor Stringize), then differentiate in this string (#variadic_parameter) each passed argument by locating the separators (usually a comma).

Array Upper Bound
Using an ellipsis in place of the upper bound in an array declaration causes the upper bound to be set according to the data that appears in the expression_list. When the ellipsis is used in this manner, an initializer must appear, and cannot be Any.

Example

Declare Function foo cdecl (x As Integer, ...) As Integer

Dim As Integer myarray(0 To ...) = {0, 1, 2, 3}
Print LBound(myarray), UBound(myarray)   '' 0, 3

'' Using a variadic macro to wrap a variadic function
#include "crt.bi"
#define eprintf(Format, args...) fprintf(stderr, Format, args)
eprintf(!"Hello from printf: %i %s %i\n", 5, "test", 123)

'' LISP-like accessors allowing to modify comma-separated lists
#define car(a, b...) a
#define cdr(a, b...) b

Differences from QB

  • New to FreeBASIC

See also

  • cdecl
  • Cva_List
  • Cva_Arg
  • va_arg
  • va_first
  • va_next
  • Dim
  • Static
  • #define


__DATE__
Intrinsic define (macro value) set by the compiler

Syntax

__DATE__

Description

Substitutes the compiler date in a literal string ("mm-dd-yyyy" format) where used.

Example

Print "Compile Date: " & __DATE__

Compile Date: 09-29-2011

Differences from QB

  • New to FreeBASIC

See also

  • __DATE_ISO__
  • __TIME__
  • Date


__DATE_ISO__
Intrinsic define (macro value) set by the compiler

Syntax

__DATE_ISO__

Description

Substitutes the compiler date in a literal string ("yyyy-mm-dd" format) where used. This format is in line with ISO 8601 and can be used for lexicographical date comparisons.

Example

Print "Compile Date: " & __DATE_ISO__

If __DATE_ISO__ < "2011-12-25" Then
    Print "Compiled before Christmas day 2011"
Else
    Print "Compiled after Christmas day 2011"
End If

Compile Date: 2011-09-29
Compiled before Christmas day 2011

Differences from QB

  • New to FreeBASIC

See also

  • __DATE__
  • __TIME__
  • Date


__FB_64BIT__
Intrinsic define set by the compiler

Syntax

__FB_64BIT__

Description

Define created at compile time if the the compilation target is 64bit, otherwise undefined.

Example

#ifdef __FB_64BIT__
  '...instructions for 64bit OSes...
#else
  '...instructions for other OSes
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_LINUX__
  • __FB_FREEBSD__
  • __FB_OPENBSD__
  • __FB_NETBSD__
  • __FB_CYGWIN__
  • __FB_DARWIN__
  • __FB_PCOS__
  • Compiler Option: -target


__FB_ANDROID__

Syntax

__FB_ANDROID__

Description

Defined without a value at compile time when the Android target is being used. It can be used to compile parts of a program only for Android.

Example

#IFDEF __FB_ANDROID__
    print "Android target"
#ENDIF

Predefined Defines

See also
Compiling FB for Android
Compiler Option: -target

Compiler Meta-Commands


__FB_ARG_COUNT__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_ARG_COUNT__( args... )

Parameters

args...
argument list

Description

Counts the number of arguments in the argument list (args...) and returns the corresponding value.
A value is always returned, with 0 corresponding to an empty argument list.

Because the argument separator is the comma (,), the returned value for a non-empty argument list is the number of main commas (non-nested) plus 1.

Example

#macro m( args... )
    Print __FB_ARG_COUNT__( args )
#endmacro

m()
m(a)
m(b,c)
m(,d)
m(,e,)
m(,,,)

Sleep

/' Output:
 0
 1
 2
 2
 3
 4
'/

' macro with a variadic parameter which can contain several sub-parameters:
'   To distinguish between the different arguments passed by a variadic_parameter,
'   you can first convert the variadic_parameter to a string using the Operator # (Preprocessor Stringize),
'   then differentiate in this string (#variadic_parameter) each passed argument by locating the separators (usually a comma)
'   in a [For...Next] loop based on the number of arguments (__FB_ARG_COUNT__) passed to the macro.

#macro average(result, arg...)
    Scope
        Dim As String s = #arg
        If s <> "" Then
            result = 0
            For I As Integer = 1 To __FB_ARG_COUNT__( arg ) - 1
                Dim As Integer k = InStr(1, s, ",")
                result += Val(Left(s, k - 1))
                s = Mid(s, k + 1)
            Next I
            result += Val(s)
            result /= __FB_ARG_COUNT__( arg )
        End If
    End Scope
#endmacro

Dim As Double result
average(result, 1, 2, 3, 4, 5, 6)
Print result

Sleep

/' Output :
 3.5
'/

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_ARG_LEFTOF__
  • __FB_ARG_RIGHTOF__
  • __FB_ARG_EXTRACT__
  • __Fb_Arg_Listexpand__


__FB_ARG_EXTRACT__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_ARG_EXTRACT__( index, args... )

Parameters

index
zero-based offset from the first argument (zero or positive integer value)
args...
argument list

Description

Returns the argument from the argument list (args...) corresponding to the index value (index).

If the requested index value refers to an argument that does not exist in the supplied argument list (index value too large), nothing is returned.

Example

#print __FB_ARG_EXTRACT__( 1, 7, 89.78, "Postman" )

/' Compiler output:
89.78
'/

'   In this example, the '__FB_EVAL__' is absolutely mandatory in this 'print_last' macro,
'   because the numeric expression '__FB_ARG_COUNT__( args ) - 1' must be fully evaluated
'   before being used as the index argument of '__FB_ARG_EXTRACT__'

#macro print_last( args... )
    #define last_arg_num __FB_EVAL__( __FB_ARG_COUNT__( args ) - 1 )
    #print __FB_ARG_EXTRACT__( last_arg_num, args )
#endmacro

print_last( 7, 89.78, "Postman" )

/' Compiler output:
Postman
'/

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_ARG_LEFTOF__
  • __FB_ARG_RIGHTOF__
  • __FB_ARG_COUNT__
  • __Fb_Arg_Listexpand__


__FB_ARG_LEFTOF__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_ARG_LEFTOF__( arg, sep [, ret] )

Parameters

arg
argument
sep
separator, obviously different from the comma (,)
ret
default return if separator not found

Description

Returns the left token of the argument (arg), based on the separator (sep).
(in the expression of the argument, the tokens and the separator must be spaced)

By default, if the default return (ret) is not given, the macro returns nothing (empty token) if the separator (sep) is not found.
Otherwise, if the default return (ret) is given, the macro returns the default return (ret) if the separator (sep) is not found.

Example

#macro m( arg )
    Scope
        Var v = __FB_ARG_LEFTOF__( arg, versus, "Not found 'versus'" )
        Print v
    End Scope
#endmacro

m(1 versus 2)
m("left-side" versus "right-side")
m(3.14 verso pi)

Sleep

/' Output:
 1
left-side
Not found 'versus'
'/

See also __FB_ARG_RIGHTOF__ example.

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_ARG_RIGHTOF__
  • __FB_ARG_COUNT__
  • __FB_ARG_EXTRACT__
  • __Fb_Arg_Listexpand__


__Fb_Arg_Listexpand__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_ARG_LISTEXPAND__( macroname, macroargcount, args... )

Parameters

macroname
name of the macro used for extension calls
macroargcount
number of parameters for the 'macroname' macro
expand the parameter list 'args...' according to the 'macroargcount' value:
- 'macroargcount > 0' : pass 'macroargcount' parameters each time
- 'macroargcount = 0' : pass all parameters
- 'macroargcount < 0' : for each parameter passed, the previous 'macroargcount' parameters are automatically removed on the next pass
args...
argument list

Description

Expands to one or more 'macroname( .... )' depending on the value of macroargcount and number of arguments in the args... list.

Returns empty string on invalid index, rather than compile error.

Example

#macro m( arg... )
    #print "   "##arg
#endmacro

#print "macroargcount>0 (=1):"
__FB_ARG_LISTEXPAND__( m, 1, Hello1, Hello2, Hello3, Hello4)
#print " "
#print "macroargcount=0 (=0):"
__FB_ARG_LISTEXPAND__( m, 0, Hello1, Hello2, Hello3, Hello4)
#print " "
#print "macroargcount<0 (=-1):"
__FB_ARG_LISTEXPAND__( m, -1, Hello1, Hello2, Hello3, Hello4)

/' Compiler output:
macroargcount>0 (=1):
   Hello1
   Hello2
   Hello3
   Hello4

macroargcount=0 (=0):
   Hello1, Hello2, Hello3, Hello4

macroargcount<0 (=-1):
   Hello1, Hello2, Hello3, Hello4
   Hello2, Hello3, Hello4
   Hello3, Hello4
   Hello4
'/

Version

  • Since fbc 1.20.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_ARG_RIGHTOF__
  • __FB_ARG_LEFTOF__
  • __FB_ARG_COUNT__
  • __FB_ARG_EXTRACT__


__FB_ARG_RIGHTOF__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_ARG_RIGHTOF__( arg, sep [, ret] )

Parameters

arg
argument
sep
separator, obviously different from the comma (,)
ret
default return if separator not found

Description

Returns the right token of the argument (arg), based on the separator (sep).
(in the expression of the argument, the tokens and the separator must be spaced)

By default, if the default return (ret) is not given, the macro returns nothing (empty token) if the separator (sep) is not found.
Otherwise, if the default return (ret) is given, the macro returns the default return (ret) if the separator (sep) is not found.

Example

#macro m( arg )
    Scope
        Var v = __FB_ARG_RIGHTOF__( arg, versus, "Not found 'versus'" )
        Print v
    End Scope
#endmacro

m(1 versus 2)
m("left-side" versus "right-side")
m(pi verso 3.14)

Sleep

/' Output:
 2
right-side
Not found 'versus'
'/

#macro count( range )
    Scope
        Dim x As Integer = __FB_ARG_LEFTOF__( range, To )
        Dim y As Integer = __FB_ARG_RIGHTOF__( range, To )
        Dim s As Integer = Sgn(y - x)
        Print "Counting " & #range
        For i As Integer = x To y Step s
            Print i
        Next i
    End Scope

#endmacro

count( 4 To 10 )
count( 7 To 2 )

Sleep

/' Output:
Counting 4 to 10
 4
 5
 6
 7
 8
 9
 10
Counting 7 to 2
 7
 6
 5
 4
 3
 2
'/

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_ARG_LEFTOF__
  • __FB_ARG_COUNT__
  • __FB_ARG_EXTRACT__
  • __Fb_Arg_Listexpand__


__FB_ARGC__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_ARGC__

Description

Substituted with the number of arguments passed in on the command line including the name of the executable.
(for example : myprogram parm1, parm2, param3 --> __FB_ARGC__ = 4)

__FB_ARGC__ is the name of a parameter passed to the program's implicit main function, and therefore is only defined in the module level code of the main module for an application.

Example

Dim i As Integer
For i = 0 To __FB_ARGC__ - 1
        Print "arg "; i; " = '"; Command(i); "'"
Next i

Differences from QB

  • New to FreeBASIC

See also

  • __FB_ARGV__
  • Command


__FB_ARGV__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_ARGV__

Description

Substituted with a pointer to a list of pointers to the zero terminated command line arguments passed in on the command line including the name of the executable.

__FB_ARGV__ is the name of a parameter passed to the program's implicit main function, and therefore is only defined in the module level code of the main module for an application.

Example

Sub displayCommandLineArguments( ByVal argc As Integer, ByVal argv As ZString Ptr Ptr )
    Dim i As Integer
    For i = 0 To argc - 1
        Print "arg "; i; " = '"; *argv[i]; "'"
    Next i
End Sub

displayCommandLineArguments( __FB_ARGC__, __FB_ARGV__ )

Sleep

Differences from QB

  • New to FreeBASIC

See also

  • __FB_ARGC__
  • Command


__FB_ARM__
Intrinsic define set by the compiler

Syntax

__FB_ARM__

Description

Define created at compile time if the compilation target uses the ARM CPU architecture, otherwise undefined.

Example

#ifdef __FB_ARM__
  '...instructions for ARM OSes...
#else
  '...instructions for other OSes
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_X86__
  • __FB_LINUX__
  • __FB_FREEBSD__
  • __FB_OPENBSD__
  • __FB_NETBSD__
  • __FB_CYGWIN__
  • __FB_DARWIN__
  • __FB_PCOS__
  • Compiler Option: -target


__FB_ASM__
Intrinsic define set by the compiler

Syntax

__FB_ASM__

Description

__FB_ASM__ returns a string equal to "intel" or "att" depending on whether inline assembly blocks should use the Intel format or the GCC/AT&T format.

Example

Dim a As Long
#if __FB_ASM__ = "intel"
    Asm
         inc dword Ptr [a]
    End Asm
#else
    Asm
        "incl %0\n" : "+m" (a) : :
    End Asm
#endif

Version

  • Since fbc 1.02.0

Differences from QB

  • New to FreeBASIC

See also

  • Compiler Option: -asm


__FB_BACKEND__
Intrinsic define set by the compiler

Syntax

__FB_BACKEND__

Description

Defined to either "gas" or "gcc", depending on which backend was specified via -gen.

Example

Print "Back end is: " & __FB_BACKEND__

Differences from QB

  • Did not exist in QB


__FB_BIGENDIAN__
Intrinsic define set by the compiler

Syntax

__FB_BIGENDIAN__

Description

Define without a value created at compile time if compiling for a big endian target.

It can be used to compile parts of the program only if the target is big endian.

Example

#ifdef __FB_BIGENDIAN__
   '...instructions only for big endian machines
#else
  '...instructions only for little endian machines
#endif

Differences from QB

  • Did not exist in QB


__FB_BUILD_DATE__
Intrinsic define (macro string) set by the compiler

Syntax

__FB_BUILD_DATE__

Description

Substituted with the quoted string containing the date (MM-DD-YYYY) the compiler was built on.

Example

    Print "This program compiled with a compiler built on this date:" & __FB_BUILD_DATE__

Differences from QB

  • New to FreeBASIC

See also

  • __FB_BUILD_DATE_ISO__
  • __DATE__
  • __DATE_ISO__


__FB_BUILD_DATE_ISO__
Intrinsic define (macro string) set by the compiler

Syntax

__FB_BUILD_DATE_ISO__

Description

Substituted with the quoted string containing the date (YYYY-MM-DD) the compiler was built on in ISO date format.

Example

    Print "This program compiled with a compiler built on this date:" & __FB_BUILD_DATE_ISO__

Version

  • Since fbc 1.07.1

Differences from QB

  • New to FreeBASIC

See also

  • __FB_BUILD_DATE__
  • __DATE__
  • __DATE_ISO__


__FB_BUILD_FORK_ID__
Intrinsic define (macro string) set by the compiler

Syntax

__FB_BUILD_FORK_ID__

Description

Substituted by a string of the compiler's custom defined project fork identifier name if defined at fbc build time, or an empty string if it was not defined.

This value is an empty string for releases made by the FreeBASIC Development Team. Community developers and experimenters may define this value when building their own fbc packages using the makefile to help users identify that fbc may be a special build and may have specific features not in the main release packages.

Example

Print "fbc fork id: "; __FB_BUILD_FORK_ID__

Version

  • Since fbc 1.20.0

Differences from QB

  • New to FreeBASIC


__FB_BUILD_SHA1__
Intrinsic define (macro string) set by the compiler

Syntax

__FB_BUILD_SHA1__

Description

Substituted by a string of the compiler's source SHA-1 revision. The value is set using the SHA-1 commit id of the source code revision used to build the compiler binary, if available. If the SHA-1 was not available at the time of building the compiler, the value will be an empty string.

Example

Print __FB_BUILD_SHA1__

Version

  • Since fbc 1.07.1

Differences from QB

  • New to FreeBASIC


__FB_CYGWIN__
Intrinsic define set by the compiler

Syntax

__FB_CYGWIN__

Description

Define without a value created at compile time in the Cygwin version of the compiler, or when the -target cygwin command line option is used. It can be used to compile parts of the program only if the target is Cygwin.

Example

#ifdef __FB_CYGWIN__
  '...instructions only for Cygwin...
#else
  '...instructions not for Cygwin...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_LINUX__
  • __Fb_Win32_
  • __FB_UNIX__
  • Compiler Option: -target


__FB_DARWIN__
Intrinsic define set by the compiler

Syntax

__FB_DARWIN__

Description

Define without a value created at compile time in the Darwin version of the compiler, or when the -target darwin command line option is used. It can be used to compile parts of the program only if the target is Darwin.

Example

#ifdef __FB_DARWIN__
  '...instructions only for Darwin...
#else
  '...instructions not for Darwin...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_LINUX__
  • __FB_WIN32__
  • __FB_UNIX__
  • Compiler Option: -target


__FB_DEBUG__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_DEBUG__

Description

__FB_DEBUG__ indicates if the the generate debug information option -g or the enable intrinsic define __FB_DEBUG__ option Compiler Option: -edebug was specified on the command line at the time of compilation.

Returns non-zero (-1) if either option was specified. Returns zero (0) otherwise.

__FB_ERR__ can be used to determine in user source code if the -g or -edebug option was specified or implied on the command line.

Example

#if __FB_DEBUG__ <> 0
        #print Debug mode
#else
        #print Release mode
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_ERR__
  • __FB_MT__
  • Compiler Option: -edebug
  • Compiler Option: -g


__FB_DOS__
Intrinsic define set by the compiler

Syntax

__FB_DOS__

Description

Define without a value created at compile time if compiling for the DOS target. Default in the DOS hosted version, or active when the -target dos command line option is used. It can be used to compile parts of the program only if the target is DOS. Note: the DOS hosted version cannot compile to other targets than DOS by now.

Example

#ifdef __FB_DOS__
  ' ... instructions only for DOS ...
  ' ... INT 0x31
#else
  ' ... instructions not for DOS ...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_LINUX__
  • __FB_WIN32__
  • __FB_PCOS__
  • DOS related FAQ
  • Compiler Option: -target


__FB_ERR__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_ERR__

Description

__FB_ERR__ indicates if -e, -ex, or -exx was specified on the compiler command line at the time of compilation of a module.

__FB_ERR__ indicates if -earray, -enullptr, or -elocation was specified on the compiler command line, or implied by use of -exx, at the time of compilation of a module.

__FB_ERR__ indicates if -edebug, -edebuginfo, or -eassert was specified on the compiler command line, or implied by use of -g, at the time of compilation of a module.

__FB_ERR__ indicates if -eunwind was specified on the compiler command line, or implied by use of -e, -ex, or -exx, at the time of compilation of a module.

__FB_ERR__ indicates if -earraydims was specified on the compiler command line, or implied by use of -exx, at the time of compilation of a module.

Returns bit-wise OR of the following values:
value | flag | description
0 | no flag |
1 | errorcheck | implied by '-e', '-ex', '-exx'
2 | resumeerr | implied by '-ex', '-exx'
4 | extraerrchk | implied by '-exx'
8 | arrayboundchk | implied by '-earray', '-exx'
16 | nullptrchk | implied by '-enullptr', '-exx'
32 | assertions | implied by '-eassert', '-g'
64 | debuginfo | implied by '-edebuginfo', '-g'
128 | debug | implied by '-edebug', '-g'
256 | errlocation | implied by '-elocation', '-exx'
512 | unwindinfo | implied by '-eunwind', '-e', '-ex', '-exx'
1024 | arraydimscheck | implied by '-earraydims', '-exx'

__FB_ERR__ is always defined.

Example

'Example code to demonstrate a use of __FB_ERR__

Dim fb_err_value As Integer
fb_err_value = __FB_ERR__
If fb_err_value = 0 Then
    Print "no flag enabled"
Else
    If fb_err_value And 1 Then
        Print "'errorcheck' flag enabled"
    End If
    If fb_err_value And 2 Then
        Print "'resumeerr' flag enabled"
    End If
    If fb_err_value And 4 Then
        Print "'extraerrchk' flag enabled"
    End If
    If fb_err_value And 8 Then
        Print "'arrayboundchk' flag enabled"
    End If
    If fb_err_value And 16 Then
        Print "'nullptrchk' flag enabled"
    End If
    If fb_err_value And 32 Then
        Print "'assertions' flag enabled"
    End If
    If fb_err_value And 64 Then
        Print "'debuginfo' flag enabled"
    End If
    If fb_err_value And 128 Then
        Print "'debug' flag enabled"
    End If
    If fb_err_value And 256 Then
        Print "'errlocation' flag enabled"
    End If
    If fb_err_value And 512 Then
        Print "'unwindinfo' flag enabled"
    End If
    If fb_err_value And 1024 Then
        Print "'arraydimscheck' flag enabled"
    End If
End If

Version

  • Since fbc 1.20.0: -earraydims indication.
  • Since fbc 1.10.0: -eunwind indication.
  • Since fbc 1.07.0: -earray, -enullptr, -elocation, -edebug, -edebuginfo, and -eassert indications.

Differences from QB

  • New to FreeBASIC

See also

  • __FB_MT__
  • __FB_DEBUG__
  • Compiler Option: -e
  • Compiler Option: -ex
  • Compiler Option: -exx
  • Compiler Option: -eunwind
  • Error Handling


__FB_EVAL__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_EVAL__( arg )

Parameters

arg
argument

Description

Evaluates the argument (constant-expression) at compile time.

When the argument evaluation produces a string, __FB_EVAL__ returns a string formatted with a preprocessor operator:
- a Non-Escaped String Literal (of form: $"text"),
- or an Escaped String Literal (of form: !"text") if needed.
For other datatypes produced, simple Literals (of Integers, Floating Points, Booleans) without prefix/suffix are returned.

__FB_EVAL__ macro is useful where there is any of the following:
- non functional expression (i.e. side-effects),
- needs to be evaluated (i.e. simplified, respecting the operator precedence) before passing it on,
- used in a place where fbc would not allow an expression.

Example

#print 1 + 2
#print __FB_EVAL__( 1 + 2 )
#print 4 * Atn(1)
#print __FB_EVAL__( 4 * Atn(1) )

/' Compiler output:
1 + 2
3
4 * Atn(1)
3.141592653589793
'/

'   In this example, the three '__FB_EVAL__' are absolutely mandatory in this 'assign()' macro.
'   Even for '__FB_QUOTE__( __FB_EVAL__( expr ) )', because for the case of expr = cos(1/x),
'   'cos(1/x)' must be properly evaluated before be quoted (after the previous 'assign("x", x+1)'),
'   otherwise in that case 'cos(1/x+1)' is taken into account (giving 'cos(2)') instead of 'cos(1/(x+1))' (giving 'cos (1/2)')
'   because the operator precedence is not applied by '__FB_QUOTE__'.

#macro assign( sym, expr )
    __FB_UNQUOTE__( __FB_EVAL__( "#undef " + sym ) )
    __FB_UNQUOTE__( __FB_EVAL__( "#define " + sym + " " + __FB_QUOTE__( __FB_EVAL__( expr ) ) ) )
#endmacro

#define x

assign( "x", 1 )
Print x

assign( "x", x+1 )
Print x

assign( "x", Cos(1/x) )
Print x

assign( "x", "hello" )
Print x

assign( "x", x+x )
Print x

/' Output:
 1
 2
 0.877582...
hello
hellohello
'/

See also __FB_ARG_EXTRACT__ example.

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_QUOTE__
  • __FB_UNQUOTE__


__FB_FPMODE__
Intrinsic define set by the compiler

Syntax

__FB_FPMODE__

Description

Defined as "fast" if SSE fast arithmetics is enabled, or "precise" otherwise.

Example

#if __FB_FPMODE__ = "fast"
  ' ... instructions for using fast-mode math ...
#else
  ' ... instructions for using normal math ...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • Compiler Option: -fpmode


__FB_FPU__
Intrinsic define set by the compiler

Syntax

__FB_FPU__

Description

For x86 and x86_64 targets:
    • Defined as "sse" if SSE floating point arithmetics is enabled, and
    • defined as "x87" otherwise

For arm and aarch64 targets:
    • Defined as "neon" if Advanced SIMD (Neon) extension is enabled, and
    • defined as "x87" otherwise

Example

#if __FB_FPU__ = "sse"
  ' ... instructions only for SSE ...
#else
  ' ... instructions not for SSE ...
#endif

Version

  • __FB_FPU__ = neon added in fbc 1.20.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_SSE__
  • Compiler Option: -fpu


__FB_FREEBSD__
Intrinsic define set by the compiler

Syntax

__FB_FREEBSD__

Description

Define without a value created at compile time in the FreeBSD version of the compiler, or when the -target freebsd command line option is used. It can be used to compile parts of the program only if the target is FreeBSD.

Example

#ifdef __FB_FREEBSD__
  '...instructions only for FreeBSD...
#else
  '...instructions not for FreeBSD...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_LINUX__
  • __FB_WIN32__
  • __FB_UNIX__
  • Compiler Option: -target


__FB_GCC__
Intrinsic define set by the compiler

Syntax

__FB_GCC__

Description

Defined to true (-1) if -gen gcc is used, or false (0) otherwise.

Example

#if __FB_GCC__ <> 0
        #print Backend Code Emitter And Assembler: gcc
#else
        #print Backend Code Emitter And Assembler: Not gcc
#endif

Differences from QB

  • Did not exist in QB


__FB_GUI__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_GUI__

Description

__FB_GUI__ indicates whether GUI subsystem mode is active at the current point in compilation.

Returns non-zero (-1) if the executable subsystem option -s gui was specified, or if #Pragma Gui is active. Returns zero (0) otherwise.

Example

#if __FB_GUI__ <> 0
        #print Executable subsystem: gui
#else
        #print Executable subsystem: console
#endif

Version

  • Since fbc 1.06.0

Platform Differences

  • On Windows and Cygwin, this controls the linker subsystem.
  • Other targets may use the same mode value for target-specific startup or runtime behavior.

Differences from QB

  • New to FreeBASIC

See also

  • Compiler Option: -s
  • #pragma


__FB_IIF__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_IIF__( compare-expr, true-expr, false-expr )

Parameters

compare-expr
The comparison expression to test.
A non-zero value evaluates as true, while a value of zero evaluates as false.
true-expr
Source code to return if compare-expr is true.
false-expr
Source code to return if compare-expr is false.

Description

__FB_IIF__ returns source code text depending on the result of a comparison expression evaluated at preprocessing-time.
Its typical use is in the middle of an expression; it avoids splitting it to put a conditional in the middle.

This intrinsic define (macro) differs from the IIf conditional statement in that __FB_IIF__ is evaluated at compile-time only and returns source code text. IIf is evaluated at compile-time only if the compare-expr is a constant. Otherwise IIf expressions are evaluated at run-time.

Example

' From the example of the '#ELSE' documentation page:
    '#DEFINE MODULE_VERSION 1
    'Dim a As String
    '#IF (MODULE_VERSION > 0)
    '    a = "Release"
    '#ELSE
    '    a = "Beta"
    '#ENDIF
    'Print "Program is "; a

' Simpler code using '__FB_IIF__':
    #define MODULE_VERSION 1
    Dim a As String
    a = __FB_IIF__( MODULE_VERSION > 0, "Release", "Beta" )
    Print "Program is "; a

Version

  • Since fbc 1.10.0

Differences from QB

  • New to FreeBASIC

See also

  • #if
  • #else
  • #endif


__FB_JOIN__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_JOIN__( arg1, arg2 )

Parameters

arg1, arg2
left (1) and right (2) arguments to join

Description

Joins two token arguments together as one, similar to token pasting operator (##) but more powerfull (will resolve arguments before joining).

Example

#macro m ( arg1, arg2 )
    #print arg1##arg2
    #print __FB_JOIN__( arg1, arg2 )
#endmacro

m(Free, BASIC)

/' Compiler output:
FreeBASIC
FreeBASIC
'/

#define PREFIX p
#define SUFFIX _T

'' this won't work - arguments not expanded
#define   makename1( x )  PREFIX##x##SUFFIX

'' this will work - can do this in older versions of fbc too
#define join( a, b ) a##b
#define makename2( x ) join( PREFIX, join( x, SUFFIX ) )

'' built in __FB_JOIN__() -- works pretty much like join() above
#define   makename3( x )  __FB_JOIN__( PREFIX, __FB_JOIN__( x, SUFFIX ) )

#macro dump( arg )
    #print #arg
#endmacro

dump( makename1(text) )
dump( makename2(text) )
dump( makename3(text) )

/' Compiler output:
PREFIXtextSUFFIX
ptext_T
ptext_T
'/

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • Operator ## (Preprocessor Concatenate)


__FB_JS__

Syntax

__FB_JS__

Description

Defined without a value at compile time when the JavaScript target is being used. This is normally selected by fbc-js or by the -target js-asmjs command line option. It can be used to compile parts of a program only for the browser/Emscripten target.

The JavaScript target also defines __FB_LINUX__ for compatibility with Emscripten's POSIX-like runtime environment.

Example

#IFDEF __FB_JS__
    print "JavaScript target"
#ENDIF

Predefined Defines

See also
Compiling FB for JavaScript
Compiler Option: -target

Compiler Meta-Commands


__FB_LANG__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_LANG__

Description

__FB_LANG__ indicates which language compatibility option was set at the time of compilation of a module. By default __FB_LANG__ will be set to "fb". The language compatibility option can be changed using one (or more) of the following methods:
    • -lang command line option
    • -forcelang command line option
    • #lang directive
    • $Lang metacommand

Returns a lower case string with one of the following values:
value | description
''fb'' | FreeBASIC compatibility (default)
''qb'' | QBASIC compatibility
''fblite'' | FreeBASIC language compatibility, with a more QBASIC-compatible coding style
''deprecated'' | FBC version 0.16 compatibility

__FB_LANG__ is always defined.

Example

'' Set option explicit always on

#ifdef __FB_LANG__
  #if __FB_LANG__ <> "fb"
    Option Explicit
  #endif
#else
  '' Older version - before lang fb
  Option Explicit
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_VERSION__
  • #lang
  • Compiler Option: -lang
  • Compiler Option: -forcelang
  • Compiler Dialects


__FB_LINUX__
Intrinsic define set by the compiler

Syntax

__FB_LINUX__

Description

Define without a value created at compile time when compiling to the Linux target. Default in the Linux hosted version of the compiler, or active when the -target linux command line option is used. It can be used to compile parts of the program only if the target is Linux.

Example

#ifdef __FB_LINUX__
  ' ... instructions only for Linux ...
  ' ... #libpath "/usr/X11/lib"
#else
  ' ... instructions not for Linux ...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_DOS__
  • __FB_WIN32__
  • __FB_UNIX__
  • Compiler Option: -target


__Fb_Loongarch64__
Intrinsic define set by the compiler

Syntax

__FB_LOONGARCH64__

Description

Define created at compile time if the compilation target uses the 64-bit LoongArch CPU architecture, otherwise undefined.

Example

#ifdef __FB_LOONGARCH64__
  '...instructions for 64-bit LoongArch targets...
#else
  '...instructions for other targets...
#endif

Version

  • Since fbc 1.20.1

Differences from QB

  • New to FreeBASIC

See also

  • __FB_64BIT__
  • __FB_LINUX__
  • Compiler Option: -target


__FB_MAIN__
Intrinsic define set by the compiler

Syntax

__FB_MAIN__

Description

__FB_MAIN__ is defined in the main module and not defined in other modules.

The main module is determined by the compiler as either the first source file listed on the command line or explicitly named using the -m option on the command line.

Example

#ifdef __FB_MAIN__
  #print Compiling the main module
#else
  #print Compiling an additional module
#endif

Differences from QB

  • New to FreeBASIC

See also

  • Compiler Option: -m
  • #ifdef
  • #ifndef


__FB_MIN_VERSION__
Macro function to test minimum compiler version

Syntax

#define __FB_MIN_VERSION__( major, minor, patch) _
((__FB_VER_MAJOR__ > major) or _
((__FB_VER_MAJOR__ = major) and ((__FB_VER_MINOR__ > minor) or _
(__FB_VER_MINOR__ = minor and __FB_VER_PATCH__ >= patch_level))))

Usage

__FB_MIN_VERSION__( major, minor, patch)

Parameters

major
minimum major version to test
minor
minimum minor version to test
patch
minimum patch version to test

Return Value

Returns zero (0) if the compiler version is less than the specified version, or non-zero (-1) if the compiler version is greater than or equal to specified version

Description

__FB_MIN_VERSION__ tests for a minimum version of the compiler.

Example

#if Not __FB_MIN_VERSION__(0, 18, 2)
     #error fbc must be at least version 0.18.2 To compile This module
#endif

Differences from QB

  • New to FreeBASIC

See also

  • #if


__FB_MT__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_MT__

Description

__FB_MT__ indicates if the the multithreaded option -mt was specified on the command line at the time of compilation, or whether one of the Threadtcreate or ThreadCall keywords is used more above in the source code.

Returns non-zero (-1) if the option was specified. Returns zero (0) otherwise.

Example

#if __FB_MT__
        #print Using multi-threaded library
#else
        #print Using Single-threaded library
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_DEBUG__
  • Compiler Option: -mt


__FB_NETBSD__
Intrinsic define set by the compiler

Syntax

__FB_NETBSD__

Description

Define without a value created at compile time in the NetBSD version of the compiler, or when the -target netbsd command line option is used. It can be used to compile parts of the program only if the target is NetBSD.

Example

#ifdef __FB_NETBSD__
  '...instructions only for NetBSD...
#else
  '...instructions not for NetBSD...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_LINUX__
  • __FB_WIN32__
  • __FB_UNIX__
  • Compiler Option: -target


__FB_OPENBSD__
Intrinsic define set by the compiler

Syntax

__FB_OPENBSD__

Description

Define without a value created at compile time in the OpenBSD version of the compiler, or when the -target openbsd command line option is used. It can be used to compile parts of the program only if the target is OpenBSD.

Example

#ifdef __FB_OPENBSD__
  '...instructions only for OpenBSD...
#else
  '...instructions not for OpenBSD...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_LINUX__
  • __FB_WIN32__
  • __FB_UNIX__
  • Compiler Option: -target


__FB_OPTIMIZE__
Intrinsic define set by the compiler

Syntax

__FB_OPTIMIZE__

Description

Always defined and will have a value from 0 to 3 (0 by default) to indicate the optimization level passed to the backend compiler via -O.

Example

Print "Optimization level is: " & __FB_OPTIMIZE__

Version

  • Since fbc 1.09.0

Differences from QB

  • Did not exist in QB

See also

  • Compiler Option: -O


__FB_OPTION_BYVAL__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_OPTION_BYVAL__

Description

Indicates if parameters to a Function or Sub are passed by reference as with ByRef, or by value as with ByVal by default when the by value / by reference specifier is not explicitly stated.

__FB_OPTION_BYVAL__ is set to non-zero (-1) if by default parameters are passed value, and zero (0) if by default parameters are passed by reference.

The default for passing parameters by reference or by value is determined by the -lang command line option used during compilation or usage of Option ByVal in the source file.

Example

#if( __FB_OPTION_BYVAL__ <> 0 )
  #error Option ByVal must Not be used With This source
#endif

Differences from QB

  • New to FreeBASIC

See also

  • ByVal
  • ByRef
  • Option ByVal


__FB_OPTION_DYNAMIC__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_OPTION_DYNAMIC__

Description

__FB_OPTION_DYNAMIC__ is defined as true (negative one (-1)) if a recent Option Dynamic statement or '$Dynamic meta-command was issued. Otherwise, it is defined as zero (0).

Example

#if __FB_OPTION_DYNAMIC__ <> 0
#error This module must Not use Option Dynamic
#endif

Differences from QB

  • New to FreeBASIC

See also

  • Option Dynamic
  • Option Static


__FB_OPTION_ESCAPE__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_OPTION_ESCAPE__

Description

Indicates if by default, string literals are processed for escape characters when not explicitly prefixed with the $ Operator for non-escaped strings, or the ! Operator for escaped strings.

The default method for processing string literals is set by usage of the -lang command line option during compilation or usage of Option Escape in the source file.

__FB_OPTION_ESCAPE__ returns zero (0) if the option has not been set. Returns non-zero (-1) if the option has been set.

Example

#if( __FB_OPTION_ESCAPE__ <> 0 )
  #error Option Escape must Not be used With This include file
#endif

Differences from QB

  • New to FreeBASIC

See also

  • Option Escape


__FB_OPTION_EXPLICIT__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_OPTION_EXPLICIT__

Description

__FB_OPTION_EXPLICIT__ indicates if Option Explicit has been used previously in the source.

Returns zero (0) if the option has not been set. Returns non-zero (-1) if the option has been set.

Example

#if( __FB_OPTION_EXPLICIT__ = 0 )
  #error Option Explicit must used With This module
#endif

Differences from QB

  • New to FreeBASIC

See also

  • Dim
  • Option Explicit


__FB_OPTION_GOSUB__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_OPTION_GOSUB__

Description

Indicates how GoSub and Return will be handled at compile time. If the option is set (-1) then GoSub is allowed and Return is recognized as return-from-gosub only. If the option is not set (0) then GoSub is not allowed and Return is recognized as return-from-procedure only.

This macro value can be changed at compile time. Option Gosub will set the option (enable gosub support) and Option Nogosub will clear the option (disable gosub support).

__FB_OPTION_GOSUB__ returns zero (0) if the option has not been set. Returns non-zero (-1) if the option has been set.

Example

#if( __FB_OPTION_GOSUB__ <> 0 )
    '' turn off gosub support
    Option nogosub
#endif

Dialect Differences

  • Defaults to -1 in the -lang qb dialect and 0 in all other dialects.

Differences from QB

  • New to FreeBASIC

See also

  • Option Gosub
  • Option Nogosub


__FB_OPTION_PRIVATE__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_OPTION_PRIVATE__

Description

Indicates if by default Function's and Sub's have module scope or global scope when not explicitly specified with Private or Public.

The default scope specifier for functions and subs is set by usage of the -lang command line option during compilation or usage of Option Private in the source file.

Default scope of procedures can be changed during compilation with #Pragma Private.

__FB_OPTION_PRIVATE__ returns zero (0) if the option has not been set. Returns non-zero (-1) if the option has been set.

Example

#if( __FB_OPTION_PRIVATE__ <> 0 )
  #error Option Private must Not be used With This module
#endif

Differences from QB

  • New to FreeBASIC

See also

  • Option Private
  • Private
  • Public


__FB_OPTION_PROFILE__
Intrinsic define set by the compiler

Syntax

__FB_OPTION_PROFILE__

Description

Intrinsic define set to an integer to indicate that the profiling code is generated:
__FB_OPTION_PROFILE__ is set to non-zero when profiling code is generated
__FB_OPTION_PROFILE__ is set to zero when profiling code is not generated

#Pragma Profile allows to control the generation of profiling code.
This allows optionally including or excluding sections of source code to be profiled:
when #pragma profile = true, then profiling code is generated
when #pragma profile = false, then profiling code is not generated

Example

#cmdline "-profgen fb"
' profiling code generated
Print __FB_OPTION_PROFILE__

#pragma profile = False
' profiling code not generated
Print __FB_OPTION_PROFILE__

#pragma profile = True
' profiling code generated
Print __FB_OPTION_PROFILE__

Version

  • Since fbc 1.20.0

Differences from QB

  • New to FreeBASIC

See also

  • Compiler Option: -profgen
  • Profiling for gmon/gprof
  • Profiling with fb's profiler
  • #pragma


__FB_OUT_DLL__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_OUT_DLL__

Description

__FB_OUT_DLL__ indicates that the specified output file type on the compiler command line at the time of compilation is a shared library.

Returns non-zero (-1) if the output is a shared library. Returns zero (0) otherwise.

Only one of __FB_OUT_DLL__, __FB_OUT_EXE__, __FB_OUT_LIB__, or __FB_OUT_OBJ__ will evaluate to non-zero (-1). All others will evaluate to zero (0).

Example

#if __FB_OUT_DLL__
        '... specific instructions when making a shared library (DLL)
#else
        '... specific instructions when not making a shared library (DLL)
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OUT_EXE__
  • __FB_OUT_LIB__
  • __FB_OUT_OBJ__
  • Compiler Option: -dll


__FB_OUT_EXE__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_OUT_EXE__

Description

__FB_OUT_EXE__ indicates that the specified output file type on the compiler command line at the time of compilation is an executable.

Returns non-zero (-1) if the output is an executable. Returns zero (0) otherwise.

Only one of __FB_OUT_DLL__, __FB_OUT_EXE__, __FB_OUT_LIB__, or __FB_OUT_OBJ__ will evaluate to non-zero (-1). All others will evaluate to zero (0).

Example

#if __FB_OUT_EXE__
        '... specific instructions when making an executable
#else
        '... specific instructions when not making an executable
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OUT_DLL__
  • __FB_OUT_LIB__
  • __FB_OUT_OBJ__


__FB_OUT_LIB__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_OUT_LIB__

Description

__FB_OUT_LIB__ indicates that the specified output file type on the compiler command line at the time of compilation is a static library.

Returns non-zero (-1) if the output is a static library. Returns zero (0) otherwise.

Only one of __FB_OUT_DLL__, __FB_OUT_EXE__, __FB_OUT_LIB__, or __FB_OUT_OBJ__ will evaluate to non-zero (-1). All others will evaluate to zero (0).

Example

#if __FB_OUT_LIB__
        '... specific instructions when making a static library
#else
        '... specific instructions when not making a static library
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OUT_EXE__
  • __FB_OUT_DLL__
  • __FB_OUT_OBJ__
  • Compiler Option: -lib


__FB_OUT_OBJ__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_OUT_OBJ__

Description

__FB_OUT_OBJ__ indicates that the specified output file type on the compiler command line at the time of compilation is an object module.

Returns non-zero (-1) if the output is an object module. Returns zero (0) otherwise.

Only one of __FB_OUT_DLL__, __FB_OUT_EXE__, __FB_OUT_LIB__, or __FB_OUT_OBJ__, will evaluate to non-zero (-1). All others will evaluate to zero (0).

Example

#if __FB_OUT_OBJ__
        '... specific instructions when compiling to an object file only
#else
        '... specific instructions when not compiling to an object file only
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OUT_EXE__
  • __FB_OUT_DLL__
  • __FB_OUT_LIB__


__FB_PCOS__
Intrinsic define set by the compiler

Syntax

__FB_PCOS__

Description

Define created at compile time if the OS has filesystem behavior styled like common PC OSes, e.g. DOS, Windows, OS/2, Symbian OS, possibly others. Drive letters, backslashes, that stuff, otherwise undefined.

Example

#ifdef __FB_PCOS__
  '...instructions for PC-ish OSes...
#else
  '...instructions for other OSes
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_WIN32__
  • __FB_DOS__
  • __FB_XBOX__
  • __FB_UNIX__
  • Compiler Option: -target


__FB_PPC__
Intrinsic define set by the compiler

Syntax

__FB_PPC__

Description

Define created at compile time if the compilation target uses the PowerPC CPU architecture, otherwise undefined.

Example

#ifdef __FB_PPC__
  '...instructions for PowerPC OSes...
#else
  '...instructions for other OSes
#endif

Version

  • Since fbc 1.09.0

Differences from QB

  • New to FreeBASIC

See also

  • Compiler Option: -target


__FB_PROFILE__
Intrinsic define set by the compiler

Syntax

__FB_PROFILE__

Description

Intrinsic define set to an integer to indicate the profiling method.
The different value are defined in the ./inc/fbc-int/profile.bi include file (inside Namespace: FB and Enum: PROFGEN_ID):
    • PROFGEN_ID_NONE: profiling is not enabled
    • PROFGEN_ID_GMON: code is being generated for gmon/gprof
    • PROFGEN_ID_CALLS: code is being generated for fb's function call profiler
    • PROFGEN_ID_CYCLES: code is being generated for fb's cycle count call profiler

Example

#cmdline "-profgen fb"
#include Once "fbc-int/profile.bi"
Using FBC

Print "__FB_PROFILE__ = ";

Select Case __FB_PROFILE__
Case PROFGEN_ID_NONE
    Print "PROFGEN_ID_NONE"
Case PROFGEN_ID_GMON
    Print "PROFGEN_ID_GMON"
Case PROFGEN_ID_CALLS
    Print "PROFGEN_ID_CALLS"
Case PROFGEN_ID_CYCLES
    Print "PROFGEN_ID_CYCLES"
End Select

Version

  • Since fbc 1.20.0

Differences from QB

  • New to FreeBASIC

See also

  • Compiler Option: -profgen
  • Profiling for gmon/gprof
  • Profiling with fb's profiler


__Fb_Query_Symbol__
Intrinsic define (macro) performed by the compiler, and its associated sub-macros.

Syntax

__FB_QUERY_SYMBOL__( what, sym )

Usage

The ./inc/fbc-int/symbol.bi header exposes internals of the fbc compiler through the built-in macro __FB_QUERY_SYMBOL__.

In addition, many convenient sub-macros with 'is' as name prefix (also defined in the file ./inc/fbc-int/symbol.bi) help the user to indirectly call the generic built-in macro __FB_QUERY_SYMBOL__:

#include once "fbc-int/symbol.bi"
using FBC

' then:
Dim b As Boolean
...
b = isXXXXX( sym )

Parameters

sym
symbol name to query
b
boolean to store the return value from the macro
XXXXX
body of the sub-macro name standing the kind of query

Description

__FB_QUERY_SYMBOL__ is a generic built-in macro for querying fbc symbol internals.

In the file ./inc/fbc-int/symbol.bi, many convenient and specialized sub-macros with 'is' as name prefix ('isXXXXX') are also defined (where 'XXXXX' stands for the kind of query).
They help the user to indirectly call the generic built-in macro __FB_QUERY_SYMBOL__ without knowing the encoding of its first parameter and its return value, but passing the only sym parameter to those sub-macros which only return -1 (for True) or 0 (for False).

For three of the query types, convenient and specialized sub-macros are available:
- 'class of symbols' (like variable, constant, procedure, namespace, ...),
- 'class of data' (like integer, float, string, UDT, ...),
- 'type of data' (like boolean, byte, ubyte, short, ...).
in order to test whether a symbol matches an element of the considered family.

Example

Using convenient sub-macros from the ./inc/fbc-int/symbol.bi file (indirectly calling __FB_QUERY_SYMBOL__)
#include Once "fbc-int/symbol.bi"
Using FBC

Type T
    i As Integer
End Type
Dim x As T

Print isUDT( T )       '' returns -1 (true)
Print isVariable( T )  '' returns 0 (false)
Print isUDT( x )       '' returns 0 (false)
Print isVariable( x )  '' returns -1 (true)

Sleep

Using directly __FB_QUERY_SYMBOL__ and some declarations from the ./inc/fbc-int/symbol.bi file to output the data class of a symbol:
#include Once "fbc-int/symbol.bi"
Using FBC

Function dataclassToStr( ByVal classid As fbc.FB_DATACLASS ) As String
    Static As ZString Ptr classnames _
        ( FB_DATACLASS.FB_DATACLASS_INTEGER To FB_DATACLASS.FB_DATACLASS_UDT ) _
        = { @"integer", @"float", @"string", @"udt" }

    Select Case classid
    Case LBound(classnames) To UBound(classnames)
        Return *classnames(classid)
    Case Else
        Return "*invalid*"
    End Select
End Function

#macro show_dataclass( sym )
    Scope
        Var classid = __FB_QUERY_SYMBOL__( FB_QUERY_SYMBOL.dataclass, sym )
        Print Left( "   [" & classid & "] " & dataclassToStr(classid) + Space(16), 16 ) & ": ";
        Print #sym
    End Scope
#endmacro

Dim As Byte b
Dim As Double d
Dim As String s

Type T
    Dim As Long l
End Type
Dim As T t1

Print "EXAMPLES OF '__FB_QUERY_SYMBOL__( FB_QUERY_SYMBOL.dataclass, sym )':"

show_dataclass( b )
show_dataclass( d )
show_dataclass( s )
show_dataclass( T )
show_dataclass( T.l )
show_dataclass( t1 )
show_dataclass( t1.l )

Sleep

Using directly __FB_QUERY_SYMBOL__ and some declarations from the ./inc/fbc-int/symbol.bi file to output the typename as text, the typename as text with special characters replaced with '_', and the decorated (mangled) type name (WIP), of a symbol:
#include Once "fbc-int/symbol.bi"
Using FBC

Namespace NS
    Type T
        Dim As Const String Const Ptr Ptr pps
    End Type
End Namespace

#print TypeOf(NS.T.pps)

Print "type name    : " & __FB_QUOTE__( __FB_QUERY_SYMBOL__( FB_QUERY_SYMBOL.typename, NS.T.pps ) )
Print "type name id : " & __FB_QUOTE__( __FB_QUERY_SYMBOL__( FB_QUERY_SYMBOL.typenameid, NS.T.pps ) )
Print "mangled type : " & __FB_QUOTE__( __FB_QUERY_SYMBOL__( FB_QUERY_SYMBOL.mangletype, NS.T.pps ) )

Sleep

Version

  • Since fbc 1.20.0 : __FB_QUERY_SYMBOL__ extended to return mangled names
  • Since fbc 1.10.0

Differences from QB

  • New to FreeBASIC


__FB_QUOTE__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_QUOTE__( arg )

Parameters

arg
argument

Description

Converts the argument to a string, similar to stringize operator (#) but can be used anywhere (will expand the argument before conversion).
More precisely, __FB_QUOTE__ returns an over-quoted text (prefixed with the Operator $ (Non-Escaped String Literal)) compared to the one passed through the argument (the argument may already be a string, and so the return will be an over-quoted string in this case).

Example

#macro m( arg )
    Scope
        Dim s1 As String = #arg
        Print s1
        Dim s2 As String = __FB_QUOTE__( arg )
        Print s2
    End Scope
#endmacro

m(Hello)
Print
m("Hello")

Sleep

/' Output:
Hello
Hello

"Hello"
"Hello"
'/

#macro m( arg1, arg2 )
    Scope
        'Dim s0 As String = #arg1##arg2  ' does not work because arg1##arg2 is not developped before applying #
        Dim s1 As String = #arg1###arg2  ' workaround because #arg => $"arg" and not only "arg"
                                         '    (otherwise the result would be "arg1""arg2" => "arg1"arg2")
        Print s1
        Dim s2 As String = __FB_QUOTE__( arg1##arg2 )
        Print s2
    End Scope
#endmacro

m(Free, BASIC)

Sleep

/' Output:
FreeBASIC
FreeBASIC
'/

See also __FB_UNQUOTE__ example.

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_UNQUOTE__
  • __FB_EVAL__


__Fb_Riscv64__
Intrinsic define set by the compiler

Syntax

__FB_RISCV64__

Description

Define created at compile time if the compilation target uses the 64-bit RISC-V CPU architecture, otherwise undefined.

Example

#ifdef __FB_RISCV64__
  '...instructions for 64-bit RISC-V targets...
#else
  '...instructions for other targets...
#endif

Version

  • Since fbc 1.20.1

Differences from QB

  • New to FreeBASIC

See also

  • __FB_64BIT__
  • __FB_LINUX__
  • Compiler Option: -target


__Fb_S390X__
Intrinsic define set by the compiler

Syntax

__FB_S390X__

Description

Define created at compile time if the compilation target uses the s390x CPU architecture, otherwise undefined.

Example

#ifdef __FB_S390X__
  '...instructions for s390x targets...
#else
  '...instructions for other targets...
#endif

Version

  • Since fbc 1.20.1

Differences from QB

  • New to FreeBASIC

See also

  • __FB_BIGENDIAN__
  • __FB_64BIT__
  • __FB_LINUX__
  • Compiler Option: -target


__FB_SIGNATURE__
Intrinsic define (macro string) set by the compiler

Syntax

__FB_SIGNATURE__

Description

Substituted by a signature of the compiler where used.

Example

Print __FB_SIGNATURE__

FreeBASIC 0.21.1

Differences from QB

  • New to FreeBASIC

See also

  • __FB_VERSION__
  • __FB_WIN32__
  • __FB_LINUX__
  • __FB_DOS__


__FB_SSE__
Intrinsic define set by the compiler

Syntax

__FB_SSE__

Description

Define without a value created at compile time if SSE floating point arithmetics is enabled.

Example

#ifdef __FB_SSE__
  ' ... instructions only for SSE ...
#else
  ' ... instructions not for SSE ...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_FPU__
  • Compiler Option: -fpu


__FB_UNIQUEID__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_UNIQUEID__( stack-id )

Parameters

stack-id
the name of the stack to access

Description

Gets the identifier at the top of stack identified by stack-id (the size of the stack is not modified).
(__FB_UNIQUEID_PUSH__ allows to push a new unique identifier on to the stack, and __FB_UNIQUEID_POP__ allows to pop an identifier off of the stack)

Note:
- The 'stack-id' name itself is a separate namespace from all other symbols.
- The stack can only contain 'unique identifiers'.
- 'unique identifier' is a name of an fb symbol that is unique to the module, so does not conflict or shadow other symbol names ('unique identifier' will have the form 'LT_xxxx' as a name so it might not be completely unique).
- fb uses the form 'LT_xxxx' internally for labels, symbols, temp variables, etc (so should avoid naming fbc symbols of this form for any fbc program since version 0.0).

__FB_UNIQUEID__ simply expands to unquoted text. So the name, for example 'Lt_0004', can be used wherever an fb symbol is required (a variable, procedure name, type name, etc.).
__FB_UNIQUEID__ returns an unquoted empty string when the stack is empty or has never been filled.

Example

__FB_UNIQUEID_PUSH__( stk )
#print __FB_UNIQUEID__( stk )

    __FB_UNIQUEID_PUSH__( stk )
    #print __FB_UNIQUEID__( stk )

        __FB_UNIQUEID_PUSH__( stk )
        #print __FB_UNIQUEID__( stk )
        __FB_UNIQUEID_POP__( stk )

    #print __FB_UNIQUEID__( stk )
    __FB_UNIQUEID_POP__( stk )

#print __FB_UNIQUEID__( stk )
__FB_UNIQUEID_POP__( stk )

/' Compiler output example:
Lt_0006
Lt_0007
Lt_0008
Lt_0007
Lt_0006
'/

See also __FB_UNIQUEID_PUSH__ and __FB_UNIQUEID_POP__ examples.

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_UNIQUEID_PUSH__
  • __FB_UNIQUEID_POP__


__FB_UNIQUEID_POP__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_UNIQUEID_POP__( stack-id )

Parameters

stack-id
the name of the stack to pop

Description

Pops an identifier off of stack identified by stack-id (the size of the stack is decreased by 1).
(__FB_UNIQUEID__ allows to get the identifier at the top of the stack, and __FB_UNIQUEID_PUSH__ allows to push a new unique identifier on to the stack)

Note:
- The "stack-id" name itself is a separate namespace from all other symbols.
- The stack can only contain "unique identifiers".
- "unique identifier" is a name of an fb symbol that is unique to the module, so does not conflict or shadow other symbol names ("unique identifier" will have the form "LT_xxxx" as a name so it might not be completely unique).
- fb uses the form "LT_xxxx" internally for labels, symbols, temp variables, etc (so should avoid naming fbc symbols of this form for any fbc program since version 0.0).

When such a stack is no longer in use, it is recommended that it be empty (at the end there must have been applied as many __FB_UNIQUEID_POP__ as __FB_UNIQUEID_PUSH__ for this stack).
At any time of its use, the number of __FB_UNIQUEID_POP__ applied from the beginning must always be less than or equal to the number of __FB_UNIQUEID_PUSH__ applied.

Example

See also __FB_UNIQUEID__ example.

#macro repeat ? ( count )  '' with user named variable
    Scope
        Dim __counter__ As UInteger = count
        While( __counter__)
#endmacro

#macro end_repeat  '' with user named variable
            __counter__ -= 1
        Wend
    End Scope
#endmacro

Print "With user named variable:"
repeat 3
    Print "   outer"
    repeat 2
        Print "   --- inner"
    end_repeat
end_repeat
Print

#undef repeat
#undef end_repeat

#macro repeat ? ( count )  '' with "unique identifier" variable
    __FB_UNIQUEID_PUSH__( ctx )
    Dim __FB_UNIQUEID__( ctx ) As UInteger = count
    While( __FB_UNIQUEID__( ctx ) )
#endmacro

#macro end_repeat  '' with "unique identifier" variable
        __FB_UNIQUEID__( ctx ) -= 1
    Wend
    __FB_UNIQUEID_POP__( ctx )
#endmacro

Print "With ""unique identifier"" variable:"
repeat 3
    Print "   outer"
    repeat 2
        Print "   --- inner"
    end_repeat
end_repeat

Sleep

/' Output:
With user named variable:
   outer
   --- inner
   --- inner
   outer
   --- inner
   --- inner
   outer
   --- inner
   --- inner

With "unique identifier" variable:
   outer
   --- inner
   --- inner
   outer
   --- inner
   --- inner
   outer
   --- inner
   --- inner
'/

The first part of code works, because the '__counter__' variable is defined in a [Scope...End Scope] block and therefore allows nesting.
The second part of code works (without [Scope...End Scope] block) because of using a "unique identifier" (provided by the compiler).

See also __FB_UNIQUEID_PUSH__ example.

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_UNIQUEID_PUSH__
  • __FB_UNIQUEID__


__FB_UNIQUEID_PUSH__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_UNIQUEID_PUSH__( stack-id )

Parameters

stack-id
the name of the stack to push

Description

Pushes a new unique identifier on to a stack identified by stack-id (the size of the stack is increased by 1).
(__FB_UNIQUEID__ allows to get the identifier at the top of the stack, and __FB_UNIQUEID_POP__ allows to pop an identifier off of the stack)

Note:
- The "stack-id" name itself is a separate namespace from all other symbols.
- The stack can only contain "unique identifiers".
- "unique identifier" is a name of an fb symbol that is unique to the module, so does not conflict or shadow other symbol names ("unique identifier" will have the form "LT_xxxx" as a name so it might not be completely unique).
- fb uses the form "LT_xxxx" internally for labels, symbols, temp variables, etc (so should avoid naming fbc symbols of this form for any fbc program since version 0.0).

When such a stack is no longer in use, it is recommended that it be empty (at the end there must have been applied as many __FB_UNIQUEID_POP__ as __FB_UNIQUEID_PUSH__ for this stack).
At any time of its use, the number of __FB_UNIQUEID_POP__ applied from the beginning must always be less than or equal to the number of __FB_UNIQUEID_PUSH__ applied.

Example

See also __FB_UNIQUEID__ example.

' As the "unique identifiers" (used as jump labels) are successively pushed on to a stack,
' the jump-code bodies must be defined in the reversed order than the jump calls.

#macro go
    __FB_UNIQUEID_PUSH__( stk )
    Goto __FB_UNIQUEID__( stk )
    End If
#endmacro

#macro end_go
    __FB_UNIQUEID__( stk ):
    __FB_UNIQUEID_POP__( stk )
#endmacro

Dim As Integer N

Do
    Input "Enter a value between 1 and 4 (0 or empty input for exit) ? ", N

    If N = 0 Then go
    If N = 1 Then go
    If N = 2 Then go
    If N = 3 Then go
    If N = 4 Then go
    Continue Do

    end_go
        Print "You entered 4" : Continue Do
    end_go
        Print "You entered 3" : Continue Do
    end_go
        Print "You entered 2" : Continue Do
    end_go
        Print "You entered 1" : Continue Do
    end_go
        Print "End"           : Exit Do
Loop

Sleep

See also __FB_UNIQUEID_POP__ example.

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_UNIQUEID_POP__
  • __FB_UNIQUEID__


__FB_UNIX__
Intrinsic define set by the compiler

Syntax

__FB_UNIX__

Description

Define created at compile time if the OS is reasonably enough like UNIX that you can call it UNIX, otherwise undefined.

Example

#ifdef __FB_UNIX__
  '...instructions for UNIX-family OSes...
#else
  '...instructions for other OSes
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_LINUX__
  • __FB_FREEBSD__
  • __FB_OPENBSD__
  • __FB_NETBSD__
  • __FB_CYGWIN__
  • __FB_DARWIN__
  • __FB_PCOS__
  • Compiler Option: -target


__FB_UNQUOTE__
Intrinsic define (macro) performed by the compiler.

Syntax

__FB_UNQUOTE__( arg )

Parameters

arg
argument

Description

Takes a literal string and converts it back to tokens.
More precisely, __FB_UNQUOTE__ returns a sub-quoted text compared to the one passed through the argument (the argument may already be an over-quoted string, and so the return will be a simple string in this case).

Example

#macro m( arg )
    Scope
        Var v1 = arg
        #print TypeOf(v1)
        Print v1
        Var v2 = __FB_UNQUOTE__( arg )
        #print TypeOf(v2)
        Print v2
    End Scope
#endmacro

m("""Hello""")
m("1")

Sleep

/' Compiler output:
STRING
STRING
STRING
INTEGER
'/

/' Output:
"Hello"
Hello
1
 1
'/

#define X __FB_QUOTE__( Print "hello" )
#macro Y( arg )
  __FB_UNQUOTE__( arg )
#endmacro

Print X
Y( X )

/' Output:
print "hello"
hello
'/

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_QUOTE__
  • __FB_EVAL__


__FB_VECTORIZE__
Intrinsic define set by the compiler

Syntax

__FB_VECTORIZE__

Description

Defined as the vectorisation level number set by the -vec command-line option.

Example

#if __FB_VECTORIZE__ = 2
  ' ... instructions only for vectorization level 2...
#else
  ' ...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • Compiler Option: -vec


__FB_VER_MAJOR__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_VER_MAJOR__

Description

__FB_VER_MAJOR__ will return the major version of FreeBASIC currently being used. For example, the major version is 0 for FreeBASIC 0.90, and will remain 0 until FreeBASIC version 1.0 is released.

Example

Dim fbMajorVersion As Integer
Dim fbMinorVersion As Integer
Dim fbPatchVersion As Integer

fbMajorVersion = __FB_VER_MAJOR__
fbMinorVersion = __FB_VER_MINOR__
fbPatchVersion = __FB_VER_PATCH__

Print "Welcome to FreeBASIC " & fbMajorVersion & "." & fbMinorVersion & "." & fbPatchVersion

Differences from QB

  • New to FreeBASIC

See also

  • __FB_VER_MINOR__
  • __FB_VER_PATCH__


__FB_VER_MINOR__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_VER_MINOR__

Description

__FB_VER_MINOR__ will return the minor version of FreeBASIC currently being used. For FreeBASIC version 0.90.1, for example, the minor version number is 90.

Example

Dim fbMajorVersion As Integer
Dim fbMinorVersion As Integer
Dim fbPatchVersion As Integer

fbMajorVersion = __FB_VER_MAJOR__
fbMinorVersion = __FB_VER_MINOR__
fbPatchVersion = __FB_VER_PATCH__

Print "Welcome to FreeBASIC " & fbMajorVersion & "." & fbMinorVersion & "." & fbPatchVersion

Differences from QB

  • New to FreeBASIC

See also

  • __FB_VER_MAJOR__
  • __FB_VER_PATCH__


__FB_VER_PATCH__
Intrinsic define (macro value) set by the compiler

Syntax

__FB_VER_PATCH__

Description

__FB_VER_PATCH__ will return the patch/subversion/revision number the version of FreeBASIC currently being used. For FreeBASIC 0.18, for example, there were subversions 1, 2, 3, 4, 5 and 6, resulting in versions 0.18.1 through 0.18.6.

Example

Dim fbMajorVersion As Integer
Dim fbMinorVersion As Integer
Dim fbPatchVersion As Integer

fbMajorVersion = __FB_VER_MAJOR__
fbMinorVersion = __FB_VER_MINOR__
fbPatchVersion = __FB_VER_PATCH__

Print "Welcome to FreeBASIC " & fbMajorVersion & "." & fbMinorVersion & ", revision " & fbPatchVersion

Differences from QB

  • New to FreeBASIC

See also

  • __FB_VER_MAJOR__
  • __FB_VER_MINOR__


__FB_VERSION__
Intrinsic define (macro string) set by the compiler

Syntax

__FB_VERSION__

Description

Substituted by the version number of the compiler where used.

Example

#if __FB_VERSION__ < "0.18"
#error  Please compile With FB version 0.18 Or above
#endif

This will stop the compilation if the compiler version is below 0.18

Differences from QB

  • Did not exist in QB

See also

  • __FB_SIGNATURE__
  • __FB_WIN32__
  • __FB_LINUX__
  • __FB_DOS__


__FB_WIN32__
Intrinsic define set by the compiler

Syntax

__FB_WIN32__

Description

Define without a value created at compile time if compiling to the Win (32-bit or 64-bit) target. Default in Win hosted version, or active if the -target win32 or -target win64 command line option is used. It can be used to compile parts of the program only if the target is Win.

Example

#ifdef __FB_WIN32__
  ' ... instructions only for Win ...
  ' ... GetProcAddress ...
#else
  ' ... instructions not for Win ...
#endif

Differences from QB

  • New to FreeBASIC

See also

  • __FB_DOS__
  • __FB_LINUX__
  • __FB_PCOS__
  • Compiler Option: -target


__FB_X86__
Intrinsic define set by the compiler

Syntax

__FB_X86__

Description

Define created at compile time if the compilation target uses the X86 or X86_64 CPU architecture, otherwise undefined.

Example

#ifdef __FB_X86__
  '...instructions for X86 OSes...
#else
  '...instructions for other OSes
#endif

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • __FB_ARM__
  • Compiler Option: -target


__FB_XBOX__

Syntax

__FB_XBOX__

Description

Defined without a value at compile time when the original Xbox target is being used. It can be used to compile parts of a program only for the Xbox target.

Example

#IFDEF __FB_XBOX__
    print "Xbox target"
#ENDIF

Predefined Defines

See also
Compiling FB for Xbox
Xbox port FAQ
Compiler Option: -target

Compiler Meta-Commands


__FB_WII__

Syntax

__FB_WII__

Description

Defined without a value at compile time when the Wii target is being used. This is normally selected by fbc-wii or by the -target wii command line option. It can be used to compile parts of a program only for the Wii target.

Example

#IFDEF __FB_WII__
    print "Wii target"
#ENDIF

Predefined Defines

See also
Compiling FB for Wii
Compiler Option: -target

Compiler Meta-Commands


__FILE__
Intrinsic define (macro string) set by the compiler

Syntax

__FILE__

Description

Substituted with the quoted source file name where used.

An example of normal use is to report wrong values in debugging.

Example

Dim a As Integer
If a<0 Then
    Print "Error: a = " & a & " in " & __FILE__ & " (" & __FUNCTION__ & ") line " & __LINE__
End If

Error: a = -32767 in test.bas (MAIN) line 47

Differences from QB

  • Did not exist in QB

See also

  • __FILE_NQ__
  • __FUNCTION__
  • __LINE__


__FILE_NQ__
Intrinsic define (macro string) set by the compiler

Syntax

__FILE_NQ__

Description

Substituted with the non-quoted source file name where used.

Example

#print __FILE_NQ__

Differences from QB

  • New to FreeBASIC

See also

  • __FILE__
  • __FUNCTION_NQ__
  • __LINE__


__FUNCTION__
Intrinsic define (macro string) set by the compiler

Syntax

__FUNCTION__

Description

Substituted with the quoted name of the current function block where used.

Its normal use is to report wrong values in debugging.

If __FUNCTION__ is used at the module level, the function name given will be "__FB_MAINPROC__" for the main module, or "__FB_MODLEVELPROC__" for a different module.

Example

Dim a As Integer

'...

If a < 0 Then '' this shouldn't happen
    Print "Error: a = " & a & " in " & __FILE__ & " (" & __FUNCTION__ & ") line " & __LINE__
End If

Error: a = -32767 in test.bas (__FB_MAINPROC__) line 47

Differences from QB

  • Did not exist in QB

See also

  • __FILE__
  • __FUNCTION_NQ__
  • __LINE__


__FUNCTION_NQ__
Intrinsic define (macro string) set by the compiler

Syntax

__FUNCTION_NQ__

Description

Substituted with the non-quoted name of the current function block where used.

If __FUNCTION_NQ__ is used at the module level, the function name given will be __FB_MAINPROC__ for the main module, or __FB_MODLEVELPROC__ for a different module. This is not the actual function name though, so it's not as useful there.

Example

Sub MySub
  Print "Address of " + __FUNCTION__ + " is ";
  Print Hex( @__FUNCTION_NQ__ )
End Sub

MySub

Address of MYSUB is 4012D0

Differences from QB

  • Did not exist in QB

See also

  • __FILE_NQ__
  • __FUNCTION__
  • __LINE__


__LINE__
Intrinsic define (macro value) set by the compiler

Syntax

__LINE__

Description

Substituted with the current line number of the source file where used.

Its normal use is to report wrong values in debugging.

Example

Dim a As Integer

If a < 0 Then
    Print "Error: a = " & a & " in " & __FILE__ & " (" & __FUNCTION__ & ") line " & __LINE__
End If

Error: a = -32767 in test.bas (MAIN) line 47

Differences from QB

  • Did not exist in QB

See also

  • __FILE__
  • __FUNCTION__


__PATH__
Intrinsic define (macro string) set by the compiler

Syntax

__PATH__

Description

Set to the quoted absolute path of the source file at the time of compilation.

Example

' Tell the compiler to seach the source file's
' directory for libraries

#libpath __PATH__

Differences from QB

  • New to FreeBASIC

See also

  • __FILE__


__TIME__
Intrinsic define (macro value) set by the compiler

Syntax

__TIME__

Description

Substitutes the compiler time in a literal string (24 clock, "hh:mm:ss" format) where used.

Example

Print "Compile Time: " & __TIME__

Compile Time: 13:42:57
Differences from QB

  • New to FreeBASIC

See also

  • __DATE__
  • __DATE_ISO__
  • Time


#assert
Preprocessor conditional directive

Syntax

#assert condition

Parameters

condition
A conditional expression that is assumed to be true

Description

Asserts the truth of a conditional expression at compile time. If condition is false, compilation will stop with an error.

This statement differs from the Assert macro in that #assert is evaluated at compile-time and Assert is evaluated at run-time.

Example

Const MIN = 5, MAX = 10
#assert MAX > MIN '' cause a compile-time error if MAX <= MIN

Differences from QB

  • New to FreeBASIC

See also

  • Assert
  • #if
  • #error


#Cmdline
Preprocessor directive

Syntax

#cmdline "args..."

Parameters

args...
argument list (separated by spaces) of valid Compiler Options except -print and also generic option -help.

Description

#cmdline is a pre-processor directive that allows specifying compiler options from inside the first specified fb source file. The first source file is the first specified '.bas' file given on the shell or IDE command line invoking the fbc compiler. #cmdline directives specified in the first source file affect all source files subsequently compiled. #cmdline directives inside source files other than the first source file are ignored.

#cmdline is only allowed at module scope and can be conditionally processed with #if pre-processor statements. #cmdline statements must appear before any declarative or executable statements. Any #cmdline statements after the first declarative or executable statement are ignored.

#cmdline directives are processed on the first pass of the the first '.bas' source file when they appear in the source file. If an invalid command line option is given in the #cmdline statment, compilation immediately aborts. #cmdline directives are ignored on the second pass of the source file even if a conditional #if would add something different on the second pass.

At first fbc initializes as usual and begins parsing the fb source code using the options given from the shell or IDE's fbc ... command line. As #cmdline directives are processed, they are merged into the current compiler configuration. Depending on the command line options encountered, fbc can continue parsing, restart the parser, or restart fbc (for example: no restart after #cmdline "-mt", restart the parser after #cmdline "-gen gcc", restart the build after #cmdline "-target win64").

fbc has no clever way to detect when all the #cmdline directives in source have been processed, so there are 2 pseudo command line options to instruct fbc what to next if it is necessary:
#cmdline "-end"
option to restart parser or build if needed.
#cmdline "-restart"
option to always restart build.
If neither #cmdline "-end" nor #cmdline "-restart" were encountered, fbc continues processing to the end of the first source file and restarts only if necessary.

Adding -z nocmdline in the shell/IDE command line option ignores #cmdline directives completely in source and allows user to override all source directives using the shell/IDE fbc compiler command line only.

Adding -w all on the shell/IDE command line option allows to get warnings about ignored #cmdline directives.

Example

Add a simple option in source to set the optimization level for GCC:
#cmdline "-O 2"

Print __FB_OPTIMIZE__  '' just to check the optimization level

Sleep

When #cmdline is not processed (no 'invalid command-line option' error message is reported):
(fbc parser is active when checking for #cmdline, so it's expected that it follows sames rules as any other source code)
'' not processed in single line comments
'#cmdline "asdf"

'' not processed in multi line comments
/'
#cmdline "-asdf"
'/

'' not processed in strings
Print "#cmdline ""-asdf"""

'' not processed if skipping over a conditional
#if 0
   #cmdline "-asdf"
#endif

'' not processed when defining macros (as long as the macro is not called)
#macro DOARGS
   #cmdline "-asdf"
#endmacro

Sleep

Set options in source based on real fbc ... compiler command line option given:
(full error checking is activated depending if debug option '-g' was given on the fbc ... compiler command line)
'' '-g' command line option given on the real ##//fbc ...//## compiler command line?

#if __FB_DEBUG__
   #cmdline "-exx -w pedantic -w constness"
#endif

Define a global symbol for all modules, starting with two modules: main.bas and tools.bas, and compile both with the single '$ fbc main.bas' compiler command line:
(this is different than #include tools.bas in main.bas because the two modules are compiled separately then linked)
'' main.bas
''
'' compile with:
''   $ fbc main.bas
''
'' and will be same as if we did:
''   $ fbc main.bas tools.bas -d DoTrickyStuff

'' add the tools module
#cmdline "-b tools.bas"  '' or: #cmdline "tools.bas"

'' gobal #define for all modules
#cmdline "-d DoTrickyStuff"

Declare Function IsTrickyTools() As Boolean

#ifdef DoTrickyStuff
Print "DoTrickyStuff is defined in the main.bas module"
#endif

If IsTrickyTools() Then
Print "DoTrickyStuff is defined in the tools.bas module"
End If

Sleep

'' tools.bas
''

Function IsTrickyTools() As Boolean
   #ifdef DoTrickyStuff
    Return True
   #else
      Return False
   #endif
End Function

Version

  • Since fbc 1.09.0

Differences from QB

  • New to FreeBASIC

See also

  • fbc command-line
  • Compiler Options
  • Intrinsic Defines


#define
Preprocessor directive to define a macro

Syntax

#define identifier body
#define identifier( [ parameters ] ) body
#define identifier( [ parameters, ] Variadic_Parameter... ) body

Description

#define allows to declare text-based preprocessor macros. Once the compiler has seen a #define, it will start replacing further occurrences of identifier with body. body may be empty. The expansion is done recursively, until there is nothing more to expand and the compiler can continue analyzing the resulting code. #undef can be used to make the compiler forget about a #define.

Parameters turn a define into a function-like macro, allowing text arguments to be passed to the macro. Any occurrences of the parameter names in the body will be replaced by the given argument text during expansion.
If a literal is passed to a macro, the name of the corresponding parameter in the macro body can not be used as a local variable as in a procedure body. To emulate the same functioning as for a procedure, the user must then explicitly declare a local variable (with another name) in the body of the macro and initialize it with the passed parameter name (replaced at preprocessing by the literal passed).

The # Stringize operator can be used on macro parameters to turn them into string literals, and the ## Concatenate operator can be used to merge tokens together.

Note: In the function-like #define declaration, the identifier should be followed by the opening parentheses (() immediately without any white-space in between, otherwise the compiler will treat it as part of the body.

Defines are scoped; they are only visible in the scope they were defined in. If defined at module level, the define is visible throughout the module. If the identifier is defined inside a compound statement having scope (Sub, For..Next, While..Wend, Do..Loop, Scope..End Scope, etc), the identifier define is only visible within that scope. Namespaces on the other hand do not have any effect on the visibility of a define.

Identifiers can be checked for with #ifdef and others, which can be used to hide parts of code from the compiler (conditional compiling).

Some user-defined symbols are recognized by the compiler itself. For example, Fb_No_Gfxlib and Fb_No_Sfxlib change how selected library command words are parsed.

The result of macro expansion can be checked by using the -pp compiler option.

#defines are often used to declare constants. The Const statement is a type-safe alternative.

WARNING: When the define body contains an expression with one operator at least, it may be mandatory to have to surround some terms (parameters, whole body) by parentheses in order to not undergo an unwanted precedence change of operators (if passing as argument an expression with also operators, or if using the define in a main expression with also operators).

Example

'' Definition and check
#define DEBUGGING
#ifdef DEBUGGING
  ' ... statements
#endif

'' Simple definition/text replacement
#define False 0
#define True (Not False)

'' Function-like definition
#define MyRGB(R,G,B) (((R)Shl 16)  Or ((G)Shl 8) Or (B))
Print Hex( MyRGB(&hff, &h00, &hff) )

'' Line continuation and statements in a definition
#define printval(bar) _
    Print #bar; " ="; bar

'' #defines are visible only in the scope where they are defined
Scope
    #define LOCALDEF 1
End Scope

#ifndef LOCALDEF
#    Print LOCALDEF Is Not defined
#endif

'' namespaces have no effect on the visibility of a define
Namespace foo
#    define NSDEF
End Namespace

#ifdef NSDEF
#    Print NSDEF Is defined
#endif

Differences from QB

  • New to FreeBASIC

See also

  • #macro
  • # Preprocessor Stringize
  • ## Preprocessor Concatenate
  • #ifdef
  • #undef
  • Const
  • ...
  • Fb_No_Gfxlib
  • Fb_No_Sfxlib


#else
Preprocessor conditional directive

Syntax

#if (expression)
' Conditionally included statements if expression is True
#else
' Conditionally included statements if expression is False
#endif

Description

#else can be added to an #if, #ifdef, or #ifndef block to provide an alternate result to the conditional expression.

Example

#define MODULE_VERSION 1
Dim a As String
#if (MODULE_VERSION > 0)
  a = "Release"
#else
  a = "Beta"
#endif
Print "Program is "; a

Differences from QB

  • New to FreeBASIC

See also

  • #define
  • #macro
  • #if
  • #elseif
  • #elseifdef
  • #elseifndef
  • #endif
  • #ifdef
  • #ifndef
  • #undef
  • defined


#elseif
Preprocessor conditional directive

Syntax

#if (expression1)
' Conditionally included statements if expression1 is True
#elseif (expression2)
' Conditionally included statements if expression2 is True
#else
' Conditionally included statements if both
' expression1 and expression2 are False
#endif

Description

#elseif can be added to an #if block to provide an additional conditions.

Example

#define WORDSIZE 16
#if (WORDSIZE = 16)
  ' Do some some 16 bit stuff
#elseif (WORDSIZE = 32)
  ' Do some some 32 bit stuff
#else
  #error WORDSIZE must be set To 16 Or 32
#endif

Differences from QB

  • New to Freebasic

See also

  • #define
  • #macro
  • #if
  • #else
  • #elseifdef
  • #elseifndef
  • #endif
  • #ifdef
  • #ifndef
  • #undef
  • defined


#elseifdef
Preprocessor conditional directive

Syntax

#ifdef A
' Conditionally included statements if A is defined
#elseifdef B
' Conditionally included statements if A is not defined and B is defined
#else
' Conditionally included statements if both A and B are not defined
#endif

Description

#elseifdef can be added to an #if block to provide an additional condition.

#elseifdef symbol is equivalent to #elseif defined (symbol)

Example

#define B

#ifdef A
    Print "A is defined"
#elseifdef B
    Print "A is not defined and B is defined"
#else
    Print "both A and B are not defined"
#endif

Version

  • Since fbc 1.20.0

Differences from QB

  • New to Freebasic

See also

  • #define
  • #macro
  • #if
  • #else
  • #elseif
  • #elseifndef
  • #endif
  • #ifdef
  • #ifndef
  • #undef
  • defined


#elseifndef
Preprocessor conditional directive

Syntax

#ifdef A
' Conditionally included statements if A is defined
#elseifndef B
' Conditionally included statements if both A and B are not defined
#else
' Conditionally included statements if A is not defined and B is defined
#endif

Description

#elseifndef can be added to an #if block to provide an additional condition.

#elseifndef symbol is equivalent to #elseif Not defined(symbol)

Example

#ifdef A
    Print "A is defined"
#elseifndef B
    Print "both A and B are not defined"
#else
    Print "A is not defined and B is defined"
#endif

Version

  • Since fbc 1.20.0

Differences from QB

  • New to Freebasic

See also

  • #define
  • #macro
  • #if
  • #else
  • #elseif
  • #elseifdef
  • #endif
  • #ifdef
  • #ifndef
  • #undef
  • defined


#endif
Preprocessor conditional directive

Syntax

#endif

Description

Ends a group of conditional directives

See #if, #ifdef, or #ifndef for examples of usage.

Example

#define DEBUG_LEVEL 1
#if (DEBUG_LEVEL = 1)
  'Conditional statements
#endif

Differences from QB

  • New to FreeBASIC

See also

  • #define
  • #macro
  • #if
  • #else
  • #elseif
  • #elseifdef
  • #elseifndef
  • #ifdef
  • #ifndef
  • #undef
  • defined


#Macro...#Endmacro
Preprocessor directive to define a multiline macro

Syntax

#macro identifier [?] ( [ parameters ] )
body
#endmacro

#macro identifier [?] ( [ parameters, ] Variadic_Parameter... )
body
#endmacro

Description

#macro is the multi-line version of #define.

If using the optional question mark (?) after the identifier in the definition syntax, macros with parameters can be invoked without using parentheses around the arguments.
Note: Beware of the possibility of triggering so a conflict with expressions containing the name of the macro as one of their terms.

Note: Unlike the function-like #define declaration, spaces can be put between the macro name and the opening parenthesis for any declaration syntax of macro.

WARNING: In the macro body, it may be mandatory to have to surround by parentheses any used parameter if it is inside an expression with one operator at least, in order to not undergo an unwanted precedence change of operators (if passing as argument an expression with also operators).

Example

' macro as an expression value

#macro Print1( a, b )
    a + b
#endmacro

Print Print1( "Hello ", "World!" )

/' Output :
Hello World!
'/

' macro as multiple statements

#macro Print2( a, b )
    Print a;
    Print " ";
    Print b;
    Print "!"
#endmacro

Print2( "Hello", "World" )

/' Output :
Hello World!
'/

' macro with a variadic parameter

#macro test1( arg1, arg2... )
    Print arg1
    #if #arg2 = ""
        Print "2nd argument not passed"
    #else
        Print arg2
    #endif
#endmacro

test1( "1", "2" )
Print "-----------------------"
test1( "3" )
Print "-----------------------"
test1( 5, 6 )
Print "-----------------------"
test1( 7 )

/' Output :
1
2
-----------------------
3
2nd argument not passed
-----------------------
 5
 6
-----------------------
 7
2nd argument not passed
'/

' macro with a variadic parameter which can contain several sub-parameters:
'   To distinguish between the different arguments passed by variadic_parameter,
'   you can first convert variadic_parameter to a string using the Operator # (Preprocessor Stringize),
'   then differentiate in this string (#variadic_parameter) each passed argument by locating the separators (usually a comma).

#macro test2( arg1, arg2... )
    Print "'" & Trim(#arg1) & "'"
    Scope
        Dim As String s = Trim(#arg2)
        If s <> "" Then
            Do
                Dim As Integer k = InStr(1, s, ",")
                If k = 0 Then
                    Print "'" & s & "'"
                    Exit Do
                End If
                Print "'" & Left(s, k - 1) & "'"
                s = Trim(Mid(s, k+1))
            Loop
        End If
    End Scope
#endmacro

test2( 5 )
Print "----"
test2( 5,6, 7, , 9, 10, ,,13, 14 )

/' Output :
'5'
----
'5'
'6'
'7'
''
'9'
'10'
''
''
'13'
'14'
'/

Version

  • Before fbc 1.08.0, the optional question mark (?) was not available.

Differences from QB

  • New to FreeBASIC

See also

  • #define
  • #ifdef
  • #undef


#error
Preprocessor diagnostic directive

Syntax

#error error_text

Parameters

error_text
The display message

Description

#error interrupts compiling to display error_text when compiler finds it, and then parsing continues.

This keyword must be surrounded by an #if <condition> ...#endif, so the compiler can reach #error only if <condition> is met.

In any case, the final status will be "Failed to compile".

Example

#define c 1

#if c = 1
  #error Bad value of c
#endif

Differences from QB

  • New to FreeBASIC

See also

  • #if
  • #print
  • #assert


#if
Preprocessor conditional directive

Syntax

#if (expression)
' Conditionally included statements
#endif

Description

Conditionally includes statements at compile time.

Statements contained within the #if / #endif block are included if expression evaluates to True (non-zero) and excluded (ignored) if expression evaluates to False (0).

This conditional directive differs from the If conditional statement in that #if is evaluated at compile-time and If is evaluated at run-time.

Example

#define DEBUG_LEVEL 1
#if (DEBUG_LEVEL >= 2)
  ' This line is not compiled since the expression is False
  Print "Starting application"
#endif

Differences from QB

  • New to FreeBASIC

See also

  • #define
  • #macro
  • #else
  • #elseif
  • #elseifdef
  • #elseifndef
  • #endif
  • #ifdef
  • #ifndef
  • #undef
  • defined
  • #assert


#ifdef
Preprocessor conditional directive

Syntax

#ifdef symbol
' Conditionally included statements
#endif

Description

Conditionally includes statements at compile time.

Statements within the #ifdef...#endif block are included if symbol is defined and excluded (ignored) if symbol is not defined.

#ifdef symbol is equivalent to #if defined (symbol)

Example

#define _DEBUG
#ifdef _DEBUG
    ' Special statements for debugging
#endif

Differences from QB

  • New to Freebasic

See also

  • #define
  • #macro
  • #if
  • #else
  • #elseif
  • #elseifdef
  • #elseifndef
  • #endif
  • #ifndef
  • #undef
  • defined


#ifndef
Preprocessor conditional directive

Syntax

#ifndef symbol
' Conditionally included statements
#endif

Description

Conditionally includes statements at compile time.

Statements within the #ifndef...#endif block are included if symbol is not defined and excluded (ignored) if symbol is defined.

#ifndef symbol is equivalent to #if Not defined(symbol)

Example

#ifndef __MYFILE_BI__
#define __MYFILE_BI__
    ' Declarations
#endif

Differences from QB

  • New to FreeBASIC

See also

  • #define
  • #macro
  • #if
  • #else
  • #elseif
  • #elseifdef
  • #elseifndef
  • #endif
  • #ifdef
  • #undef
  • defined


#inclib
Preprocessor directive

Syntax

#inclib "libname"

Description

Includes a library in the linking process as if the user specified -l libname on the command line.

Example

'' incomplete code snippet

'' this will include libmystuff.a in the link process
#inclib "mystuff"

Differences from QB

  • New to FreeBASIC

See also

  • #include
  • Compiler Option: -l
  • Compiler Option: -p
  • Static Libraries
  • Shared Libraries


#include
Preprocessor statement to include contents of another source file

Syntax

#include [once] "file"

Description

#include inserts source code from another file at the point where the #include directive appears. This has the effect of compiling the source code from the include file as though it were part of the source file that includes it. Once the compiler has reached the end of the include file, the original source file continues to be compiled.

This is useful to remove code from a file and separate it into more files. It is useful to have a single file with declarations in a program formed by several modules. You may include files within an include file, although avoid including the original file into itself, this will not produce valid results. Typically, include files will have an extension of .bi and are mainly used for declaring subs/functions/variables of a library, but any valid source code may be present in an include file.

The Once specifier tells the compiler to include the file only once even if it is included several times by the source code.

$Include is an alternative form of include, existing only for compatibility with QuickBASIC. It is recommended to use #include instead.

The compiler will automatically convert path separator characters ( '/' and '\' ) as needed to properly load the file. The filename name may be an absolute or relative path.

For relative paths, or where no path is given at all, the include file is search for in the following order:
  • Relative from the directory of the source file
  • Relative from the current working directory
  • Relative from addition directories specified with the -i command line option
  • The include folder of the FreeBASIC installation (FreeBASIC\inc, where FreeBASIC is the folder where the fbc executable is located)

Example

' header.bi file
Type FooType
    Bar As Byte
    Barbeque As Byte
End Type

' main.bas file
#include "header.bi"

Dim Foo As FooType

Foo.Bar = 1
Foo.Barbeque = 2

Differences from QB

  • New to FreeBASIC

See also

  • #define
  • #inclib
  • Compiler Option: -i
  • Compiler Option: -include


#lang
Preprocessor statement to set the compiler dialect.

Syntax

#lang "lang"

Parameters

"lang"
The dialect to set, enclosed in double quotes, and must be one of "fb", "fblite", "qb", or "deprecated".

Description

If the -forcelang option was not given on the command line, #lang can be used to set the dialect for the source module in which it appears. At most two passes will be made on the source module. On the first pass, if the specified dialect is anything other than the default dialect (chosen with -lang, or "fb" by default), the compiler will reset the parser for another pass and restart compilation at the beginning of the source module. If this directive is encountered again on the second pass, and the specified dialect does not match the new current dialect, a warning is issued and compilation continues. If any errors were encountered on the first pass, the compiler will not attempt a second pass."

#lang may not be used in any compound statement, scope, or subroutine. However, it may be nested in module level preprocessor statements or used in an include file.

There is currently no restriction on where this directive may be placed in a source module. In future this may change, therefore best practice would be to use this directive before the first declaration, definition, or executable statement in the source.

This directive overrides the -lang option if it was given on the command line. However, if the -forcelang option was given on the command line, this directive will have no effect. A warning is issued, the directive is ignored, and compilation will continue. This allows the user to explicitly override #lang directives.

Example

#lang "fblite"

Differences from QB

  • New to FreeBASIC

See also

  • $Lang
  • __FB_LANG__
  • Compiler Option: -lang
  • Compiler Option: -forcelang
  • FreeBASIC Dialects
  • #Cmdline


#libpath
Preprocessor statement to add a search path for libraries

Syntax

#libpath "path"

Description

Adds a library search path to the linker's list of search paths as if it had been specified on the command line with the '-p' option.

Paths are relative to the working directory where fbc was invoked and not relative to the directory of the source file.

No error is generated if the path does not exist and compilation and linking will continue.

Example

' search the lib directory for external libraries
#libpath "lib"

Differences from QB

  • New to FreeBASIC

See also

  • #inclib
  • #include
  • Compiler Option: -p
  • #Cmdline
  • Static Libraries
  • Shared Libraries


#line
Preprocessor directive to set the current line number and file name

Syntax

#line number [ "name" ]

Parameters

number
new line number
"name"
new file name (optional)

Description

Informs the compiler of a change in line number and file name and updates the __FILE__ and __LINE__ macro values accordingly.

Both compile time messages and run-time messages are affected by this directive.

This directive allows other programs to generate source code for the FreeBASIC compiler and have it return warning and/or error messages that refer to the original source used by the other program.

Example

#line 155 "outside.src"

Error 1000

'' Output is:
'' Aborting due to runtime error 1000 at line 157 of outside.src()

Differences from QB

  • New to FreeBASIC

See also

  • __FILE__
  • __LINE__


#pragma
Preprocessor directive

Syntax

#pragma option [ = value ]
Or
#pragma push ( option [, value ] )
Or
#pragma pop ( option )

Parameters

Possible values for option and related values:

Option | Value | Description
msbitfields | false | Use bitfields compatible with gcc (default)
msbitfields | true | Use bitfields compatible with those used in Microsoft C compilers
once | N/A | cause the source file in which the pragma appears to behave as though it was included with #include once ...
constness | false | disable 'CONST qualifier discarded' warning in current source
constness | true | enable 'CONST qualifier discarded' warning in current source
lookup108 | false | use normal symbol lookups for unqualified symbol names
lookup108 | true | use fbc-1.08.x or earlier symbol lookups for unqualified symbol names
gui | false | compile in console subsystem mode
gui | true | compile in GUI subsystem mode
private | false | allow procedures in sources to be compiled as separate modules with public linkage
private | true | allow procedures in sources to be compiled as privately included with minimal overhead for changing desired linkage
profile | false | profiling code is not generated
profile | true | profiling code is generated

If value is not given, the compiler assumes True (-1). A zero (0) value may be used in place of False. A non-zero (for example, -1) value may be used in place of True.

Description

Allows the setting of compiler options inside the source code.

Push saves the current value of the option onto a stack, then assigns the new value (or True) to it. Pop restores the option to its previous value, and removes it from the stack. This mechanism allows options to be changed for a certain part of source code, regardless of the setting used by the context, which is especially useful inside #include header files.

gui sets the same GUI/console subsystem mode represented by the -s compiler option and __FB_GUI__. This pragma must be used at module level.

constness pragma is added for testing fbc compiler. It will be removed in future at should not be relied upon.

Example

'' MSVC-compatible bitfields: save the current setting and then enable them
#pragma push(msbitfields)

'' do something that requires MS-compatible bitfields here

'' restore original setting
#pragma pop(msbitfields)

Version

  • #pragma gui added in version 1.20.1
  • #pragma private and #pragma profile added in version 1.20.0
  • #pragma lookup108 added in version 1.09.0, and to be removed in next minor release

Differences from QB

  • New to FreeBASIC

See also

  • #Pragma Reserve
  • __FB_GUI__
  • Compiler Option: -s
  • #include
  • #Cmdline
  • Preprocessor Overview


#Pragma Reserve
Preprocessor directive

Syntax

#pragma reserve symbol
or
#pragma reserve (extern) symbol
or
#pragma reserve (asm) symbol
or
#pragma reserve (asm, extern) symbol) or #pragma reserve (extern, asm) symbol)

Parameters

symbol
symbol name to reserve.

Description

#pragma reserved statements are preprocessor directives that allow user to reserve symbol names by accessing to the internal symbol tables of the fbc compiler:
- #pragma reserve symbol
statement will reserve a symbol name in the current scope / namespace and generate an error if the symbol is redefined or used in an expression.
- #pragma reserve (extern) symbol
statement will reserve a global symbol name and generate a warning if the reserved symbol is used for a module level procedure or shared variable in the global namespace.
- #pragma reserve (asm) symbol
statement will reserve an ASM symbol name in all ASM statements and blocks.
- #pragma reserve (asm, extern) symbol) or #pragma reserve (extern, asm) symbol)
Do both previous statements: this combined statement will reserve a global ASM symbol name and generate a warning if the reserved symbol is used for a module level procedure or shared variable in the global namespace.

The primary objective with the two first syntaxes is to create a mechanism to help deal with some symbols causing compile errors or run-time crashes.
Some symbol names in fbc compiler are emitted as-is to the backend compilers (gcc, as, etc) where the symbol name is a reserved keyword by the backend compiler.
There are typically two outcomes:
- compilation error in the backend,
- bad code generation in the backend (successful compile and unexpected run time crashes).
Using #pragma reserve symbol statement allows the fbc compiler to output an error message dedicated to the illegal use of this symbol name in the current scope / namespace.
Using #pragma reserve (extern) symbol statement allows the fbc compiler to output a warning message dedicated to the illegal use of this symbol name for a module level procedure or shared variable in the global namespace.

A closely related secondary objective with the third and forth syntax is ASM words used in ASM blocks and statements.
This allows to add new ASM instruction name in the fbc ASM keywords list (so not yet implicitly reserved by fbc).
Therefore, using #pragma reserve (asm) symbol or #pragma reserve (asm, extern) symbol or any #pragma reserve (extern, asm) symbol statement allows the fbc compiler to always emit an undecorated ASM instruction symbol name to the backend compiler.

Note: #pragma reserve (extern) and #pragma reserve (asm) (and any combined syntax) throw an error if used in any scope block or procedure.

Example

Example (for the mechanism only) to prohibit/warn the definition/use of a symbol name:
#pragma reserve myName1
#pragma reserve myName2
#pragma reserve myName3
#pragma reserve myName4
#pragma reserve myName5
#pragma reserve (Extern) myName11
#pragma reserve (Extern) myName12
#pragma reserve (Extern) myName13
#pragma reserve (Extern) myName14
#pragma reserve (Extern) myName15

Dim As Integer myName1             '' error: Duplicated definition, myName1 in 'Dim As Integer myName1 ...
Print myName1                      '' error: Illegal use of reserved symbol, found 'myName1' in 'Print myName1 ...

Scope
    Dim As Integer myName2         '' OK
    Print myName2                  '' OK
End Scope

Dim As Integer myName11            '' OK
Print myName11                     '' OK
Dim Shared As Integer myName12     '' warning: Use of reserved global or backend symbol, myName12
Print myName12                     '' OK

Namespace N
    Dim As Integer myName3         '' OK
    Dim As Integer myName13        '' OK
    Sub myName4()                  '' OK
    End Sub
    Sub myName14()                 '' OK
    End Sub
End Namespace
Print N.myName3                    '' OK
Print N.myName13                   '' OK
N.myName4()                        '' OK
N.myName14()                       '' OK

Sub myName5()                      '' error: Duplicated definition, before ''' in 'Sub myName4() ...
End Sub
myName5()                          '' error: Illegal use of reserved symbol, found 'myName4' in 'myName4() ...

Sub myName15()                     '' warning: Use of reserved global or backend symbol, myName14
End Sub
myName15()                         '' OK

Suppose that 'xyz' is a new ASM instruction not yet entered in the fbc ASM keywords list (so not yet implicitly reserved by fbc), and this 'xyz' symbol is also used to define a global variable name:
- thus fbc emits to the backend compiler a decorated 'xyz' symbol (XYZ$) in the inserted ASM block:
'' for x86_64

#cmdline "-gen gas64 -r"

Dim Shared xyz As Integer

Sub proc Naked()
    Asm
        xyz
        ret
    End Asm
End Sub

/'
OUTPUT in the .asm file:

   .intel_syntax noprefix
   .section .text
   .text
   .globl PROC
PROC:
   .L_0004:
   XYZ$
   ret
   .L_0005:
   ret
.....

'/

- after reserving the 'xyz' ASM symbol, fbc now emits to the backend compiler an undecorated 'xyz' symbol (xyz) in the inserted ASM block:
'' for x86_64

#cmdline "-gen gas64 -r"
#pragma reserve(Asm) xyz

Dim Shared xyz As Integer

Sub proc Naked()
    Asm
        xyz
        ret
    End Asm
End Sub

/'
OUTPUT in the .asm file:

   .intel_syntax noprefix
   .section .text
   .text
   .globl PROC
PROC:
   .L_0004:
   xyz
   ret
   .L_0005:
   ret
.....

'/

Version

  • Since fbc 1.09.0

Differences from QB

  • New to FreeBASIC

See also

  • #pragma
  • Preprocessor Overview


#print
Preprocessor diagnostic directive

Syntax

#print text

Description

Causes compiler to output text to screen during compilation.

Example

#print Now compiling module foo

Differences from QB

  • New to FreeBASIC

See also

  • #error


#undef
Preprocessor directive to undefine a macro

Syntax

#undef symbol

Description

Undefines a symbol previously defined with #define.

Can be used to ensure that a macro or symbol has a limited lifespan and does not conflict with a similar macro definition that may be defined later in the source code.

(Note: #undef should not be used to undefine variable or function names used in the current function scope. The names are needed internally by the compiler and removing them can cause strange and unexpected results.)

Example

#define ADD2(a_, b_)  ((a_) + (b_))
Print ADD2(1, 2)
' Macro no longer needed so get rid of it ...
#undef ADD2

Differences from QB

  • New to Freebasic

See also

  • #define
  • #macro
  • #if
  • #else
  • #elseif
  • #elseifdef
  • #elseifndef
  • #endif
  • #ifdef
  • #ifndef
  • defined


$Dynamic
Metacommand to change the way arrays are allocated

Syntax

'$Dynamic
or
Rem $Dynamic

Description

'$Dynamic is a metacommand that specifies that any following array declarations are variable-length, whether they are declared with constant subscript ranges or not. This remains in effect for the rest of the module in which '$Dynamic is used, and can be overridden with '$Static. It is equivalent to the Option Dynamic statement.

Example

' compile with -lang fblite or qb

#lang "fblite"

'$DYNAMIC
Dim a(100)
'......
ReDim a(200)

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • When used inside comments it must be the first token

See also

  • $Static
  • Dim
  • ReDim
  • Erase
  • Option Dynamic


$Include
Metacommand statement to include contents of another source file

Syntax

'$Include [once]: 'file'
or
Rem $Include [once]: 'file'

Description

$Include inserts source code from another file at the point where the $Include metacommand appears. This has the effect of compiling the source code from the include file as though it were part of the source file that includes it. Once the compiler has reached the end of the include file, the original source file continues to be compiled.

The Once specifier tells the compiler to include the file only once even if it is included several times by the source code.

'$Include: exists for compatibility with QuickBASIC. It is recommended to use #include instead.

Example

' header.bi file
Type FooType
    Bar As Byte
    Barbeque As Byte
End Type
Dim Foo As FooType

'' compile with -lang fblite or qb

#lang "fblite"

'' main.bas file

'$INCLUDE: "header.bi"

Foo.Bar = 1
Foo.Barbeque = 2

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • None

See also

  • #include


$Lang
Metacommand statement to set the compiler dialect.

Syntax

'$lang: "lang"
or
Rem $lang: "lang"

Parameters

"lang"
The dialect to set, enclosed in double quotes, and must be one of "fb", "fblite", "qb", or "deprecated".

Description

If the -forcelang option was not given on the command line, $Lang can be used to set the dialect for the source module in which it appears. At most two passes will be made on the source module. On the first pass, if the specified dialect is anything other than the default dialect (chosen with -lang, or "fb" by default), the compiler will reset the parser for another pass and restart compilation at the beginning of the source module. If this metacommand is encountered again on the second pass, and the specified dialect does not match the new current dialect, a warning is issued and compilation continues. If any errors were encountered on the first pass, the compiler will not attempt a second pass.

$Lang may not be used in any compound statement, scope, or subroutine. However, it may be nested in module level preprocessor statements or used in an include file.

There is currently no restriction on where this directive may be placed in a source module. In future this may change, therefore best practice would be to use this directive before the first declaration, definition, or executable statement in the source.

This directive overrides the -lang option if it was given on the command line. However, if the -forcelang option was given on the command line, this directive will have no effect. A warning is issued, the directive is ignored, and compilation will continue. This allows the user to explicitly override $Lang metacommands.

This metacommand was introduced in FreeBASIC version 0.20.0. Older versions of FB, and QuickBASIC, will treat it as a comment and silently ignore it.

Example

'$lang: "qb"

Differences from QB

  • New to FreeBASIC
  • QB handles '$lang: as a normal comment

See also

  • #lang
  • __FB_LANG__
  • Compiler Option: -lang
  • Compiler Option: -forcelang
  • FreeBASIC Dialects


$Static
Metacommand to change the way arrays are allocated

Syntax

'$Static
or
Rem $Static

Description

'$Static is a metacommand that overrides the behavior of $Dynamic, that is, arrays declared with constant subscript ranges are fixed-length. This remains in effect for the rest of the module in which '$Static is used, and can be overridden with $Dynamic. It is equivalent to the Option Static statement.

Example

' compile with -lang fblite or qb

#lang "fblite"

'$dynamic
Dim a(100)   '<<this array will be variable-length
'$static
Dim b(100)   '<<this array will be fixed-length

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • When used inside comments it must be the first token

See also

  • $Dynamic
  • Dim
  • Erase
  • ReDim
  • Option Dynamic
  • Option Static


(Print | ?)
Writes text to the screen

Syntax

(Print | ?) [ expressionlist ] [ , | ; ]

Parameters

expressionlist
list of items to print

Description

Print outputs a list of values to the screen. Numeric values are converted to their string representation, with left padding for the sign. Objects of user-defined types must overload Operator Cast () As String.

Consecutive values in the expression list are separated either by a comma (,) or semicolon (;). A comma indicates printing should take place at the next 14 column boundary, while a semicolon indicates values are printed with no space between them. This has a similar effect to concatenating expressions using + or &.

Print also supports the special expressions, Spc() and Tab(). These can be used to space out expressions, or to align the printing to a specific column.

A new-line character is printed after the values in the expression list unless the expression list is followed by a comma or semicolon. A Print without any expressions or separators following it will just print a new-line.

NOTE: Print resets the Err value after each expression is printed.

NOTE: In graphics mode, Draw String provides a flexible alternative to Print: it prints a string to the screen with pixel positioning, transparent background, and can use a user-supplied font.

Example

'' print "Hello World!", and a new-line
Print "Hello World!"

'' print several strings on one line, then print a new-line
Print "Hello";
Print "World"; "!";
Print

'' column separator
Print "Hello!", "World!"

'' printing variables/expressions
Dim As Double pi = Atn(1) * 4
Dim As String s = "FreeBASIC"

Print "3 * 4 ="; 3 * 4

Print "Pi is approximately"; pi
Print s; " is great!"

Dialect Differences

  • In the -lang qb dialect, an extra space is printed after any integer number.

Differences from QB

  • None, when using QBASIC's variable types in -lang qb, except that no extra space is displayed after any floating-point number.
  • Unsigned numbers are printed without a space before them.
  • QB did not support casting for UDTs, so didn't allow them to be Printed.

See also

  • Spc
  • Tab
  • Print #
  • ? #
  • Print Using
  • ? Using
  • Write
  • Draw String
  • Input


(Print | ?) #
Writes a list of values to a file or device

Syntax

(Print | ?) # filenum, [ expressionlist ] [ , | ; ]

Parameters

filenum
The file number of a file or device opened for Output or Append.
expressionlist
List of values to write.

Description

Print # outputs a list of values to a text file or device. Numeric values are converted to their string representation, with left padding for the sign. Objects of user-defined types must overload Operator Cast () As String.

Consecutive values in the expression list are separated either by a comma (,) or semicolon (;). A comma indicates printing should take place at the next 14 column boundary, while a semicolon indicates values are printed with no space between them.

A new-line character is printed after the values in the expression list unless the expression list is followed by a comma or semicolon.

Note that the comma (,) immediately following the file number is still necessary, even the expression list is empty. In this case a new-line is printed, just as with a normal expression list that doesn't have a comma or semicolon at the end.

Note:
- Using Print # is naturally dedicated to Output/Append Access file mode.
- It is also allowed in Binary/Random Access file mode, but this was never well tested and results may vary.

Example

Open "bleh.dat"  For Output As #1

    Print #1, "abc def"
    Print #1, 1234, 5678.901, "xyz zzz"

    Close #1

Dialect Differences

  • In the -lang qb dialect, an extra space is printed after numbers.

Differences from QB

  • None, when using QBASIC's variable types in -lang qb.
  • Unsigned numbers are printed without a space before them.
  • QB did not support casting for UDTs, so didn't allow them to be Printed.

See also

  • Print Using
  • ? Using
  • Print
  • ?
  • Write #
  • Open


(Print | ?) Using
Outputs formatted text to the screen or output device.

Syntax

(Print | ?) [# filenum ,] [ printexpressionlist {,|;} ] Using formatstring ; [ expressionlist [ ; ] ]

Parameters

filenum
The file number of a file or device opened for Output or Append. (Alternatively LPrint may be used where appropriate, instead of Print #)
printexpressionlist
Optional preceding list of items to print, separated by commas (,) or semi-colons (;) (see Print for more details).
formatstring
Format string to use.
expressionlist
List of items to format, separated by semi-colons (;).

Description

Print to screen various expressions using a format determined by the formatstring parameter. Internally, Print Using uses a buffer size of 2048 bytes: while it is highly unlikely that this buffer would be filled, it should be noted that output would be truncated should this limit be reached.

If no expression list is given, the format string will be printed up to the first special marker. Note that the semi-colon after formatstring is still necessary, even if no expression list is given.

The format string dictates how the expressions are to be formatted when output to the screen, indicated by the use of special marker characters. There are markers for formatting both string and numeric output:

String formatting

Marker | Formatting
! | prints the first character of a string
\ \ | prints two or more characters of a string - as many as are occupied by the backslashes ('\') and any enclosed space characters (' ')
& | prints the entire string

If an exact amount of characters is specified (with "!" or "\ \"), the string will be truncated or padded with spaces on the right as needed to output that many characters.

Numeric formatting

Marker | Formatting
# | placeholder for either an integer digit, or a decimal digit if a decimal point precedes it
, | placed after integer digit indicates groups of 3 digits should be separated by commas in fixed-point notation
. | placed near # indicates place for the decimal point
^^^ | uses exponential notation (E+/-#) when placed after the digit characters
^^^^ | uses exponential notation (E+/-##) when placed after the digit characters
^^^^^ | uses exponential notation (E+/-###) when placed after the digit characters
+ | placed before/after the format string, controls whether the sign of a number is prepended/appended, and causes an explicit '+' sign to be printed for positive numbers
- | placed after the format string, causes the sign of the number to be appended rather than prepended, appending a space/negative sign for positive/negative numbers
$$ | placed at the start of integer digits, causes a dollar sign to be prepended to the number (after the sign if one is prepended)
** | placed at the start of integer digits, causes any padding on the left to be changed from spaces to asterisks
**$ | placed at the start of integer digits, pads on the left with asterisks, and prepends a dollar sign after the asterisks
& | prints a number intelligently, using the exact number of digits required (new to version 0.21.0b)

All of the special marker characters can be escaped by preceding them with the underscore character "_", allowing them to be printed directly. For example, "_!" is printed as "!", and "__" is printed as "_".

If a numerical value cannot fit in the number of digits indicated by the format string, the formatting is adapted to fit the number, possibly switching to scientific notation, and the number is printed preceded by the percent "%" character. E.g., the number 1234 with a formatstring of "##.##" would be printed as "%1234.00".

All other characters within the format string are printed as they appear.

A new-line character is printed after the values in the expression list unless the expression list is followed by a semicolon (;).

Example

Print Using "The value is #.## seconds"; 1.019
Print Using "The ASCII code for the pound sign (_#) is ###"; Asc("#")
Print Using "The last day in the year is & \ \"; 31; "December"
Print
For exponent As Integer = 1 To 5
   Print Using "10 ^ # = ######"; exponent; 10 ^ exponent
Next exponent

will produce the output:
The value is 1.02 seconds
The ASCII code for the pound sign (#) is  35
The last day in the year is 31 Dec

10 ^ 1 =     10
10 ^ 2 =    100
10 ^ 3 =   1000
10 ^ 4 =  10000
10 ^ 5 = 100000
Differences from QB

  • QB didn't allow "&" to be used for printing numbers.

See also

  • Print
  • ?
  • Print #
  • ? #
  • Format
  • Using
  • Palette Using


Abs
Calculates the absolute value of a number

Syntax

Declare Function Abs ( ByVal number As Integer ) As Integer
Declare Function Abs ( ByVal number As UInteger ) As UInteger
Declare Function Abs ( ByVal number As Double ) As Double

Usage

result = Abs( number )

Parameters

number
Value to find the absolute value of.

Return Value

The absolute value of number.

Description

The absolute value of a number is its positive magnitude. If a number is negative, its value will be negated and the positive result returned. For example, Abs(-1) and Abs(1) both return 1. The required number argument can be any valid numeric expression.
Unsigned numbers will be treated as if they were signed, i.e. if the highest bit is set the number will be treated as negative, and its value negated.
The value returned will be greater than or equal to 0, with the exception of signed integers containing the lowest possible negative value that can be stored in its type, in which case negating it will overflow the result.

The Abs unary Operator can be overloaded with user defined types.

Example

Dim n As Integer

Print Abs( -1 )
Print Abs( -3.1415 )
Print Abs( 42 )
Print Abs( n )

n = -69

Print Abs( n )

Output:
1
3.1415
42
0
69

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Sgn
  • Operator


Abstract
Declare abstract methods

Syntax

Type typename Extends base_typename
Declare Abstract Sub|Function|Property|Operator ...
End Type

Description

Abstract is a special form of Virtual. The difference is that abstract methods do not have a body, but just the declaration. Essentially this allows the declaration of an interface which can be implemented by various derived types.

In order to call an abstract method, it must have been overridden and implemented by a derived data type, or else the program will abort.
As a result, only types that implement all the abstract methods are allowed to create objects. For the same reason, a constructor should not call an unimplemented method.

Constructors cannot be abstract, since they cannot be virtual. In addition, abstract Destructors are not supported either, because a destructor body (no matter whether implicit or explicit) is needed in order to call base and field destructors.

Abstracts are called "pure virtual" in C++ (unlike FreeBASIC, C++ allows pure virtuals to have a body, but accessible only statically).

Note: In a multi-level inheritance, a same named method (same identifier and signature) can be declared Abstract, Virtual or normal (without specifier) at each inheritance hierarchy level. When there is mixing of specifiers, the usual order is abstract -> virtual -> normal, from top to bottom of the inheritance hierarchy.
The access control (Public/Protected/Private) of an overriding method is not taken into account by the internal polymorphism process, but only for the initial call at compile-time.
A derived static method cannot override a base virtual/abstract method, but can shadow any base method (including virtual/abstract).

Example

Type Hello Extends Object
    Declare Abstract Sub hi( )
End Type

Type HelloEnglish Extends Hello
    Declare Sub hi( )
End Type

Type HelloFrench Extends Hello
    Declare Sub hi( )
End Type

Type HelloGerman Extends Hello
    Declare Sub hi( )
End Type

Sub HelloEnglish.hi( )
    Print "hello!"
End Sub

Sub HelloFrench.hi( )
    Print "Salut!"
End Sub

Sub HelloGerman.hi( )
    Print "Hallo!"
End Sub

    Randomize( Timer( ) )

    Dim As Hello Ptr h

    For i As Integer = 0 To 9
        Select Case( Int( Rnd( ) * 3 ) + 1 )
        Case 1
            h = New HelloFrench
        Case 2
            h = New HelloGerman
        Case Else
            h = New HelloEnglish
        End Select

        h->hi( )
        Delete h
    Next

Dialect Differences

  • Only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Virtual
  • Type
  • Extends
  • Extends Zstring
  • Extends Wstring
  • Object
  • Override


Access
Clause of the Open statement to specify requested privileges

Syntax

Open filename for Binary Access {Read | Write | Read Write} as [#]filenum

Usage

open filename for binary Access Read as #filenum
open filename for binary Access Write as #filenum
open filename for binary Access Read Write as #filenum

Parameters

Read
Open the file with only read privileges.
Write
Open the file with only write privileges.
Read Write
Open the file with read and write privileges.

Description

Access is used with the Open statement to request read, write, or read and write privileges. If the Access clause is not specified, Read Write is assumed.

Example

This example shows how to open the file "data.raw" with Read and then "data.out" with Write access, in Binary mode, in an open file number returned by FreeFile.
Dim As Long o

  '' get an open file number.
  o = FreeFile

  '' open file for read-only access.
  Open "data.raw" For Binary Access Read As #o

    '' make a buffer in memory thats the entire size of the file
    Dim As UByte file_char( LOF( o ) - 1 )

      '' get the file into the buffer.
      Get #o, , file_char()

  Close

  '' get another open file number.
  o = FreeFile

  '' open file for write-only access.
  Open "data.out" For Binary Access Write As #o

    '' put the buffer into the new file.
    Put #o, , file_char()

  Close

  Print "Copied file ""data.raw"" to file ""data.out"""

  Sleep

Differences from QB

  • None known.

See also

  • Open
  • Read
  • Write


Acos
Finds the arccosine of an angle

Syntax

Declare Function Acos ( ByVal number As Double ) As Double

Usage

result = Acos( number )

Parameters

number
A cosine value in the range [-1..1].

Return Value

The arccosine of number, in radians, in the range [0..Pi].

Description

Acos returns the arccosine of the argument number as a Double within the range of 0 to Pi. The arccosine is the inverse of the Cos function. The returned angle is measured in radians (not degrees).

Acos can be overloaded as operator to accept user-defined types.

Example

Dim h As Double
Dim a As Double
Input "Please enter the length of the hypotenuse of a triangle: ", h
Input "Please enter the length of the adjacent side of the triangle: ", a
Print ""
Print "The angle between the sides is"; Acos ( a / h )
Sleep

The output would look like:
Please enter the length of the hypotenuse of a triangle: 5
Please enter the length of the adjacent side of the triangle: 4

The angle between the sides is 0.6435011087932843

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Acos.

Differences from QB

  • New to FreeBASIC

See also

  • Cos
  • A Brief Introduction To Trigonometry


Add
Parameter to the Put graphics statement which selects addition as the blitting method

Syntax

Put [ target, ] [ STEP ] ( x,y ), source [ ,( x1,y1 )-( x2,y2 ) ], Add[ ,multiplier ]

Parameters

Add
Required.
multiplier
Optional value between 0 and 255. The source pixels are premultiplied by (multiplier / 256) before being added. If omitted, this value defaults to 255.

Description

Add selects addition as the method for blitting an image buffer. For each source and target pixel, the values of each respective component are added together to produce the result.
The addition is saturated - i.e. if the sum of the two values is 256 or more, then it will be cropped down to 255.

This method will work in all color modes. Mask colors (color 0 for indexed images, magenta (RGB(255, 0, 255)) for full color images) will be skipped, though full color values of 0 (RGBA(0, 0, 0, 0)) will have also have no effect.

Example

''open a graphics window
ScreenRes 320, 200, 16

''create a sprite containing a circle
Const As Integer r = 32
Dim c As Any Ptr = ImageCreate(r * 2 + 1, r * 2 + 1, 0)
Circle c, (r, r), r, RGB(255, 255, 192), , , 1, f

''put the sprite at three different multipier
''levels, overlapping each other in the middle
Put (146 - r, 108 - r), c, Add,  64
Put (174 - r, 108 - r), c, Add, 128
Put (160 - r,  84 - r), c, Add, 192

''free the memory used by the sprite
ImageDestroy c

''pause the program before closing
Sleep

Differences from QB

  • New to FreeBASIC

See also

  • Trans
  • Alpha
  • Custom
  • Put (Graphics)


Alias (Name)
Clause of the Sub and Function statements that provides an alternate internal name.

Syntax

[Declare] { Sub | Function } usablename Alias "alternatename" (...)

Usage

declare sub usablename Alias "alternatename" ( ... )
or
declare function usablename Alias "alternatename" ( ... )
or
sub usablename Alias "alternatename" ( ... )
...
end sub
or
function usablename Alias "alternatename" ( ... )
...
end function
or
type typename Alias "alternatename" ( ... )
...
end type

Description

Alias gives an alternate name to a procedure. This alternate name cannot be used within the program to call the procedure, but it is visible (if the function is not private) to the linker when linking with code written in other languages.

Alias is commonly used for procedures in libraries written in other languages when such procedure names are valid in the other language but invalid in BASIC. When using Alias with Declare, only the alternate name is used by the linker.

Differently from normal procedure names, Alias does not change the case of the alternate name, so it is useful when external code requires an exported function with a particular name or with a particular case.

Alias can be used to specify an alternate name for Type definitions.

Alias can be used as a modifier that specifies an alternate name mangling for procedure parameters. See Alias (Modifier)

Example

If there is a sub (without parameter) called xClearScreen in an external library and you want to reference it with the name ClearVideoScreen, here is sample code to do so:
Declare Sub ClearVideoScreen Alias "xClearScreen" ()

If there is a function (with parameters and return value) called MyFunctioninLib in an external library and you want to reference it with the name myfunction, here is sample code to do so:
Declare Function myfunction Alias "MyFunctioninLib" (ByVal As Long, ByVal As Long, ByVal As ZString Ptr) As Integer

A procedure meant to be used by external C code, exported as MyExportedProc:
Function MultiplyByFive cdecl Alias "MyExportedProc" (ByVal Parameter As Integer) As Integer Export
  Return Parameter * 5
End Function

Differences from QB

  • In QB, Alias only worked with Declare.

See also

  • Declare
  • Export
  • Type (Alias)
  • Alias (Modifier)


Alias (Modifier)
Modifies the data type name mangling (decoration) of a public symbol

Syntax

... As [ Const ] datatype alias "modifier" [ Const [ Ptr ... ] ]

Usage

Dim variable As datatype alias "modifier"
Type name As datatype alias "modifier"
Declare Sub name ( param As datatype alias "modifier", ... )
Declare Function name ( param As datatype alias "modifier", ... ) As datatype alias "modifier"

Parameters

datatype
Standard data type or user defined data type to modify
modifier
One of the supported modifiers as described in Description section following
Description

Alias "modifier", when specified following a data type, gives an alternate meaning to the data type, which may be needed for linking with languages other than FreeBASIC.

Public symbol names are mangled (decorated) to encode information about the data type that is used for the symbol. When linking with the c language, the special meaning of the alias modifier is meaningless, since the extra information is not encoded in to the public name. When linking with the c++ language, typically more information is encoded in to the public symbol, and the alias modifier may be required. The public name is written to the compiled object file, and used by the linker to match symbol names from one object module to another.

The same rules for mapping data types is used regardless of which backend (gas or gcc) code emitter is used, And the intent is that FB's compiled code can link consistently with it's own object modules and object modules (or libraries) compiled from other languages.

Supported Modifiers
Byte alias "char"
UByte alias "char"
Used to map FB's 8-bit byte types to c/c++'s 8-bit char type.

Long alias "long"
ULong alias "long"
On Win 64-bit targets, used to map FB's 32-bit Long and ULong types to c/c++'s 32-bit long [int] type, instead of the 32-bit int type.

Any alias "char" Ptr
Maps any ptr to c/c++'s char *. In c/c++, char, signed char, and unsigned char, are three distinct types.
      • Byte Ptr maps to signed char *
      • UByte Ptr maps to unsigned char *
      • On some platforms the variable argument list va_list type is a typed as a char *, but FB does not have an equivalent type, therefore Any Ptr is used instead. Linking with names encoded with this type will fail since, normally, FB encodes void * data type instead of char *.
      • alias "char" keeps the any ptr behaviour in FB but then encodes the public name as char * for linking.

any alias "__builtin_va_list" ptr
Maps the data type to gcc's __builtin_va_list type
      • expected that gcc's built-in type is a pointer type
      • used on dos, win32, win64, linux-x86, targets
      • see Cvalist for default usage in the cva_list data type

alias "__builtin_va_list"
Maps the data type to gcc's __builtin_va_list type
      • expected that gcc's built-in type is a struct type
      • used on aarch64 target
      • see Cvalist for default usage in the cva_list data type

alias "__builtin_va_list[]"
Maps the data type to gcc's __builtin_va_list type
      • expected that gcc's built-in type is a struct array type
      • used on linux-x86_64 target
      • see Cvalist for default usage in the cva_list data type

Data Type Mapping Details

On all targets, FB to c/c++:
Several of FB's data types are consistently mapped across all targets:
    • 8-bit Byte maps to signed char
    • 8-bit UByte maps to unsigned char
    • 16-bit Short maps to [signed] short [int]
    • 16-bit UShort maps to unsigned short [int]
    • 32-bit Long maps to int
    • 32-bit ULong maps to unsigned int
    • 64-bit LongInt maps to long long [int]
    • 64-bit ULongInt maps to unsigned long long [int]

On Dos/Win/Linux 32-bit targets, FB to c/c++:
Integer on 32-bit targets is 32-bits wide
    • 32-bit Integer maps to long [int]
    • 32-bit UInteger maps to unsigned long [int]

On Linux 64-bit targets, FB to c/c++:
Integer on 64-bit targets is 64-bits wide
    • 64-bit Integer maps to long [int]
    • 64-bit UInteger maps to unsigned long [int]

On Win 64-bit targets, FB to c/c++:
Integer on 64-bit targets is 64-bits wide. However, on Win target, c/c++'s long int type is 32-bit, not 64-bit, and we can't use the long long int mangling because it's already used by FB's LongInt type. Reusing the same mangling (decoration) for two different data types would cause function overloading to fail or have duplicate definitions. To preserve FB's behaviour that Integer on 64-bit targets is always 64-bits, we mangle (decorate) the symbol with a custom datatype and keep the size at 64-bit.
    • 64-bit Integer maps to custom INTEGER
    • 64-bit UInteger maps to custom UINTEGER
To create a data type in FB that will map to c/c++'s long [int] 32-bit on win, we must use alias modifier.
    • 32-bit Long alias "long" maps to long [int]
    • 32-bit ULong alias "long" maps to unsigned long [int]

For example extern c++ : declare sub proc( byval as long alias "long" ) : end extern. This allows FreeBASIC to call external c++ procedures (on win-64) requiring a 32-bit long int type. Usage of Alias in this way affects win-64 targets only, and is ignored on all other targets.

Example

See example at Alias (Name).

Version

  • Before fbc 1.10.0, the [u]byte alias "char" modifier was not supported.
  • Since fbc 1.06.0

Differences from QB

  • In QB, Alias only worked with Declare.

See also

  • Alias (Name)
  • Declare
  • Export
  • Type (Alias)


Allocate
Allocates a block of memory from the free store

Syntax

Declare Function Allocate cdecl ( ByVal count As UInteger ) As Any Ptr

Usage

result = Allocate( count )

Parameters

count
The size, in bytes, of the block of memory to allocate.

Return Value

If successful, the address of the start of the allocated memory is returned. Otherwise, if the requested block size could not be allocated, or if count < 0, then the null pointer (0) is returned.

Description

Allocates, or reserves, count number of bytes from the free store (heap). The newly allocated memory is not initialized.

As the initial value of newly allocated memory is unspecified, Allocate must not be directly used with String or Udt containing string, because the string descriptor being not cleared (containing random data), that may induce corrupted string or more (trying to write to a random place in memory or trying to deallocate a random pointer). It is mandatory in that case (with string or UDT containing string) to use CAllocate (clearing memory), or New Expression (calling constructor) in case of UDT, or at worst after Allocate to explicitly clear the descriptor (setting to 0) before the first string use.
For allocating memory for a ZString or a WString, see the corresponding page.

The pointer that is returned is an Any Ptr and points to the start of the allocated memory. This pointer is guaranteed to be unique, even if count is zero.

Allocated memory must be deallocated, or returned back to the free store, with Deallocate when no longer needed.

Example

'' This program uses the ALLOCATE(...) function to create a buffer of 15 integers that is
'' then filled with the first 15 numbers of the Fibonacci Sequence, then output to the
'' screen. Note the call to DEALLOCATE(...) at the end of the program.

    Const integerCount As Integer = 15

    '' Try allocating memory for a number of integers.
    ''
    Dim buffer As Integer Ptr
    buffer = Allocate(integerCount * SizeOf(Integer))

    If (0 = buffer) Then
        Print "Error: unable to allocate memory, quitting."
        End -1
    End If

    '' Prime and fill the memory with the fibonacci sequence.
    ''
    buffer[0] = 0
    buffer[1] = 1
    For i As Integer = 2 To integerCount - 1
        buffer[i] = buffer[i - 1] + buffer[i - 2]
    Next

    '' Display the sequence.
    ''
    For i As Integer = 0 To integerCount - 1
        Print buffer[i] ;
    Next

    Deallocate(buffer)
    End 0

Output is:
 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
It is important to free allocated memory if it's not going to be used anymore. Unused memory that isn't freed is simply wasting memory, and if the address of that memory is somehow overwritten or forgotten, that memory can never be freed. This condition is known as a memory leak, and should be avoided at all costs. Note that leaked memory is always completely freed when the application terminates, either by an "ordinary" exit or crash, so the leak "persists" only as long as the application runs, nevertheless it's a good habit to free any allocated memory inside your application. The following example demonstrates a function with a memory leak, where the address of allocated memory is lost and isn't and can't be freed anymore. If such a function is called frequently, the total amount of memory wasted can add up quickly.

'' Bad example of Allocate usage, causing memory leaks

Sub BadAllocateExample()

    Dim p As Byte Ptr

    p = Allocate(420)   '' assign pointer to new memory

    p = Allocate(420)   '' reassign same pointer to different memory,
                        '' old address is lost and that memory is leaked

    Deallocate(p)

End Sub

    '' Main
    BadAllocateExample() '' Creates a memory leak
    Print "Memory leak!"
    BadAllocateExample() '' ... and another
    Print "Memory leak!"
    End

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Allocate

Differences from QB

  • New to FreeBASIC

See also

  • CAllocate
  • Reallocate
  • Deallocate


Alpha
Parameter to the Put graphics statement which selects alpha blending as the method

Syntax

Put [ target, ] [ STEP ] ( x,y ), source [ ,( x1,y1 )-( x2,y2 ) ], Alpha
Put [ target, ] [ STEP ] ( x,y ), source [ ,( x1,y1 )-( x2,y2 ) ], Alpha, alphaval

Parameters

Alpha
Required.
alphaval
Optional alpha parameter in the range [0..255]. Overrides alpha values in individual pixels.

Description

Alpha selects alpha blending as the method for Putting an image. If the alphaval parameter is specified, it overrides the alpha value of each pixel, and the mask color (magenta) will be treated as transparent. This works in 15, 16, 24, or 32-bit color depths.

If alphaval is not specified, Alpha will only work in 32-bit color depth, and Put will use the alpha value embedded within each pixel. Pixels using the mask color will be treated as normal, and drawn with their given alpha value.

Alpha also has another mode which allows an 8-bit image to be Put on top of a 32-bit image. In this case, it will replace the alpha channel of the 32-bit image with the contents of the 8-bit image.

Alpha values range between 0 and 255. An alpha value of 0 will not draw the image at all. All other alpha values are incremented by 1 to get a range between 2 and 256, and the result is then divided by 256 to get a value between 1/128 and 1, which is used to calculate the exact value of each pixel from the source and destination pixels. Thus, 255 is practically equivalent to drawing using Put with Trans blitting mode, 0 is equivalent to doing nothing at all, and all the other alpha values blend is expected.

Example

This example compares the two different Alpha modes, including how they react to the mask color
'' Set up a 32-bit screen
ScreenRes 320, 200, 32

'' Draw checkered background
For y As Integer = 0 To 199
    For x As Integer = 0 To 319
        PSet (x, y), IIf((x Shr 2 Xor y Shr 2) And 1, RGB(160, 160, 160), RGB(128, 128, 128))
    Next x
Next y

'' Make image sprite for Putting
Dim img As Any Ptr = ImageCreate(32, 32, RGBA(0, 0, 0, 0))
For y As Single = -15.5 To 15.5
    For x As Single = -15.5 To 15.5
        Dim As Integer r, g, b, a
        If y <= 0 Then
            If x <= 0 Then
                r = 255: g = 0: b = 0   '' red
            Else
                r = 0: g = 0: b = 255   '' blue
            End If
        Else
            If x <= 0 Then
                r = 0: g = 255: b = 0   '' green
            Else
                r = 255: g = 0: b = 255 '' magenta (transparent mask color)
            End If
        End If
        a = 255 - (x ^ 2 + y ^ 2)
        If a < 0 Then a = 0': r = 255: g = 0: b = 255
        PSet img, (15.5 + x, 15.5 - y), RGBA(r, g, b, a)
    Next x
Next y

'' Put with single Alpha value, Trans for comparison
Draw String (32, 10), "Single alpha"
Put (80 - 16,  50 - 16), img, Alpha, 64
Put (80 - 16, 100 - 16), img, Alpha, 192
Put (80 - 16, 150 - 16), img, Trans

'' Put with full Alpha channel
Draw String (200, 10), "Full alpha"
Put (240 - 16, 100 - 16), img, Alpha

'' Free the image memory
ImageDestroy img

'' Wait for a keypress
Sleep

This example shows the special method for setting a 32-bit alpha channel using an 8-bit image
Dim As Any Ptr img8, img32
Dim As Integer x, y, i

'' Set up an 8-bit graphics screen
ScreenRes 320, 200, 8
For i = 0 To 255
    Palette i,  i, i, i
Next i
Color 255, 0

'' Create an 8-bit image
img8 = ImageCreate(64, 64, 0,  8)
For y = 0 To 63
    For x = 0 To 63
        Dim As Single x2 = x - 31.5, y2 = y - 31.5
        Dim As Single t = Sqr(x2 ^ 2 + y2 ^ 2) / 5
        PSet img8, (x, y), Sin(t) ^ 2 * 255
    Next x
Next y

Draw String (16, 4), "8-bit Alpha sprite"
Put (16, 16), img8
Sleep

'' Set up a 32-bit graphics screen
ScreenRes 320, 200, 32
For y = 0 To 199
    For x = 0 To 319
        PSet (x, y), IIf(x - y And 3, RGB(160, 160, 160), RGB(128, 128, 128))
    Next x
Next y

'' Create a 32-bit, fully opaque sprite
img32 = ImageCreate(64, 64, 0, 32)
For y = 0 To 63
    For x = 0 To 63
        PSet img32, (x, y), RGB(x * 4, y * 4, 128)
    Next x
Next y

Draw String (16, 4), "Original Alpha channel"
Put (16, 16), img32, Alpha

'' Put a new alpha channel using the 8-bit image
Put img32, (0, 0), img8, Alpha

Draw String (16, 104), "New Alpha channel"
Put (16, 116), img32, Alpha

''Free the memory for the two images
ImageDestroy img8
ImageDestroy img32

Sleep

Differences from QB

  • New to FreeBASIC

See also

  • Put (Graphics)
  • Trans
  • Custom


Operator And (Conjunction)
Returns the bitwise-and (conjunction) of two numeric values

Syntax

Declare Operator And ( ByRef lhs As T1, ByRef rhs As T2 ) As Ret

Usage

result = lhs And rhs

Parameters

lhs
The left-hand side expression.
T1
Any numeric or boolean type.
rhs
The right-hand side expression.
T2
Any numeric or boolean type.
Ret
A numeric or boolean type (varies with T1 and T2).

Return Value

Returns the bitwise-and (conjunction) of the two operands.

Description

This operator returns the bitwise-and of its operands, a logical operation that results in a value with bits set depending on the bits of the operands (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value).

The truth table below demonstrates all combinations of a boolean-and operation:

Lhs Bit | Rhs Bit | Result
0 | 0 | 0
1 | 0 | 0
0 | 1 | 0
1 | 1 | 1

No short-circuiting is performed - both expressions are always evaluated.

The return type depends on the types of values passed. Byte, UByte and floating-point type values are first converted to Integer. If the left and right-hand side types differ only in signedness, then the return type is the same as the left-hand side type (T1), otherwise, the larger of the two types is returned. Only if the left and right-hand side types are both Boolean, the return type is also Boolean.

This operator can be overloaded for user-defined types.

Example

' Using the AND operator on two numeric values
Dim As UByte numeric_value1, numeric_value2
numeric_value1 = 15 '00001111
numeric_value2 = 30 '00011110

'Result =  14  =     00001110
Print numeric_value1 And numeric_value2
Sleep

' Using the AND operator on two conditional expressions
Dim As UByte numeric_value1, numeric_value2
numeric_value1 = 15
numeric_value2 = 25

If numeric_value1 > 10 And numeric_value1 < 20 Then Print "Numeric_Value1 is between 10 and 20"
If numeric_value2 > 10 And numeric_value2 < 20 Then Print "Numeric_Value2 is between 10 and 20"
Sleep

' This will output "Numeric_Value1 is between 10 and 20" because
' both conditions of the IF statement is true
' It will not output the result of the second IF statement because the first
' condition is true and the second is false.

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • AndAlso
  • Operator Truth Tables


Operator And= (Conjunction And Assign)
Performs a bitwise-and (conjunction) and assigns the result to a variable

Syntax

Declare Operator And= ( ByRef lhs As T1, ByRef rhs As T2 )

Usage

lhs And= rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric or boolean type.
rhs
The value to perform a bitwise-and (conjunction) with lhs.
T2
Any numeric or boolean type.

Description

This operator performs a bitwise-and and assigns the result to a variable (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value). It is functionally equivalent to:
lhs = lhs And rhs

And= compares each bit of its operands, lhs and rhs, and if both bits are 1, then the corresponding bit in the first operand, lhs, is set to 1, otherwise it is set to 0.

And= cannot be used in conditional expressions.

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol 'And=>' can be also used.

Example

' Using the AND= operator on two numeric values
Dim As UByte numeric_value1, numeric_value2
numeric_value1 = 15 '' 00001111
numeric_value2 = 30 '' 00011110

numeric_value1 And= numeric_value2

'' Result =  14  =     00001110
Print numeric_value1
Sleep

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • And


Operator Andalso (Short Circuit Conjunction)
Returns the short circuit-and (conjunction) of two numeric values

Syntax

Declare Operator AndAlso ( ByRef lhs As T1, ByRef rhs As T2 ) As Ret

Usage

result = lhs AndAlso rhs

Parameters

lhs
The left-hand side expression.
T1
Any numeric or boolean type.
rhs
The right-hand side expression.
T2
Any numeric or boolean type.
Ret
A numeric or boolean type (varies with T1 and T2).

Return Value

Returns the short circuit-and (conjunction) of the two operands.

Description

This operator evaluates the left hand side expression. If the result is zero, then zero is immediately returned. If the result is nonzero then the right hand side is evaluated, and the logical result from that is returned.
(for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value)

The truth table below demonstrates all combinations of a short circuit-and operation, the '-' denotes that the operand is not evaluated.

Lhs Value | Rhs Value | Result
0 | (not evaluated) | 0
nonzero | 0 | 0
nonzero | nonzero | -1

AndAlso equivalent to:
Iif( Lhs <> 0, Rhs <> 0, 0 )

Short-circuiting is performed - only expressions needed to calculate the result are evaluated. The left hand side lhs is evaluated first, and only if it evaluates to non-zero (true) is the right hand side rhs also evaluated. If the left hand side evaluation lhs returns zero (false), it is known that at that point that the overall condition is false, so the right hand side rhs is not evaluated (skipped).

The return type is almost always an Integer, of the value 0 or -1, denoting false and true respectively. Except if the left and right-hand side types are both Boolean, then the return type is also Boolean.

This operator cannot be overloaded for user-defined types.

Example

'' Using the ANDALSO operator to guard against array access
'' when the index is out of range

Dim As Integer isprime(1 To 10) = { _
    _ ' 1  2  3  4  5  6  7  8  9  10    (2, 3, 5 and 7 are the only prime numbers)
        0, 1, 1, 0, 1, 0, 1, 0, 0, 0 _
    }

Dim As Integer n
Input "Enter a number between 1 and 10: ", n

'' isprime() array will only be accessed if n is in range
If (n >= 1 And n <= 10) AndAlso isprime(n) Then
    Print "n is prime"
Else
    Print "n is not prime, or out of range"
End If
Sleep

Differences from QB

  • This operator was not available in QB.

See also

  • OrElse
  • And
  • Operator Truth Tables


And (Graphics Put)
Parameter to the Put graphics statement which uses a bit-wise And as the blitting method

Syntax

Put [ target, ] [ STEP ] ( x,y ), source [ ,( x1,y1 )-( x2,y2 ) ], And

Parameters

And
Required.

Description

The And method combines each source pixel with the corresponding destination pixel, using the bit-wise And function. The result of this is output as the destination pixel.
This method works in all graphics modes. There is no mask color, although color values with all bits set (255 for 8-bit palette modes, or RGBA(255, 255, 255, 255) in full-color modes) will have no effect, because of the behavior of And.

In full-color modes, each component (red, green, blue and alpha) is kept in a discrete set of bits, so the operation can be made to only affect some of the channels, by making sure the all the values of the other channels are set to 255.

Example

''open a graphics window
ScreenRes 320, 200, 16
Line (0, 0)-(319, 199), RGB(255, 255, 255), bf

''create 3 sprites containing cyan, magenta and yellow circles
Const As Integer r = 32
Dim As Any Ptr cc, cm, cy
cc = ImageCreate(r * 2 + 1, r * 2 + 1, RGBA(255, 255, 255, 255))
cm = ImageCreate(r * 2 + 1, r * 2 + 1, RGBA(255, 255, 255, 255))
cy = ImageCreate(r * 2 + 1, r * 2 + 1, RGBA(255, 255, 255, 255))
Circle cc, (r, r), r, RGB(0, 255, 255), , , 1, f
Circle cm, (r, r), r, RGB(255, 0, 255), , , 1, f
Circle cy, (r, r), r, RGB(255, 255, 0), , , 1, f

''put the three sprites, overlapping each other in the middle
Put (146 - r, 108 - r), cc, And
Put (174 - r, 108 - r), cm, And
Put (160 - r,  84 - r), cy, And

''free the memory used by the sprites
ImageDestroy cc
ImageDestroy cm
ImageDestroy cy

''pause the program before closing
Sleep

Differences from QB

  • None

See also

  • And
  • Put (Graphics)


Any
The Any keyword is used as a placeholder for a type or value in various ways.

Syntax

Dim identifier As Any Pointer|Ptr
or
Declare Sub|Function identifier ( ByRef identifier As Any [ , ... ] )
or
Dim identifier(Any [, Any...]) As DataType
or
[ Declare ] { Sub | Function } proc_name ( param(Any [, Any...]) As DataType )
or
Dim identifier As DataType = Any
or
New DataType ( Any )
or
New(Address) DataType [count] { Any }
or
InStr|InStrRev ( identifier, Any substring )
or
ProcPtr ( identifier, [Virtual] Any )

Description

  • Pointers (1st syntax):
A special pointer type called the Any Ptr (or "Any Pointer") allows pointing to any variable type. If you cast it to a DataType Ptr, it can be indexed or dereferenced to access the memory as an instance of DataType. Pointer arithmetic is allowed on an Any Ptr, and treats it like a Byte Ptr: The pointer is changed by increments of 1.

A pure Any Ptr has no type checking by the compiler. It can be implicitly converted to and from other pointer types through assignment or parameter passing.

Any on its own is not a valid data type for a variable. Also, it is illegal to dereference an Any Ptr (although an Any Ptr Ptr may be dereferenced to produce a Any Ptr).

This should not be confused with Variant, a Visual Basic data type which can contain any type of variable. FreeBASIC does not provide native support for a Variant type.

  • Byref parameters (2nd syntax):
Any can be used in procedure prototypes (in a Declare statement) with ByRef parameters to disable the compiler checking for the correct type of the variable passed (this includes the array parameters because always implicitly passed by reference).
Obviously, this does not work with overload procedures, and therefore neither with UDT member procedures.
This use of Any exists for compatibility with QB, but can be superseded by procedure overloading.

  • Array dimensions (3rd/4th syntax):
In array declarations, Any can be specified in place of the array bounds in order to create a dynamic array with a certain amount of dimensions that is determined based on the number of Anys specified (use the syntax with Any is mandatory when declaring a dynamic array member inside a Type).

In parameter declarations, Any can be also specified instead of empty parenthesis in order to fix the amount of dimensions.

  • Initialization (5th/6th/7th syntax):
Any can be used as a fake initializer to disable the default initialization of variables to 0, leaving the variable uninitialized. This may save time in critical sections of a program. It is the program's responsibility to fill the variables with meaningful data before reading it.

For a UDT, the member data fields are also not initialized, and the default constructors, if they exist, are not called.

Comparison to C/C++: This matches the behavior of a variable declaration without initialization value in C/C++.

Any is not allowed when the DataType is a var-len String, because there is no string character data, and not initializing the descriptor would be an aberration.

For a var-len array, Any is used only once at the declaration level, not at each resize level with ReDim (Erase does not reset this initialization state).

Similar to Any initializers for variables, Any can also be used with the New Expression or Placement New operators in order to leave the newly created object uninitialized (only allowed with data types that do not have a constructor, also excluding the var-len string type).

  • Instr/InstrRev (8th syntax):
Any can be used with InStr or InStrRev as a qualifier for the substring parameter, to indicate that any individual character in it may be matched.

  • Procptr (9th syntax):
Any, ie any procedure signature, does not induce any particular selection (compared to its non-use), but just allows for writing ProcPtr always with 2 arguments.

Example

Declare Sub echo(ByVal x As Any Ptr) '' echo will accept any pointer type

Dim As Integer a(0 To 9) = Any '' this variable is not initialized
Dim As Double  d(0 To 4)

Dim p As Any Ptr

Dim pa As Integer Ptr = @a(0)
Print "Not initialized ";
echo pa       '' pass to echo a pointer to integer

Dim pd As Double Ptr = @d(0)
Print "Initialized ";
echo pd       '' pass to echo a pointer to double

p = pa     '' assign to p a pointer to integer
p = pd     '' assign to p a pointer to double

Sleep

Sub echo (ByVal x As Any Ptr)
    Dim As Integer i
    For i = 0 To 39
        'echo interprets the data in the pointer as bytes
        Print Cast(UByte Ptr, x)[i] & " ";
    Next
    Print
End Sub

'Example of ANY disabling the variable type checking
Declare Sub echo (ByRef a As Any) '' ANY disables the checking for the type of data passed to the function

Dim x As Single
x = -15
echo x                  '' Passing a single to a function that expects an integer. The compiler does not complain!!
Sleep

Sub echo (ByRef a As Integer)
  Print Hex(a)
End Sub

Dim a(Any) As Integer ' 1-dimensional dynamic array
Dim b(Any, Any) As Integer ' 2-dimensional dynamic array
Dim c(Any, Any, Any) As Integer ' 3-dimensional dynamic array
' etc.

' Further Redims or array accesses must have a matching amount of dimensions
ReDim a(0 To 1)
ReDim b(1 To 10, 2 To 5)
ReDim c(0 To 9, 0 To 5, 0 To 1)

Dialect Differences

  • Not available in the -lang qb dialect.

Differences from QB

  • Pointers and initializers are new to FreeBASIC.

See also

  • Dim
  • Declare


Append
Specifies text file to be opened for append mode

Syntax

Open filename for Append [Encoding encoding_type] [Lock lock_type] as [#]filenum

Parameters

filename
file name to open for append
encoding_type
indicates encoding type for the file
lock_type
locking to be used while the file is open
filenum
unused file number to associate with the open file

Description

A file mode used with Open to open a text file for writing.

This mode is used to add text to an existing file with Print #, or comma separated values with Write#.

Text files can't be simultaneously read and written in FreeBASIC, so if both functions are required on the same file, it must be opened twice.

filename must be a string expression resulting in a legal file name in the target OS, without wildcards. The file will be sought for in the present directory, unless the filename contains a path . If the file does not exist, it is created. The pointer is set after the last character of the file.

Encoding_type indicates the Unicode Encoding of the file, so characters are correctly read. If omitted, "ascii" encoding is defaulted. Only little endian character encodings are supported at the moment.
    • "utf8"
    • "utf16"
    • "utf32"
    • "ascii" (the default)

Lock_type indicates the way the file is locked for other processes, it is one of:
    • Read - the file can be opened simultaneously by other processes, but not for reading
    • Write - the file can be opened simultaneously by other processes, but not for writing
    • Read Write - the file cannot be opened simultaneously by other processes (the default)

filenum Is a valid FreeBASIC file number (in the range 1..255) not being used for any other file presently open. The file number identifies the file for the rest of file operations. A free file number can be found using the FreeFile function.

Example

Dim i As Integer
For i = 1 To 10
    Open "test.txt" For Append As #1
    Print #1, "extending test.txt"
    Close #1
Next

Differences from QB

  • None

See also

  • Input (File Mode)
  • Open
  • Output
  • Print #
  • ? #
  • Write #


Arraylen
Provides the length of an array given its array-name.

Syntax

Declare Function ArrayLen ( arrayname() As Const Any ) As UInteger

Usage

#include once "fbc-int/array.bi"
using FB
...
result = ArrayLen(arrayname())

Parameters

arrayname
The name of the array for which the length is returned.

Description

Returns the total number of elements of the array specified by arrayname() (taking into account all dimensions of array).

Example

#include Once "fbc-int/array.bi"
Using FB

Dim As LongInt array(4, 5)
Dim As UInteger array_length

array_length = ArrayLen(array())
Print array_length                '' 30

Version

  • Since fbc 1.09.0.

Differences from QB

  • New to FreeBASIC.

See also

  • Arraysize
  • Fbarray (Array Descriptor Structure And Access)


Arraysize
Provides the size of an array given its array-name.

Syntax

Declare Function ArraySize ( arrayname() As Const Any ) As UInteger

Usage

#include once "fbc-int/array.bi"
using FB
...
result = ArraySize(arrayname())

Parameters

arrayname
The name of the array for which the size is returned.

Description

Returns the total size (in bytes) of the array specified by arrayname() (size of array element multiplied by total number of array elements).

Example

#include Once "fbc-int/array.bi"
Using FB

Dim As LongInt array(4, 5)
Dim As UInteger array_size

array_size = ArraySize(array())
Print array_size                 '' 240

Version

  • Since fbc 1.09.0.

Differences from QB

  • New to FreeBASIC.

See also

  • Arraylen
  • Fbarray (Array Descriptor Structure And Access)


As
Part of a declaration which specifies a data type, or part of the Open statement which specifies a file handle.

Syntax

symbolname As datatype

Open ... As #filenumber
Type ... As datatype

Description

As is used to declare the type of variables, fields or arguments and is also used in the Open statement to determine the file handle. As is also used with the Type (Alias) syntax, similar to C's typedef statement.

Example

'' don't try to compile this code, the examples are unrelated
Declare Sub mySub (X As Integer, Y As Single, Z As String)
' ...

Dim X As Integer
' ...

Type myType
  X As Integer
  Y As Single
  Z As String
End Type
' ...

Type TheNewType As myType
' ...

Open "test" For Input As #1
' ...

Differences from QB

  • The Type (Alias) syntax was not supported in QB.

See also

  • Declare
  • Dim
  • Type
  • Open


Asc
Returns the corresponding ASCII or Unicode integer representation of a character.

Syntax

Declare Function Asc ( ByRef str As Const String, ByVal position As Integer = 1 ) As ULong
Declare Function Asc ( ByVal str As Const ZString Ptr, ByVal position As Integer = 1 ) As ULong
Declare Function Asc ( ByVal str As Const WString Ptr, ByVal position As Integer = 1 ) As ULong

Usage

result = Asc( str [, position ] )

Parameters

str
The source string.
position
The position in the string of a character.

Return Value

The raw character value stored at position in str.
If both str and position can be evaluated at compile time (like Asc("a") or Asc(chr(97)) or Asc("abc", 2) ...), the value is returned in a UInteger result, otherwise in a ULong result.

Description

If str is a String or a ZString, the UByte value at that position is returned. This will be a 7-bit ASCII code, or even a 8-bit character value from some code-page, depending on the string data stored in str.

If str is a WString, the UShort (Windows) or ULong (Linux) value at that position is returned. This will be a 16bit value on Windows (WStrings use UTF16 there), or a 32bit value on Linux (WStrings use UTF32 there).

The function returns zero (0) if the string is a zero length string, position is less than one (1), or position is greater than the number of characters in str.

Chr performs the opposite function for ASCII strings, while WChr is the opposite for Unicode strings, returning a string containing the character represented by the code passed as an argument.

Example

Print "the ascii code of 'a' is:"; Asc("a")
Print "the ascii code of 'b' is:"; Asc("abc", 2)

will produce the output:
the ascii code of 'a' is: 97
the ascii code of 'b' is: 98
Unicode example (Note to documentation editors: don't put inside %%(qbasic) markers or the Russian text will disappear!)
will produce the output:
dim a as wstring * 12
a = "Привет, мир"
print "the Unicode of the second char of " & a & " is: " & asc(a, 2)
Platform Differences

  • DOS does not support the wide-character string version of Asc.

Differences from QB

  • The optional position argument is new to FreeBASIC.
  • QB does not support the wide-character string version of Asc

See also

  • ASCII Character Codes
  • Chr
  • Str
  • Val

the Unicode of the second char of Привет, мир is: 1088


Asin
Finds the arcsine of a number

Syntax

Declare Function Asin ( ByVal number As Double ) As Double

Usage

result = Asin( number )

Parameters

number
Sine value in the range [-1..1].

Return Value

The arcsine of number, in radians, in the range [-Pi/2..Pi/2].

Description

Asin returns the arcsine of the argument number as a Double within the range of -Pi/2 to Pi/2. The arcsine is the inverse of the Sin function. The returned angle is measured in radians (not degrees).

Asin can be overloaded as operator to accept user-defined types.

Example

Dim h As Double
Dim o As Double
Input "Please enter the length of the hypotenuse of a triangle: ", h
Input "Please enter the length of the opposite side of the triangle: ", o
Print ""
Print "The angle between the sides is"; Asin ( o / h )
Sleep

The output would look like:
Please enter the length of the hypotenuse of a triangle: 5
Please enter the length of the opposite side of the triangle: 3
The angle between the sides is 0.6435011087932844

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Asin.

Differences from QB

  • New to FreeBASIC

See also

  • Sin
  • A Brief Introduction To Trigonometry


Asm
Code block that allows the use of architecture-specific instructions.

Syntax

Asm
architecture-dependent instructions
End Asm

Or

Asm architecture-dependent instructions

Description

The Asm block is used to insert specific machine-code instructions in a program in order to perform operations that cannot be carried out using the features of the language or to hand-optimize performance-sensitive sections of code.

The current FreeBASIC compiler currently only produces code for Intel 80x86-based machines; however, in the future, the compiler might be ported to a platform which does not support the same instruction set. Therefore, Asm blocks should only be used when necessary, and a FreeBASIC-only alternative should be provided if possible.

The return value of a function may be set by using the Function keyword within brackets as shown in the example below.

Asm block comments have the same syntax as usual FreeBASIC Comments - use FreeBASIC-like " ' " comments, not " ; " as usual in assembly code.

x86 Specific:

Syntax
The syntax of the inline assembler is a simplified form of Intel syntax. Intel syntax is used by the majority of x86 assemblers, such as MASM, TASM, NASM, YASM and FASM. In general, the destination of an instruction is placed first, followed by the source. Variables and functions defined by a program may be referenced in an Asm block. The assembler used by FreeBASIC is GAS, using the .intel_syntax noprefix directive, and Asm blocks are passed through unmodified, except for the substitution of local variable names for stack frame references, and commenting removal.

Instruction syntax is mostly the same as FASM uses, one important difference is that GAS requires size settings to be followed by the word "ptr".

' Assuming "n" is a FB global or local ULONG variable
mov  eax, [n]        ' OK: size is apparent from eax
inc  [n]             ' Not OK: size is not given
inc  dword [n]       ' Not OK: size given, but still not accepted by GAS
inc  dword Ptr [n]   ' OK: "ptr" is needed by GAS here

Register Preservation
When an Asm block is opened, the registers ebx, esi, and edi are pushed to the stack, when the block is closed, these registers are popped back from the stack. This is because these registers are required to be preserved by most or all OS's using the x86 CPU. You can therefore use these registers without explicitly preserving them yourself. You should not change esp and ebp, since they are usually used to address local variables.
Note: Inside a Naked procedure, there is no such register preservation.

Register Names
The names of the registers for the x86 architecture are written as follows in an Asm block:
      • 4-byte integer registers: eax, ebx, ecx, edx, ebp, esp, edi, esi
      • 2-byte integer registers: ax, bx, cx, dx, bp, sp, di, si (low words of 4-byte e- registers)
      • 1-byte integer registers: al, ah, bl, bh, cl, ch, dl, dh (low and high bytes of 2-byte -x registers)
      • Floating-point registers: st(0), st(1), st(2), st(3), st(4), st(5), st(6), st(7)
      • MMX registers (aliased onto floating-point registers): mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7
      • SSE registers: xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7

Instruction Set
See these external references:
      • Original Intel 80386 manual from 1986
      • Latest Intel Pentium 4 manuals
      • NASM x86 Instruction Reference (Please note that NASM is not the assembler used by FreeBASIC, but this page provides a good overview of x86 instructions)

Unsafe instructions
Note that the FreeBASIC compiler produces 32-bit protected-mode code for the x86 which usually runs in an unprivileged user level; therefore, privileged and sensitive instructions will assemble fine, but possibly won't work correctly or cause a runtime "General Protection Fault", "Illegal instruction", or SIGILL error. The following are the privileged and sensitive instructions as of the Intel Pentium 4 and Xeon:
      • cli *1
      • clts
      • hlt
      • in *1
      • ins *1
      • int *1
      • into *1
      • invd
      • invlpg
      • lgdt
      • lidt
      • lldt
      • lmsw
      • ltr
      • mov to/from CRn, DRn, TRn
      • out *1
      • outs *1
      • rdmsr
      • rdpmc *2
      • rdtsc *2
      • sti *1
      • str
      • wbinvd
      • wrmsr
      • all SSE2 and higher instructions *2

*1: sensitive to IOPL, fine in DOS
*2: sensitive to permission bits in CR4, see below

The privileged instructions will work "correctly" in DOS when running on a Ring 0 DPMI kernel, like the (non-default) Ring 0 version of CWSDPMI, WDOSX or D3X, nevertheless most of them are not really useful and dangerous when executed from DPMI code. RDTSC (Read Time Stamp Counter) has been shown to be allowed by most, or all OS'es.

However the usefulness of RDTSC has been diminished with the advent of multi-core and hibernating CPUs. SSE2 and higher instructions are disabled "by default" after CPU initialization, Windows and Linux usually do enable them, in DOS it is business of the DPMI host: HDPMI32 will enable them, CWSDPMI won't. The INT instruction is usable in the DOS version/target only, note that it works slightly differently from real mode DOS, see also FaqDOS.

The segment registers (cs, ds, es, fs, gs) should not be changed from an Asm block, except in certain cases with the DOS port (note that they do NOT work the same way as in real-mode DOS, see also FaqDOS). The operating system or DPMI host is responsible for memory management; the meaning of segments (selectors) in protected mode is very different from real-mode memory addressing.

Note that those "unsafe" instructions are not guaranteed to raise a "visible" crash even when ran with insufficient privilege - the OS or DPMI host can decide to "emulate" them, either functionally (reading from some CRx works under HDPMI32), or "dummy" (nothing happens, instruction will pass silently, like a NOP).

Example

'' This is an example for the x86 architecture.
Function AddFive(ByVal num As Long) As Long
    Asm
        mov eax, [num]
        Add eax, 5
        mov [Function], eax
    End Asm
End Function

Dim i As Long = 4

Print "4 + 5 ="; AddFive(i)

4 + 5 = 9

FreeBASIC's Assembler is AS / GAS, the assembler of GCC, so an external program. Some quirks apply:
    • The error lines returned by FBC for Asm blocks are not related the FB source file. As FBC simply displays the errors returned by AS , the lines are related to the assembly file. To make FreeBASIC preserve them, the compiler must be invoked with the -R option ("don't delete ASM files").
    • The label names are case sensitive inside Asm blocks.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Asm.

Differences from QB

  • New to FreeBASIC

See also

  • Function
  • Naked


Assert
Debugging macro that halts program execution if an expression is evaluated to 0 (false).

Syntax

#define Assert(expression) If (expression) = 0 Then : fb_Assert( __FILE__, __LINE__, __FUNCTION__, #expression ) : End If

Usage

Assert( expression )

Parameters

expression
Any valid conditional/numeric expression. If expression evaluates to 0 (i.e. "false"), execution is halted.

Description

The Assert macro is intended for use in debugging and works only if the -g or -eassert option is specified on the fbc command line. In this case it prints an error message and stops the program execution if expression evaluates to 0.

Its normal use is to check the correct value of the variables or expressions during debugging.

If -g and -eassert are not passed to fbc, the macro does not generate any code, and has no effect.

Note: If an Assert fails while the program is in a graphics Screen (Graphics), the error message will not be visible as it will be printed to the graphics screen, which will be closed immediately after.

Example

Sub foo
 Dim a As Integer
 a=0
 Assert(a=1)
End Sub

foo

'' If -g or -eassert is used, this code stops with: test.bas(3): assertion failed at FOO: a=1

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __ASSERT.

Differences from QB

  • New to FreeBASIC

See also

  • #assert
  • AssertWarn
  • Compiler Option: -eassert
  • Compiler Option: -g


AssertWarn
Debugging macro that prints a warning if an expression evaluates to 0.

Syntax

#define AssertWarn(expression) If (expression) = 0 Then : fb_AssertWarn( __FILE__, __LINE__, __FUNCTION__, #expression ) : End If

Usage

AssertWarn( expression )

Parameters

expression
Any valid expression. If expression evaluates to 0, a warning message is printed to stderr (console).

Description

The AssertWarn macro is intended for use in debugging and works only if the -g option is selected in the FBC command line. In this case it prints a warning message if expression evaluates to 0. It doesn't stop the program execution like Assert does.

Its normal use is to check the correct value of the variables during debugging.

If -g is not passed to fbc, the macro does not generate any code.

Example

Sub foo
  Dim a As Integer
  a=0
  AssertWarn(a=1)
End Sub

foo

'' If -g is used this code prints: test.bas(3): assertion failed at FOO: a=1

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __ASSERTWARN.

Differences from QB

  • New to FreeBASIC

See also

  • Assert


Atan2
Returns the arctangent of a ratio

Syntax

Declare Function Atan2 ( ByVal y As Double, ByVal x As Double ) As Double

Usage

result = ATan2( y, x )

Parameters

y
Vertical component of the ratio.
x
Horizontal component of the ratio.

Return Value

The angle whose tangent is y/x, in radians, in the range [-Pi..Pi].

Description

ATan2 returns the arctangent of the ratio y/x as a Double within the range of -Pi to Pi. The arctangent is the inverse of the Tan function. The returned angle is measured in radians (not degrees).

ATan2 cannot be overloaded as operator to accept user-defined types.

Example

Print Atan2 ( 4, 5 )     'this is the same as PRINT ATN ( 4 / 5 )

The output would be:
0.6747409422235527

Differences from QB

  • New to FreeBASIC

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Atan2.

See also

  • Tan
  • Atn
  • A Brief Introduction To Trigonometry


Atn
Returns the arctangent of a number

Syntax

Declare Function Atn ( ByVal number As Double ) As Double

Usage

result = Atn( number )

Parameters

number
A number.

Return Value

The angle, in radians, whose tangent is number, in the range [-Pi/2..Pi/2].

Description

Atn returns the arctangent of the argument number as a Double within the range of -Pi/2 to Pi/2. The arctangent is the inverse of the Tan function. The returned angle is measured in radians (not degrees).

Atn can be overloaded as operator to accept user-defined types.

Example

Print "Pi ="; Atn ( 1.0 ) * 4
Print Atn ( 4 / 5 )

The output would be:
Pi = 3.141592653589793
0.6747409422235527

Differences from QB

  • None

See also

  • Tan
  • Atan2
  • A Brief Introduction To Trigonometry


BALANCE
Sets or returns master stereo balance.

Syntax

Balance position
position = Balance()

Description

BALANCE moves the whole sfxlib mix left or right. It is a master stereo control, so it affects the combined output rather than one channel.

A centered value keeps the left and right sides even. Moving the value in one direction makes the whole mix lean toward that speaker. This can be useful for accessibility, diagnostics, or simple stereo effects.

Calling BALANCE as a function returns the current master balance.

Example

balance 0.0
print "balance="; balance()

Notes

  • Use PAN when only one channel should move left or right.

See also

  • Sound Library Reference
  • Generated Sound Commands


Base (Initializer)
Specifies an initializer for the base UDT in derived Udt constructors

Syntax

Base ( constructor-parameters... )
or:
Base ( UDT-initializers... )

Description

The Base initializer can be used at the top of constructors of derived UDTs. It allows to specify an explicit constructor call or UDT initializers to be used to initialize the base object. It will replace the implicit default initialization, and must appear above any other statements in the constructor it is used in.

Note: Unlike "Base( )", a "Base (Member Access).Constructor( )" statement does not replace the implicit default initialization done by the constructor of a derived UDT, and can usually not be used legally, because it would result in two constructor calls for the base object (for an inheritance structure extending the built-in Object type, this second base constructor call may also corrupt the vtable pointer to point to the base-type vtable instead of to the type vtable).

Example

Type SimpleParent
    As Integer a, b, c
End Type

Type Child Extends SimpleParent
    Declare Constructor( )
End Type

Constructor Child( )
    '' Simple UDT initializer
    Base( 1, 2, 3 )
End Constructor

Type ComplexParent
    As Integer i
    Declare Constructor( ByVal As Integer = 0 )
End Type

Constructor ComplexParent( ByVal i As Integer = 0 )
    this.i = i
End Constructor

Type Child Extends ComplexParent
    Declare Constructor( )
    Declare Constructor( ByRef As Child )
End Type

Constructor Child( )
    '' Base UDT constructor call
    Base( 1 )
End Constructor

Constructor Child( ByRef rhs As Child )
    '' Base UDT constructor call
    Base( rhs.i )
End Constructor

Dialect Differences

  • Methods are only supported in the -lang fb dialect, hence Base has no function in other dialects.

Differences from QB

  • New to FreeBASIC

See also

  • Base (Member Access)
  • This
  • Type
  • Extends
  • Extends Zstring
  • Extends Wstring
  • Option Base


Base (Member Access)
Provides explicit access to base type members in non-static methods of a Type

Syntax

Base.member
Base [ .Base ... ] .member

Description

Base provides a way to explicitly access members of a specific base type, in the context of non-static methods of a user-defined type derived from another type using Extends.

By using Base repeatedly, as in base.base.base.member, it is possible to access any desired base type, in case there are multiple levels of inheritance.

Base is especially useful when a base type's member is shadowed by a local variable or member of a derived type using the same identifier. Base then allows unambiguous access to the base type.

For virtual methods, base.method() always calls the base method and never the overriding method.

Note: There is no specific syntax with Base to access a member operator of a specific base type. The only way is to apply the operator on the instance beforehand up-casted to the right type (but for virtual operators, this workaround does not allow to call the base operator when overridden, because that does not modify the run-time type of the instance but only its compile-time type).

Example

Type Parent
    As Integer a
    Declare Constructor(ByVal As Integer = 0)
    Declare Sub show()
End Type

Constructor Parent(ByVal a As Integer = 0)
    This.a = a
End Constructor

Sub Parent.show()
    Print "parent", a
End Sub

Type Child Extends Parent
    As Integer a
    Declare Constructor(ByVal As Integer = 0)
    Declare Sub show()
End Type

Constructor Child(ByVal a As Integer = 0)
    '' Call base type's constructor
    Base(a * 3)
    This.a = a
End Constructor

Sub Child.show()
    '' Call base type's show() method, not ours
    Base.show()

    '' Show both a fields, the base type's and ours'
    Print "child", Base.a, a
End Sub

Type GrandChild Extends Child
    As Integer a
    Declare Constructor(ByVal As Integer = 0)
    Declare Sub show()
End Type

Constructor GrandChild(ByVal a As Integer = 0)
    '' Call base type's constructor
    Base(a * 2)
    This.a = a
End Constructor

Sub GrandChild.show()
    '' Call base type's show() method, not ours
    Base.show()

    '' Show both a fields, the base.base type's, the base type's and ours'
    Print "grandchild", Base.Base.a, Base.a, a
End Sub

Dim As GrandChild x = GrandChild(3)
x.show()

Dialect Differences

  • Methods are only supported in the -lang fb dialect, hence Base has no function in other dialects.

Differences from QB

  • New to FreeBASIC

See also

  • Base (Initializer)
  • This
  • Type
  • Extends
  • Extends Zstring
  • Extends Wstring
  • Option Base


Beep
Produces a beep sound.

Syntax

Declare Sub Beep ( )
Declare Sub Beep ( ByVal duration As Single, ByVal pitch As Single )

Usage

Beep
Beep duration, pitch

Parameters

duration
Duration in seconds.
pitch
Pitch as semitones from middle C, matching the sfxlib pitch form.

Description

Beep tells the system to sound a beep noise. The no-argument form keeps the traditional console behavior. The two-argument form uses sfxlib and produces a generated note with an explicit duration and pitch.

For more control over generated sound, use the sfxlib commands such as Sound, Tone, Note, and Play.

Example

Beep

Differences from QB

  • In QB, this was a single tone noise generated through the PC speaker. Now this might not be the case.

See also

  • Out - producing sound using CPU ports
  • Sound Library Reference
  • Generated Sound Commands


Bin
Returns a binary (base 2) string representation of an integer

Syntax

Declare Function Bin ( ByVal number As UByte ) As String
Declare Function Bin ( ByVal number As UShort ) As String
Declare Function Bin ( ByVal number As ULong ) As String
Declare Function Bin ( ByVal number As ULongInt ) As String
Declare Function Bin ( ByVal number As Const Any Ptr ) As String

Declare Function Bin ( ByVal number As UByte, ByVal digits As Long ) As String
Declare Function Bin ( ByVal number As UShort, ByVal digits As Long ) As String
Declare Function Bin ( ByVal number As ULong, ByVal digits As Long ) As String
Declare Function Bin ( ByVal number As ULongInt, ByVal digits As Long ) As String
Declare Function Bin ( ByVal number As Const Any Ptr, ByVal digits As Long ) As String

Usage

result = Bin[$]( number [, digits ] )

Parameters

number
A number or expression evaluating to a number. A floating-point number will be converted to a LongInt.
digits
Desired number of digits in the returned string.

Return Value

A string containing the unsigned binary representation of number.

Description

Returns a string representing the unsigned binary value of the integer number. Binary digits range from 0 to 1.

If you specify digits > 0, the result string will be exactly that length. It will be truncated or padded with zeros on the left, if necessary.

The length of the string will not go longer than the maximum number of digits required for the type of number (32 for a Long, 64 for a LongInt).

If you want to do the opposite, i.e. convert an binary string back into a number, the easiest way to do it is to prepend the string with "&B", and convert it to an integer type, using a function like CInt, similarly to a normal numeric string. E.g. CInt("&B101")

Example

Print Bin(54321)
Print Bin(54321, 5)
Print Bin(54321, 20)

will produce the output:

1101010000110001
10001
00001101010000110001
Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Bin.
  • The string type suffix "$" is ignored in the -lang fblite dialect, always warn.
  • The string type suffix "$" is ignored in the -lang fb dialect, always warn.

Differences from QB

  • New to FreeBASIC

See also

  • Oct
  • Hex
  • ValInt
  • ValLng


Binary
Specifies file or device to be opened for binary mode

Syntax

Open filename for Binary [Access access_type] [Lock lock_type] as [#]filenum

Parameters

filename
file name to open
access_type
indicates whether the file may be read from, written to or both
lock_type
locking to be used while the file is open
filenum
unused file number to associate with the open file

Description

Opens a file or device for reading and/or writing binary data in the file filenum, with free format.
If the file does not exist, a new file will be created. The file pointer is initialized by Open at byte no. 1.
Get # and Put # file operations move the file pointer according to the size of the data, the pointer can be set to any byte in the file.
The data existing in the file is preserved by Open.
This file mode can use any buffer variable to read/write data in the file.
The data is saved in binary mode, in the same internal format FreeBASIC uses, by means of Get # and Put #.

filename must be a string expression resulting in a legal file name in the target OS, without wildcards. The file will be sought for in the present directory, unless a path is given.

Access_type By default Binary mode allows to both read and write the file, unless an Access type is specified, it must be one of:
    • Read - the file is opened for input only
    • Write - the file is opened for output only
    • Read Write - the file is opened for input and output (the default)

Lock_type indicates the way the file is locked for other processes (users or threads), it is one of:
    • Shared - The file can be freely accessed by other processes
    • Lock Read - The file can't be opened simultaneously for reading
    • Lock Write - The file can't be opened simultaneously for writing
    • Lock Read Write - The file cannot be opened simultaneously by other processes.
If no lock type is stated, the file will be Shared for other threads of the program and Lock Read Write for other programs.
Lock and Unlock can be used to restrict temporally access to parts of a file.

filenum is a valid file number (in the range 1..255) not being used for any other file presently open. The file number identifies the file for the rest of file operations. A free file number can be found using the FreeFile function.

Example

'' Create a binary data file with one number in it
Dim x As Single = 17.164

Open "MyFile.Dat" For Binary As #1
  '' put without a position setting will put from the last known file position
  '' in this case, the very beginning of the file.
  Put #1, , x
Close #1

'' Now read the number from the file
Dim x As Single = 0

Open "MyFile.Dat" For Binary As #1
  Get #1, , x
Close #1

Print x

'' Read entire contents of a file to a string
Dim txt As String

Open "myfile.txt" For Binary Access Read As #1
  If LOF(1) > 0 Then
    '' our string has as many characters as the file has in bytes
    txt = String(LOF(1), 0)
    '' size of txt is known.  entire string filled with file data
    Get #1, , txt
  End If
Close #1

Print txt

Differences from QB

  • None

See also

  • Open
  • Put #
  • Get #
  • Random
  • Append
  • Output
  • Input


Bit
Gets the state of an individual bit in an integer value.

Syntax

#define Bit( value, bit_number ) (((value) And (Cast(TypeOf(value), 1) Shl (bit_number))) <> 0)

Usage

result = Bit( value, bit_number )

Parameters

value
The integer value.
bit_number
The index of the bit.

Return Value

Returns an Integer value of -1 if the bit is set, or 0 if the bit is cleared.

Description

This macro expands to an integer value indicating whether or not the bit specified by bit_number is set in the integer value. Behaves as `(value And 1 Shl bit_number) <> 0`.

The valid range of values for bit_number depends on the size, in bits, of `TypeOf(value)`, which is `0` (from the lowest bit) through `SizeOf(value) * 8 - 1` (up to the highest bit). See Standard Datatype Limits for a table of the standard datatypes and their sizes.
For the bit_number values outside the valid range, the results of this macro are undefined.

Example

Print Bit(&B1000, 3)
Print Bit(4,2)
Print Bit(5,1)
Print Bit(&H8000000000000000ULL,63)

will produce the output:

-1
-1
 0
-1

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Bit.

Differences from QB

  • New to FreeBASIC

See also

  • BitSet
  • BitReset


BitReset
Gets the value with a specified bit cleared, from a copied integer.

Syntax

#define BitReset( value, bit_number ) ((value) And Not (Cast(TypeOf(Value), 1) Shl (bit_number)))

Usage

result = BitReset( value, bit_number )

Parameters

value
The integer value.
bit_number
The index of the bit to clear.

Return Value

Returns the integer value with the specified bit cleared.

Description

This macro expands to a copy of the integer value with the specified bit_number cleared (to off, or `0`). Behaves as `value And Not (1 Shl bit_number)`.
To clear a specified bit in a variable, the following assignment can be used: variable = BitReset( variable, bit_number )

The valid range of values for bit_number depends on the size, in bits, of `TypeOf(value)`, which is `0` (from the lowest bit) through `SizeOf(value) * 8 - 1` (up to the highest bit). See Standard Datatype Limits for a table of the standard datatypes and their sizes.
For the bit_number values outside the valid range, the results of this macro are undefined.

Example

Print Bin(BitReset(&b10101, 2))
Print BitReset(5,0)
Print Hex(BitReset(&h8000000000000001,63))

will produce the output:

10001
 4
1

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Bitreset.

Differences from QB

  • New to FreeBASIC.

See also

  • Bit
  • BitSet


BitSet
Gets the value with a specified bit set, from a copied integer.

Syntax

#define BitSet( value, bit_number ) ((value) Or (Cast(TypeOf(Value), 1) Shl (bit_number)))

Usage

result = BitSet( value, bit_number )

Parameters

value
The integer value.
bit_number
The index of the bit to set.

Return Value

Returns the integer value with the specified bit set.

Description

This macro expands to a copy of the integer value with the specified bit_number set (to on, or `1`). Behaves as `value Or (1 Shl bit_number)`.
To set a specified bit in a variable, the following assignment can be used: variable = BitSet( variable, bit_number )

The valid range of values for bit_number depends on the size, in bits, of `TypeOf(value)`, which is `0` (from the lowest bit) through `SizeOf(value) * 8 - 1` (up to the highest bit). See Standard Datatype Limits for a table of the standard datatypes and their sizes.
For the bit_number values outside the valid range, the results of this macro are undefined.

Example

Print Bin(BitSet(&b10001,2))
Print BitSet(4, 0)
Print Hex(BitSet(1ull, 63))

will produce the output:

10101
 5
8000000000000001

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Bitset.

Differences from QB

  • New to FreeBASIC.

See also

  • Bit
  • BitReset


BLoad
Loads arbitrary data from a file created with BSave, or a compatible BMP image file.

Syntax

Declare Function BLoad ( ByRef filename As Const String, ByVal dest As Any Ptr = 0, ByVal pal As Any Ptr = 0 ) As Long

Usage

result = BLoad( filename [, [ dest ] [, pal ] ] )

Parameters

filename
the name of the file to load the image from; can include a file path
dest
the memory location to load the image to, or null (0) to copy the image to the current graphics screen work page
pal
the memory location to load the palette to, or null (0) to change the current graphics screen palette, if it uses one

Return Value

Returns zero (0) if successful, or a non-zero error code to indicate a failure. (throws a runtime error)

Description

BLoad can be used to load image data or any other data from a file created with BSave, and store that data in an array or paste it to the screen. If dest is absent or null (0), the image data is pasted to the current graphics screen work page. Otherwise it is loaded as image data to the address given by dest.
BLoad must be called only if a graphics mode is initialized, else the program crashes (see BLOAD/BSAVE text mode work-around to work in text mode).

BLoad can load 3 different types of files:
    • Old QB-like data files, saved with BSAVE from QB code, containing "raw" data preceded by a 7-byte header, beginning with &HFD, up to 64 KiB in size
    • New FB-like data files, saved with BSave from FB code, containing "raw" data preceded by a 5-byte header, beginning with &HFE. There is no 64 KiB limit with this format
    • BMP image files, supports a subset of valid ("Windows") .BMP files, beginning with "BM", saved from FB code with BSave, or created / saved in a compatible format using a graphics editor / converter.
QB-like data files and BMP files are converted to an FB-compatible image format when opened.

Image files with 8-bit per pixel resolution or lower contain a palette that describes the color values used in the images. If pal is not null (0), the palette is copied to memory starting at the address specified. Otherwise, if the current graphics screen uses a palette then its palette is changed to match that of the image file.

When using one of the 2 "non-BMP" file formats to save images, the image files must have been created with BSave in the same graphics screen mode as it is being loaded into. When using the BMP file format, this restriction doesn't apply.

When loading a BMP file using BLoad, the images must be true-color (15-, 16-, 24- or 32-bits per pixel) or palettized/indexed (8-bit or lower). The image data will be converted to the proper pixel format for the current color depth, except that true-color can't be reduced to a palettized image. BLoad doesn't support BMP files using RLE compression or other image file types (PNG, JPG, GIF, ...). BLoad will load alpha channel information, if available, from 32-bit BMP files with BITMAPV4HEADER or BITMAPV5HEADER file headers.

The error code returned by BLoad can be checked using Err in the next line. The function version of BLoad returns directly the error code as a 32 bit Long.

Runtime errors:
BLoad throws one of the following runtime errors:
(1) Illegal function call
      • dest was not specified or was null (0), and no graphics screen was set.
      • The Bitmap uses an unsupported BMP file compression type (BI_RLE4, BI_RLE8)
      • The Bitmap is true-color (16, 24, or 32 bits per pixel) and the current graphics screen uses a palette (8 bits per pixel or lower).
(2) File not found
      • The file filename could not be found.
(3) File I/O error
      • The file doesn't have any of the supported types
      • A general read error occurred.
Note: When you use BLoad to load a BMP file into an image buffer, the original dimensions of the image are not changed. If you want the image buffer to have the same dimensions as the BMP file, you have to find out the dimensions beforehand, and create an image of the right size yourself. See the example below for an example of how to do this.

Example

'Load a graphic to current work page
Screen 18, 32
Cls
BLoad "picture.bmp"
Sleep

'Load a 48x48 bitmap into an image
ScreenRes 320, 200, 32
Dim myImage As Any Ptr = ImageCreate( 48, 48 )
BLoad "picture.bmp", myImage
Put (10,10), myImage
ImageDestroy( myImage )
Sleep

ScreenRes 640, 480, 8 '' 8-bit palette graphics mode
Dim pal(0 To 256-1) As Integer '' 32-bit integer array with room for 256 colors

'' load bitmap to screen, put palette into pal() array
BLoad "picture.bmp", , @pal(0)

WindowTitle "Old palette"
Sleep

'' set new palette from pal() array
Palette Using pal(0)

WindowTitle "New palette"
Sleep

'' A function that creates an image buffer with the same
'' dimensions as a BMP image, and loads a file into it.

Const NULL As Any Ptr = 0

Function bmp_load( ByRef filename As Const String ) As Any Ptr

    Dim As Long filenum, bmpwidth, bmpheight
    Dim As Any Ptr img

    '' open BMP file
    filenum = FreeFile()
    If Open( filename For Binary Access Read As #filenum ) <> 0 Then Return NULL

        '' retrieve BMP dimensions
        Get #filenum, 19, bmpwidth
        Get #filenum, 23, bmpheight

    Close #filenum

    '' create image with BMP dimensions
    img = ImageCreate( bmpwidth, Abs(bmpheight) )

    If img = NULL Then Return NULL

    '' load BMP file into image buffer
    If BLoad( filename, img ) <> 0 Then ImageDestroy( img ): Return NULL

    Return img

End Function

Dim As Any Ptr img

ScreenRes 640, 480, 32

img = bmp_load( "picture.bmp" )

If img = NULL Then
    Print "bmp_load failed"

Else

    Put (10, 10), img

    ImageDestroy( img )

End If

Sleep

Differences from QB

  • Support for loading BMP files is new to FreeBASIC.
  • Support for retrieving the palette from BMP files is new to FreeBASIC.
  • FreeBASIC uses a different file format from QBASIC internally, which doesn't have the 64 KiB limit, and is unsupported by QBASIC.

See also

  • BSave
  • Palette
  • ImageCreate
  • ImageDestroy
  • Internal Graphics Formats


Boolean
Standard data type

Syntax

Dim variable As Boolean

Description

Boolean data type. Can hold the values True or False
Default value on initialization is False

Notes on definition of boolean data type:
- Ideally, the definition of the boolean data type is that it holds the value of True or False, and that's it. However, to make this concept a reality, we need a definition that uses real world connections.
- A more realistic definition is that the boolean data type is a 1-bit integer, having the value 0 to indicate False and 1 to indicate True.
- For a practical definition, we must consider, yet again, additional factors. The most significant factor is that the hardware (processor) on which code is executed does not directly support a 1-bit data type; the smallest register or memory size we can work with is 8-bits or 1-byte.
- Assume "false" is 0 in both C/C++ and FB. C/C++ has logical 'not' operator '!' such that '!0' produces '1'. FB has a bitwise Not operator such that 'not 0' produces '-1'.
- Nevertheless the definition under the hood for a FB boolean remains an 1-bit integer, zero extended to fill larger integer types.
- Therefore when assigning a boolean with an integer value (by implicit conversion and not with the False or True value), '0' induces the False state and '1' or '-1' induces the True state (any other value also induces the True state, but with a warning message).
- Otherwise when assigning a numeric type with a boolean (by implicit conversion), False induces the '0' value and True induces the '-1' value.
- However, the purpose and intent of the boolean data type remains, that it should only ever hold a True value or False value, regardless of the underlying details.

Example

Dim boolvar As Boolean
boolvar = True
Print "boolvar = ", boolvar

Output:
boolvar =     true
Version

  • Since fbc 1.04.0

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Boolean.

Differences from QB

  • New to FreeBASIC

See also

  • CBool
  • True
  • False
  • Table with variable types overview, limits and suffixes


BSave
Saves an array of arbitrary data and palette information to a file on disk

Syntax

Declare Function BSave ( ByRef filename As Const String, ByVal source As Any Ptr, ByVal size As ULong = 0, ByVal pal As Any Ptr = 0, ByVal bitsperpixel As Long = 0 ) As Long

Usage

result = BSave( filename, source [,[ size ][,{ pal | pal, bitsperpixel }]] )

Parameters

filename
the name of the file to create for storing the pixel and palette data.
source
the address of the data to store, or null (0) to store pixel data from the current screen work page.
size
optional, the total number of bytes of data to store. This value is needed unless the output is a BMP file.
pal
optional, the address of a buffer holding 256 Palette colors, or null (0) for the current screen palette.
bitsperpixel
optional, a requested bit depth for the output BMP image. If bitsperpixel is specified, pal must also be specified (at a least null (0) value), otherwise an error is obtained.

Return Value

Returns zero (0) if successful, or a non-zero error code to indicate a failure. (throws a runtime error)

Description

BSave is used for saving arbitrary data from memory into a file, using a file format specific to FB, or saving images into a standard BMP image file, replacing an existing file if necessary.
BSave must be called only if a graphics mode is initialized, else the program crashes (see BLOAD/BSAVE text mode work-around to work in text mode).

BSave outputs a total of size bytes of arbitrary data located at source to a specified file. If source is null (0), then BSave outputs a maximum of size bytes from the current work page's pixel buffer, which is structured in the current screen mode's internal pixel format. (This data is not compatible with the image buffer format as it has no header.) For 8-bit images, palette information is obtained from pal if present and non-null, or if pal omitted or null (0), from the current screen palette.

A BMP image file can be created if filename has a file extension of ".bmp" (case insensitive). source is assumed to point to a valid image buffer whose entire pixel data will be stored in the BMP file. If source is null (0), the contents of the current work page will be stored instead. For 8-bit images, palette information is obtained from pal if non-null, or if null (0), from the current screen palette. The size parameter is ignored when saving BMP files.

The default bit depth for BMP files is 8-bit for 8-bit (palette) images, 24-bit for 16-bit images, and 32-bit for 32-bit images. The bitsperpixel parameter can be used to request 24-bit output for 8-bit images, or 24-bit output for 32-bit images.

The error code returned by BSave can be checked using Err in the next line. The function version of BSave returns directly the error code as a 32 bit Long.

Runtime errors:
BSave throws one of the following runtime errors:

(1) Illegal function call
    • size is less than zero (0), or size is zero and source is non-null, or a problem is detected with the image buffer.
(2) File not found
    • The file could not be created.
(3) File I/O error
    • The file could not be written to.

Example

' Set gfx mode
ScreenRes 320, 200, 32

' Clear with black on white
Color RGB(0, 0, 0), RGB(255, 255, 255)
Cls

Locate 13, 15: Print "Hello world!"

' Save screen as BMP
BSave "hello.bmp", 0

Save image buffer to bitmap file:
'set graphics screen 640 x 480 pixels, 32 bit colors
Const W = 640, H = 480 'width & hight
ScreenRes W, H, 32
'draw a smiley at screen center
Circle (W \ 2, H \ 2), 180, &h00ffff00, , , , f 'yellow circle
Circle (W \ 2 - 55, H \ 2 - 70), 35, &h00000000, , , 1.5, f 'left eye
Circle (W \ 2 + 55, H \ 2 - 60), 35, &h00000000, , , 1.5, f 'right eye
Circle (W \ 2, H \ 2 + 80), 70, &h00000000, , , 0.4, f 'mouth
'allocate memory for image buffer
Dim As Any Ptr pImageBuffer = ImageCreate(250, 250)
'copy screen section to buffer
Get (W \ 2 - 125, H \ 2 - 125)-Step(250 - 1, 250 - 1), pImageBuffer
'save image buffer to file
Dim As String fileName = "Smiley.bmp"
If BSave(fileName, pImageBuffer) = 0 Then
    Print "Saved succesful: " + fileName
Else
    Print "Error saving: " + fileName
End If
'free memory for image buffer
ImageDestroy(pImageBuffer)
'keep graphics screen open until key press
Sleep

Differences from QB

  • Support for saving more than 64KiB of arbitrary data is new to FreeBASIC.
  • Support for saving BMP files is new to FreeBASIC.
  • QB cannot use BLoad to load files created with BSave in FreeBASIC, but FreeBASIC can use BLoad to load files created with BSave in QB

See also

  • BLoad
  • Palette


Byref (Parameters)
Declaration specifier to explicitly pass a parameter by reference

Syntax

ByRef param As datatype

Usage

[ Declare ] { Sub | Function } proc_name ( ByRef param As datatype )

Description

Passes a variable by reference, that is its address, to a subroutine or function. When a variable is passed by reference, the contents of the variable can be changed by the target subroutine or function.

In -lang qb and -lang fblite dialects, ByRef is the default parameter passing convention, unless Option ByVal is in effect.

Opposite of ByVal.

Note: A constant or a literal expression can also be passed to such a procedure (which gets by reference), but they are obviously not modifiable from the procedure body. In that case, the compiler passes by reference a temporary variable initialized with the constant or the literal expression.

Note: Since fbc version 1.20.0, arguments of STRING*N type are copied to a temporary variable when passed to BYREF ZSTRING (or BYVAL ZSTRING PTR) parameters (since there is no expectation that STRING*N will have it's own terminating null character) and copied back after the call.

Warning: When passing by reference, it is recommended to pass an argument of the same type (or fully compatible, like a derived type for example) as that of the declared parameter. Although in some cases the compiler accepts to pass a different type, often the result is not the one expected.

Example

Dim MyVar As Integer

Sub ChangeVar(ByRef AVar As Integer)
    AVar = AVar + 1
End Sub

MyVar = 1
Print "MyVar: "; MyVar 'output = 1
ChangeVar MyVar
Print "MyVar: "; MyVar 'output = 2
Sleep
End

Dialect Differences

  • In -lang fb dialect, ByVal is the default parameter passing convention for all built-in types except String and user-defined Type which are passed ByRef by default. The ZString and WString built-in types are also passed ByRef by default, but passing ByVal is forbidden. Arrays are always passed ByRef and the use of the specifier ByRef or ByVal is forbidden.
  • In -lang qb and -lang fblite dialects, ByRef is the default parameter passing convention.

Differences from QB

  • New to FreeBASIC

See also

  • Passing Arguments to Procedures
  • Declare
  • ByVal
  • Byref (Function Results)
  • Byref (Variables)


Byref (Function Results)
Specifies that a function result is returned by reference

Syntax

Function name ( parameter-list ) ByRef As datatype

Description

Causes the function result to be returned by reference, rather than by value. A function returning ByRef will return the address of a variable, instead of making a copy like when returning by value. This allows the caller of the function to modify the variable which the function result points to.

If ByRef is not specified, the default is to return the function result by value.

Functions with ByRef result should not return local variables from the function, because they will be destroyed upon returning from the function, invalidating any pointer or reference to them. To help with writing safe code, the compiler will show an error message when a local variable is used with Function = x (or name = x) assignments and Return x statements.

Note: On the left-hand side of an assignment expression using the '=' symbol, the result of the function (returned by reference) must be enclosed in parentheses when the function calls one single argument, in order to solve the parsing ambiguity. '=>' can be used for assignments, in place of '=', same as for initializers, allowing to avoid parsing ambiguity (without parentheses). As for the arguments list, it should always be surrounded with parentheses even if empty.

Operators (member or global), when used as functions, have also the capability to return results by reference, by using the same syntax.

Example

Function min( ByRef I As Integer , ByRef J As Integer ) ByRef As Integer
    '' The smallest integer will be returned by reference, no copy will be created.
    If I < J Then
        Return I
    Else
        Return J
    End If
End Function

Dim As Integer A = 13, B = 7
Print A, B
Print min( A , B )
min( A , B ) = 0
Print A, B

Function f( ) ByRef As Const ZString
    '' This string literal (because statically allocated in memory) will be returned by reference, no copy will be created.
    Function = "abcd"
End Function

Print f( )

Dim Shared As String s

Function f1( ) ByRef As String
   '' This variable-length string will be returned by reference, no copy will be created.
   Function = s
End Function

Function f2( ByRef _s As String ) ByRef As String
   '' This variable-length string will transit by reference (input and output), no copy will be created.
   Function = _s
End Function

s = "abcd"
Print s

f1( ) = f1( ) & "efgh"
Print s

'' The enclosing parentheses are required here on the left-hand side.
( f2( s ) ) = f2( s ) & "ijkl"
Print s

'' The enclosing parentheses are not required here on the left-hand side.
f2( s ) => f2( s ) & "mnop"
Print s

Function power2( ByRef _I As Integer ) ByRef As Integer
   _I *= _I
   '' This integer will be returned by reference, no copy will be created.
   Function = _I
End Function

Dim As Integer I = 2
power2( power2( power2( I ) ) )  '' Function return-byref cascading is equivalent to ((I*I)*(I*I))*((I*I)*(I*I)) = I^8
Print I

Differences from QB

  • New to FreeBASIC

See also

  • Returning values
  • Byref (Parameters)
  • Byref (Variables)


Byref (Variables)
Declares a reference

Syntax

(Dim | Static) [Shared] ByRef name1 As DataType = variable1 [, ByRef name2 As DataType = variable2, ...]
or
(Dim | Static) [Shared] ByRef As DataType name1 = variable1 [, name2 = variable2, ...]
or
[Static] Var [Shared] ByRef name1 = variable1 [, ByRef name2 = variable2, ...]

Parameters

name
reference name
variable
variable name to refer

Description

Declares a reference (by name) to a variable.

A reference is an entity that is a way to access data located in memory. A reference is not the data itself but only information about its location. A reference can be thought of as a pointer that is implicitly dereferenced. In many cases, it can be used as an alternative to pointer.

A reference must always be initialized with a variable when it is created.
DataType must be the same type as that of the variable, or a compatible type (for example one from the types of its Bases in case of inheritance):
    • Only when the two types are identical (or using the third syntax with Var), a reference can be considered as an alias of the variable. One can do the same operations through such a reference as one can do with the original variable.
    • Otherwise (types compatible but not identical), one cannot do all same operations than with the original variable:
For example, a base type reference referring to a derived type object allows to activate polymorphism when a virtual method is called on it, similarly to a base type pointer referring to a derived type object. One can do the same operations through such a reference as one can do with a dereferenced pointer of same type (but for both not the same operations as using directly the derived type instance).

A reference can be reassigned to refer to another variable (of compatible type) by doing:
@refname = @othervariable

NOTE: The arrays of references and the non-static reference fields for UDT are not supported yet.

Example

'' Comparison between:
''   - a copy ('ci') of a variable ('i')
''   - a reference ('ri') to a variable ('i')

Dim As Integer i = 12
Print @i, i

Dim As Integer ci = i  '' or Var ci = i
Print @ci, ci

Dim ByRef As Integer ri = i  '' or Var Byref ri = i
Print @ri, ri

Print

Print i, ci, ri
i = 34
Print i, ci, ri
ci = 56
Print i, ci, ri
ri = 78
Print i, ci, ri

Sleep

'' Use reference allows to simplify expressions compared to pointer
'' (avoid to use operator '@' and especially '*')

Function fp () As ZString Ptr
    Static As ZString * 256 z
    Return @z
End Function

Dim As ZString Ptr pz = fp()
*pz = "FreeBASIC Zstring Ptr"
Print *pz
*pz &= " 1.3.0"
Print *pz

Print

Function fr () ByRef As ZString
    Static As ZString * 256 z
    Return z
End Function

Dim ByRef As ZString rz = fr()  '' or Var Byref rz = fr()
rz = "FreeBASIC Zstring Ref"
Print rz
rz &= " 1.4.0"
Print rz

Sleep

'' It is possible to reassign a reference.
'' An example with an UDT to control the successive constructions & destructions of objects handled with one only reference.

Type UDT
  Declare Constructor ()
  Declare Destructor ()
  Dim As Integer I
End Type

Constructor UDT ()
  Static As Integer nb
  nb += 1
  This.I = nb
  Print "UDT.Constructor()"
End Constructor

Destructor UDT ()
  Print "UDT.Destructor()"
End Destructor

Var ByRef ru = *New UDT  '' or Dim Byref As UDT ru = *New UDT
Print ru.I
Delete @ru

Print

@ru = New UDT
Print ru.I
Delete @ru

Sleep

'' Polymorphism (by using inheritance and virtuality) can be activated through any of the 3 following kinds of entities:
''   - base-type pointers referring to derived-type objects,
''   - dereferenced base-type pointers referring to derived-type objects,
''   - base-type references referring to derived-type objects.
'
'' If in the first line of the below code, FALSE is put instead TRUE, the polymorphism by virtuality is no more activated.

#define virtuality True

Type myBase Extends Object
  #if virtuality = True
    Declare Virtual Sub hello()
  #else
    Declare Sub Hello()
  #endif
End Type

Sub myBase.hello()
  Print "myBase.hello()"
End Sub

Type myDerived Extends myBase
  Declare Sub hello()
End Type

Sub myDerived.hello()
  Print "myDerived.hello()"
End Sub

Dim As myBase mb
Dim As myBase Ptr pmb = @mb
Dim ByRef As myBase rmb = mb  '' or Var Byref rmb = mb
pmb->hello()    '' pmb is a base-type pointer referring to a base-type object
(*pmb).hello()  '' *pmb is a dereferenced base-type pointer referring to a base-type object
rmb.hello()     '' rmb is a base-type reference referring to a base-type object

Print

Dim As myDerived md
Dim As myBase Ptr pmd = @md
Dim ByRef As myBase rmd = md  '' only syntax because the reference data-type must be different from the one of object
pmd->hello()    '' pmd is a base-type pointer referring to a derived-type object
(*pmd).hello()  '' *pmd is a dereferenced base-type pointer referring to a derived-type object
rmd.hello()     '' rmd is a base-type reference referring to a derived-type object

Sleep

Version

  • Since fbc 1.04.0

Dialect Differences

  • Only supported in -lang fb, -lang fblite and -lang deprecated dialects.

Differences from QB

  • New to FreeBASIC.

See also

  • Dim
  • Static
  • Var
  • Shared
  • Byref (Parameters)
  • Byref (Function Results)


Byte
Standard data type: 8 bit signed

Syntax

Dim variable As Byte

Description

8-bit signed whole-number data type. Can hold a value in the range of -128 to 127.

Example

  Dim bytevar As Byte
  bytevar = 100
  Print "bytevar= ", bytevar

Dim x As Byte = CByte(&H80)
Dim y As Byte = CByte(&H7F)
Print "Byte Range = "; x; " to "; y

Output:
Byte Range = -128 to  127

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Byte.

Differences from QB

  • New to FreeBASIC

See also

  • UByte
  • CByte
  • Table with variable types overview, limits and suffixes


ByVal
Declaration specifier to explicitly pass a parameter by value

Syntax

ByVal param As datatype

Usage

[ Declare ] { Sub | Function } proc_name ( ByVal param As datatype )

Description

ByVal in a parameter list of a declare statement causes a copy of the variable to be passed to the procedure (for example, a sub or function) by its value.

This means that if the value of the variable x is passed, then the original variable x will not be modified in any way; however, if the variable were passed ByRef, the value of the original variable x could be modified by the called function.

Opposite of ByRef.

The ByVal keyword is also used in the context of Byref Parameters and Function Results, where it can be used to explicitly override the by-reference semantics in order to pass or assign a pointer as-is to a Byref parameter or function result. For reference:
    • Manually passing pointers to by-reference parameters
    • Manually returning pointers as-is from Byref functions

Note: ByVal (passing by copy) is good for small objects (4 or 8 bytes, a bit more will still be ok) that are easy to copy, like numeric types. Passing ByVal avoids the overhead of the pointer used by ByRef.
ByRef is better for passing huge objects like strings or big UDTs that should not be copied. Even though ByRef has some overhead because it has to pass a pointer (and dereference at each access to the object), this is still better than copying a multitude of bytes on to the stack every-time the procedure is called.

Example

Sub MySub(ByVal value As Integer)
    value += 1
End Sub

Dim MyVar As Integer

MyVar = 1
Print "MyVar: "; MyVar 'output = 1
MySub MyVar
Print "MyVar: "; MyVar 'output = 1, because byval won't change the values passed into it globally.
Sleep
End

Dialect Differences

  • In the -lang fb dialect, ByVal is the default parameter passing convention for all built-in types except String and user-defined Type which are passed ByRef by default. The ZString and WString built-in types are also passed ByRef by default, but passing ByVal is forbidden. Arrays are always passed ByRef and the use of the specifier ByRef or ByVal is forbidden.
  • In -lang qb and -lang fblite dialects, ByRef is the default parameter passing convention.

Differences from QB

  • QB only used ByVal in declarations to non-Basic subroutines

See also

  • Passing Arguments to Procedures
  • Declare
  • ByRef


Call
Statement to invoke a subroutine

Syntax

Call procname ([parameter list])

Description

Calls a Sub or Function.

This keyword is a holdover from earlier dialects of BASIC, and is mainly deprecated.

In -lang qb, it can be used to call Subs in code before they are declared. The function will be implicitly Declare'd, with any parameters passed ByRef As Any.
Note: until the function is declared, no type-checking is done on the parameters, so it is up to the programmer to ensure they are of the correct type.

Example

'' Compile with -lang qb or -lang fblite

#lang "fblite"

Declare Sub foobar(ByVal x As Integer, ByVal y As Integer)
Call foobar(35, 42)

Sub foobar(ByVal x As Integer, ByVal y As Integer)
Print x; y
End Sub

'' Compile with -lang qb or -lang fblite

#lang "fblite"

Function f ( ) As Integer
f = 42
End Function

Call f ' execute function f, but ignore the answer

'' Compile with -lang qb

'$lang: "qb"

Call mysub(15, 16) '' call "mysub" before it has been declared, or even mentioned.

Sub mysub(ByRef a As Integer, ByRef b As Integer)

    Print a, b

End Sub

Dialect Differences

  • The use of Call is not allowed in the -lang fb dialect.
  • The -lang fblite dialect does not allow you to call functions that have not been previously declared.

Differences from QB

  • The procedure must have already been declared.
  • Call in QB will make a copy of all parameters, so changes made to the arguments inside the called Sub will not be reflected in the variables in the caller.

See also

  • Declare
  • Sub


CAllocate
Allocates memory for a certain number of elements from the free store and clears the contents

Syntax

Declare Function CAllocate cdecl ( ByVal num_elements As UInteger, ByVal size As UInteger = 1 ) As Any Ptr

Usage

result = CAllocate( num_elements [, size ] )

Parameters

num_elements
The number of elements to allocate memory for.
size
The size, in bytes, of each element.

Return Value

If successful, the address of the start of the allocated memory is returned. Otherwise, the null pointer (0) is returned.

Description

CAllocate initializes the allocated memory with zeros.
Consequently, CAllocate can be also directly used with String or Udt containing string, because the string descriptor is cleared (set to 0) first.
Similarly, CAllocate can also be used directly with ZString or WString because string data is created with null characters.

Example

' Allocate and initialize space for 10 integer elements.
Dim p As Integer Ptr = CAllocate(10, SizeOf(Integer))

' Fill the memory with integer values.
For index As Integer = 0 To 9
    p[index] = (index + 1) * 10
Next

' Display the integer values.
For index As Integer = 0 To 9
    Print p[index] ;
Next

' Free the memory.
Deallocate(p)

Outputs:
 10 20 30 40 50 60 70 80 90 100
Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Callocate.

Differences from QB

  • New to FreeBASIC

See also

  • Allocate
  • Deallocate
  • Reallocate


Case
Control flow statement

Syntax

Case expression

Example

See example at Select Case.

Differences from QB

  • None

See also

  • Select Case


Cast
Converts an expression to a specified data type

Syntax

Cast( datatype, expression )

Parameters

datatype
a built-in data type (standard type)
expression
a variable of another built-in data type

Description

Converts expression into a different datatype. Useful to be used in macros when datatype is unknown and also when converting to Type Alias.

This is a general form of conversion operators such as CInt or CDbl.
Cast is more versatile because it can be used on built-in types that have a built-in Cast Operator, but don't have a built-in keyword for it: e.g. Cast( Byte, boolean ).
It is also suitable for use in cases where the type of a variable is not fixed in the code - for example, it might be Defined earlier, or may be the Type Of a different variable or expression.

Note: If you want to use an operator specifically for converting to different types of Pointers, consider using CPtr instead.

Upcasting: In an inheritance structure, the upcasting is converting a derived type reference or pointer into a base type.
In this special use, Cast applied on a derived-type instance (expression) can be used to return a base-type (datatype) reference.

By using a member Operator Cast, Cast can be overloaded for a user defined type expression.

Example

'' will print -128 because the integer literal will be converted to a signed Byte
'' (this Casting operation is equivalent to using CByte)
Print Cast( Byte, &h0080 )

'' will print 3 because the floating-point value will be converted to an Integer
'' (this Casting operator is equivalent to using CInt)
Print Cast( Integer, 3.1 )

Sleep

'' macro sizeofDerefPtr(): returns the size of the dereferenced pointer
#define sizeofDerefPtr(ptrToDeref) SizeOf(*Cast(TypeOf(ptrToDeref), 0))

'' macro typeofDerefPtr(): returns the type of the dereferenced pointer
#define typeofDerefPtr(ptrToDeref) TypeOf(*Cast(TypeOf(ptrToDeref), 0))

' Allocate dynamically memory for a Double by New
Dim As Double Ptr pd
pd = New typeofDerefPtr(pd)
*pd = 3.14159
Print *pd

' Allocate dynamically memory for a Zstring*10 by Callocate
Dim As ZString Ptr pz
pz = CAllocate(10, sizeofDerefPtr(pz))
*pz = "FreeBASIC"
Print *pz

Sleep
Delete pd
Deallocate(pz)

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Cast.

Differences from QB

  • New to FreeBASIC

See also

  • Cast (Operator)
  • CPtr
  • CInt
  • TypeOf
  • Coercion and Conversion


CAPTURE
Controls sfxlib audio capture where the active backend supports recording.

Syntax

result = Capture Start()
Capture Pause
Capture Resume
Capture Stop
status = Capture Status()
frames = Capture Available()
result = Capture Save(filename)
frames = Capture Read(samples, frames)

Description

CAPTURE records audio input through the active sfxlib backend when that backend supports recording. It is the input side of the sound library, while commands such as SOUND, MUSIC, and SFX are output commands.

Capture Start begins recording. Capture Pause temporarily stops adding new input without throwing away the capture state. Capture Resume continues after a pause. Capture Stop ends the recording session.

Capture Available reports how many frames can be read. A frame is one moment of audio across the capture channels. Capture Read copies captured samples into a Single buffer supplied by the program. Capture Save writes the buffered recording to a file.

Capture is inherently platform and device dependent. A laptop microphone, a loopback device, and a headless server all behave differently, so programs should check return values and be ready for capture to be unavailable.

Example

dim as single samples(0 to 1023)

if capture start() >= 0 then
sleep 500, 1
print "frames available="; capture available()
capture read @samples(0), 512
capture stop
else
print "capture not available"
end if

Notes

  • Use DEVICE to inspect or select the active output backend; capture support still depends on the backend and platform.
  • Capture Read expects a buffer large enough for the requested frames and channels.

See also

  • DEVICE
  • sfxlib backends
  • Sound Library Reference


CBool
Converts numeric or string expression to a boolean (Boolean)

Syntax

Declare Function CBool ( ByVal expression As datatype ) As Boolean

Type typename
Declare Operator Cast ( ) As Boolean
End Type

Usage

result = CBool( numeric expression )
result = CBool( string expression )
result = CBool( user defined type )

Parameters

expression
a numeric, string, or user defined type to cast to a Boolean value
datatype
any numeric, string, or user defined type
typename
a user defined type

Return Value

A Boolean value.

Description

The CBool function converts a zero value to False and a non-zero value to True.

The name can be explained as 'Convert to Boolean'.

If the argument is a string expression, it is converted to boolean using a case insensitive to the string "false" to return a False value or "true" to return a True value.

Example

' Using the CBOOL function to convert a numeric value

'Create an BOOLEAN variable
Dim b As Boolean

'Convert a numeric value
b = CBool(1)

'Print the result, should return True
Print b
Sleep

Version

  • Since fbc 1.04.0

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Cbool.

Differences from QB

  • New to FreeBASIC

See also

  • CByte
  • CUByte
  • CShort
  • CUShort
  • CInt
  • CUInt
  • CLng
  • CULng
  • CLngInt
  • CULngInt
  • CSng
  • CDbl
  • Str


CByte
Converts numeric or string expression to Byte.

Syntax

Declare Function CByte ( ByVal expression As datatype ) As Byte

Type typename
Declare Operator Cast ( ) As Byte
End Type

Usage

result = CByte( numeric expression )
result = CByte( string expression )
result = CByte( user defined type )

Parameters

expression
A numeric, string, or pointer expression to cast to a Byte value.
datatype
Any numeric, string, or pointer data type.
typename
A user defined type.

Return Value

A Byte value.

Description

The CByte function rounds off the decimal part and returns a 8-bit Byte value. The function does not check for an overflow, and results are undefined for values which are less than -128 or larger than 127.

The name can be explained as 'Convert to Byte'.

If the argument is a string expression, it is converted to numeric by using ValInt.

Example

' Using the CBYTE function to convert a numeric value

'Create an BYTE variable
Dim numeric_value As Byte

'Convert a numeric value
numeric_value = CByte(-66.30)

'Print the result, should return -66
Print numeric_value
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Cbyte.

Differences from QB

  • New to FreeBASIC

See also

  • CUByte
  • CShort
  • CUShort
  • CInt
  • CUInt
  • CLng
  • CULng
  • CLngInt
  • CULngInt
  • CSng
  • CDbl


CDbl
Converts numeric or string expression to Double precision floating point

Syntax

Declare Function CDbl ( ByVal expression As datatype ) As Double

Type typename
Declare Operator Cast ( ) As Double
End Type

Usage

result = CDbl( numeric expression )
result = CDbl( string expression )
result = CDbl( user defined type )

Parameters

expression
a numeric, string, or pointer expression to cast to a Double value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

A Double precision value.

Description

The CDbl function returns a 64-bit Double value. The function does not check for an overflow, so be sure not to pass a value outside the representable range of the Double data type. The name can be explained as 'Convert to DouBLe'.

If the argument to CDbl is a string expression, it is first converted to numeric by using Val.

Example

' Using the CDBL function to convert a numeric value

'Create an DOUBLE variable
Dim numeric_value As Double

'Convert a numeric value
numeric_value = CDbl(-12345678.123)

'Print the result, should return -12345678.123
Print numeric_value
Sleep

Differences from QB

  • The string argument was not allowed in QB

See also

  • CByte
  • CUByte
  • CShort
  • CUShort
  • CInt
  • CUInt
  • CLng
  • CULng
  • CLngInt
  • CULngInt
  • CSng


cdecl
Specifies a cdecl-style calling convention in a procedure declaration

Syntax

Sub name cdecl [Overload] [Alias "alias"] ( parameters )
Function name cdecl [Overload] [Alias "alias"] ( parameters ) [ ByRef ] As return_type

Description

In procedure declarations, cdecl specifies that a procedure will use the cdecl calling convention. In the cdecl calling convention, any parameters are to be passed (pushed onto the stack) in the reverse order in which they are listed, that is, from right to left. The procedures need not preserve the EAX, ECX or EDX registers, and must not clean up the stack (pop any parameters) before it returns - that is left to the calling code.

cdecl is allowed to be used with variadic procedure declarations (those with the last parameter listed as "...").

cdecl is the default calling convention on Linux, the *BSDs, and DOS, unless another calling convention is explicitly specified or implied by one of the Extern Blocks. cdecl is typically the default calling convention for C compilers, and it's used almost exclusively on Unix-like systems.

If a procedure definition has a declaration (with calling convention explicit or by default) and the definition does not explicitly specify a calling convention, then the calling convention is implied by the declaration.

Example

' declaring 'strcpy' from the standard C library
Declare Function strcpy cdecl Alias "strcpy" (ByVal dest As ZString Ptr, ByVal src As ZString Ptr) As ZString Ptr

Differences from QB

  • New to FreeBASIC

See also

  • pascal, stdcall
  • Declare
  • Sub, Function


Chain
Temporarily transfers control to an external program

Syntax

Declare Function Chain ( ByRef program As Const String ) As Long

Usage

result = Chain( program )

Parameters

program
The file name (including file path) of the program (executable) to transfer control to.

Return Value

Returns the external program's exit code if executed successfully, or negative one (-1) otherwise.

Description

Transfers control over to an external program. When the program exits, execution resumes immediately after the call to Chain.

Example

#ifdef __FB_LINUX__
    Dim As String program = "./program"
#else
    Dim As String program = "program.exe"
#endif

Print "Running " & program & "..."
If (Chain(program) <> 0) Then
    Print program & " not found!"
End If

Platform Differences

  • Linux requires the program name case matches the real name of the file. Windows and DOS are case insensitive. The program chained may be case sensitive for its command line parameters.
  • Path separators in Linux are forward slashes / . Windows uses backward slashes \ but it allows for forward slashes . DOS uses backward \ slashes.
  • Exit code is limited to 8 bits in DOS.

Differences from QB

  • Common does not allow to keep the values of certain variables when chaining programs with Chain.

See also

  • Exec transfer temporarily, with arguments
  • Run one-way transfer
  • Command pick arguments


CHANNEL
Sets or returns the default generated-sound channel.

Syntax

Channel channel
channel = Channel()

Description

CHANNEL selects the default sfxlib channel used by generated-sound commands that do not name a channel themselves. Think of channels as lanes in the sound mixer. Each lane can have its own pan, volume, and current generated sound behavior.

Using separate channels keeps a program organized. For example, one channel can be used for melody, another for warning beeps, and another for generated effects. Commands such as TONE, PAN, and VOLUME can then target a specific channel.

Calling CHANNEL as a function returns the current default channel.

Example

channel 2
sound 440, 0.20
print "current channel="; channel()

Notes

  • Channels are zero-based.

See also

  • Sound Library Reference
  • Generated Sound Commands


ChDir
Changes the current drive and directory

Syntax

Declare Function ChDir ( ByRef path As Const String ) As Long

Usage

result = ChDir( path )

Parameters

path
A String argument specifying the path to change to.

Return Value

Returns zero (0) on success and negative one (-1) on failure.

Description

Changes the current drive and directory to that specified.

Example

Dim pathname As String = $"x:\folder"
Dim result As Long = ChDir(pathname)

If result <> 0 Then Print "error changing current directory to " & pathname & "."

Platform Differences

  • Linux requires the filename case matches the real name of the file. Windows and DOS are case insensitive.
  • Path separators in Linux are forward slashes / . Windows uses backward slashes \ but it allows for forward slashes . DOS uses backward \ slashes.

Differences from QB

  • In QB, the drive could not be specified.

See also

  • CurDir
  • MkDir
  • RmDir


Chr
Returns a string of characters from one or more ASCII integer values

Syntax

Declare Function Chr ( ByVal ch As Integer [, ... ] ) As String

Usage

result = Chr[$]( ch0 [, ch1 ... chN ] )

Parameters

ch
The ASCII integer value of a character (up to 32 characters).

Return Value

Returns a string containing the character(s).

Description

Chr returns a string containing the character(s) represented by the ASCII values passed to it.

When Chr is used with numerical constants or literals, the result is evaluated at compile-time, so it can be used in variable initializers.

Asc performs the opposite function, returning the ASCII code of a character represented by a string.

Example

Print "the character represented by";
Print " the ASCII code of 97 is: "; Chr(97)

Print Chr(97, 98, 99) ' prints abc

' s initially has the value "abc"
Dim s As String = Chr(97, 98, 99)

Print s

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • FreeBASIC accepts multiple integer values as arguments, QB accepted only one.
  • FreeBASIC evaluates the CHR function at compile time when used with constants or literals.

See also

  • ASCII Character Codes
  • WChr
  • Asc
  • Str
  • Val


CInt
Converts a numeric or string expression to an Integer or an Integer<bits>

Syntax

Declare Function CInt ( ByVal expression As datatype ) As Integer
Declare Function CInt<bits> ( ByVal expression As datatype ) As Integer<bits>

Type typename
Declare Operator Cast ( ) As Integer
Declare Operator Cast ( ) As Integer<bits>
End Type

Usage

result = CInt( expression )
result = CInt( string expression )
result = CInt( user defined type )

Parameters

bits
A numeric constant expression indicating the size in bits of integer desired. The values allowed are 8, 16, 32 or 64.
expression
a numeric, string, or pointer expression to cast to a Integer value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

An Integer or Integer<bits> containing the converted value.

Description

If CInt is passed a numeric expression, it rounds it using using the round-to-even method - i.e. it rounds to the closest integer value, choosing the closest even integer if the number is equidistant from two integers - and returns an Integer, or if a bits value is supplied, an integer type of the given size.

The function does not check for an overflow; for example, for a 32-bit Integer the results are undefined for values which are less than -2 147 483 648 or larger than 2 147 483 647.

If the argument is a string expression, it is converted to numeric by using ValInt or ValLng, depending on the size of the result type.

The name "CINT" is derived from 'Convert to INTeger'.

Example

' Using the CINT function to convert a numeric value

'Create an INTEGER variable
Dim numeric_value As Integer

'Convert a numeric value
numeric_value = CInt(300.5)

'Print the result, should return 300, because 300 is even

numeric_value = CInt(301.5)

'Print the result, should return 302, because 301 is odd
Print numeric_value

Dialect Differences

  • In the -lang qb dialect, CInt will return a 16-bit integer, like in QB.

Differences from QB

  • The string argument was not allowed in QB
  • The <bits> parameter was not allowed in QB

See also

  • Cast
  • CByte
  • CUByte
  • CShort
  • CUShort
  • CUInt
  • CLng
  • CULng
  • CLngInt
  • CULngInt
  • CSng
  • CDbl
  • Integer


Circle
Graphics statement to draw an ellipse or a circle

Syntax

Circle [target,] [STEP] (x,y), radius[, [color][, [start][, [end][, [aspect][, F]]]]]

Parameters

target
optional; specifies the image buffer to draw on
STEP
indicates that coordinates are relative
(x, y)
coordinates of the center of the ellipse
radius
the radius of the circle - or for an ellipse, the semi-major axis (i.e. the longest radius)
color
the color attribute
start
starting angle
end
ending angle
aspect
aspect ratio of the ellipse, the ratio of the height to the width
F
fill mode indicator

Description

Circle will draw a circle, ellipse, or arc based on the parameters given to it.

target specifies a buffer to draw on. target may be an image created with ImageCreate or Get (Graphics). If omitted, target defaults to the screen's current work page. (See ScreenSet)

The center of the shape will be placed on the destination surface at (x, y).

Radius denotes the radius of the shape. If aspect ratio is not 1.0, the biggest radius must be given here.

Color denotes the color attribute, which is mode specific (see Color and Screen (Graphics) for details). If omitted, the current foreground color as set by the Color statement is used.

The Step option specifies that x and y are offsets relative to the current graphics cursor position.

start and end are angles are in radians. These can range -2*PI to 2*PI, where PI is the constant π, approximately 3.141593; if you specify a negative angle, its value is changed sign and a line is drawn from the center up to that point in the arc. end angle can be less than start. If you do not specify start and end, a full circle/ellipse is drawn; if you you specify start but not end, end is assumed to be 2*PI; if you specify end but not start, start is assumed to be 0.0.

aspect is the aspect ratio, or the ratio of the y radius over the x radius. If omitted, the default for ScreenRes modes is 1.0, while for Screen (Graphics) modes the default value is the value required to draw a perfect circle on the screen, keeping the pixel aspect ratio in mind. This value can be calculated as follows:
ratio = (y_radius / x_radius) * pixel_aspect_ratio
where pixel_aspect_ratio is the ratio of the current mode width over the current mode height, assuming a 4:3 standard monitor. If aspect ratio is less than 1.0, radius is the x radius; if aspect is more or equal to 1.0, radius is the y radius.

F is the fill flag. If you specify this flag, the circle/ellipse will be filled with the selected color. This only takes effect if you are drawing a full circle/ellipse.

Custom coordinates system set up by Window and/or View (Graphics) affect the drawing operation; clipping set by View also applies. When Circle finishes drawing, the current graphics cursor position is set to the supplied center.

Note: Curves drawn with Circle can induce pixels overdrawn at some locations. Thus, the resultant (blended) color of these overdrawn pixels is affected if a transparent color (in conjunction with the GFX_ALPHA_PRIMITIVES option flag) is used.

Example

' Set 640x480 mode, 256 colors
Screen 18

' Draws a circle in the center
Circle (320, 240), 200, 15

' Draws a filled ellipse
Circle (320, 240), 200, 2, , , 0.2, F

' Draws a small arc
Circle (320, 240), 200, 4, 0.83, 1.67, 3

Sleep

Differences from QB

  • target is new to FreeBASIC
  • The FreeBASIC implementation uses a different algorithm for ellipse/arc drawing than QB, so the result may not be equal to QB for every pixel.
  • The F flag to draw filled circles/ellipses is new to FreeBASIC.

See also

  • Screen (Graphics)
  • Color


Class
Declares a class object

Syntax

Class typename ...

Parameters

typename
name of the Class

Description

We would have put something useful here (honest) except this feature isn't implemented in the compiler yet. But since it will get added in future, and there are several other document pages that need to link here, we thought it safe to include in anyway.

Example

'' sample code

Output:
sample output

Dialect Differences

  • Object-related features are supported only in the -lang fb option

Differences from QB

  • New to FreeBASIC

See also

  • Enum
  • Type


Clear
Clears or initializes some memory

Syntax

Declare Sub Clear cdecl ( ByRef dst As Any, ByVal value As Long = 0, ByVal bytes As UInteger )

Usage

Clear( dst, [value], bytes )

Parameters

dst
starting address of some memory
value
the value to set all bytes equal to
bytes
number of bytes to clear

Description

Clear sets one or more bytes in memory to a certain value (the default value is zero (0) if not specified). The starting address is taken from a reference to a variable or array element.

NOTE: In order to clear memory referenced by a Pointer, it must be dereferenced first (or else specifying in argument term the ByVal keyword in front of the pointer name). Otherwise, Clear will try to clear the bytes at the pointer variable's memory location.

Example

'create an array with 100 elements
Dim array(0 To 99) As Integer

'clear the contents of the array to 0, starting with the first element
Clear array(0), , 100 * SizeOf(Integer)

'allocate 20 bytes of memory
Dim As Byte Ptr p = Allocate(20)

'set each of the first ten bytes to 0
Clear *p, 0, 10

'set each of the next ten bytes to 42
Clear p[10], 42, 10

'check the values of the allocated bytes
For i As Integer = 0 To 19
    Print i, p[i]
Next

'deallocate memory
Deallocate p

Differences from QB

  • The behavior and usage is new to FreeBASIC
  • The keyword CLEAR was used in QB to erase all variables, close all files, and optionally change the stack size. This use is not supported in FreeBASIC.

See also

  • Erase
  • Reset


CLng
Converts numeric or string expression to Long

Syntax

Declare Function CLng ( ByVal expression As datatype ) As Long

Type typename
Declare Operator Cast ( ) As Long
End Type

Usage

result = CLng( numeric expression )
result = CLng( string expression )
result = CLng( user defined type )

Parameters

expression
a numeric, string, or pointer expression to cast to a Long value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

A Long value.

Description

The CLng function rounds off the decimal part and returns a 32-bit Long value. The function does not check for an overflow, and results are undefined for values which are less than -2 147 483 648 or larger than 2 147 483 647.

The name can be explained as 'Convert to LoNG'.

If the argument is a string expression, it is converted to numeric by using ValInt.

Example

' Using the CLNG function to convert a numeric value

'Create an LONG variable
Dim numeric_value As Long

'Convert a numeric value
numeric_value = CLng(-300.23)

'Print the result, should return -300
Print numeric_value
Sleep

Differences from QB

  • The string argument was not allowed in QB

See also

  • CByte
  • CUByte
  • CShort
  • CUShort
  • CInt
  • CUInt
  • CULng
  • CLngInt
  • CULngInt
  • CSng
  • CDbl


CLngInt
Converts numeric or string expression to 64-bit integer (LongInt)

Syntax

Declare Function CLngInt ( ByVal expression As datatype ) As LongInt

Type typename
Declare Operator Cast ( ) As LongInt
End Type

Usage

result = CLngInt( numeric expression )
result = CLngInt( string expression )
result = CLngInt( user defined type )

Parameters

expression
a numeric, string, or pointer expression to cast to a LongInt value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

A LongInt value.

Description

The CLngInt function rounds off the decimal part and returns a 64-bit LongInt value. The function does not check for an overflow, and results are undefined for values which are less than -9 223 372 036 854 775 808 or larger than 223 372 036 854 775 807.

The name can be explained as 'Convert to LoNG INTeger'.

If the argument is a string expression, it is converted to numeric by using ValLng.

Example

' Using the CLNGINT function to convert a numeric value

'Create an LONG INTEGER variable
Dim numeric_value As LongInt

'Convert a numeric value
numeric_value = CLngInt(-12345678.123)

'Print the result, should return -12345678
Print numeric_value
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Clngint.

Differences from QB

  • New to FreeBASIC

See also

  • CByte
  • CUByte
  • CShort
  • CUShort
  • CInt
  • CUInt
  • CLng
  • CULng
  • CULngInt
  • CSng
  • CDbl


Close
Stream I/O function to terminate access to a device

Syntax

Close [[#]filenum ] [, [#]filenum ...]
or
result = Close( [#filenum] )

Parameters

filenum
List of file numbers to close.

Return Value

Close returns a 32 bit Long: a zero (0) on success and a non-zero error code otherwise.

Description

Closes the files whose file numbers are passed as arguments. If an unused file number is passed, Close returns an error.

Close without arguments closes all the files presently opened.

Terminating the program using an End statement will automatically close all files.

The error code returned by Close can be checked using Err in the next line. The function version of Close returns directly the error code as a 32 bit Long.

Example

' Create a string and fill it.
Dim buffer As String, f As Integer

buffer = "Hello World within a file."

' Find the first free file number.
f = FreeFile

' Open the file "file.ext" for binary usage, using the number "f".
Open "file.ext" For Binary As #f

  ' Place our string inside the file, using number "f".
  Put #f, , buffer

' Close the file.  We could also do 'Close #f', but it's only necessary if more than one number is open.
Close

' End of program. (Check the file "file.ext" upon running to see the output.)

Differences from QB

  • Close can be called as a function that returns an error code.
  • FB throws an error on trying to close an unused file number, if compiled with error checking and if not used with the function-style syntax

See also

  • Open
  • Put (File I/O)
  • Get (File I/O)
  • FreeFile


Cls
Clears the screen in both text modes and graphics modes

Syntax

Declare Sub Cls ( ByVal mode As Long = 1 )

Usage

Cls mode

Parameters

mode
A optional numeric variable with a value from 0 to 2. If omitted, it defaults to 1.

Description

An optional mode parameter may be given,

If omitted, Cls clears either the text or graphics viewport. If a graphics viewport has been defined using the View (Graphics) statement, the graphics viewport is cleared. Otherwise, the text viewport, defined by View Print, is cleared. (If there is no explicit text viewport defined, the entire screen is cleared.)

If 0, clears the entire screen

If 1, clears the graphics viewport if defined. Otherwise, clears the text viewport

If 2, clears the text viewport

Example

'' set the color to light grey text on a blue background
Color 7, 1

'' clear the screen to the background color
Cls

'' print text in the center of the screen
Locate 12, 33
Print "Hello Universe!"

In graphics modes, if you want to clear the entire screen to color 0, it can be faster using Clear to write zeroes to the screen memory than calling Cls.

Dim scrbuf As Byte Ptr, scrsize As Integer
Dim As Long scrhei, scrpitch
Dim As Integer r = 0, dr = 1

ScreenRes 640, 480, 8

scrbuf = ScreenPtr: Assert( scrbuf <> 0 )
ScreenInfo( , scrhei, , , scrpitch )
scrsize = scrpitch * scrhei

Do

    '' lock the screen (must do this while working directly on screenbuffer)
    ScreenLock

        '' clear the screen (could use Cls here):
        Clear *scrbuf, 0, scrsize

        '' draw circle
        Circle (320, 240), r

    ScreenUnlock

    '' grow/shrink circle radius
    r += dr
    If r <= 0 Then dr = 1 Else If r >= 100 Then dr = -1

    '' short pause in each frame (prevents hogging the CPU)
    Sleep 1, 1

    '' run loop until user presses a key
Loop Until Len(Inkey) > 0

Differences from QB

  • None

See also

  • Color
  • Locate
  • Print
  • ?
  • View (Graphics)


Color
Sets the display foreground / background color that is used with console output and graphics output of text

Syntax

Declare Function Color ( ByVal foreground As ULong , ByVal background As ULong ) As ULong

Usage

Color [foreground] [, background]
result = Color [( [foreground] [, background] )]

Parameters

foreground
the foreground color to set
background
the background color to set

Return Value

Returns a 32-bit value containing the current foreground color in the Low Word and the current background color in the High Word.
In hi/truecolor modes, only the foreground color is returned, taking up the whole 32 bits. Instead, see ScreenControl to return the current graphics mode color (foreground and background).

The old color values can be retrieved at the same time as setting new ones.

Description

The Color statement sets the current foreground and/or background colors. Circle, Draw, Line (Graphics), Cls, Paint, Print, PReset and PSet all use the last colors set by this function when you don't specify a color to them, where applicable. The color values that Color accepts depend on the current graphics mode.

Mode | Meaning
1 | foreground is screen color (ranging 0-15). background is the emulated CGA palette to be used: 0 (green, red, and brown), 1 (cyan, magenta and white), 2 (same as 0, but with bright colors) or 3 (same as 1, but with bright colors)
2, 11 | foreground is a color index in current palette (ranging 0-1). background is a color index in current palette (ranging 0-1).
7, 8 | foreground is a color index in current palette (ranging 0-15). background is screen color index in current palette (ranging 0-15).
9 | foreground is a color index in current palette (ranging 0-63). background is screen color index in current palette (ranging 0-63).
12 | foreground is a color index in current palette (ranging 0-15). background is a color index in current palette (ranging 0-15).
13 and up | foreground is a color index in current palette (ranging 0-255). background is a color index in current palette (ranging 0-255).

If you are using a color depth higher than 8bpp, foreground and background are direct RGB color values in the form &hAARRGGBB, where AA, RR, GG and BB are the alpha, red, green and blue components ranging &h00-&hFF (0-255 in decimal notation). While in hi/truecolor modes, you can use the RGB or RGBA macro to obtain a valid color value.
A Default Palette is automatically set when entering a Screen (Graphics) mode.

Example

' Sets 320x240 in 32bpp color depth
Screen 14, 32

' Sets orange foreground and dark blue background color
Color RGB(255, 128, 0), RGB(0, 0, 64)

' Clears the screen to the background color
Cls

' Prints "Hello World!" in the middle of the screen
Locate 15, 14
Print "Hello World!"

Sleep

Dim c As ULong

'retrieve current color values
c = Color()

'extract color values from c using LOWORD and HIWORD
Print "Console colors:"
Print "Foreground: " & LoWord(c)
Print "Background: " & HiWord(c)

' In 32-bit color depth, Function Color() returns only the foreground color

#include "fbgfx.bi"

'' screencontrol expects integer/uinteger
Dim As Long fgcolor, bkcolor

ScreenRes 500, 500, 32
Width 500\8, 500\16
Color &HFFFF00, &H0000FF
Cls

Print "From Function Color():"
Print "  Foreground Color: "; Hex(Color(), 8)
Print

ScreenControl FB.GET_COLOR, fgcolor, bkcolor
Print "From Sub ScreenControl():"
Print "  Foreground Color: "; Hex(fgcolor, 8)
Print "  Background Color: "; Hex(bkcolor, 8)

Sleep

Differences from QB

  • Direct color modes were not supported in QB.
  • There is no border argument.

See also

  • RGB
  • RGBA
  • LoWord
  • HiWord
  • Locate
  • Palette
  • Screen (Graphics)


Command
Returns command line parameters used to call the program

Syntax

Declare Function Command ( ByVal index As Long = -1 ) As String

Usage

result = Command[$]( [ index ] )

Parameters

index
Zero-based index for a particular command-line argument.

Return Value

Returns the command-line arguments(s).

Description

Command returns command-line arguments passed to the program upon execution.

If index is less than zero (< 0), a space-separated list of all command-line arguments is returned, otherwise, a single argument is returned. A value of zero (0) returns the name of the executable; and values of one (1) and greater return each command-line argument.

If index is greater than the number of arguments passed to the program, the null string ("") is returned.

When the command line is parsed for arguments, everything between double quotes in the parameter list will be considered as a single block, and is returned without the double quotes.

By default, filename globbing for arguments (expansion of wildcards to filenames) is used on all ports of FreeBASIC for compatibility. Arguments on the command line containing wildcards are typically not expanded if when no file is matched or if properly quoted. Other special characters for redirection are typically not returned unless properly quoted. Consult the documentation on the shell being used for more information on the proper quoting of command line arguments.
There may be some strange behavior when using backslash(es) in command line arguments, and the result may even depend on the platform.

WARNING: By nature of constructor precedence in FreeBASIC and main() initialization, calling Command within a global constructor (module constructor or UDT constructor called for static/shared object) is not safe (may even induce a runtime error).

Disabling filename globbing under Windows
Define the following global variable(s) somewhere in the source:
'' For MinGW.org and Cygwin runtimes:
Extern _CRT_glob Alias "_CRT_glob" As Long
Dim Shared _CRT_glob As Long = 0

'' For MinGW-w64 runtime:
Extern _dowildcard Alias "_dowildcard" As Long
Dim Shared _dowildcard As Long = 0

Disabling filename globbing under Dos
Define the following function somewhere in the source:
Function __crt0_glob_function Alias "__crt0_glob_function" ( ByVal arg As UByte Ptr ) As UByte Ptr Ptr
  Return 0
End Function

Disabling filename globbing under Linux
Filename globbing is handled by the command shell. Quote the argument containing wildcards or disable filename globbing in the shell prior to executing the command. For example in bash use 'set -f' to disable wildcard expansion

Example

Print "program launched via: " & Command(0)

Dim As Long i = 1
Do
    Dim As String arg = Command(i)
    If Len(arg) = 0 Then
        Exit Do
    End If

    Print "command line argument " & i & " = """ & arg & """"
    i += 1
Loop

If i = 1 Then
    Print "(no command line arguments)"
End If

Sleep

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • The numeric argument was not supported in QB.
  • QB converted the parameter list to uppercase before returning it, FreeBASIC doesn't.
  • By default arguments containing wildcard characters are expanded (filename globbing)

See also

  • __FB_ARGC__
  • __FB_ARGV__
  • Exec
  • Run


Common
Variable declaration and scope modifier

Syntax

Common [Shared] symbolname[()] [AS DataType] [, ...]

Description

Declares a variable which is shared between code modules, including those to be compiled as static and dynamic libraries (DLLs).
A matching Common statement must appear in all other code modules using the variable.

Common variables cannot be initialized.
Common arrays are always variable-length, and must be defined with an empty parameter list (), and its dimensions set in a later Dim or ReDim statement.
Common variables cannot be instances of a user-defined type having a constructor or a destructor even implicit.

The Shared optional parameter makes the variable global so that it can be used inside Subs and Functions, as well as at module level.

Example

'' common1.bas

Declare Sub initme()

Common Shared foo() As Double

ReDim foo(0 To 2)

initme()

Print foo(0), foo(1), foo(2)

'' common2.bas

Common Shared foo() As Double

Sub initme()
  foo(0) = 4*Atn(1)
  foo(1) = foo(0)/3
  foo(2) = foo(1)*2
End Sub

After compiling the two files like:
fbc common1.bas common2.bas
running common1 produces the output:
 3.141592653589793           1.047197551196598           2.094395102393195
Platform Differences

  • Windows does not support Common with a dynamic library (compiled with -dll or -dylib).

Differences from QB

  • The arrays will be always variable-length.
  • blockname is not needed and must be removed because the order of declaration no longer matters, only the symbol names.
  • Common does not allow to keep the values of certain variables when chaining programs with Chain.

See also

  • Dim
  • Erase
  • Extern
  • LBound
  • ReDim
  • Preserve
  • Shared
  • Static
  • UBound
  • Var


CondBroadcast
Restarts all threads CondWaiting for the handle

Syntax

Declare Sub CondBroadcast ( ByVal handle As Any Ptr )

Usage

CondBroadcast ( handle )

Parameters

handle
The handle of a conditional variable.

Description

Once the conditional is CondCreate and the threads are started, one of more of them (including the implicit main thread executing main program) can be set to CondWait for the conditional, they will be stopped until some other thread CondSignals that the waiting thread can restart. CondBroadcast can be used to restart all threads waiting for the conditional. At the end of the program CondDestroy must be used to avoid leaking resources in the OS.

Condbroadcast must be used instead of CondSignal to restart all threads waiting on the conditional.

Example

See also CondCreate

'' How can all threads wait for each other before continuing to execute their concomitant sections of code ?
''
'' This is the main code that manages the synchronization of concomitant sections of code:
''    - Each thread signals to main code that it is waiting for synchronization.
''    - The main code waits for each of all threads, then sends the synchronization signal to all threads.
''
'' The synchronization uses the following keywords from threading:
''   - In each thread : 'Condsignal' (to send signal to main code) then 'Condwait' (to wait signal from main code).
''   - In main code : 'Condwait' (to wait signal from each thread) then 'Condbroadcast' (to send signal to all threads).
'' With the associated mutexes and conditional variables and flags.
''
'' Test example highlighting the structure of threads and their behaviors, when synchronizing all threads to execute their concomitant sections of code:

Dim threadnumber As Integer = 4

'' global data for all threads
    Dim Shared restart As Integer = 0
    Dim Shared threadcount As Integer = 0

    Dim Shared hmutexrestart As Any Ptr
    Dim Shared hcondrestart As Any Ptr
    Dim Shared hmutexready As Any Ptr
    Dim Shared hcondready As Any Ptr

Sub mythread(ByVal p As Any Ptr)

    Dim id As Integer = Cast(Integer, p)

    '' for visualizing thread status
        Print "   Thread #" & id & " is started..."

    '' instead of starting user code
        Sleep id * 40, 1

    '' for visualizing thread status
        Print "      Thread #" & id & " is running..."
        Sleep id * 20, 1  '' just to well visualize thread interlacing
        Print "   Thread #" & id & " is waiting for synchronization..."

    '' for sending signal to main code
        MutexLock hmutexready
        threadcount += 1
        CondSignal hcondready
        MutexUnlock hmutexready

    '' for waiting signal from main code
        MutexLock hmutexrestart
        Do While restart = 0
            CondWait hcondrestart, hmutexrestart
        Loop
        MutexUnlock hmutexrestart

    '' for visualizing thread status
        Print "   Thread #" & id & " is reactivated..."

    '' instead of synchronized user code
        Sleep id * 40, 1

    '' for visualizing thread status
        Print "      Thread #" & id & " is continuing..."
        Sleep id * 20, 1  '' just to well visualize thread interlacing
        Print "   Thread #" & id & " is finishing execution..."

End Sub

Dim threads(1 To threadnumber) As Any Ptr

hcondrestart = CondCreate()
hmutexrestart = MutexCreate()
hcondready = CondCreate()
hmutexready = MutexCreate()

'' for visualizing main code status
    Print "Start all threads from main code :"

'' for starting all threads
    For i As Integer = 1 To threadnumber
        threads(i) = ThreadCreate(@mythread, Cast(Any Ptr, i))
    Next i

'' for waiting for all threads waiting for synchronisation
    MutexLock hmutexready
    Do Until threadcount = threadnumber
        CondWait(hcondready, hmutexready)
    Loop
    MutexUnlock hmutexready

'' for visualizing main code status
    Print "All thread seen waiting from main code"
    Print
    Print "Reactivate all threads from main code :"

'' for reactivating all threads
    MutexLock hmutexrestart
    restart = 1
    CondBroadcast hcondrestart
    MutexUnlock hmutexrestart

'' for waiting for all threads to be completed
    For i As Integer = 1 To threadnumber
        If threads(i) <> 0 Then
            ThreadWait threads(i)
        End If
    Next i

'' for visualizing main code status
    Print "All thread seen completed from main code"

MutexDestroy hmutexready
CondDestroy hcondready
MutexDestroy hmutexrestart
CondDestroy hcondrestart

Sleep

Platform Differences

  • Condbroadcast is not available with the DOS version / target of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.
  • In Linux the threads are always started in the order they are created, this can't be assumed in Win32. It's an OS, not a FreeBASIC issue.

Dialect Differences

  • Threading is not allowed in -lang qb

Differences from QB

  • New to FreeBASIC

See also

  • CondCreate
  • CondDestroy
  • CondSignal
  • CondWait
  • ThreadCreate


CondCreate
Creates a conditional variable to be used in synchronizing threads

Syntax

Declare Function CondCreate ( ) As Any Ptr

Usage

result = CondCreate

Return Value

A handle to a newly created conditional variable, or the null pointer (0) on failure.

Description

Once the conditional is Condcreated and the threads are started, one or more of them (including the implicit main thread executing main program) can be set to CondWait for the conditional, they will be stopped until some other thread CondSignals that the waiting thread can restart. CondBroadcast can be used to restart all threads waiting for the conditional. At the end of the program CondDestroy must be used to avoid leaking resources in the OS.

Example

See also CondWait and CondSignal and CondBroadcast

''
'' make newly-created threads wait until all threads are ready, then start them all at once
''

Dim Shared hcondstart As Any Ptr
Dim Shared hmutexstart As Any Ptr
Dim Shared start As Integer = 0

Dim Shared threadcount As Integer
Dim Shared hmutexready As Any Ptr
Dim Shared hcondready As Any Ptr

Sub mythread(ByVal id_ptr As Any Ptr)
    Dim id As Integer = Cast(Integer, id_ptr)

    '' signal that this thread is ready
    MutexLock hmutexready
    threadcount += 1
    Print "Thread #" & id & " is waiting..."
    CondSignal hcondready
    MutexUnlock hmutexready

    '' wait for the start signal
    MutexLock hmutexstart
    Do While start = 0
        CondWait hcondstart, hmutexstart
    Loop

    '' now this thread holds the lock on hmutexstart

    MutexUnlock hmutexstart

    '' print out the number of this thread
    For i As Integer = 1 To 40
        Print id;
    Next i
End Sub

Dim threads(1 To 9) As Any Ptr

hcondstart = CondCreate()
hmutexstart = MutexCreate()

hcondready = CondCreate()
hmutexready = MutexCreate()

threadcount = 0

MutexLock(hmutexready)
For i As Integer = 1 To 9
    threads(i) = ThreadCreate(@mythread, Cast(Any Ptr, i))
    If threads(i) = 0 Then
        Print "unable to create thread"
    End If
Next i

Print "Waiting until all threads are ready..."

Do Until threadcount = 9
    CondWait(hcondready, hmutexready)
Loop
MutexUnlock(hmutexready)

Print
Print "Go!"

MutexLock hmutexstart
start = 1
CondBroadcast hcondstart
MutexUnlock hmutexstart

'' wait for all threads to complete
For i As Integer = 1 To 9
    If threads(i) <> 0 Then
        ThreadWait threads(i)
    End If
Next i

MutexDestroy hmutexready
CondDestroy hcondready

MutexDestroy hmutexstart
CondDestroy hcondstart

'Visual example of mutual exclusion + mutual synchronization between 2 threads
'by using Mutex and CondVar:
'the "user-defined thread" computes the points coordinates on a circle,
'and the "main thread" plots the points.
'
'Principle of mutual exclusion + mutual synchronisation
'          Thread#A               XOR + <==>             Thread#B
'.....                                          .....
'MutexLock(mut)                                 MutexLock(mut)
'  Do_something_with_exclusion                    Do_something_with_exclusion
'  While bool#1 <> true <------------------------ bool#1 = true
'    CondWait(cond#1, mut) <--------------------- CondSignal(cond#1)
'  Wend <-----------------------------------.     Do_something_with_exclusion
'  bool#1 = false               .---------- | --> While bool#2 <> true
'  Do_something_with_exclusion  |   .------ | ----> CondWait(cond#2, mut)
'  bool#2 = true ---------------'   |   .-- | --> Wend
'  CondSignal(cond#2) --------------'   |   |     bool#2 = false
'  Do_something_with_exclusion          |   |     Do_something_with_exclusion
'MutexUnlock(mut) ----------------------'   '-- MutexUnlock(mut)
'.....                                          .....
'
'Behavior:
'- Unnecessary to pre-calculate the first point.
'- Each calculated point is plotted one time only.
'
'If you comment out the lines containing "MutexLock" and "MutexUnlock",
'"CondWait" and "CondSignal", ".ready"
'(inside "user-defined thread" or/and "main thread"),
'there will be no longer mutual exclusion nor mutual synchronization
'between computation of coordinates and plotting of points,
'and many points will not be plotted on circle (due to non coherent coordinates).

'-----------------------------------------------------------------------------------------------------

Type ThreadUDT                                   'Generic user thread UDT
    Dim handle As Any Ptr                        'Any Ptr handle to user thread
    Dim sync As Any Ptr                          'Any Ptr handle to mutex
    Dim cond1 As Any Ptr                         'Any Ptr handle to conditional1
    Dim cond2 As Any Ptr                         'Any Ptr handle to conditional2
    Dim ready1 As Byte                           'Boolean to coordinates ready1
    Dim ready2 As Byte                           'Boolean to coordinates ready2
    Dim quit As Byte                             'Boolean to end user thread
    Declare Static Sub Thread (ByVal As Any Ptr) 'Generic user thread procedure
    Dim procedure As Sub (ByVal As Any Ptr)      'Procedure(Any Ptr) to be executed by user thread
    Dim p As Any Ptr                             'Any Ptr to pass to procedure executed by user thread
    Const False As Byte = 0                      'Constante "false"
    Const True As Byte = Not False               'Constante "true"
End Type

Static Sub ThreadUDT.Thread (ByVal param As Any Ptr) 'Generic user thread procedure
    Dim tp As ThreadUDT Ptr = param                  'Casting to generic user thread UDT
    Do
        Static As Integer I
        MutexLock(tp->sync)                          'Mutex (Lock) for user thread
        tp->procedure(tp->p)                         'Procedure(Any Ptr) to be executed by user thread
        I += 1
        Locate 30, 38
        Print I;
        tp->ready1 = True                            'Set ready1
        CondSignal(tp->cond1)                        'CondSignal to send signal1 to main thread
        While tp->ready2 <> True                     'Process loop against spurious wakeups
            CondWait(tp->cond2, tp->sync)            'CondWait to receive signal2 from main-thread
        Wend
        tp->ready2 = False
        If tp->quit = tp->True Then                  'Test for ending user thread
            MutexUnlock(tp->sync)                    'Mutex (Unlock) for user thread
            Exit Do
        End If
        MutexUnlock(tp->sync)                        'Mutex (Unlock) for user thread
        Sleep 5, 1
    Loop
End Sub

'-----------------------------------------------------------------------------------------------------

Type Point2D
    Dim x As Integer
    Dim y As Integer
End Type

Const x0 As Integer = 640 / 2
Const y0 As Integer = 480 / 2
Const r0 As Integer = 200

Const pi As Single = 4 * Atn(1)

Sub PointOnCircle (ByVal p As Any Ptr)
    Dim pp As Point2D Ptr = p
    Dim teta As Single = 2 * pi * Rnd
    pp->x = x0 + r0 * Cos(teta)
    Sleep 5, 1                         'To increase possibility of uncorrelated data occurrence
    pp->y = y0 + r0 * Sin(teta)
End Sub

Screen 12
Locate 30, 2
Print "<any_key> : exit";
Locate 30, 27
Print "calculated:";
Locate 30, 54
Print "plotted:";

Dim Pptr As Point2D Ptr = New Point2D

Dim Tptr As ThreadUDT Ptr = New ThreadUDT
Tptr->sync = MutexCreate
Tptr->cond1 = CondCreate
Tptr->cond2 = CondCreate
Tptr->procedure = @PointOnCircle
Tptr->p = Pptr
Tptr->handle = ThreadCreate(@ThreadUDT.Thread, Tptr)

Do
    Static As Integer I
    MutexLock(Tptr->sync)                 'Mutex (Lock) for main thread
    While Tptr->ready1 <> Tptr->True      'Process loop against spurious wakeups
        CondWait(Tptr->cond1, Tptr->sync) 'CondWait to receive signal1 from user-thread
    Wend
    Tptr->ready1 = Tptr->False
    PSet (Pptr->x, Pptr->y)               'Plotting one point
    I += 1
    Locate 30, 62
    Print I;
    Tptr->ready2 = Tptr->True             'Set ready2
    CondSignal(Tptr->cond2)               'CondSignal to send signal2 to user thread
    If Inkey <> "" Then
        Tptr->quit = Tptr->True           'Set quit
        MutexUnlock(Tptr->sync)           'Mutex (Unlock) for main thread
        Exit Do
    End If
    MutexUnlock(Tptr->sync)               'Mutex (Unlock) for main thread
    Sleep 5, 1
Loop

ThreadWait(Tptr->handle)
MutexDestroy(Tptr->sync)
CondDestroy(Tptr->cond1)
CondDestroy(Tptr->cond2)
Delete Tptr
Delete Pptr

Sleep

See also the similar MutexCreate example

Platform Differences

  • Condcreate is not available with the DOS version / target of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.

Dialect Differences

  • Threading is not allowed in -lang qb

Differences from QB

  • New to FreeBASIC

See also

  • CondBroadcast
  • CondDestroy
  • CondSignal
  • CondWait
  • MutexCreate
  • MutexLock
  • MutexUnlock
  • ThreadCreate


CondDestroy
Destroys a multi-threading conditional variable when it is no more needed

Syntax

Declare Sub CondDestroy ( ByVal handle As Any Ptr )

Usage

CondDestroy ( handle )

Parameters

handle
The handle of a conditional variable to destroy.

Description

Once the conditional is CondCreated and the threads are started, one of more of them (including the implicit main thread executing main program) can be set to CondWait for the conditional, they will be stopped until some other thread CondSignals that the waiting thread can restart. CondBroadcast can be used to restart all threads waiting for the conditional. At the end of the program CondDestroy must be used to avoid leaking resources in the OS.

Conddestroy destroys a condition variable, freeing the resources it might hold. No threads must be waiting on the condition variable on entrance to Conddestroy.

Example

See CondCreate, CondWait, CondSignal, and CondBroadcast

Platform Differences

  • Conddestroy is not available with the DOS version / target of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.

Dialect Differences

  • Threading is not allowed in -lang qb

Differences from QB

  • New to FreeBASIC

See also

  • CondCreate
  • CondBroadcast
  • CondSignal
  • CondWait
  • ThreadCreate


CondSignal
Restarts a thread suspended by a call to CondWait

Syntax

Declare Sub CondSignal ( ByVal handle As Any Ptr )

Usage

CondSignal ( handle )

Parameters

handle
The handle of a conditional variable.

Description

Once the conditional is created with CondCreate and the threads are started, one of more of them (including the implicit main thread executing main program) can be set to CondWait for the conditional, they will be stopped until some other thread CondSignals that the waiting thread can restart. CondBroadcast can be used to restart all threads waiting for the conditional. At the end of the program CondDestroy must be used to avoid leaking resources in the OS.

Condsignal restarts one thread waiting. It should be called after mutex is locked (using the same mutex as one used with CondWait). If no threads are waiting on the conditional, nothing happens (the signal is lost forever); if several are waiting, only one is restarted. The caller must then unlock mutex in order for CondWait subroutine to complete.

Example

See also CondCreate and CondWait and CondBroadcast

' This very simple example code demonstrates the use of several condition variable routines.
' The main routine initializes a string and creates one thread.
' The main routine waits until receive the condition signal from the thread, then print the complemented string.
' The thread complements the string, then sends a condition signal.
'
'Principle of mutual exclusion + simple synchronization
'          Thread#A             XOR + ==>            Thread#B
'.....                                     .....
'MutexLock(mut)                            MutexLock(mut)
'  Do_something_with_exclusion               Do_something_with_exclusion
'  Thread_signal = true -------------------> While Thread_signal <> true
'  CondSignal(cond) -------------------------> CondWait(cond, mut)
'  Do_something_with_exclusion      .------> Wend
'MutexUnlock(mut) ------------------'        Thread_signal = false
'.....                                       Do_something_with_exclusion
'.....                                     MutexUnlock(mut)
'.....                                     .....

Dim Shared As Any Ptr mutex
Dim Shared As Any Ptr cond
Dim Shared As String txt
Dim As Any Ptr pt
Dim Shared As Integer ok = 0

Sub thread (ByVal p As Any Ptr)
    Print "thread is complementing the string"
    MutexLock(mutex)
    Sleep 400, 1
    txt &= " complemented by thread"
    ok = 1
    CondSignal(cond)
    MutexUnlock(mutex)
    Print "thread signals the processing completed"
End Sub

mutex = MutexCreate
cond = CondCreate

txt = "example of text"
Print "main() initializes a string = " & txt
Print "main creates one thread"
Print
pt = ThreadCreate(@thread)
MutexLock(mutex)
While ok <> 1
    CondWait(cond, mutex)
Wend
Print
Print "back in main(), the string = " & txt
ok = 0
MutexUnlock(mutex)

ThreadWait(pt)
MutexDestroy(mutex)
CondDestroy(cond)

Dialect Differences

  • Threading is not allowed in -lang qb

Platform Differences

  • Condsignal is not available with the DOS version / target of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.
  • In Linux the threads are always started in the order they are created, this can't be assumed in Win32. It's an OS, not a FreeBASIC issue.

Differences from QB

  • New to FreeBASIC

See also

  • CondCreate
  • CondDestroy
  • CondBroadcast
  • CondWait
  • ThreadCreate


CondWait
Stops execution of current thread until some condition becomes true

Syntax

Declare Sub CondWait ( ByVal handle As Any Ptr, ByVal mutex As Any Ptr )

Usage

CondWait ( handle, mutex )

Parameters

handle
The handle of a conditional variable.
mutex
The mutex associated with this conditional variable, which must be locked when testing the condition and calling CondWait.

Description

Function that stops the thread where it is called until some other thread CondSignals or CondBroadcasts the handle.

Once the conditional variable is created with CondCreate and the threads are started, one of more of them (including the implicit main thread executing main program) can be set to CondWait for the conditional; they will be stopped until some other thread CondSignals that the waiting thread can restart. CondBroadcast can be used to restart all threads waiting for the conditional. At the end of the program CondDestroy must be used to avoid leaking resources in the OS.

When calling CondWait, mutex should already be locked (using the same mutex as one used with CondSignal or CondBroadcast). An atomic unlock of the mutex and wait on the conditional variable will occur. The calling thread execution is suspended and does not consume any CPU time until the condition variable is signaled. When the condition variable becomes signaled, mutex will be locked again and then execution will return to the thread after the CondWait call, but with the mutex owned by the caller. The caller is then responsible for unlocking mutex in order to complete CondWait subroutine so that execution after the CondWait call can then resume.

Note: It is a good habit to use CondWait in a protected way against eventual spurious wakeups.
For that, CondWait is put within a loop for checking that a Boolean predicate is indeed true (predicate set true by another thread just before executing CondSignal or CondBroadcast) when the thread has finished waiting:
signal-caller:
predicate = true
Condsignal(handle)
waiting-called:
While predicate <> true
Condwait(handle, mutex)
Wend
predicate = false
the loop can terminate only when the predicate is true.
On the other hand, if the predicate is already true before the thread reaches the loop, CondWait is downright skipped (allowing to take into account a case of CondSignal or CondBroadcast that would have been lost otherwise, because prematurely executed in a second thread before the first thread is really waiting for this).
See example below for detailed coding.

Example

See also CondCreate and CondSignal

' This simple example code demonstrates the use of several condition variable routines.
' The main routine creates three threads.
' Two of the threads update a "count" variable.
' The third thread waits until the count variable reaches a specified value.

#define numThread  3
#define countThreshold 6

Dim Shared As Integer count = 0
Dim Shared As Any Ptr countMutex
Dim Shared As Any Ptr countThresholdCV
Dim As Any Ptr threadID(0 To numThread-1)
Dim Shared As Integer ok = 0

Sub threadCount (ByVal p As Any Ptr)
    Print "Starting threadCount(): thread#" & p
    Do
        Print "threadCount(): thread#" & p & ", locking mutex"
        MutexLock(countMutex)
        count += 1
        ' Check the value of count and signal waiting thread when condition is reached.
        ' Note that this occurs while mutex is locked.
        If count >= countThreshold Then
            If count = countThreshold Then
                Print "threadCount(): thread#" & p & ", count = " & count & ", threshold reached, unlocking mutex"
                ok = 1
                CondSignal(countThresholdCV)
            Else
                Print "threadCount(): thread#" & p & ", count = " & count & ", threshold exceeded, unlocking mutex"
            End If
            MutexUnlock(countMutex)
            Exit Do
        End If
        Print "threadCount(): thread#" & p & ", count = " & count & ", unlocking mutex"
        MutexUnlock(countMutex)
        Sleep 100, 1
    Loop
End Sub

Sub threadWatch (ByVal p As Any Ptr)
    Print "Starting threadWatch(): thread#" & p & ", locking mutex, waiting for conditional"
    MutexLock(countMutex)
    ' Note that the Condwait routine will automatically and atomically unlock mutex while it waits.
    While ok = 0
        CondWait(countThresholdCV, countMutex)
    Wend
    Print "threadWatch(): thread#" & p & ", condition signal received"
    Print "threadWatch(): thread#" & p & ", count now = " & count & ", unlocking mutex"
    MutexUnlock(countMutex)
End Sub

' Create mutex and condition variable
countMutex = MutexCreate
countThresholdCV = CondCreate
' Create threads
threadID(0) = ThreadCreate(@threadWatch, Cast(Any Ptr, 1))
threadID(1) = ThreadCreate(@threadCount, Cast(Any Ptr, 2))
threadID(2) = ThreadCreate(@threadCount, Cast(Any Ptr, 3))
' Wait for all threads to complete
For I As Integer = 0 To numThread-1
    ThreadWait(threadID(I))
    Print "Main(): Waited on thread#" & I+1 & " Done"
Next I
MutexDestroy(countMutex)
CondDestroy(countThresholdCV)

Platform Differences

  • Condwait is not available with the DOS version / target of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.
  • In Linux the threads are always started in the order they are created, this can't be assumed in Win32. It's an OS, not a FreeBASIC issue.

Dialect Differences

  • Threading is not allowed in -lang qb

Differences from QB

  • New to FreeBASIC

See also

  • CondCreate
  • CondDestroy
  • CondBroadcast
  • CondSignal
  • MutexCreate
  • MutexLock
  • MutexUnlock
  • ThreadCreate


Const
Non-modifiable variable declaration.

Syntax

Const symbolname1 [AS DataType] = value1 [, symbolname2 [AS DataType] = value2, ...]
or
Const [AS DataType] symbolname1 = value1 [, symbolname2 = value2, ...]

Description

Declares non-modifiable constant data that can be integer or decimal (floating-point) numbers or strings. The constant type will be inferred if DataType isn't explicitly given.

Specifying String * Size, Zstring * Size or Wstring * Size as DataType is not allowed.
Specifying String as DataType is tolerated but without effect because the resulting type is always a Zstring * Size.

Constants follow visibility rules similar to those of variables for Namespace blocks, Type blocks, and compound block statements.

Example

Const Red = RGB(252, 2, 4)
Const Black As UInteger = RGB(0, 0, 0)
Const Text = "This is red text on a black bkgnd."

Locate 1, 1
Color Red, Black
Print Text
Sleep
End

Differences from QB

  • QB does not support the As datatype syntax.

See also

  • #define
  • Const (Qualifier)
  • Const (Member)
  • Enum
  • Var


Const (Member)
Specifies that a member procedure is read only.

Syntax

Type typename
Declare Const Sub|Function|Property|Operator ...
End Type

[Const] Sub|Function|... typename ...
...
End Sub|Function|...

Description

Specifies that a method does not change the object it is called on. The hidden This parameter will be considered read-only. The declaration can be read as 'invoking a const method promises not to change the object', and the compiler will error if the member procedure tries to change any of the data fields, or calls a non-const member procedure.

Read-only (Const) declarations are a measure of type safety that can be read as 'promises not to change.' The compiler uses the const declarations to check operations on variables and parameters and generate an error at compile time if their data could potentially change. There is no runtime overhead for using Const qualifiers since all of the checks are made at compile time.

Constructors and destructors cannot be Const (not useful).
Member procedures can not be both Const and Static since static member procedures do not have a hidden This parameter.

For methods with Const in their declaration, Const can also be specified on the corresponding method bodies, for improved code readability.

Example

'' Const Member Procedures

Type foo
  x As Integer
  c As Const Integer = 0
  Declare Const Sub Inspect1()
  Declare Const Sub Inspect2()
  Declare Sub Mutate1()
  Declare Sub Mutate2()
End Type

''
Sub foo.Mutate1()
  '' we can change non-const data fields
  x = 1

  '' but we still can't change const data
  '' fields, they are promised not to change
  '' c = 1 '' Compile error

End Sub

''
Sub foo.Mutate2()
  '' we can call const members
  Inspect1()

  '' and non-const members
  Mutate1()

End Sub

''
Sub foo.Inspect1()
  '' can use data members
  Dim y As Integer
  y = c + x

  '' but not change them because Inspect1()
  '' is const and promises not to change foo
  '' x = 10 '' Compile error

End Sub

''
Sub foo.Inspect2()
  '' we can call const members
  Inspect1()

  '' but not non-const members
  '' Mutate1() '' Compile error

End Sub

Differences from QB

  • New to FreeBASIC

See also

  • Const
  • Const (Qualifier)
  • Dim
  • Type


Const (Qualifier)
Specifies that a data type or pointer data type is read only.

Syntax

... As [Const] datatype [ [Const] Ptr ... ]

Parameters

datatype
Name of a standard or user defined data type.

Description

Specifies that the datatype or Ptr immediately to the right of the Const qualifier is to be considered as read only. Read-only (Const) declarations are a measure of type safety that can be read as 'promises not to change.' The compiler uses the const declarations to check operations on variables and parameters and generate an error at compile time if their data could potentially change. There is no runtime overhead for using Const qualifiers since all of the checks are made at compile time.

Const can be used anywhere data type declarations are made. This includes variables, parameters, function return results, user defined type fields, type aliases, and casting. The datatype can be any built-in standard data type or user defined type.

Read-only variables must have an initializer since modifying a read-only variable through an assignment will generate a compiler error. The initializer may appear after the declaration of the variable.

Both non-const and const variables may be passed to a procedure expecting a const parameter. However, a const variable may not be passed to a procedure taking a non-const parameter, and will generate a compile error.

Procedures can be overloaded based on the const-ness of parameters. For example a procedure can be overloaded where one version of the procedure takes a 'byref foo as bar' parameter and another version of the procedure takes a 'byref foo as const bar' parameter.

With pointer declarations, Const can be used to indicate which part of the pointer declaration is read-only (all other parts are by default read-write). The read-only portion of the pointer data type could be the pointer itself (the address), what the pointer points to (the data), or both. In a declaration with more than one level of Ptr indirection, the right most Ptr indicates the highest order level of indirection and is therefore dereferenced first.

The compiler has an internal hard-limit of eight (8) levels of pointer indirection with respect to const qualifiers and the behavior of using Const with Ptr data types having greater than eight (8) levels of indirection is undefined.

Example

'' Const Variables

'' procedure taking a const parameter
Sub proc1( ByRef x As Const Integer )

  '' can't change x because it is const
  '' x = 10 '' compile error

  '' but we can use it in expressions and
  '' assign it to other variables
  Dim y As Integer
  y = x
  y = y * x + x

End Sub

'' procedure taking a non-const parameter
Sub proc2( ByRef x As Integer )
  '' we can change the value
  x = 10
End Sub

'' declare a non-const and const variable
Dim a As Integer
Dim b As Const Integer = 5

'' proc1() will accept a non-const or const
'' argument because proc1() promises not to
'' change the variable passed to it.
proc1( a )
proc1( b )

'' proc2() will accept a non-const argument
proc2( a )

'' but not a const argument because proc2()
'' might change the variable's data and we
'' promised that 'b' would not change.
'' proc2( b ) '' compile error

'' Const Pointers

'' an integer
Dim x As Integer = 1
Dim y As Integer = 2
Dim z As Integer = 3

'' To check that the compiler generates errors
'' when attempting to reassign const variables,
'' uncomment the assignments below.

''
Scope
  '' a pointer to an integer
  Dim p As Integer Ptr = @x

  p = @y       /' OK - pointer can be changed '/
  *p = z       /' OK - data can be changed '/

End Scope

''
Scope
  '' a pointer to a constant integer
  Dim p As Const Integer Ptr = @x

  p = @y       /' OK - pointer can be changed '/
  '' *p = z    /' Error - data is const '/

End Scope

''
Scope
  '' a constant pointer to an integer
  Dim p As Integer Const Ptr = @x

  '' p = @y    /' Error - pointer is const '/
  *p = z       /' OK - data can be changed '/

End Scope

''
Scope
  '' a constant pointer to a constant integer
  Dim p As Const Integer Const Ptr = @x

  '' p = @y    /' Error - pointer is const '/
  '' *p = z    /' Error - data is const '/

End Scope

'' Const Parameters in an Overloaded Procedure

'' procedure with non-const parameter
Sub foo Overload( ByRef n As Integer )
  Print "called 'foo( byref n as integer )'"
End Sub

'' procedure with const parameter
Sub foo Overload( ByRef n As Const Integer )
  Print "called 'foo( byref n as const integer )'"
End Sub

Dim x As Integer = 1
Dim y As Const Integer = 2

foo( x )
foo( y )

'' OUTPUT:
'' called 'foo( byref n as integer )'
'' called 'foo( byref n as const integer )'

Differences from QB

  • New to FreeBASIC

See also

  • Const
  • Const (Member)
  • Dim
  • Type


Constructor
Called automatically when a class or user defined type is created

Syntax

Type typename
Declare Constructor ( )
Declare Constructor ( [ ByRef | ByVal ] parameter As datatype [ = default ] [, ... ] )
End Type

Constructor typename ( [ parameters ] ) [ Export ]
statements
End Constructor

Parameters

typename
name of the Type or Class

Description

Constructor methods are called when a user defined Type or Class variable is created.

typename is the name of the type for which the Constructor method is declared and defined. Name resolution for typename follows the same rules as procedures when used in a Namespace.

More than one constructor may exist for a Type or Class, but only one will be called when a user defined Type or Class variable is created, and the exact constructor method called depends on the parameter signature matched when the variable is initialized.
More than one parameter may exist in a constructor method declaration.

A constructor method is passed a hidden This parameter having the same type as typename. This is optionally used to access the fields of the Type or Class which is to be initialized in the Constructor method.

Constructors are called when declaring global or local static instances of typename and when allocating typename dynamically using the New Expression operator. See examples below for different constructor syntaxes.

A copy Constructor is a special constructor that initializes a new object from an existing object. There are three general cases where the copy Constructor is called: when instantiating one object and initializing it with another object (in one instruction), when passing an object by value, when an object is returned from a function by value (by using Return x statement).
Note: When an object is returned from a function by value, but by using Function = x (or function_identifier = x) assignment, the Constructor is called once at first, and then the Let (Assign) operator at each assignment.
A copy Constructor must be defined if the shallow implicit copy constructor is not sufficient. This happens in cases when the object manages dynamically allocated memory or other resources which need to be specially constructed or copied (for example if a member pointer points to dynamically allocated memory, the implicit copy constructor will simply do an implicit pointer construction and a copy of value instead of allocate memory and then perform the copy of data).
Note: Even if is defined an explicit default Constructor, it is never called by the implicit copy constructor.
A copy Constructor can not declare its parameter (the object to clone) by value.

Chaining of constructors in nested types is supported. Any fields that have their own default constructor are called first.
Chaining together constructors of a same type is allowed by using the keyword Constructor(parameters) at the top of constructor. It prevents the compiler from emitting field initialization code (instead, it relies on the chained constructor to initialize everything).

Constructor can be also called directly from the typename instance like the other member methods (Sub) and with the same syntax, i.e. using a member access operator, e.g. obj.Constructor(parameters). In particular, doing this.Constructor(parameters) is not treated as chaining together constructors of a same type, and it is allowed anywhere (not only at the top of constructors). In general it's not safe to manually call the constructor on an object, because no Destructor is called, and the old object state - if any - is overwritten without any of its old members being destroyed, which could cause memory/resource leaks.

Example

Simple constructor example for beginners:
Type MyObj
  Foo As Integer Ptr

    '' Constructor to create our integer, and set its value.
  Declare Constructor( ByVal DefVal As Integer = 0 )
    '' Destroy our integer on object deletion.
  Declare Destructor()
End Type

Constructor MyObj( ByVal DefVal As Integer = 0 )
  Print "Creating a new integer in MyObj!"
  Print "The Integer will have the value of: " & DefVal
  Print ""

    '' Create a pointer, and set its value to the one passed to the
    '' Constructor.
  This.Foo = New Integer
  *This.Foo = DefVal
End Constructor

Destructor MyObj()
  Print "Deleting our Integer in MyObj!"
  Print ""

    '' Delete the pointer we created in MyObj.
  Delete This.Foo
  This.Foo = 0
End Destructor

Scope
    '' Create a MyObj type object
    '' Send the value of '10' to the constructor
  Dim As MyObj Bar = 10

    '' See if the integer's been created.  Print its value.
  Print "The Value of our integer is: " & *Bar.Foo
  Print ""

  Sleep
End Scope
  '' We've just gone out of a scope.  The Destructor should be called now
  '' Because our objects are being deleted.
Sleep

More advanced construction example, showing constructor overloading among other things:
Type sample

  _text As String

  Declare Constructor ()
  Declare Constructor ( a As Integer )
  Declare Constructor ( a As Single  )
  Declare Constructor ( a As String, b As Byte )

  Declare Operator Cast () As String

End Type

Constructor sample ()
  Print "constructor sample ()"
  Print
  this._text = "Empty"
End Constructor

Constructor sample ( a As Integer )
  Print "constructor sample ( a as integer )"
  Print "  a = "; a
  Print
  this._text = Str(a)
End Constructor

Constructor sample ( a As Single )
  Print "constructor sample ( a as single )"
  Print "  a = "; a
  Print
  this._text = Str(a)
End Constructor

Constructor sample ( a As String, b As Byte )
  Print "constructor sample ( a as string, b as byte )"
  Print "  a = "; a
  Print "  b = "; b
  Print
  this._text = Str(a) + "," + Str(b)
End Constructor

Operator sample.cast () As String
  Return this._text
End Operator

Print "Creating x1"
Dim x1 As sample

Print "Creating x2"
Dim x2 As sample = 1

Print "Creating x3"
Dim x3 As sample = 99.9

Print "Creating x4"
Dim x4 As sample = sample( "aaa", 1 )

Print "Values:"
Print "  x1 = "; x1
Print "  x2 = "; x2
Print "  x3 = "; x3
Print "  x4 = "; x4

Example of copy constructor:
Type UDT
  Dim As String Ptr p                     ''pointer to string
  Declare Constructor ()                  ''default constructor
  Declare Constructor (ByRef rhs As UDT)  ''copy constructor
  Declare Destructor ()                   ''destructor
End Type

Constructor UDT ()
  This.p = CAllocate(1, SizeOf(String))
End Constructor

Constructor UDT (ByRef rhs As UDT)
  This.p = CAllocate(1, SizeOf(String))
  *This.p = *rhs.p
End Constructor

Destructor UDT ()
  *This.p = ""
  Deallocate This.p
End Destructor

Dim As UDT u0
*u0.p = "copy constructor exists"
Dim As UDT u = u0
*u0.p = ""  ''to check the independance of the result copy with the object copied
Print *u.p
Sleep

Dialect Differences

  • Object-related features are supported only in the -lang fb option

Differences from QB

  • New to FreeBASIC

See also

  • Class
  • Constructor (Module)
  • New Expression
  • Destructor
  • Type
  • Coercion and Conversion


Constructor (Module)
Specifies execution of a procedure before module-level code

Syntax

[Public | Private] Sub procedure_name [Alias "external_identifier"] [()] Constructor [priority]
{ procedure body }
End Sub

Description

The Constructor keyword is used in Sub definitions (forbidden at declaration line level) to force execution of the procedure prior to that of module-level code. Procedures defined as constructors may be used the same way as ordinary procedures, that is, they may be called from within module-level code, as well as other procedures.

The procedure must have an empty parameter list. A compile-time error will be generated if the Constructor keyword is used in a Sub definition having one or more parameters. In a set of overloaded procedures, only one (1) constructor may be defined because of the ambiguity of having multiple Subs which take no arguments.

In a single module, depending on the build and run-time environment of the target system:
    • constructors may execute in which they are defined, or reverse order
    • constructors may execute before or after global static variables having constructors
    • constructors may execute before or after other module constructors having priority attribute
    • constructors with priority attribute may execute before or after global static variables having constructors

The priority attribute, an integer between 101 and 65535, can be used to force constructors to be executed in a certain order, relative to other constructors also having priority attribute. The value of priority has no specific meaning, only the relationship of the number with other constructor priorities. 101 is the highest priority and is executed first, relative to other constructors also having priority attribute.

A module may define multiple constructor procedures, and multiple modules may define additional constructors provided no two Public constructors share the same procedure_name.

When linking with modules that also define constructors, the order of execution is not guaranteed at link-time unless the priority attribute is used. Therefore, special care should be taken when using constructors that may call on a secondary module also defining a constructor. In such a case it is advisable to use a single constructor that explicitly calls initialization procedures in those modules.

Public static member procedures (a Sub having an empty parameter list), of user defined Type can be defined as a module constructor, by adding the Constructor keyword used in the sub procedure definition.

Initialization of static simple numeric type variables, that have a value that can be determined at compile time (for example, default zero, constants, pointers to static objects, pointers to functions, etc), are initialized before any code is executed. These values are part of the executable image and have an initial value when the executable is loaded in to memory. Trivial static globals where no code is needed to initialize, are guaranteed to be initialized and can be reliably used in all code, including global static object constructors and module constructors.

The module constructor feature exposes a low-level link-time feature of the build and run-time environment. Accessing global static objects having constructors from module constructors should be avoided due to variations in execution order on different build systems.

Warning for 64-bit compiler only: See the Identifier Rules page for the choice of user procedure identifier names (and specially the 'Platform Differences' paragraph).

Example

'' ConDesExample.bas : An example program that defines two sets of
'' constructors and destructors. Demonstrates when and in what order
'' they are called when linking a single module.

Sub Constructor1() Constructor
    Print "Constructor1() called"
End Sub

Sub Destructor1() Destructor
    Print "Destructor1() called"
End Sub

Sub Constructor2() Constructor
    Print "Constructor2() called"
End Sub

Sub Destructor2() Destructor
    Print "Destructor2() called"
End Sub

    '' ----------------------
    Print "module-level code"

    End 0
    '' ----------------------

Output:
Constructor2() called
Constructor1() called
module-level code
Destructor1() called
Destructor2() called

Differences from QB

  • New to FreeBASIC

See also

  • Constructor (Class)
  • Destructor (Module)
  • Sub


Continue
Control flow statement to continue next iteration of a loop

Syntax

Continue {Do | For | While}

Description

Skips all code until the end clause of a loop structure, i.e. Do...Loop, For...Next, or a While...Wend block, then executes the limit condition check. In the case of a For...Next, the variable is incremented according to the Step specified.

Where there are multiple Do / For / While blocks nested, it will continue on the innermost block of that type, i.e. the last one entered. You can continue an earlier block of that type by giving the word multiple times, separated by commas. e.g. continue while, while

Example

Dim As Integer n

Print "Here are odd numbers between 0 and 10!"
Print
For n = 0 To 10

  If ( n Mod 2 ) = 0 Then
    Continue For
  End If

  Print n

Next n

 '' simple prime number finder

Print "Here are the prime numbers between 1 and 20!"
Print

Dim n As Integer, d As Integer

For n = 2 To 20

    For d = 2 To Int(Sqr(n))

        If ( n Mod d ) = 0 Then ' d divides n

            Continue For, For ' n is not prime, so try next n

        End If

    Next d

    Print n

Next n

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Continue.

Differences from QB

  • New to FreeBASIC

See also

  • Exit


Cos
Returns the cosine of an angle

Syntax

Declare Function Cos ( ByVal angle As Double ) As Double

Usage

result = Cos( angle )

Parameters

angle
the angle (in radians)

Return Value

Returns the cosine of the argument angle as a Double within the range of -1.0 to 1.0.

Description

The argument number is measured in radians (not degrees).

The value returned by this function is undefined for values of angle with an absolute value of 2 ^ 63 or greater.

Cos can be overloaded as operator to accept user-defined types.

Example

Const PI As Double = 3.1415926535897932
Dim a As Double
Dim r As Double
Input "Please enter an angle in degrees: ", a
r = a * PI / 180    'Convert the degrees to Radians
Print ""
Print "The cosine of a" ; a; " degree angle is"; Cos ( r )
Sleep

Output:
Please enter an angle in degrees: 30
The cosine of a 30 degree angle Is 0.8660254037844387

Differences from QB

  • None

See also

  • Acos
  • Sin
  • Tan
  • A Brief Introduction To Trigonometry


CPtr
Converts a pointer expression to a specified data type pointer

Syntax

CPtr( PointerDataType, expression )

Description

Converts expression to PointerDataType.

PointerDataType must be a Pointer type (e.g. a DataType Ptr or an Any Ptr), or a Type (Alias) to one.
expression may have a different pointer type or be an Integer.

Note: Currently, FB does not actually enforce that PointerDataType must be a pointer. This will likely change in future versions though. Currently, it may display a warning if you try to convert to a non-pointer (for any conversion type if you compile with the -w pedantic compiler switch).

Example

Dim intval As Integer
Dim intptr As Integer Ptr
intval = &h0080
intptr = @intval
'' will print -128 and 128, as the first expression will be "seen" as an signed byte
Print *CPtr( Byte Ptr, intptr ), *intptr

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Cptr.

Differences from QB

  • New to FreeBASIC

See also

  • Ptr
  • Cast
  • CByte
  • CShort
  • CInt
  • CLngInt
  • CSng
  • CDbl


CShort
Converts numeric or string expression to an integer (Short)

Syntax

Declare Function CShort ( ByVal expression As datatype ) As Short

Type typename
Declare Operator Cast ( ) As Short
End Type

Usage

result = CShort( numeric expression )
result = CShort( string expression )
result = CShort( user defined type )

Parameters

expression
a numeric, string, or pointer expression to cast to a Short value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

A Short value.

Description

The CShort function rounds off the decimal part and returns a 16-bit Short value. The function does not check for an overflow, and results are undefined for values which are less than -32 768 or larger than 32 767.

The name can be explained as 'Convert to Short'.

If the argument is a string expression, it is converted to numeric by using ValInt.

Example

' Using the CSHORT function to convert a numeric value

'Create an SHORT variable
Dim numeric_value As Short

'Convert a numeric value
numeric_value = CShort(-4500.66)

'Print the result, should return -4501
Print numeric_value
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Cshort.

Differences from QB

  • New to FreeBASIC

See also

  • CByte
  • CUByte
  • CUShort
  • CInt
  • CUInt
  • CLng
  • CULng
  • CLngInt
  • CULngInt
  • CSng
  • CDbl


CSign
Converts an expression to signed

Syntax

CSign ( expression )

Usage

variable = CSign ( expression )

Description

Converts an unsigned expression to a signed one, useful to force signed behavior of division or multiplication (including with Shl and Shr).

This is the opposite of CUnsg.

Example

Dim value As UShort = 65535
Print CSign(value)  '' will print -1

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Csign.

Differences from QB

  • New to FreeBASIC

See also

  • CUnsg


CSng
Converts numeric or string expression to Single precision floating point

Syntax

Declare Function CSng ( ByVal expression As datatype ) As Single

Type typename
Declare Operator Cast ( ) As Single
End Type

Usage

result = CSng( numeric expression )
result = CSng( string expression )
result = CSng( user defined type )

Parameters

expression
a numeric, string, or pointer expression to cast to a Single value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

A Single precision value.

Description

The CSng function returns a 32-bit Single value. The function does not check for an overflow, so be sure not to pass a value outside the representable range of the Single data type. The name can be explained as 'Convert to SiNGle'.

If the argument to CSng is a string expression, it is first converted to numeric by using Val.

Example

' Using the CSNG function to convert a numeric value

'Create an SINGLE variable
Dim numeric_value As Single

'Convert a numeric value
numeric_value = CSng(-12345.123)

'Print the result, should return -12345.123
Print numeric_value
Sleep

Differences from QB

  • The string argument was not allowed in QB

See also

  • CByte
  • CUByte
  • CShort
  • CUShort
  • CInt
  • CUInt
  • CLng
  • CULng
  • CLngInt
  • CULngInt
  • CDbl


CsrLin
Returns the row position of the cursor

Syntax

Declare Function CsrLin ( ) As Long

Usage

result = CsrLin

Return Value

An Long specifying the current row of the cursor.

Description

Returns the current row the cursor is on (i.e. the "cursor line"). The topmost row is number 1.

Example

Print "The cursor is on row:"; CsrLin

Differences from QB

  • None

See also

  • Locate
  • Pos


CUByte
Converts numeric or string expression to an unsigned byte (UByte)

Syntax

Declare Function CUByte ( ByVal expression As datatype ) As UByte

Type typename
Declare Operator Cast ( ) As UByte
End Type

Usage

result = CUByte( numeric expression )
result = CUByte( string expression )
result = CUByte( user defined type )

Parameters

expression
a numeric, string, or pointer expression to cast to a UByte value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

A UByte value.

Description

The CUByte function rounds off the decimal part and returns a 8-bit UByte value. The function does not check for an overflow, and results are undefined for values which are less than 0 or larger than 255.

The name can be explained as 'Convert to Unsigned Byte'.

If the argument is a string expression, it is converted to numeric by using ValUInt.

Example

' Using the CUBYTE function to convert a numeric value

'Create an UNSIGNED BYTE variable
Dim numeric_value As UByte

'Convert a numeric value
numeric_value = CUByte(123.55)

'Print the result, should return 124
Print numeric_value
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Cubyte.

Differences from QB

  • New to FreeBASIC

See also

  • CByte
  • CShort
  • CUShort
  • CInt
  • CUInt
  • CLng
  • CULng
  • CLngInt
  • CULngInt
  • CSng
  • CDbl


CUInt
Converts numeric or string expression to a UInteger or UInteger<bits>

Syntax

Declare Function CUInt ( ByVal expression As datatype ) As UInteger
Declare Function CUInt<bits> ( ByVal expression As datatype ) As UInteger<bits>

Type typename
Declare Operator Cast ( ) As UInteger
Declare Operator Cast ( ) As UInteger<bits>
End Type

Usage

result = CUInt( numeric expression )
result = CUInt( string expression )
result = CUInt( user defined type )

Parameters

bits
A numeric constant expression indicating the size in bits of unsigned integer desired. The values allowed are 8, 16, 32 or 64.
expression
a numeric, string, or pointer expression to cast to a UInteger or UInteger<bits> value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

A UInteger or UInteger<bits> containing the converted value.

Description

The CUInt function rounds off the decimal part and returns a UInteger value, or if a bits value is supplied, an unsigned integer type of the given size.

The function does not check for an overflow; for example, for a 32-bit UInteger results are undefined for values which are less than 0 or larger than 4 294 967 296.

The name can be explained as 'Convert to Unsigned INTeger'.

If the argument is a string expression, it is converted to numeric by using ValUInt or ValULng, depending on the size of the result type.

Example

' Using the CUINT function to convert a numeric value

'Create an UNSIGNED INTEGER variable
Dim numeric_value As UInteger

'Convert a numeric value
numeric_value = CUInt(300.23)

'Print the result = 300
Print numeric_value

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Cuint.

Differences from QB

  • New to FreeBASIC

See also

  • CByte
  • CUByte
  • CShort
  • CUShort
  • CInt
  • CLng
  • CULng
  • CLngInt
  • CULngInt
  • CSng
  • CDbl
  • UInteger


CULng
Converts numeric or string expression to ULong

Syntax

Declare Function CULng ( ByVal expression As datatype ) As ULong

Type typename
Declare Operator Cast ( ) As ULong
End Type

Usage

result = CULng( numeric expression )
result = CULng( string expression )
result = CULng( user defined type )

Parameters

expression
a numeric, string, or pointer expression to cast to a ULong value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

A ULong value.

Description

The CULng function rounds off the decimal part and returns a 32 bit ULong value. The function does not check for an overflow. The name can be explained as 'Convert to Unsigned LoNG'.

If the argument is a string expression, it is converted to numeric by using ValUInt or ValULng.

Example

' Using the CULNG function to convert a numeric value

'Create an UNSIGNED LONG variable
Dim numeric_value As ULong

'Convert a numeric value
numeric_value = CULng(300.23)

'Print the result = 300
Print numeric_value
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Culng.

Differences from QB

  • New to FreeBASIC

See also

  • CByte
  • CUByte
  • CShort
  • CUShort
  • CInt
  • CUInt
  • CLng
  • CLngInt
  • CULngInt
  • CSng
  • CDbl


CULngInt
Converts numeric or string expression to 64-bit unsigned integer (ULongInt)

Syntax

Declare Function CULngInt ( ByVal expression As datatype ) As ULongInt

Type typename
Declare Operator Cast ( ) As ULongInt
End Type

Usage

result = CULngInt( numeric expression )
result = CULngInt( string expression )
result = CULngInt( user defined type )

Parameters

expression
a numeric, string, or pointer expression to cast to a ULongInt value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

A ULongInt value.

Description

The CULngInt function rounds off the decimal part and returns a 64-bit ULongInt value. The function does not check for an overflow, and results are undefined for values which are less than 0 or larger than 18 446 744 073 709 551 615. Additionally, casts from floating-point expressions are currently not guaranteed to work for values higher than 2^63 (9 223 372 036 854 775 808).

The name can be explained as 'Convert to Unsigned LoNG INTeger'.

If the argument is a string expression, it is converted to numeric by using ValULng.

Example

' Using the CLNGINT function to convert a numeric value

'Create an UNSIGNED LONG INTEGER variable
Dim numeric_value As ULongInt

'Convert a numeric value
numeric_value = CULngInt(12345678.123)

'Print the result, should return 12345678
Print numeric_value
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Culngint.

Differences from QB

  • New to FreeBASIC

See also

  • CByte
  • CUByte
  • CShort
  • CUShort
  • CInt
  • CUInt
  • CLng
  • CULng
  • CLngInt
  • CSng
  • CDbl


CUnsg
Converts an expression to unsigned

Syntax

CUnsg ( expression )

Usage

variable = CUnsg ( expression )

Converts a signed expression to an unsigned one, useful to force unsigned behavior of division or multiplication (including with Shl and Shr).

This is the opposite of CSign.

Example

Dim value As Short = -1
Print CUnsg(value)  '' will print 65535

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Cunsg.

Differences from QB

  • New to FreeBASIC

See also

  • CSign


CurDir
Returns the current directory/folder

Syntax

Declare Function CurDir ( ) As String

Usage

result = CurDir

Return Value

A String which is set to the name of the current directory/folder.

Description

Returns the current directory/folder.

Example

Print CurDir

output will vary.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Curdir.

Differences from QB

  • New to FreeBASIC

See also

  • Open
  • Dir
  • ChDir
  • MkDir
  • RmDir


CUShort
Converts numeric or string expression to an unsigned integer (UShort)

Syntax

Declare Function CUShort ( ByVal expression As datatype ) As UShort

Type typename
Declare Operator Cast ( ) As UShort
End Type

Usage

result = CUShort( numeric expression )
result = CUShort( string expression )
result = CUShort( user defined type )

Parameters

expression
a numeric, string, or pointer expression to cast to a UShort value
datatype
any numeric, string, or pointer data type
typename
a user defined type

Return Value

A UShort value.

Description

The CUShort function rounds off the decimal part and returns a 16-bit UShort value. The function does not check for an overflow, and results are undefined for values which are less than 0 or larger than 65 535.

The name can be explained as 'Convert to Unsigned Short'.

If the argument is a string expression, it is converted to numeric by using ValUInt.

Example

' Using the CUSHORT function to convert a numeric value

'Create an USHORT variable
Dim numeric_value As UShort

'Convert a numeric value
numeric_value = CUShort(36000.4)

'Print the result, should return 36000
Print numeric_value
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Cushort.

Differences from QB

  • New to FreeBASIC

See also

  • CByte
  • CUByte
  • CShort
  • CInt
  • CUInt
  • CLng
  • CULng
  • CLngInt
  • CULngInt
  • CSng
  • CDbl


Custom
Parameter to the Put graphics statement which selects a custom method

Syntax

Put [ target, ] [ STEP ] ( x,y ), source [ ,( x1,y1 )-( x2,y2 ) ], Custom, custom_function_ptr [, parameter]

Parameters

Custom
Required.
custom_function_ptr
name of the custom user defined function.
parameter
optional Pointer to be passed to the custom function; if omitted, the default value is zero (0).

Description

Custom selects a custom user defined function as the method for blitting an image buffer.

The Custom method uses a user-defined function to calculate the final pixel values to be drawn to the destination buffer. This function will be called once for every pixel of the source image, and will receive the source and destination pixel values, and a data pointer passed by the Put function. The pixel value returned will be the value used to draw to the destination buffer. The function has the form:

Declare Function identifier ( _
ByVal source_pixel As ULong, _
ByVal destination_pixel As ULong, _
ByVal parameter As Any Ptr _
) As ULong

identifier is the name of the function.
source_pixel is the current pixel value of the source image.
destination_pixel is the current pixel value of the destination image.
parameter is the parameter that is passed by the Put command. If it was omitted, its value will be zero.

Example

Function dither ( ByVal source_pixel As ULong, ByVal destination_pixel As ULong, ByVal parameter As Any Ptr ) As ULong

    ''either returns the source pixel or the destination pixel, depending on the outcome of rnd

    Dim threshold As Single = 0.5
    If parameter <> 0 Then threshold = *CPtr(Single Ptr, parameter)

    If Rnd() < threshold Then
        Return source_pixel
    Else
        Return destination_pixel
    End If

End Function

Dim img As Any Ptr, threshold As Single

'' set up a screen
ScreenRes 320, 200, 16, 2
ScreenSet 0, 1

'' create an image
img = ImageCreate(32, 32)
Line img, ( 0,  0)-(15,  15), RGB(255,   0,   0), bf
Line img, (16,  0)-(31,  15), RGB(  0,   0, 255), bf
Line img, ( 0, 16)-(15,  31), RGB(  0, 255,   0), bf
Line img, (16, 16)-(31,  31), RGB(255,   0, 255), bf

'' dither the image with varying thresholds
Do Until Len(Inkey)

    Cls

    threshold = 0.2
    Put ( 80 - 16, 100 - 16), img, Custom, @dither, @threshold

    '' default threshold = 0.5
    Put (160 - 16, 100 - 16), img, Custom, @dither

    threshold = 0.8
    Put (240 - 16, 100 - 16), img, Custom, @dither, @threshold

    ScreenCopy
    Sleep 25

Loop

'' free the image memory
ImageDestroy img

Dialect Differences

  • Not available in the -lang qb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Put (Graphics)


Cva_Arg
Macro to obtain the next argument from a variadic argument list object.

Syntax

variable = Cva_Arg ( argument_list, datatype )

Parameters

argument_list
Cva_List data type variable to access for next value
datatype
The datatype of the next value in the variable argument list argument_list

Description

The Cva_Arg macro allows the use of a variable number of arguments within a function:
    • Cva_Arg returns the current argument in the list, argument_list, with an expected data type of datatype.
    • Before first Cva_Arg use, argument_list must be initialized with the command Cva_Start or Cva_Copy.
    • Cva_Arg automatically increments argument_list to the next argument within the list after obtaining the value of the current argument.

Example

'' Example of a simple custom printf
Sub myprintf cdecl(ByRef formatstring As String, ...)
    Dim As Cva_List args

    '' Initialize the cva_list object to first var-arg
    Cva_Start( args, formatstring )

    '' For each char in format string...
    Dim As UByte Ptr p = StrPtr(formatstring)
    Dim As Integer todo = Len(formatstring)
    While (todo > 0)
        Dim As Integer char = *p
        p += 1
        todo -= 1

        '' Is it a format char?
        If (char = Asc("%")) Then
            If (todo = 0) Then
                '' % at the end
                Print "%";
                Exit While
            End If

            '' The next char should tell the type
            char = *p
            p += 1
            todo -= 1

            '' Print var-arg, depending on the type
            Select Case char
            '' integer?
            Case Asc("i")
                Print Str(Cva_Arg(args, Integer));

            '' long integer? (64-bit)
            Case Asc("l")
                Print Str(Cva_Arg(args, LongInt));

            '' single or double?
            '' Note: because the C ABI, all singles passed on
            '' var-args are converted to doubles.
            Case Asc( "f" ), Asc( "d" )
                Print Str(Cva_Arg(args, Double));

            '' string?
            Case Asc("s")
                '' Strings are passed byval, so the length is unknown
                Print *Cva_Arg(args, ZString Ptr);

            End Select

        '' Ordinary char, just print as-is
        Else
            Print Chr( char );
        End If
    Wend

    Cva_End( args )

End Sub

Dim As String s = "bar"

myprintf(!"integer=%i, longint=%l single=%f, double=%d, string=%s, string=%s\n", _
         1, 1ll Shl 32, 2.2, 3.3, "foo", s)

Version

  • Since fbc 1.07.0

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __cva_arg.

Differences from QB

  • New to FreeBASIC

See also

  • ... (Ellipsis)
  • Cva_Copy
  • Cva_End
  • Cva_List
  • Cva_Start


Cva_Copy
Macro to initialize variadic argument list object variable from an already initialized variadic argument list object variable

Syntax

Cva_Copy( dst_list, src_list )

Parameters

dst_list
destination Cva_List variable to initialize
src_list
source Cva_List variable to copy from

Description

Copies one Cva_List type variable to another Cva_List type variable. dst_list is initialized using current state of src_list

src_list must already have been initialized with a previous Cva_Start or Cva_Copy statement.

Cva_Copy is like a copy constructor for the a variadic argument list object and must eventually have a matching call to Cva_End, which is like the destructor. After Cva_End for dst_list has been called, dst_list can be reused and reinitialized with another call to Cva_Start or Cva_Copy. The Cva_Copy and Cva_End calls must both be called in the same procedure (for cross platform compatibility).

Example

'' example of using cva_copy to create
'' a copy of the variable argument list

Sub proc cdecl(count As Integer, ... )
    Dim args1 As Cva_List
    Dim args2 As Cva_List

    '' first list
    Cva_Start( args1, count )

    '' create a copy
    Cva_Copy( args2, args1 )

    For i As Integer = 1 To count
        Print Cva_Arg( args1, Integer ), Cva_Arg( args2, Integer )
    Next

    '' clean-up
    Cva_End( args2 )
    Cva_End( args1 )

End Sub

proc( 4, 4000, 300, 20, 1 )

'' example of using cva_copy to create
'' a copy of the variable argument list
'' and pass it to another procedure

Sub vproc cdecl(count As Integer, ByRef args As Cva_List )

    '' if we don't know that caller made a copy
    '' of args, it is safe to make our own copy
    '' and leave the passed in args untouched

    Dim a As Cva_List
    Cva_Copy( a, args )

    Print "vproc"
    For i As Integer = 1 To count
        Print Cva_Arg( a, Integer )
    Next

    '' clean-up
    Cva_End( a )

End Sub

Sub proc cdecl(count As Integer, ... )

    Dim args As Cva_List
    Cva_Start( args, count )

    '' if don't know that the called procedure
    '' will make it's own copy, it is safe to
    '' make a copy here and pass that instead

    Dim tmp As Cva_List
    Cva_Copy( tmp, args )
    vproc( count, tmp )
    Cva_End( tmp )

    '' args is still valid, we can use it
    Print "proc"
    For i As Integer = 1 To count
        Print Cva_Arg( args, Integer )
    Next

    '' clean-up
    Cva_End( args )

End Sub

proc( 4, 4000, 300, 20, 1 )

Version

  • Since fbc 1.07.0

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __cva_copy.

Differences from QB

  • New to FreeBASIC

See also

  • ... (Ellipsis)
  • Cva_Arg
  • Cva_End
  • Cva_List
  • Cva_Start


Cva_End
Macro to clean-up a variadic argument list object variable

Syntax

Cva_End( argument_list )

Parameters

argument_list
Cva_List variable to clean up

Description

Cleans-up a Cva_List type variable that was previously initialized with Cva_Start or Cva_Copy, like a destructor.

argument_list must already have been initialized with a previous Cva_Start or Cva_Copy statement in the same procedure as Cva_End (for cross platform compatibility).

Example

Sub proc cdecl(count As Integer, ... )
    Dim args As Cva_List

    Cva_Start( args, count )

    For i As Integer = 1 To count
        Print Cva_Arg( args, Integer )
    Next

    Cva_End( args )
End Sub

proc( 4, 4000, 300, 20, 1 )

Version

  • Since fbc 1.07.0

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __cva_end.

Differences from QB

  • New to FreeBASIC

See also

  • ... (Ellipsis)
  • Cva_Arg
  • Cva_Copy
  • Cva_List
  • Cva_Start


Cva_List
Variadic argument list object type

Syntax

Dim variable As Cva_List

Description

Cva_List is a built in data type for working with the variable length argument list in a variadic procedure.

Use Cva_Start to initialize the Cva_List variable (constructor).
Use Cva_Copy to copy a Cva_List variable (copy constructor).
Use Cva_Arg to obtain the value of the next argument.
Use Cva_End when finished with a Cva_List variable (destructor).

The exact type and size of Cva_List varies depending on the target platform. This builtin type provides a layer of abstraction over the underlying mechanism for obtaining the values passed to a variadic procedure.

fbc will select a default Cva_List type depending on -target, -arch, and -gen command line options, as described in the following code.
#if (__FB_BACKEND__ = "gcc")
    #if defined( __FB_64BIT__ )
        #if defined( __FB_ARM__ )
            Type __va_list Alias "__va_list"
                As Any Ptr __stack
                As Any Ptr __gr_top
                As Any Ptr __vr_top
                As Long __gr_offs
                As Long __vr_offs
            End Type
            Type Cva_List As __va_list Alias "__builtin_va_list"
        #elseif defined( __FB_WIN32__ )
            Type Cva_List As Any Alias "__builtin_va_list" Ptr
        #else
            Type __va_list_tag Alias "__va_list_tag"
                As ULong gp_offset
                As ULong fp_offset
                As Any Ptr overflow_arg_area
                As Any Ptr reg_save_area
            End Type
            Type Cva_List As __va_list_tag Alias "__builtin_va_list[]"
        #endif
    #else
        Type Cva_List As Any Alias "__builtin_va_list" Ptr
    #endif
#else
    Type Cva_List As Any Alias "char" Ptr
#endif

Example

Function average cdecl(count As Integer, ... ) As Double

    Dim sum As Double = 0
    Dim i As Integer

    Dim args As Cva_List '' argument list object
    Cva_Start( args, count ) '' constructor
    For i = 1 To count
        sum += Cva_Arg(args, Double)
    Next
    Cva_End( args ) '' destructor

    Return sum / count
End Function

Print average(4, 3.4 ,5.0, 3.2, 4.1) '' all passed variable arguments must be of type double
Print average(2, 65.2, 454.65481)    '' all passed variable arguments must be of type double

The output would look like:
 3.925
 259.927405

'' pass the args list to a function taking an cva_list type argument
#include "crt/stdio.bi"

Sub myprintf cdecl( fmt As ZString Ptr, ... )
    Dim args As Cva_List
    Cva_Start( args, fmt )
    vprintf( fmt, args )
    Cva_End( args )
End Sub

Dim As String s = "bar"

myprintf( !"integer=%i, longint=%lli float=%f\n", _
    1, 1ll Shl 32, 3.3 )

myprintf( !"string=%s, string=%s\n", "foo", s )

Version

  • Since fbc 1.07.0

Platform Differences

  • The exact type and size of Cva_List depends on -target, -arch, -gen command line options.
  • In -gen gas, Cva_List is a pointer
  • In -gen gcc, Cva_List is a pointer, a struct, or a struct array. The Cva_List type is replaced by "__builtin_va_list" in gcc.
  • On 32-bit targets, gas backend: type Cva_List as any alias "char" ptr.
  • On 32-bit targets, gcc backend: type Cva_List as any alias "__builtin_va_list" ptr
  • On Windows 64-bit, gcc backend: type Cva_List as any alias "__builtin_va_list" ptr
  • On Linux x86_64, gcc backend: type Cva_List as __va_list_tag alias "__builtin_va_list[]"
  • On arm64, gcc backend: type Cva_List as __va_list alias "__builtin_va_list"

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __cva_list.

Differences from QB

  • New to FreeBASIC

See also

  • ... (Ellipsis)
  • Cva_Arg
  • Cva_Start
  • Cva_Copy
  • Cva_End


Cva_Start
Macro to initialize variadic argument list object variable

Syntax

Cva_Start( argument_list, last_param )

Parameters

argument_list
Cva_List data type variable to initialize
last_param
The last parameter in the procedures parameter list before the Ellipsis ...

Description

In a variadic procedure definition, argument_list is a variable having the Cva_List type and must be initialized with Cva_Start to work with the variable length argument list passed to the procedure.

last_param is the last parameter before the Ellipsis ... in the variadic procedure definition.

Cva_Start can only be used in variadic procedures. A variadic procedure is declared or defined by specifying the Ellipsis ... as the last parameter, and will accept a variable number of arguments when calling the procedure.

Cva_Start is like a constructor for the variadic argument_list object and must eventually have a matching call to Cva_End, which is like a destructor. After Cva_End for argument_list has been called, argument_list can be reused and reinitialized with another call to Cva_Start. The Cva_Start and Cva_End calls must both be called in pairs in the same procedure (for cross platform compatibility).

Cva_Copy is similar to Cva_Start except it initializes a variadic argument_list object from an already initialized variadic argument_list object, like a copy constructor.

Example

'' typical usage of iterating through all arguments

Sub proc cdecl(count As Integer, ... )
    Dim args As Cva_List
    Cva_Start( args, count )
    For i As Integer = 1 To count
        Print Cva_Arg( args, Integer )
    Next
    Cva_End( args )
End Sub

proc( 4, 4000, 300, 20, 1 )

'' example of using cva_start to get the first argument
'' then restarting to get all the arguments

Sub proc cdecl(count As Integer, ... )
    Dim args As Cva_List

    '' get the first argument only
    Cva_Start( args, count )
    Print Cva_Arg( args, Integer )
    Cva_End( args )

    '' restart and get all the arguments
    Cva_Start( args, count )
    For i As Integer = 1 To count
        Print Cva_Arg( args, Integer )
    Next
    Cva_End( args )

End Sub

proc( 4, 4000, 300, 20, 1 )

Version

  • Since fbc 1.07.0

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __cva_start.

Differences from QB

  • New to FreeBASIC

See also

  • ... (Ellipsis)
  • Cva_Arg
  • Cva_Copy
  • Cva_End
  • Cva_List


CVD
Converts a 64-bit integer or 8-byte string to a double-precision value

Syntax

Declare Function CVD ( ByVal l As LongInt ) As Double
Declare Function CVD ( ByRef str As Const String ) As Double

Usage

result = CVD( l )
result = CVD( str )

Parameters

l
A 64-bit LongInt with a binary copy of a double-precision variable stored in it.
str
A String at least 8 bytes in length with a binary copy of a double-precision variable stored in it.

Return Value

Returns a Double value holding a binary copy of the input value.

Description

Does a binary copy from a 64-bit LongInt or 8-byte String to a Double variable. A value of zero (0.0) is returned if the string is less than 8 bytes in length. The result will make sense only if the parameter contained a IEEE-754 formatted double-precision value, such as one generated by CVLongInt or MKD.

This function is useful to read numeric values from buffers without using a Type definition.

Example

Dim d As Double, l As LongInt
d = 1.125
l = CVLongInt(d)

Print Using "l = _&H&"; Hex(l)
Print Using "cvd(i) = &"; CVD(l)

Differences from QB

  • QB did not support integer arguments.

See also

  • MKD
  • CVS
  • CVLongInt


CVI
Converts a floating-point number or string to an integer variable using a binary copy

Syntax

32-bit:
Declare Function CVI ( ByVal f As Single ) As Integer
64-bit:
Declare Function CVI ( ByVal f As Double ) As Integer

Declare Function CVI ( ByRef str As Const String ) As Integer
Declare Function CVI<bits> ( expr As DataType ) As Integer<bits>

Usage

result = CVI( sng )
result = CVI( str )
result = CVI<bits>( expr )

Parameters

f
A floating-point number with a binary copy of an integer variable stored in it. Its precision (Single or Double) depends on the size of Integer on the current platform
str
A String with a binary copy of an integer variable stored in it.
bits
Specifies a size of integer type to return. The types and sizes of expr accepted will depend on the corresponding function called.
expr
An expression that will be copied into an Integer<bits>.

Return Value

An Integer or Integer<bits> variable containing a binary copy of the input expression.

Description

Returns an integer value using the binary data contained in a floating-point value, or a String. A value of zero (0) is returned if the string contains fewer characters than the size of the return type.

CVI can be used to convert strings created with MKI.

This function can also be used to convert Integer-sized values from a memory or file buffer without the need for a Type structure. However, just as with the type structure, special care should be taken when using CVI to convert strings that have been read from a buffer.

CVI supports an optional <bits> parameter before the argument. If bits is 16, CVShort will be called instead; if bits is 32, CVL will be called; if bits is 64, CVLongInt will be called. The return type and accepted argument types will depend on which function is called. See each function's page for more information.

CVI's behaviour changes depending on the size of the Integer data type on the current platform.
  • For 16-bit Integer (-lang qb), a 16-bit value is returned, and no floating-point types are accepted.
  • For 32-bit Integer, a 32-bit value is returned, and numeric arguments are interpreted as Single precision values.
  • For 64-bit Integer, a 64-bit value is returned, and numeric arguments are interpreted as Double precision values.

Example

Dim i As Integer, s As String
s = "ABCD"
i = CVI(s)
Print Using "s = ""&"""; s
Print Using "i = _&H&"; Hex(i)

Dialect Differences

  • In the -lang qb dialect, CVI expects a 2-byte string, since a QB integer is only 16 bits. Only the first two bytes of the string are used, even if the string happens to be longer than two bytes.
  • In the -lang qb dialect, CVI will not take a floating-point argument, since a QB integer is only 16 bits and there is no 16-bit floating-point data type. Instead, CVI<32>/CVI<64> or CVL/CVLongInt may be used.

Differences from QB

  • In QB an error occurs if the string passed is fewer than two bytes in length.
  • QB did not support floating-point arguments.
  • QB did not support a <bits> parameter.

See also

  • MKI
  • CVShort
  • CVL
  • CVLongInt
  • Integer


CVL
Converts a single-precision floating-point number or four-byte string to an integer (Long) variable

Syntax

Declare Function CVL ( ByVal sng As Single ) As Long
Declare Function CVL ( ByRef str As Const String ) As Long

Usage

result = CVL( sng )
result = CVL( str )

Parameters

sng
A Single floating-point number with a binary copy of an integer variable stored in it.
str
A String at least four bytes in length with a binary copy of an integer variable stored in it.

Return Value

A Long variable to copy the binary copy of a integer to.

Description

Returns a 32-bit Long integer value using the binary data contained in a Single, or a String of at least four bytes in length. A value of zero (0) is returned if the string is less than four bytes in length.

CVL is used to convert 4-byte strings created with MKL.

This function can also be used to convert 32-bit integer values from a memory or file buffer without the need for a Type structure. However, just as with the type structure, special care should be taken when using CVL to convert strings that have been read from a buffer.

Example

Dim l As Long, s As String
s = "ABCD"
l = CVL(s)
Print Using "s = ""&"""; s
Print Using "l = &"; l

Differences from QB

  • In QB an error occurs if the string passed is less than four bytes in length.
  • QB did not support floating-point arguments.

See also

  • MKL
  • CVShort
  • CVI
  • CVLongInt


CVLongInt
Converts a double-precision floating-point number or eight-byte string to a LongInt variable

Syntax

Declare Function CVLongInt ( ByVal dbl As Double ) As LongInt
Declare Function CVLongInt ( ByRef str As Const String ) As LongInt

Usage

result = CVLongInt( dbl )
result = CVLongInt( str )

Parameters

dbl
A Double floating-point number with a binary copy of a LongInt variable stored in it.
str
A String at least eight bytes in length with a binary copy of a LongInt variable stored in it.

Return Value

A LongInt variable holding a binary copy of the input variable.

Description

Returns a 64-bit LongInt value using the binary data contained in a Double, or a String of at least eight bytes in length. A value of zero (0) is returned if the string is less than eight bytes in length.

CVLongInt is used to convert 8-byte strings created with MKLongInt.

This function can also be used to convert 64-bit integer values from a memory or file buffer without the need for a Type structure. However, just as with the type structure, special care should be taken when using CVLongInt to convert strings that have been read from a buffer.

Example

Dim ll As LongInt, s As String
s = "ABCDEFGH"
ll = CVLongInt(s)
Print Using "s = ""&"""; s
Print Using "ll = _&H&"; Hex(ll)

Differences from QB

  • In QB an error occurs if the string passed is less than eight bytes in length.
  • QB did not support floating-point arguments.

See also

  • MKLongInt
  • CVShort
  • CVI
  • CVL


CVS
Converts a 32-bit integer or 4-byte string to a single-precision variable

Syntax

Declare Function CVS ( ByVal i As Integer ) As Single
Declare Function CVS ( ByRef str As Const String ) As Single

Usage

result = CVS( i )
result = CVS( str )

Parameters

i
A 32-bit Integer with a binary copy of a single-precision variable stored in it.
str
A String at least 4 bytes in length with a binary copy of a single-precision variable stored in it.

Return Value

Returns a Single value holding a binary copy of the input value.

Description

Does a binary copy from a 32-bit Integer or 4-byte String to a Single variable. A value of zero (0.0) is returned if the string is less than 4 bytes in length. The result will make sense only if the parameter contained a IEEE-754 formatted single-precision value, such as one generated by CVI or MKS.

This function is useful to read numeric values from buffers without using a Type definition.

Example

Dim f As Single, i As Integer
f = 1.125
i = CVI(f)

Print Using "i = _&H&"; Hex(i)
Print Using "cvs(i) = &"; CVS(i)

Differences from QB

  • QB did not support integer arguments.

See also

  • MKS
  • CVD
  • CVI


CVShort
Converts a two-byte string to a Short integer variable

Syntax

Declare Function CVShort ( ByRef str As Const String ) As Short

Usage

result = CVShort( str )

Parameters

str
A String at least two bytes in length with a binary copy of a Short integer variable stored in it.

Return Value

Short variable holding the binary copy of a Keypgshort.

Description

Returns a 16-bit Short integer value using the binary data contained in a String of at least two bytes in length. A value of zero (0) is returned if the string is less than two bytes in length.

CVShort is used to convert 2-byte strings created with MKShort.

This function can also be used to convert 16-bit integer values from a memory or file buffer without the need for a Type structure. However, just as with the type structure, special care should be taken when using CVShort to convert strings that have been read from a buffer.

Example

Dim si As Short, s As String
s = "AB"
si = CVShort(s)
Print Using "s = ""&"""; s
Print Using "si = _&H&"; Hex(si)

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Cvshort.

Differences from QB

  • In QB this function is called CVI

See also

  • MKShort
  • CVI
  • CVL
  • CVLongInt


Data
Statement to store data at compile time.

Syntax

Data constant_expression1 [,constant_expression2]...

Description

Data stores a list of constant numeric or alphabetical expressions that are evaluated at compile time (except with -lang qb) and stored as constants that can be read into variables by using Read.

All the Data statements in the program behave as a single chained list; after the last element of one Data statement is read, the first element of the following Data statement will be read.
The program should not attempt to Read after the last Data element. The results are (in all dialects) undefined, and the program may crash (Page Fault).

Data statements are only visible from within the module in which they are defined; they must be only entered in module-level code.

Data constants can only be of simple types (numeric or string). A numeric value can be read as a numeric literal into a string. A string read into a numeric variable will be evaluated by the Val function. Consts can be used as items of data except in the -lang qb dialect, where their names are considered as normal text.

The "Restore label" statement makes the first Data item after the label the next item to be read, allowing the user to choose specific sections of data to read.

Data is normally used to initialize variables. FreeBASIC also allows the initialization of static variables when they are Dimensioned - see Variable Initializers for more information.

Example

' Create an array of 5 integers and a string to hold the data.
Dim As Integer h(4)
Dim As String hs
Dim As Integer readindex

' Set up to loop 5 times (for 5 numbers... check the data)
For readindex = 0 To 4

  ' Read in an integer.
  Read h(readindex)

  ' Display it.
  Print "Number" ; readindex ; " = " ; h(readindex)

Next readindex

' Spacer.
Print

' Read in a string.
Read hs

' Print it.
Print  "String = " + hs

' Await a keypress.
Sleep

' Exit program.
End

' Block of data.
Data 3, 234, 435/4, 23+433, 87643, "Good" + "Bye!"

Dialect Differences

  • -lang fb and -lang fblite considers data items as constant expressions that are evaluated during compilation and its result stored in the program.
  • -lang qb considers unquoted words, including names of variables and constants, as literal strings, and stores them without change, as in QBASIC. Unquoted strings are delimited by commas, and a colon or a line-break signifies the end of the Data statement. Unquoted strings are trimmed of whitespace at the beginning and end.

Differences from QB

  • Outside of the -lang qb dialect, alphabetic string literals must be enclosed within quotation marks, in QBASIC this was optional.
  • In QBASIC empty items evaluated to number 0 or to empty strings, in FreeBASIC they give a compile error. In QBASIC a comma at the end of the statement made an additional, empty item, evaluated to 0 or an empty string. In FreeBASIC they give a compile error.

See also

  • Read
  • Restore


Date
Returns the current system date as a string

Syntax

Declare Function Date ( ) As String

Usage

result = Date[$]

Return Value

Returns the current system date, in the format mm-dd-yyyy

Description

None

Example

Print Date ' prints the current date

Differences from QB

  • The QB DATE statement (to set the system date) is now called SetDate.
  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

See also

  • SetDate
  • Time
  • Timer


DateAdd
Offset a date with a specified interval

Syntax

Declare Function DateAdd ( ByRef interval As Const String, ByVal number As Double, ByVal date_serial As Double ) As Double

Usage

#include "vbcompat.bi"
result = DateAdd( interval, number, date_serial )

Parameters

interval
string indicating which period of time corresponds to one unit of number
number
the number of intervals to add to the base date. The number will be rounded to the nearest integer.
date_serial
the base date

Return Value

Returns a Date Serial corresponding to the received date_serial plus the number of intervals.

Description

Interval is specified as follows:

value | interval
yyyy | years
q | quarter(three months)
m | months
ww | weeks
d,w,y | days
h | hours
n | minutes
s | seconds

The compiler will not recognize this function unless vbcompat.bi or datetime.bi is included.

Example

#include "vbcompat.bi"

Const fmt = "ddddd ttttt"
Dim d As Double
d = Now()

Print "1 hour from now is ";
Print Format( DateAdd( "h", 1, d ), fmt )

Print "1 day from now is ";
Print Format( DateAdd( "d", 1, d ), fmt )

Print "1 week from now is ";
Print Format( DateAdd( "ww", 1, d ), fmt )

Print "1 month from now is ";
Print Format( DateAdd( "m", 1, d ), fmt )

Differences from QB

  • Did not exist in QB. This function appeared in Visual Basic.

See also

  • Date Serials


DateDiff
Gets the difference of two dates measured by a specified interval

Syntax

Declare Function DateDiff ( ByRef interval As Const String, ByVal serial1 As Double, ByVal serial2 As Double, ByVal firstdayofweek As Long = fbUseSystem, ByVal firstdayofyear As Long = fbUseSystem ) As LongInt

Usage

#include "vbcompat.bi"
result = DateDiff( interval, date_serial1, date_serial2 [, firstdayofWeek [, firstweekofyear ] ] )

Parameters

interval
the unit of time (interval) with which to measure the difference
date_serial1
starting date serial
date_serial2
end date serial
firstdayofweek
first day of the week
firstdayofyear
first day of the year

Return Value

Returns a LongInt corresponding to the number of intervals found between two Date Serials.

If date_serial1 > date_serial2, the result is negative.

Description

interval is specified as follows:

value | interval
yyyy | years
q | quarter(three months)
m | months
w | seven day periods
ww | calendar weeks
d,y | days
h | hours
n | minutes
s | seconds

first_dayofweek Affects the counting when 'ww' interval is used.

value | first day of week | constant
omitted | sunday |
0 | local settings | fbUseSystem
1 | sunday | fbSunday
2 | monday | fbMonday
3 | tuesday | fbTuesday
4 | wednesday | fbWednesday
5 | thursday | fbThursday
6 | friday | fbFriday
7 | saturday | fbSaturday

first_weekofyear specifies which year (previous or next) that the week which spans the end of one year and the beginning of the next should included with.

value | first week of year | constant
0 | local settings | fbUseSystem
1 | January 1's week | fbFirstJan1
2 | first weeks having 4 days in the year | fbFirstFourDays
3 | first full week of year | fbFirstFullWeek

Notice if you do an arithmetical subtraction of two date serials you get the difference in days.

The compiler will not recognize this function unless vbcompat.bi or datetime.bi is included.

Example

#include "vbcompat.bi"

Dim s As String, d1 As Double, d2 As Double

Line Input "Enter your birthday: ", s

If IsDate( s ) Then
  d1 = DateValue( s )
  d2 = Now()

  Print "You are " & DateDiff( "yyyy", d1, d2 ) & " years old."
  Print "You are " & DateDiff( "d", d1, d2 ) & " days old."
  Print "You are " & DateDiff( "s", d1, d2 ) & " seconds old."

Else
  Print "Invalid date"

End If

Differences from QB

  • Did not exist in QB. This function appeared in Visual Basic.

See also

  • Date Serials


DatePart
Gets an interval from a date

Syntax

Declare Function DatePart ( ByRef interval As Const String, ByVal date_serial As Double, ByVal firstdayofweek As Long = fbUseSystem, ByVal firstdayofyear As Long = fbUseSystem ) As Long

Usage

#include "vbcompat.bi"
result = DatePart( interval, date_serial, first_dayofWeek [, first_week_of_year ] )

Parameters

interval
string indicating which part of the date is required
date_serial
the date serial to decode
firstdayofweek
first day of the week
firstdayofyear
first day of the year

Return Value

Return an integer representing the interval in the Date Serial.

Description

interval string indicating which part of the date is required is specified as follows:

value | interval
yyyy | years
q | quarter(three months)
m | months
w | weekday
ww | week of the year
y | day of the year
d | day of the month
h | hours
n | minutes
s | seconds

first_dayofweek Affects the output when 'w' interval is required.

value | first day of week | constant
omitted | sunday |
0 | local settings | fbUseSystem
1 | sunday | fbSunday
2 | monday | fbMonday
3 | tuesday | fbTuesday
4 | wednesday | fbWednesday
5 | thursday | fbThursday
6 | friday | fbFriday
7 | saturday | fbSaturday

first_weekofyear specifies which year (previous or next) that the week which spans the end of one year and the beginning of the next should included with. Affects the output when 'ww' interval is required.

value | first week of year | constant
0 | local settings | fbUseSystem
1 | January 1's week | fbFirstJan1
2 | first weeks having 4 days in the year | fbFirstFourDays
3 | first full week of year | fbFirstFullWeek

The compiler will not recognize this function unless vbcompat.bi or datetime.bi is included.

Example

#include "vbcompat.bi"

Dim d As Double

d = Now()

Print "Today is day " & DatePart( "y", d );
Print " in week " & DatePart( "ww", d );
Print " of the year " & DatePart( "yyyy", d )

Differences from QB

  • Did not exist in QB. This function appeared in Visual Basic.

See also

  • Date Serials


DateSerial
Creates a date serial

Syntax

Declare Function DateSerial ( ByVal year As Long, ByVal month As Long, ByVal day As Long ) As Long

Usage

#include "vbcompat.bi"
result = DateSerial( year, month, day )

Parameters

year
the year
month
the month of the year
day
the day of the month

Return Value

Returns a date serial containing the date formed by the values in the year, month and day parameters.The date serial returned has no decimal part.

Description

The compiler will not recognize this function unless vbcompat.bi or datetime.bi is included.

Example

#include "vbcompat.bi"

Dim a As Long = DateSerial(2005, 11, 28)

Print Format(a, "yyyy/mm/dd hh:mm:ss")

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials
  • DateSerial
  • TimeValue
  • DateValue


DateValue
Returns a Date Serial from a string

Syntax

Declare Function DateValue ( ByRef date_string As String ) As Long

Usage

#include "vbcompat.bi"
result = DateValue( date_string )

Parameters

date_string
the string to convert to a date serial

Return Value

Returns a Date Serial from a date string.

Description

The date string must be in the format set in the regional settings of the Operating System.

DateValue( Date() ) will work correctly only if the regional settings specify the same short date format QB used (mm-dd-yyyy). Consider using the Now function in the expression Fix(Now()) to obtain the current date as a date serial.

The compiler will not recognize this function unless vbcompat.bi or datetime.bi is included.

Example

#include "vbcompat.bi"

Dim As Long v1, v2
Dim As String  s1, s2

Print "Enter first date: ";
Line Input s1

If IsDate( s1 ) = 0 Then
  Print "not a date"
  End
End If

Print "Enter second date: ";
Line Input s2

If IsDate( s2 ) = 0 Then
  Print "not a date"
  End
End If

'' convert the strings to date serials
v1 = DateValue( s1 )
v2 = DateValue( s2 )

Print "Number of days between dates is " & Abs( v2 - v1 )

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials
  • DateSerial
  • TimeValue


Day
Gets the day of the month from a Date Serial

Syntax

Declare Function Day ( ByVal date_serial As Double ) As Long

Usage

#include "vbcompat.bi"
result = Day( date_serial )

Parameters

date_serial
the date

Return Value

Returns the day of the month from a variable containing a date in Date Serial format.

Description

The compiler will not recognize this function unless vbcompat.bi is included.

Example

#include "vbcompat.bi"

Dim ds As Long = DateSerial(2005, 11, 28)

Print Format(ds, "yyyy/mm/dd "); Day(ds)

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials


Deallocate
Frees previously allocated memory

Syntax

Declare Sub Deallocate cdecl ( ByVal pointer As Any Ptr )

Usage

Deallocate( pointer )

Parameters

pointer
the address of the previously allocated buffer.

Description

This procedure frees memory that was previously allocated with Allocate. pointer must be a valid pointer. After the procedure returns, pointer will be rendered invalid (pointing to an invalid memory address), and its use (dereferencing or calling Deallocate again) will result in undefined behavior.

When memory was allocated to hold a string descriptor, the string must always be destroyed (setting to "") before deallocate the string descriptor (allowing to deallocate the memory taken up by the string data), otherwise, it is not possible to deallocate it later, and it may induce memory leak in the program continuation.

Calling Deallocate on a null pointer induces no action.

Example

The following example shows how to free previously allocated memory. Note that the pointer is set to null following the deallocation:
Sub DeallocateExample1()
   Dim As Integer Ptr integerPtr = Allocate( Len( Integer ) )  '' initialize pointer to
                                                               '' new memory address

   *integerPtr = 420                                     '' use pointer
   Print *integerPtr

   Deallocate( integerPtr )                              '' free memory back to system
   integerPtr = 0                                        '' and zero the pointer
End Sub

   DeallocateExample1()
   End 0

Although in this case it is unnecessary since the function immediately exits afterwards, setting the pointer to null is a good habit to get into. If the function deallocated memory from a pointer that was passed in by reference, for instance, the pointer that was used in the function call will be rendered invalid, and it is up to the caller to either reassign the pointer or set it to null. Example 3 shows how to correctly handle this kind of situation, and the next example shows the effects of deallocating memory with multiple references.

In the following example, a different pointer is used to free previously allocated memory:
'' WARNING: "evil" example showing how things should NOT be done

Sub DeallocateExample2()
   Dim As Integer Ptr integerPtr = Allocate( Len( Integer ) )
   '' initialize ^^^ pointer to new memory

   Dim As Integer Ptr anotherIntegerPtr = integerPtr
   '' initialize ^^^ another pointer to the same memory

   *anotherIntegerPtr = 69                     '' use other pointer
   Print *anotherIntegerPtr

   Deallocate( anotherIntegerPtr )             '' free memory back to system
   anotherIntegerPtr = 0                       '' and zero other pointer

'' *integerPtr = 420                           '' undefined behavior; original
                                               '' pointer is invalid
End Sub

   DeallocateExample2()
   End 0

Note that after the deallocation, both pointers are rendered invalid. This illustrates another one of the ways that bugs can arise when working with pointers. As a general rule, only deallocate memory previously allocated when you know that there is only one (1) pointer currently pointing at it.

Function createInteger() As Integer Ptr
   Return Allocate( Len( Integer ) )                     '' return pointer to newly
End Function                                             '' allocated memory

Sub destroyInteger( ByRef someIntegerPtr As Integer Ptr )
   Deallocate( someIntegerPtr )                          '' free memory back to system
   someIntegerPtr = 0                                    '' null original pointer
End Sub

Sub DeallocateExample3()
   Dim As Integer Ptr integerPtr = createInteger()       '' initialize pointer to
                                                         '' new memory address

   *integerPtr = 420                                     '' use pointer
   Print *integerPtr

   destroyInteger( integerPtr )                          '' pass pointer by reference
   Assert( integerPtr = 0 )                              '' pointer should now be null
End Sub

   DeallocateExample3()
   End 0

In the program above, a reference pointer in a function is set to null after deallocating the memory it points to. An Assert macro is used to test if the original pointer is in fact null after the function call. This example implies the correct way to pass pointers to functions that deallocate the memory they point to is by reference.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Deallocate.

Differences from QB

  • New to FreeBASIC

See also

  • Allocate
  • Reallocate


Declare
Declares a module-level or member procedure

Syntax

Declare Sub name [ param_list ]
Declare Function name [ param_list ] [ ByRef ] As return_type
Declare Operator op_symbol param_list [ [ ByRef ] As return_type ]

Type T
Declare Constructor [ param_list ]
Declare Destructor
Declare Sub name [ param_list ]
Declare Function name [ param_list ] [ ByRef ] As return_type
Declare Operator name [ param_list ] [ [ ByRef ] As return_type ]
Declare Property name [ ( [ param_list ] ) ] [ [ ByRef ] As return_type ]
End Type

Parameters

param_list
Parenthesized comma-separated list of parameters.
return_type
The return type of a Function, Operator, or Property procedure.
name
The name or symbol of the procedure.
op_symbol
The name or symbol of an operator.
T
The name of a new user-defined type.

Description

The Declare statement declares a Sub, Function, Operator, Constructor, or Destructor.
The procedure can be referred to in code without seeing its definition, although it must be defined somewhere. Essentially, the Declare statement introduces a procedure, and states that its definition is elsewhere. For example, a function can be declared at the top of a source module, called, then defined at the bottom of the source file, as shown below the example.

A procedure's declaration is almost identical to the first line of its definition, except the declaration is preceded by the Declare keyword and has no body. Also, attributes such as Export are left off the declaration.

FreeBASIC, as QB, does not require the declaration of the functions unless they are defined in a different source file or in the same file past the point where they are called. This is no longer true for procedures declared inside a Type body, which must always be declared first in the Type's body before use. If you do not declare Type procedures you will receive an error.

As every file using a function must have its declaration, declarations are usually kept in one or more include files to allow usage of the function by any module that needs it using the #include statement.

Example

Module-level Function:
'' declare the function sum which takes two integers and returns an integer
Declare Function sum( As Integer, As Integer ) As Integer

   Print "the sum of 420 and 69 is: " & sum( 420, 69 )    '' call the function sum

'' define the function sum which takes two integers and returns an integer
Function sum( a As Integer, b As Integer ) As Integer
   Return a + b
End Function

Type-level Sub:
Type my_type
    my_data As Integer
    Declare Sub increment_data( )
End Type

Sub my_type.increment_data( )
    my_data += 1
End Sub

Dim As my_type an_instance

an_instance.my_data = 68

an_instance.increment_data( )

Print an_instance.my_data

Dialect Differences

  • In the -lang fb dialect, ByVal is the default parameter passing convention.
  • In the -lang qb and -lang deprecated dialects, ByRef is the default parameter passing convention.
  • Type-level Sub/Function/Operator/Constructor/Destructor's are only allowed in -lang fb

Differences from QB

  • In FreeBASIC, the parameter names are optional.

See also

  • Sub
  • Function
  • Operator
  • Property
  • Constructor
  • Destructor
  • Constructor (Module)
  • Destructor (Module)
  • Type
  • Dim
  • Alias (Name)
  • Alias (Modifier)


DefByte
Specifies a default data type for a range of variable names

Syntax

DefByte start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefByte specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type Byte if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make bNumber a Byte number since it's first letter starts with b:
'' Compile with -lang fblite or qb

#lang "fblite"

DefByte b
Dim bNumber

Dialect Differences

  • Available in the -lang fblite dialect.
  • Not available in the -lang qb dialect unless referenced with the alias __Defbyte.

Differences from QB

  • New to FreeBASIC

See also

  • Byte
  • DefInt
  • DefUByte
  • Dim


DefDbl
Specifies a default data type for a range of variable names

Syntax

DefDbl start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefDbl specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type Double if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make aNum a Double-precision decimal number since it is in the range of a-d:
'' Compile with -lang fblite or qb

#lang "fblite"

DefDbl a-d
Dim aNum 'implicit: As Double

Print Len(aNum) ' Prints 8, the number of bytes in a double.

Dialect Differences

  • Only available in the -lang qb and -lang fblite dialects.

Differences from QB

  • None

See also

  • DefInt
  • DefSng
  • Dim
  • Double


defined
Preprocessor function to test if a symbol has been defined

Syntax

defined (symbol_name)

Parameters

symbol_name
Name of the symbol to test

Return Value

Returns non-zero (-1) if the symbol has been defined, otherwise returns zero (0).

Description

Given the symbol name, the defined() preprocessor function returns true if the symbol has been defined - or false if the symbol is unknown.

This is used mainly with #if (or #elseif).
Similar to #ifdef (or #elseifdef) except it allows more than one check to occur because of its flexibility.

Example

'e.g. - which symbols are defined out of a, b, c, and d ?

Const a = 300
#define b 12
Dim c As Single

#if defined(a)
 Print "a is defined"
#endif
#if defined(b)
 Print "b is defined"
#endif
#if defined(c)
 Print "c is defined"
#endif
#if defined(d)
 Print "d is defined"
#endif

Differences from QB

  • New to FreeBASIC

See also

  • #define
  • #macro
  • #if
  • #else
  • #elseif
  • #elseifdef
  • #elseifndef
  • #endif
  • #ifdef
  • #ifndef
  • #undef


DefInt
Specifies a default data type for a range of variable names

Syntax

DefInt start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefInt specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type Integer if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make iNumber an Integer number since its first letter starts with i.
'' Compile with -lang fblite or qb

#lang "fblite"

DefInt i
Dim iNumber

Dialect Differences

  • Only available in the -lang qb and -lang fblite dialects.

Differences from QB

  • None

See also

  • DefByte
  • DefDbl
  • DefLng
  • DefLongInt
  • DefShort
  • DefSng
  • DefStr
  • Integer


DefLng
Specifies a default data type for a range of variable names

Syntax

DefLng start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefLng specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type Long if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make lNumber a Long integer number since it starts with l.
'' Compile with -lang fblite or qb

#lang "fblite"

DefLng l
Dim lNumber ' implicit: As Long

Print Len(lNumber) ' Displays 4, the number of bytes in a long.

Dialect Differences

  • Only available in the -lang qb and -lang fblite dialects.

Differences from QB

  • None

See also

  • DefInt
  • Defulongint
  • Dim
  • LongInt


DefLongInt
Specifies a default data type for a range of variable names

Syntax

DefLongInt start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefLongInt specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type LongInt if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make lNumber a LongInt number since it's first letter starts with l.
'' Compile with -lang fblite

#lang "fblite"

DefLongInt l
Dim lNumber

Dialect Differences

  • Available in the -lang fblite dialect.
  • Not available in the -lang qb dialect unless referenced with the alias __Deflongint.

Differences from QB

  • New to FreeBASIC

See also

  • DefInt
  • Defulongint
  • Dim
  • LongInt


DefShort
Specifies a default data type for a range of variable names

Syntax

DefShort start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefShort specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type Short if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make sNumber a Short number since its first letter starts with s.
'' Compile with -lang fblite or qb

#lang "fblite"

DefShort s
Dim sNumber

Dialect Differences

  • Available in the -lang fblite dialect.
  • Not available in the -lang qb dialect unless referenced with the alias __Defshort.

Differences from QB

  • New to FreeBASIC
  • In QBasic, to make variables default to a 2 byte integer, DEFINT is used.

See also

  • DefInt
  • DefUShort
  • Dim
  • Integer
  • Short


DefSng
Specifies a default data type for a range of variable names

Syntax

DefSng start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefSng specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type Single if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make sNumber and yNumber a Single-precision decimal number since it is in the range of s-z.
'' Compile with -lang fblite or qb

#lang "fblite"

DefSng s-z
Dim sNumber, yNumber

Dialect Differences

  • Only available in the -lang qb and -lang fblite dialects.

Differences from QB

  • None

See also

  • DefInt
  • DefDbl
  • Single


DefStr
Specifies a default data type for a range of variable names

Syntax

DefStr start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefStr specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type String if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make sMessage a String since it starts with s.
'' Compile with -lang fblite or qb

#lang "fblite"

DefStr s
Dim sMessage

Dialect Differences

  • Only available in the -lang qb and -lang fblite dialects.

Differences from QB

  • None

See also

  • DefInt
  • DefSng
  • DefLng
  • DefDbl
  • Dim
  • String


DefUByte
Specifies a default data type for a range of variable names

Syntax

DefUByte start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefUByte specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type UByte if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make uNumber a UByte number since it's first letter starts with u.
'' Compile with -lang fblite

#lang "fblite"

DefUByte u
Dim uNumber

Dialect Differences

  • Available in the -lang fblite dialect.
  • Not available in the -lang qb dialect unless referenced with the alias __Defubyte.

Differences from QB

  • New to FreeBASIC

See also

  • DefByte
  • DefInt
  • Dim
  • UByte


DefUInt
Specifies a default data type for a range of variable names

Syntax

DefUInt start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefUInt specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type UInteger if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make uNumber a UInteger number since its first letter starts with u.
'' Compile with -lang fblite

#lang "fblite"

DefInt u
Dim uNumber

Dialect Differences

  • Available in the -lang fblite dialect.
  • Not available in the -lang qb dialect unless referenced with the alias __Defuint.

Differences from QB

  • New to FreeBASIC

See also

  • DefInt
  • Dim
  • UInteger


Defulongint
Specifies a default data type for a range of variable names

Syntax

Defulongint start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

Defulongint specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type ULongInt if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make lNumber a ULongInt number since its first letter starts with l.
'' Compile with -lang fblite

#lang "fblite"

defulongint l
Dim lNumber

Dialect Differences

  • Available in the -lang fblite dialect.
  • Not available in the -lang qb dialect unless referenced with the alias __Defulongint.

Differences from QB

  • New to FreeBASIC

See also

  • DefInt
  • DefLongInt
  • Dim
  • ULongInt


DefUShort
Specifies a default data type for a range of variable names

Syntax

DefUShort start_letter[-end_letter ][, ...]

Parameters

start_letter
the first letter in the range
end_letter
the last letter in the range

Description

DefUShort specifies that variables and arrays which aren't declared with a data type - or not declared at all - are implicitly declared of type UShort if the first letter of their names matches a certain letter or lies within an inclusive range of letters.

Example

This will make uNumber a UShort number since its first letter starts with u.
'' Compile with -lang fblite

#lang "fblite"

DefUShort u
Dim uNumber

Dialect Differences

  • Available in the -lang fblite dialect.
  • Not available in the -lang qb dialect unless referenced with the alias __Defushort.

Differences from QB

  • New to FreeBASIC

See also

  • DefInt
  • DefShort
  • Dim
  • UShort


Operator Delete Statement
Operator to destroy data and free memory allocated with the Operator New Expression

Usage

Delete buf
or
Delete[] buf

Parameters

buf
A pointer to memory that has been allocated by New Expression operator or New[] Expression operator, the array-version of New Expression operator (a typed pointer must be provided in accordance to the data type to delete).

Description

The Delete Statement operator is used to destroy and free the memory of an object created with New Expression operator. When deleting a TYPE, its destructor will be called. Delete Statement operator should only be used with addresses returned from New Expression operator.

The array version of Delete Statement operator, Delete[] Statement operator, is used to destroy an array of objects previously created with New[] Expression operator, the array-version of New Expression operator. Destructors will be called here as well.

Delete Statement operator must be used with addresses returned from New Expression operator, and Delete[] Statement operator with New[] Expression operator, the array-version of New Expression operator. You cannot mix and match the different versions of the operators.

After the memory is deleted, the buf pointer will be pointing at invalid memory. Calling Delete Expression twice on the same pointer value leads to undefined behavior. It may be a good idea to set the buf pointer to null (0), in order to guard against later code using it accidentally, since null pointer dereferences are easier to find and debug.

Calling Delete Statement operator on a null pointer induces no action.

The memory deallocation process part provided by the Delete Statement operator can be overloaded for user-defined types as a member operator Delete Overload. The previous process part for data destruction can never be modified.

Note: Any operator Delete[] (Statement or Overload) and the only Overload operator Delete are not compatible with sub-type polymorphism, even using Override Virtual Destructor that may in addition induce crashing.
Instead of having to call such an operator Delete([]) Statement on derived-type pointer, the safest way is to simply call (on base-type pointer) an overridden user Virtual member procedure that will automatically launch the operator Delete([]) Statement at derived-type level.

Example

Type Rational
    As Integer numerator, denominator
End Type

' Create and initialize a Rational, and store its address.
Dim p As Rational Ptr = New Rational(3, 4)

Print p->numerator & "/" & p->denominator

' Destroy the rational and give its memory back to the system.
Delete p

' Set the pointer to null to guard against future accesses
p = 0

' Allocate memory for 100 integers, store the address of the first one.
Dim p As Integer Ptr = New Integer[100]

' Assign some values to the integers in the array.
For i As Integer = 0 To 99
    p[i] = i
Next

' Free the entire integer array.
Delete[] p

' Set the pointer to null to guard against future accesses
p = 0

Dialect Differences

  • Only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • New Expression
  • Delete Overload
  • Deallocate


Destructor
Called automatically when a class or user defined type goes out of scope or is destroyed

Syntax

Type typename
field declarations
Declare Destructor ( )
End Type

Destructor typename ( ) [ Export ]
statements
End Destructor

Parameters

typename
name of the Type of Class

Description

The destructor method is called when a user defined Type or Class variable goes out of scope or is destroyed explicitly with the Delete Statement operator.

typename is the name of the type for which the Destructor method is declared and defined. Name resolution for typename follows the same rules as procedures when used in a Namespace.

The Destructor method is passed a hidden This parameter having the same type as typename.

The destructor in a type is called before the destructors on any of its fields. Therefore, all fields are accessible with the hidden This parameter in the destructor body.

Only one destructor may be declared and defined per type (but several destructors can be called in a chained way if the type contains or inherits other types with their own destructors).

Since the End statement does not close any scope, object destructors will not automatically be called if the End statement is used to terminate the program.

Destructor can be also called directly from the typename instance like the other member methods (Sub) and with the same syntax, i.e. using a member access operator, e.g. obj.Destructor(). The object, and all its members, are assumed to be constructed and in a valid state, otherwise its effects are undefined and may cause crashes. This syntax is useful in cases where obj has been constructed manually, e.g. with obj.Constructor() or Placement New.

Example

Type T
  value As ZString * 32
  Declare Constructor ( init_value As String )
  Declare Destructor ()
End Type

Constructor T ( init_value As String )
  value = init_value
  Print "Creating: "; value
End Constructor

Destructor T ()
  Print "Destroying: "; value
End Destructor

Sub MySub
  Dim x As T = ("A.x")
End Sub

Dim x As T = ("main.x")

Scope
  Dim x As T = ("main.scope.x")
End Scope

MySub

Output:
Creating: main.x
Creating: main.scope.x
Destroying: main.scope.x
Creating: A.x
Destroying: A.x
Destroying: main.x
Dialect Differences

  • Object-related features are supported only in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Class
  • Constructor
  • Delete Statement
  • Destructor (Module)
  • Type


Destructor (Module)
Specifies execution of a procedure at program termination

Syntax

[Public | Private] Sub identifier [Alias "external_identifier"] [()] Destructor [priority] [Static]
{ procedure body }
End Sub

Description

Defines a procedure to be automatically called from a compiled program's end-code. End-code is generated by the compiler and is executed when the program terminates normally. Procedures defined as destructors may be used the same way as ordinary procedures, that is, they may be called from within module-level code, as well as other procedures.

The procedure must have an empty parameter list. A compile-time error will be generated if the Destructor keyword is used in a Sub definition having one or more parameters. In a set of overloaded procedures, only one (1) destructor may be defined because of the ambiguity of having multiple Subs which take no arguments.

In a single module, depending on the build and run-time environment of the target system:
    • destructors may execute in which they are defined, or reverse order
    • destructors may execute before or after global static variables having dstructors
    • destructors may execute before or after other module destructors having priority attribute
    • destructors with priority attribute may execute before or after global static variables having destructors

The priority attribute, an integer between 101 and 65535, can be used to force destructors to be executed in a certain order. The value of priority has no specific meaning, only the relationship of the number with other destructor priorities. 101 is the lowest priority and is executed last, relative to other destructors also having priority attribute.

A module may define multiple destructor procedures. Destructor procedures may also appear in more than one module. All procedures defined with the syntax shown above will be added to the list of procedures to be called during the program's termination.

The order in which destructors defined in multiple modules are executed is known only at link time. Therefore, special care should be taken when using destructors that may call on a secondary module also defining a destructors. In such a case it is advisable to use a single destructor that explicit calls termination procedures in multiple modules to ensure a graceful termination of the application.

Destructors will be called if the program terminates normally or if error-checking is enabled and the program terminates abnormally.

Public static member procedures (a Sub having an empty parameter list), of user defined Type can be defined as a module destructor, by adding the Constructor keyword used in the sub procedure definition.

The module destructor feature exposes a low-level link-time feature of the build and run-time environment. Accessing global static objects having destructors from module destructors should be avoided due to variations in execution order on different build systems.

Warning for 64-bit compiler only: See the Identifier Rules page for the choice of user procedure identifier names (and specially the 'Platform Differences' paragraph).

Example

Sub pauseonexit Destructor

    '' If the program reaches the end, or aborts with an error,
    '' it will run this destructor before closing

    Print "Press any key to end the program..."
    Sleep

End Sub

Dim array(0 To 10, 0 To 10) As Integer
Dim As Integer i = 0, j = 11

'' this next line will cause the program to abort with an
'' error if you compile with array bounds checking enabled (fbc -exx ...)
Print array(i, j)

Differences from QB

  • New to FreeBASIC

See also

  • Destructor (Class)
  • Constructor (Module)
  • Sub


DEVICE
Lists, selects, and queries sfxlib output devices.

Syntax

Device List
result = Device Select(device)
Device Info
Device Info()
Device Info device
Device Info(device)

Description

DEVICE is the command family for the audio output path. In sfxlib, a device usually means a compiled backend such as PulseAudio, ALSA, WASAPI, CoreAudio, WebAudio, or the null driver, depending on the platform.

Device List prints the available output choices. Device Select asks sfxlib to use one of those choices by index. Device Info prints diagnostic information about either the current device or a specific device.

Most programs can let sfxlib pick the default device automatically. DEVICE is mainly useful for setup screens, diagnostics, tests, and situations where the default backend fails but another backend might work.

For automated tests, servers, and systems without speakers, the null driver is useful because it lets sound commands run without producing audible output.

Example

device list

if device select(0) < 0 then
print "could not select device"
end if

device info

Notes

  • Set SFXLIB_DRIVER=null before program startup to force the null output driver when available.
  • Device numbers are backend-list indexes, not operating-system device ids.

See also

  • sfxlib backends
  • CAPTURE
  • Sound Library Reference


Dim
Declares a variable

Syntax

Dim [Shared] name1 As DataType [, name2 As DataType, ...]
or
Dim [Shared] As DataType name1 [, name2, ...]
Arrays:
Dim name ( [lbound To] ubound [, ...] ) As DataType
Dim name ( Any [, Any...] ) As DataType
Dim name ( ) As DataType
Initializers:
Dim scalar_symbol As DataType = expression | Any
Dim array_symbol (arraybounds) As DataType = { expression [, ...] } | Any
Dim udt_symbol As DataType = ( expression [, ...] ) | Any

Description

Declares a variable by name and reserves memory to accommodate it.

Variables must be declared before they can be used in the -lang fb dialect or when using Option Explicit in the other dialects. Only in the -lang qb and -lang fblite dialects variables may be used without first declaring them, in such a case they are called implicit variables.

Dim can be used to declare and assign variables of any of the supported data types, user defined types, or enumerations.

Depending on where and how a variable or array is declared can change how it is allocated in memory. See Storage Classes.

More than one variable may be declared in a single Dim statement by separating each variable declaration with a comma.

'' Variable declaration examples

'' One variable per DIM statement
Dim text As String
Dim x As Double

'' More than one variable declared, different data types
Dim k As Single, factor As Double, s As String

'' More than one variable declared, all same data types
Dim As Integer mx, my, mz ,mb

'' Variable having an initializer
Dim px As Double Ptr = @x

Explicit Variables with Implicit Data Types

In the -lang qb and -lang fblite dialects, even if the variable is declared explicitly, it will be given a default data type if the data type is not explicitly given either by name or by type suffix. The default data type is Single in the -lang qb dialect and Integer in the -lang fblite dialect. The default data type can be changed throughout a source listing by use of the Def### statements. (for example, DefInt, DefStr, DefSng)

'' Compile with -lang qb

'$lang: "qb"

'' All variables beginning with A through N will default to the INTEGER data type
'' All other variables default to the SINGLE data type
DefInt I-N

Dim I, J, X, Y, T$, D As Double
'' I and J are INTEGERs
'' X and Y are SINGLEs
'' T$ is STRING
'' D is DOUBLE

Arrays

As with most BASIC dialects, FreeBASIC supports arrays with indexes ranging from a lower bound to an upper bound. In the syntaxes shown, lbound refers to the lower bound, or the smallest index. ubound refers to the upper bound, or the largest index. If a lower bound is not specified, it is assumed to be zero by default, unless Option Base is used.

Const upperbound = 10

'' Declare an array with indexes ranging from 0 to upperbound,
'' for a total of (upperbound + 1) indexes.
Dim array(upperbound) As Single

Multidimensional arrays can be declared as well, and are stored in this definite order: values differing only in the last index are contiguous (row-major order).
The maximum number of dimensions of a multidimensional array is 8.

'' declare a three-dimensional array of single
'' precision floating-point numbers.
Dim array(1 To 2, 6, 3 To 5) As Single

'' The first dimension of the declared array
'' has indices from 1 to 2, the second, 0 to 6,
'' and the third, 3 to 5.

For more information on arrays see Arrays Overview.

If the values used with Dim to declare the dimensions of an array are all constants, the array will be created fixed length (of Static size, unless Option Dynamic is specified), while using one or more variables to declare the dimensions of an array makes it variable length, even if Option Static is in effect.

Arrays can be declared as variable length in several ways: Using Dim with an empty set of indexes (Dim x()), using Dim with indexes that are variables or using the keyword ReDim, or using Any in place of the array bounds, or declaring it past the metacommand $Dynamic. Variable length arrays can't use initializers.

Arrays declared with Dim having constant indexes and not preceeded by Option Dynamic are fixed length (not resizable at runtime) and can use initializers.

The upper bound can be an ellipsis (..., 3 dots). This will cause to upper bound to be set automatically based on the number of elements found in the initializer. When ellipsis is used in this manner, an initializer must be used, and it may not be Any. See the Ellipsis page for a short example.

See also Fixed-Length Arrays and Variable-Length Arrays.

Initializers

Arrays, variables, strings, and user defined types (UDTs) are initialized to zero (or False for Boolean) or null strings by default when they are created, except since fbc version 1.20.0 where fixed-length strings of STRING*N type are padded with spaces (because such a string field no longer has terminal null character).

To avoid the overhead of default variable initialization, the Any initializer can be used with Dim to tell the compiler to only reserve the place for the variable in memory but not initialize it, so the variable will contain garbage. In this case the programmer should not make assumptions about the initial values.

Fixed-length arrays, variables, zstrings and UDTs may be given a value at the time of their declaration by following the variable declaration with an initializer. Arrays, variables and UDTs are initialized as they would in a normal assignment, using an equal ( = ) sign. The => sign can also be used, allowing to avoid the declaration resembling an expression for example when declaring fixed length strings.

Array values are given in comma-delimited values enclosed by curly brackets, and UDT values are given in comma delimited values enclosed by parenthesis. These methods of initializing variables can be nested within one another for complex assignments. Nesting allows for arrays of any dimension to be initialized.

'' Declare an array of 2 by 5 elements
'' and initialize
Dim array(1 To 2, 1 To 5) As Integer => {{1, 2, 3, 4, 5}, {1, 2, 3, 4, 5}}

'' declare a simple UDT
Type mytype
    var1 As Double
    var2 As Integer
End Type

'' declare a 3 element array and initialize the first
'' 2 mytype elements
Dim myvar(0 To 2) As mytype => {(1.0, 1), (2.0, 2)}

For module-level, fixed-length, or global variables, initialized values must be constant expressions. FreeBASIC will report a compile-time error if otherwise.

Note: Initializing UDT's with strings is not supported at this time. Initializing UDT containing data-field initializer or string is not valid. Initializing UDT derived directly or indirectly from the the built-in Object type is not valid.

Explicit Variables with Type Suffixes

In the -lang qb and -lang fblite dialects, the data type of a variable may be indicated with a type suffix ( $ % # ! & ).

'' Compile with -lang qb or fblite

'$lang: "qb"

'' A string variable using the $ type suffix
Dim strVariable$

'' An integer variable using the % type suffix
Dim intVariable%

'' A long variable using the & type suffix
Dim lngVariable&

'' A single precision floating point variable using the ! type suffix
Dim sngVariable!

'' A double precision floating point variable using the # type suffix
Dim dblVariable#

Example

Dim a As Byte
Dim b As Short
Dim c As Integer
Dim d As LongInt
Dim au As UByte
Dim bu As UShort
Dim cu As UInteger
Dim du As ULongInt
Dim e As Single
Dim f As Double
Dim g As Integer Ptr
Dim h As Byte Ptr
Dim s1 As String * 10   '' fixed length string
Dim s2 As String        '' variable length string
Dim s3 As ZString Ptr   '' zstring

s1 = "Hello World!"
s2 = "Hello World from FreeBASIC!"
s3 = Allocate( Len( s2 ) + 1 )
*s3 = s2

Print "Byte: "; Len(a)
Print "Short: "; Len(b)
Print "Integer: "; Len(c)
Print "Longint: "; Len(d)
Print "UByte: "; Len(au)
Print "UShort: "; Len(bu)
Print "UInteger: "; Len(cu)
Print "ULongint: "; Len(du)
Print "Single: "; Len(e)
Print "Double: "; Len(f)
Print "Integer Pointer: "; Len(g)
Print "Byte Pointer: "; Len(h)
Print "Fixed String: "; Len(s1)
Print "Variable String: "; Len(s2)
Print "ZString: "; Len(*s3)

Deallocate(s3)

Version

  • Since fbc version 1.20.0, fixed-length strings of STRING*N type are initialized to space value (zero value previously).

Dialect Differences

  • In the -lang qb and -lang fblite dialects, variables have procedure scope if the variable is defined inside a procedure, and for the entire module if the variable is defined with Dim Shared.
  • In the -lang qb dialect, variables cannot be initialised. In the -lang fblite dialect, the variable is initialised with a default value at the start of the procedure/module, and assigned its initial value if/when the Dim statement is executed at runtime.
  • In the -lang fb and -lang deprecated dialects, variables declared inside compound block statements (For..Next, While..Wend, Do..Loop, If..Then, Select..End Select, With..End With, Scope..End Scope) have local working scopes, and are visible only within these blocks. To access duplicated symbols defined as global outside these blocks, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).
  • In the -lang fb dialect, Option statements (e.g. Option Base, Option Dynamic), metacommands(e.g. $Static) and Def### statements (e.g. DefInt) are not allowed.

Differences from QB

  • Variable Initializers are new to FreeBASIC.
  • The alternate syntax Dim As DataType symbolname, [...] is new to FreeBASIC.
  • Multidimensional arrays are stored in this definite order: values differing only in the last index are contiguous (row-major order), they were stored in opposite order in QB by default: values differing only in the first index were contiguous (column-major order).
  • Variable length arrays up to 2 GiB in size are possible in FreeBASIC. In QB, $STATIC arrays were limited to 64 KiB , or to the DOS memory available (several 100 KiB at best) if made $DYNAMIC and /AH was used.
  • The ellipsis form for upper bounds is new to FreeBASIC.

See also

  • Var
  • Common
  • Extern
  • ReDim
  • Preserve
  • Shared
  • Static
  • Byref (Variables)
  • Erase
  • LBound
  • UBound
  • ... (Ellipsis)
  • Any
  • Pointers to Procedures


Dir
Searches for and returns information about an item in the filesystem; performs a directory searchattrib

Syntax

# Include "dir.bi"

Declare Function Dir ( ByRef item_spec As Const String, ByVal attrib_mask As Integer = fbNormal, ByRef out_attrib As Integer ) As String
Declare Function Dir ( ByRef item_spec As Const String, ByVal attrib_mask As Integer = fbNormal, ByVal p_out_attrib As Integer Ptr = 0 ) As String
Declare Function Dir ( ByRef out_attrib As Integer ) As String
Declare Function Dir ( ByVal p_out_attrib As Integer Ptr = 0 ) As String

Usage

result = Dir( item_spec, [ attrib_mask ], out_attrib )
result = Dir( item_spec [, [ attrib_mask ] [, p_out_attrib ] ] )
result = Dir( out_attrib )
result = Dir( [ p_out_attrib ] )

Parameters

item_spec
The pattern to match an item's name against.
attrib_mask
The bit mask to match an item's attributes against.
out_attrib
Reference to a bit mask that's assigned each of the found item's attributes, if any.
p_out_attrib
Pointer to a bit mask that's assigned each of the found item's attributes, if any.

Return Value

If no item matching the name item_spec or the attribute mask attrib_mask was found, then out_attrib (or *p_out_attrib) is assigned to zero and an empty string is returned. Otherwise, out_attrib (or *p_out_attrib) is assigned the attribute mask of the item, and the item name, without a path, is returned.

Description

Dir returns the first filesystem item that matches the item_spec passed as an argument. To retrieve the next file items that match this item_spec pattern, call Dir again without this argument (or with an empty string).
So to obtain a list of items in a directory, Dir needs to be invoked multiple times returning one item per invocation.

If item_spec contains an absolute path, then the first procedure searches the filesystem for an item that matches the name item_spec and whose attributes are all contained in attrib_mask (fbNormal by default). Otherwise, it searches relative to the current directory (see CurDir). In any case, if a matching item is not found, out_attrib is assigned to zero and an empty string is returned. Otherwise, out_attrib is assigned with the attribute flags of the item, and the name of the item, without a path, is returned.
item_spec may include an asterisk (*, for matching any adjacent characters) or one or more question marks (?, for matching any individual character). If it does, the procedure searches for the first such item.

If an item is found, subsequent calls with item_spec omitted, or set to an empty string, will return the next item matching the name item_spec until no more such items are found. If attrib_mask is omitted from these subsequent calls, the procedure searches for items with the same attributes as in the previous call.

The second syntax behaves the same as Dir( item_spec, attrib_mask, *p_out_attrib ).
The third syntax behaves the same as Dir( "", , out_attrib ).
The fourth syntax behaves the same as Dir( "", , *p_out_attrib ).

File Attributes:
Files and directories and other items can be said to possess so-called file attributes; metadata that describes the item. The meaning of this metadata may vary depending on the operating system and the file system it uses.
The following defined constants are used as bit-flags in attrib_mask and in out_attrib or *p_out_attrib. Their values can be combined to form a mask using Operator Or. These values are the metadata that the returned files are allowed to have. To access the defined flags, you must #include "dir.bi".

Assuming the reader understands 'file' to mean any file system entry: normal file, directory, etc.
attrib_mask specifies the set of file attributes that are permitted for file names returned (if a file has an attribute that is not specified in attrib_mask, then it is excluded from file names returned).
For example:
fbDirectory will only allow the directory attribute not to be set, meaning that only files or directories with no other attributes set will be matched.
(fbReadOnly Or fbDirectory) will allow read-only directories and files, and writable directories and files.
fbArchive Or fbDirectory will match against archived files, archived directories, non-archived files and non-archived directories (it will not match against, for example, read-only files).
Logic condition:
file name returned by Dir if the equality '((attrib_mask) OR (file_attrib)) = (attrib_mask)' is true
or
file name returned by Dir if ((file_attrib) AND (Not attrib_mask)) = 0

More powerful filtering can be done by checking the returned out_attrib for specifc flags using Operator And.

# define fbReadOnly &h01
The item cannot be written to or deleted.
DOS & Windows: The item has the "read-only" attribute set.
Linux:The item has no write permissions associated with the current user or group, nor is it globally writable. (Whether or not the user has root permissions is ignored.)

# define fbHidden &h02
The item is hidden in ordinary directory listings.
DOS & Windows: The item has the "hidden" attribute set.
Linux: The item's name has a period (.) as the first character.

# define fbSystem &h04
The item is used almost exclusively by the system.
DOS & Windows: The item has the "system" attribute set.
Linux: The item is either a character device, block device, named pipe (FIFO) or Unix socket.

# define fbDirectory &h10
The item is a directory. Includes the current (.) and parent (..) directories as well.
DOS & Windows & Linux: The item is a directory.

# define fbArchive &h20
The item may be backed up after some automated operations.
DOS & Windows: The item has the "archive" attribute set (automatically set after every write access to a file).
Linux: The item is not a directory; typical filesystems do not support this metadata.

# define fbNormal (fbReadOnly or fbArchive)
The item is read-only or "archived".

(If attrib_mask does not include fbArchive, then Dir may widen the check to include fbDirectory, but it is recommended to add fbDirectory explicitly, if that is the behaviour sought.)

Items found having no attributes are always matched, regardless of the value of attrib_mask.
An item will not be matched if it has one or more attributes that aren't specified in attrib_mask.

In general it is not possible to use attrib_mask to include a file/folder with one set of attributes while excluding a file/folder with a different set. For example, it is not possible to scan for read-only directories while excluding read-only files (unless the files also have other attributes).
Finer control can be gained by checking the out_attrib value for the desired set of attributes.

Example

' Number of files in the current directory.

Dim As Integer FileCount

If Dir("*") <> "" Then  ' Start a file search with no specified filespec/attrib *AND* get the first filename.
    Filecount = 1
    While Dir() <> ""  ' If dir() is "", exit the loop: no more filenames are left to be read.
        FileCount += 1  ' Increment the counter of number of files
    Wend
End If

Print FileCount & " files in the current directory."

Sleep

#include "dir.bi" 'provides constants to use for the attrib_mask parameter

Sub list_files (ByRef filespec As String, ByVal attrib As Integer)
    Dim As String filename = Dir(filespec, attrib) ' Start a file search with the specified filespec/attrib *AND* get the first filename.
    Do While Len(filename) > 0 ' If len(filename) is 0, exit the loop: no more filenames are left to be read.
        Print filename
        filename = Dir() ' Search for (and get) the next item matching the initially specified filespec/attrib.
    Loop
End Sub

Print "directories:"
list_files "*", fbDirectory

Print
Print "archive files:"
list_files "*", fbArchive

'' show any files that have directory attribute and don't care if it is system, hidden, read-only, or archive

#include Once "dir.bi"

'' allow everything
Var mask = fbDirectory Or fbHidden Or fbSystem Or fbArchive Or fbReadOnly

Var attrib = 0
Var f = Dir( "*.*", mask, attrib )
While( f > "" )
    '' show any files that have at least a directory attribute
    If( attrib And fbDirectory ) Then
        Print f
    End If
    f = Dir( attrib )
Wend

'' Example of using DIR function and retrieving attributes

#include "dir.bi" '' provides constants to match the attributes against

'' set input attribute mask to allow files that are normal, hidden, system or directory
Const attrib_mask = fbNormal Or fbHidden Or fbSystem Or fbDirectory ' = &h37

Dim As UInteger out_attr '' unsigned integer to hold retrieved attributes

Dim As String fname '' file/directory name returned with
Dim As Integer filecount, dircount

fname = Dir("*.*", attrib_mask, out_attr) '' Get first file name/attributes, according to supplied file spec and attribute mask

Print "File listing in " & CurDir & ":"

Do Until Len(fname) = 0 '' loop until Dir returns empty string

    If (fname <> ".") And (fname <> "..") Then '' ignore current and parent directory entries

        Print fname,

        If (out_attr And fbDirectory) <> 0 Then
            Print "- directory";
            dircount += 1
        Else
            Print "- file";
            filecount += 1
        End If
        If (out_attr And fbReadOnly) <> 0 Then Print ", read-only";
        If (out_attr And fbHidden  ) <> 0 Then Print ", hidden";
        If (out_attr And fbSystem  ) <> 0 Then Print ", system";
        If (out_attr And fbArchive ) <> 0 Then Print ", archived";
        Print

    End If

    fname = Dir(out_attr) '' find next name/attributes

Loop

Print
Print "Found " & filecount & " files and " & dircount & " subdirs"

Platform Differences

  • Linux requires the filename case to match the real name of the file. Windows and DOS are case insensitive.
  • Path separators in Linux are forward slashes /. Windows uses backslashes \ but it allows for forward slashes. DOS uses backslashes.
  • In DOS, the attrib mask value of &h37 (&h3F usually works also, but &h37 is safer) returns all files and directories, including "." and "..", but no Volume: the value 8 returns the Volume, even if current directory is not the main directory.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Dir.

Differences from QB

  • Not found in QBasic but present in Visual Basic. The out_attrib parameter is new to FreeBASIC.

See also

  • Open
  • CurDir
  • ChDir
  • MkDir
  • RmDir


Do
Control flow statement for looping.

Syntax

Do [ { Until | While } condition ]
[ statement block ]
Loop

Do
[ statement block ]
Loop [ { Until | While } condition ]

Example

See example at Do...Loop.

Differences from QB

  • None

See also

  • Do...Loop


Do...Loop
Control flow statement for looping

Syntax

Do [ { Until | While } condition ]
[ statement block ]
Loop
or
Do
[ statement block ]
Loop [ { Until | While } condition ]

Description

The Do statement executes the statements in the following statement block until/while the condition, if any, evaluates to true.

If Until is used, the Do statement stops repetition of the statement block when condition evaluates to true. The While keyword has opposite effect, stopping the loop if condition evaluates to false. If both condition and either Until or While are omitted, the Do statement loops indefinitely.

If an Exit Do statement is encountered inside the statement block, the loop is terminated, and execution resumes immediately following the enclosing Loop statement. If a Continue Do statement is encountered, the rest of the statement block is skipped and execution resumes at the Do statement.

In the first syntax, the condition is checked when the Do statement is first encountered, and if the condition is met, the statement block will be skipped. In the second syntax, condition is initially checked after the statement block is executed. This means that the statement block is always guaranteed to execute at least once.

condition may be any valid expression that evaluates to False (zero) or True (non-zero).

Example

In this example, a Do loop is used to count the total number of odd numbers from 1 to 10. It will repeat until its n > 10 condition is met:
Dim As Integer n = 1                            '' number to check
Dim As Integer total_odd = 0                    '' running total of odd numbers
Do Until( n > 10 )
  If( ( n Mod 2 ) > 0 ) Then total_odd += 1    '' add to total if n is odd (has remainder from division by 2)
  n += 1
Loop
Print "total odd numbers: " ; total_odd         '' prints '5'

End 0

Here, an infinite DO loop is used to count the total number of evens. We place the conditional check inside the loop via an If...Then statement, which exits the loop if and when n > 10 becomes true:
   Dim As Integer n = 1                            '' number to check
   Dim As Integer total_even = 0                   '' running total of even numbers
   Do
      If( n > 10 ) Then Exit Do                    '' exit if we've scanned our 10 numbers

      If( ( n Mod 2 ) = 0 ) Then total_even += 1   '' add to total if n is even (no remainder from division by 2)
      n += 1
   Loop
   Print "total even numbers: " ; total_even       '' prints '5'

   End 0

Dialect Differences

  • In the -lang qb and -lang fblite dialects, variables declared inside a Do..Loop block have a function-wide scope as in QB
  • In the -lang fb and -lang deprecated dialects, variables declared inside a Do..Loop block are visible only inside the block, and can't be accessed outside it. To access duplicated symbols defined as global outside this block, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

Differences from QB

  • None

See also

  • Continue
  • Exit
  • For...Next
  • While...Wend


Double
Standard data type: 64 bit floating point

Syntax

Dim variable As Double

Description

Double is a 64-bit, floating-point data type used to store more precise decimal numbers. They can hold positive values in the range 4.940656458412465e-324 to 1.797693134862316e+308, or negative values in the range -4.940656458412465e-324 to -1.797693134862316e+308, or zero (0). They contain at most 53 bits of precision, or about 15 decimal digits.

Doubles have a greater range and precision than Singles, they still have limited accuracy which can lead to significant inaccuracies if not used properly. They are dyadic numbers - i.e. they can only accurately hold multiples of powers of two, which will lead to inaccuracies in most base-10 fractions.

Example

'Example of using a double variable.

Dim a As Double
a = 1.985766472453666
Print a

Sleep

Differences from QB

  • None

See also

  • Single Less precise float type
  • CDbl
  • Table with variable types overview, limits and suffixes


Draw
Statement for sequenced pixel plotting

Syntax

Draw [target,] cmd

Parameters

target
the buffer to draw on
cmd
a string containing the sequence of commands

Description

Drawing will take place onto the current work page set via ScreenSet or onto the target Get/Put buffer if specified.
The Draw statement can be used to issue several drawing commands all at once; it is useful to quickly draw figures. The command string accepts the following commands:

Commands to plot pixels:
Command | Description
 | Commands to plot pixels:
B | Optional prefix: move but do not draw.
N | Optional prefix: draw but do not move.
M x,y | Move to specified screen location. if a '+' or '-' sign precedes x, movement is relative to current cursor position. x's sign has no effect on the sign of y.
U [n] | Move n units up. If n is omitted, 1 is assumed.
D [n] | Move n units down. If n is omitted, 1 is assumed.
L [n] | Move n units left. If n is omitted, 1 is assumed.
R [n] | Move n units right. If n is omitted, 1 is assumed.
E [n] | Move n units up and right. If n is omitted, 1 is assumed.
F [n] | Move n units down and right. If n is omitted, 1 is assumed.
G [n] | Move n units down and left. If n is omitted, 1 is assumed.
H [n] | Move n units up and left. If n is omitted, 1 is assumed.
 | Commands to color:
C n | Changes current foreground color to n.
P p,b | PAINTs (flood fills) region of border color b with color p.
 | Commands to scale and rotate:
S n | Sets the current unit length, default is 4. A unit length of 4 is equal to 1 pixel.
A n | Rotate n*90 degrees (n ranges 0-3).
TA n | Rotate n degrees (n ranges 0-359).
 | Extra commands:
X p | Executes commands at p, where p is a STRING PTR.

Commands to set the color, size and angle will take affect all subsequent Draw operations.

Draw respects the current clipping region as set by the View (Graphics) statement, but its coordinates are not affected by the custom coordinates system.

Example

Screen 13

'Move to (50,50) without drawing
Draw "BM 50,50"

'Set drawing color to 2 (green)
Draw "C2"

'Draw a box
Draw "R50 D30 L50 U30"

'Move inside the box
Draw "BM +1,1"

'Flood fill with color 1 (blue) up to border color 2
Draw "P 1,2"

Sleep

'' Draws a flower on-screen

Dim As Integer i, a, c
Dim As String fill, setangle

'' pattern for each petal
Dim As Const String petal = _
    _
    ("X" & VarPtr(setangle)) _ '' link to angle-setting string
    _
    & "C15" _       '' set outline color (white)
    & "M+100,+10" _ '' draw outline
    "M +15,-10" _
    "M -15,-10" _
    "M-100,+10" _
    _
    & "BM+100,0" _              '' move inside petal
    & ("X" & VarPtr(fill)) _    '' flood-fill petal (by linking to fill string)
    & "BM-100,0"                '' move back out

'' set screen
ScreenRes 320, 240, 8

'' move to center
Draw "BM 160, 120"

'' set initial angle and color number
a = 0: c = 32

For i = 1 To 24

    '' make angle-setting and filling command strings
    setangle = "TA" & a
    fill = "P" & c & ",15"

    '' draw the petal pattern, which links to angle-setting and filling strings
    Draw petal

    '' short delay
    Sleep 100

    '' increment angle and color number
    a += 15: c += 1

Next i

Sleep

Differences from QB

  • target is new to FreeBASIC
  • QB used the special pointer keyword VARPTR$ with the X p command.
  • FB does not currently allow sub-pixel movements: all movements are rounded to the nearest integer coordinate.

See also

  • Draw String
  • Screen (Graphics)
  • VarPtr
  • Paint


Draw String
Graphics statement to render text to an image or screen.

Syntax

Draw String [buffer,] [STEP] (x, y), text [,color [, font [, method [, (alpha|blender) [, parameter] ] ] ] ]

Usage

Draw String [buffer,] [STEP] (x, y), text [, color]
Draw String [buffer,] [STEP] (x, y), text , , font [, method [, alpha ] ]
Draw String [buffer,] [STEP] (x, y), text , , font, Custom, blender [, parameter]

Parameters

buffer
the sprite to draw the string on. If this is not supplied, it will be drawn to the screen.
STEP
use relative coordinates. If STEP is added, the x and y coordinates are translated relative to the last drawn point.
x, y
the horizontal / vertical position to draw to, relative to the top left hand corner of the screen (unless STEP is used - see above). The top left corner of the text will be drawn at this position.
text
the string containing the text to draw
color
if no font is supplied, this allows you to choose the color of the text. If omitted, the default foreground Color is used.
If a font is supplied, color is ignored, and the font itself specifies the color for each pixel.
font
an image buffer containing a custom font. If no font is supplied, the standard font for the current text resolution is used, and the following parameters are ignored.
method | Custom
specifies how the font characters are drawn on top of the target surface. The same methods as found for the Put statement are allowed, with the only difference that the default method is Trans for this function. This parameter only applies to custom fonts.
alpha
alpha value, ranging 0-255. This parameter only applies to the Add or Alpha methods.
blender
custom blender function for the Custom drawing method; see Put (Graphics) statement description for details. This parameter only applies to the Custom method.
parameter
optional Pointer to be passed to the custom blender function; if omitted, the default value is zero (0).

Description

This graphics keyword prints a string to the screen with pixel positioning, transparent background, and can use an user-supplied font. Draw String does not update any text or graphics cursor. It doesn't wrap at the end of line. Tabs, carriage returns and other special characters have no special behavior in Draw String, and are treated as normal characters.

In graphics mode, this function provides a flexible alternative to Print. It has several key advantages:
    • Draw String can print text to any coordinate on the screen, while Print is constrained to the character grid accessible by Locate.
    • Print will override the background behind the text with the current background color. Draw String does not do this: it leaves the pixels in the background untouched.
    • Like Put, Draw String has several different methods for printing text, such as Alpha and Custom.
    • Draw String isn't limited to a single character set: it is possible to supply a custom font to be used instead.

Note: If a custom font isn't supplied, Draw String will default to the standard font, as used by Print, with character size dictated by Width. method - if passed - will be ignored, and the text will be drawn using the color supplied, with a transparent background.

Draw String coordinates are affected by custom coordinates system set via Window and View (Graphics) statements, and the drawn text respects clipping rectangle set by View (Graphics).

The custom font format:
The font is stored in a standard Get/Put buffer; the font has to be stored in a buffer using the same depth as the current color depth, otherwise Draw String will bump out with an illegal function call runtime error.

The first line of pixels in the font buffer holds the header of the font, on a byte (not pixel) basis. The very first byte identifies the font header version; currently this must be 0. The second byte gives the ascii code of the first supported character in the font; the third byte gives the ascii code of the last supported character. So if the font supports the full range 0-255, 0 and 255 will be the contents of these two bytes.
Next comes the width of each of the supported characters, each in a byte. Supposing the font holds 96 characters, ranging from 32 to 127 (inclusive), the header would have the first three bytes holding 0, 32 and 127, followed by 96 bytes giving the widths of the corresponding chars.

The font height is obtained by subtracting 1 from the buffer height, that is, while the first buffer line of pixels acts as a font header, the remaining lines define the glyphs' layout. The buffer must be as wide as necessary to hold all the supported character sprites in the same row, one after another.

Example

This gives an example of basic Draw String usage: it uses it to print "Hello world" in the center of the screen:
Const w = 320, h = 200 '' screen dimensions

Dim x As Integer, y As Integer, s As String

'' Open a graphics window
ScreenRes w, h

'' Draw a string in the centre of the screen:

s = "Hello world"
x = (w - Len(s) * 8) \ 2
y = (h - 1 * 8) \ 2

Draw String (x, y), s

'' Wait for a keypress before ending the program
Sleep

This example shows you how to create and use your own custom font. For simplicity, it uses Draw String with the default font to create the glyphs.
'' Define character range
Const FIRSTCHAR = 32, LASTCHAR = 127

Const NUMCHARS = (LASTCHAR - FIRSTCHAR) + 1
Dim As UByte Ptr p, myFont
Dim As Integer i

'' Open a 256 color graphics screen (320*200)
ScreenRes 320, 200, 8

'' Create custom font into PUT buffer

myFont = ImageCreate(NUMCHARS * 8, 9)

 '' Put font header at start of pixel data

#ifndef ImageInfo '' older versions of FB don't have the ImageInfo feature
p = myFont + IIf(myFont[0] = 7, 32, 4)
#else
ImageInfo( myFont, , , , , p )
#endif

p[0] = 0
p[1] = FIRSTCHAR
p[2] = LASTCHAR

 '' PUT each character into the font and update width information
For i = FIRSTCHAR To LASTCHAR

    '' Here we could define a custom width for each letter, but for simplicity we use
    '' a fixed width of 8 since we are reusing the default font glyphs
    p[3 + i - FIRSTCHAR] = 8

    '' Create character onto custom font buffer by drawing using default font
    Draw String myFont, ((i - FIRSTCHAR) * 8, 1), Chr(i), 32 + (i Mod 24) + 24

Next i

'' Now the font buffer is ready; we could save it using BSAVE for later use
Rem BSave "myfont.bmp", myFont

'' Here we draw a string using the custom font
Draw String (10, 10), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", , myFont
Draw String (10, 26), "abcdefghijklmnopqrstuvwxyz", , myFont
Draw String (66, 58), "Hello world!", , myFont

'' Free the font from memory, now we are done with it
ImageDestroy myFont

Sleep

Differences from QB

  • New to FreeBASIC

See also

  • Print
  • ?
  • Draw
  • ImageCreate
  • ImageDestroy
  • ImageInfo
  • Put (Graphics)
  • Width


DyLibFree
Unloads a dynamic link library from memory

Syntax

Declare Sub DyLibFree ( ByVal libhandle As Any Pointer )

Usage

DyLibFree( libhandle )

Parameters

libhandle
The handle of a library to unload.

Description

DyLibFree is used to release at runtime libraries previously linked to your program with DyLibLoad. The argument is the handle to the library returned by DyLibLoad.

Example

See the dynamic loading example on the Shared Libraries page.

Platform Differences

  • DOS: see specific Shared Libraries - DOS page

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Dylibfree.

Differences from QB

  • New to FreeBASIC

See also

  • DyLibSymbol
  • DyLibLoad
  • Export


DyLibLoad
Loads to a Dynamic Link Library (DLL) into memory at runtime

Syntax

Declare Function DyLibLoad ( ByRef libname As String ) As Any Pointer

Usage

result = DyLibLoad ( libname )

Parameters

libname
A String containing the name of the library to load.

Return Value

The Pointer handle of the library loaded. Zero on error

Description

DyLibLoad is used to link at runtime libraries to your program. This function does the link and returns a handle that must be used with DyLibSymbol when calling a function in the library and with DyLibFree when releasing the library.

Note: If the libname string (without extension) already includes a character dot (.), it may be mandatory to explicitly specify the filename extension to avoid any parser ambiguity.

Example

See the dynamic loading example on the Shared Libraries page.

Platform Differences

  • DOS: dynamic link libraries have ".dxe" file name extension and the full libname must be specified: "<name>.dxe".
  • DOS: calling dylibload( "" ), using an empty file name will initialize the run time library exports.
  • DOS: see specific Shared Libraries - DOS page

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Dylibload.

Differences from QB

  • New to FreeBASIC

See also

  • DyLibSymbol
  • DyLibFree
  • Export


DyLibSymbol
Returns the address of a function or variable in a dll

Syntax

Declare Function DyLibSymbol ( ByVal libhandle As Any Ptr, ByRef symbol As String ) As Any Ptr
Declare Function DyLibSymbol ( ByVal libhandle As Any Ptr, ByVal symbol As Short ) As Any Ptr

Usage

result = DyLibSymbol ( libhandle, symbol )

Parameters

libhandle
The Any Ptr handle of a DLL returned by DyLibLoad
symbol
A String containing name of the function, or variable in the library to return the address of. In Windows only, can also be a Short containing the ordinal of the function/variable.

Return Value

A Pointer to the function or variable in the library.

If the function fails, the return value is 0.

Description

DyLibSymbol returns a pointer to the variable or function named symbol , in the dll pointed by libhandle. libhandle is obtained by loading the dll with DyLibLoad. The symbol must have been Exported in the dll.
If libhandle is 0, the symbol is searched in the current executable or dll.

If using cdecl functions, only the name of the procedure needs to be specified. If dynamically linking to a function created using STDCALL (default in windows), then the function must be decorated. To decorate a function, use its name, '@', then the number of bytes passed as arguments. For instance if the function FOO takes 3 integer arguments, the decorated function would be 'FOO@12'. Remember, without an explicit Alias, the procedure name will be uppercase.

If linking to a dll created in Visual C++(tm), decoration need not be used. For GCC, decoration is needed.

Note: The dylibsymbol, if failing, will attempt to automatically decorate the procedure, from @0 to @256, in 4 byte increments.

Example

See the dynamic loading example on the Shared Libraries page.

Platform Differences

  • Ordinals are not supported on Linux, 0 is always returned.
  • Ordinals are not supported on DOS, 0 is always returned.
  • Windows does not support DyLibSymbol use with variables (only with procedures).
  • DOS: see specific Shared Libraries - DOS page

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Dylibsymbol.

Differences from QB

  • New to FreeBASIC

See also

  • DyLibLoad
  • DyLibFree
  • Export


Else
Control flow statement for conditional branching

Syntax

If expression Then [statement(s)] [Else [statement(s)]] [End If]
or
If expression Then : [statement(s)] [Else [statement(s)]] : End If
or
If expression Then
[statement(s)]
[ ElseIf expression Then ]
[statement(s)]
[ Else ]
[statement(s)]
End If

Remark: EndIf (without blank) is also supported like in QB for backward compatibility.

Example

See example at If...Then.

Differences from QB

  • None

See also

  • If...Then


ElseIf
Control flow statement for conditional branching

Syntax

If expression Then
[statement(s)]
[ ElseIf expression Then ]
[statement(s)]
[ Else ]
[statement(s)]
End If

Remark: EndIf (without blank) is also supported like in QB for backward compatibility.

Example

See example at If...Then.

Differences from QB

  • None

See also

  • If...Then


Encoding
Specifies character format of a text file

Syntax

Open filename for {Input|Output|Append} Encoding "utf-8"|"utf-16"|"utf-32"|"ascii" as [#]filenum

Parameters

filename for {Input|Output|Append}
file name to open for Input, Output, or Append
Encoding "utf-8"|"utf-16"|"utf-32"|"ascii"
indicates encoding type for the file
filenum
unused file number to associate with the open file

Description

Encoding specifies the format for an Unicode text file, so Winput # and Print # use the correct encoding. If omitted from an Open statement, "ascii" encoding is the default.

Only little endian character encodings are supported at the moment.
    • "utf8",
    • "utf16"
    • "utf32"
    • "ascii" (the default)

Example

'' This example will:
'' 1) Write a string to a text file with utf-16 encoding
'' 2) Display the byte contents of the file
'' 3) Read the text back from the file
''
'' WSTRING's will work as well but STRING has been
'' used in this example since not all consoles support
'' printing WSTRING's.

'' The name of the file to use in this example
Dim f As String
f = "sample.txt"

''
Scope
  Dim s As String
  s = "FreeBASIC"

  Print "Text to write to " + f + ":"
  Print s
  Print

  '' open a file for output using utf-16 encoding
  '' and print a short message
  Open f For Output Encoding "utf-16" As #1

  '' The ascii string is converted to utf-16
  Print #1, s
  Close #1
End Scope

''
Scope
  Dim s As String, n As LongInt

  '' open the same file for binary and read all the bytes
  Open f For Binary As #1
  n = LOF(1)
  s = Space( n )
  Get #1,,s
  Close #1

  Print "Binary contents of " + f + ":"
  For i As Integer = 1 To n
    Print Hex( Asc( Mid( s, i, 1 )), 2); " ";
  Next
  Print
  Print

End Scope

''
Scope
  Dim s As String

  '' open a file for input using utf-16 encoding
  '' and read back the message
  Open f For Input Encoding "utf-16" As #1

  '' The ascii string is converted from utf-16
  Line Input #1, s
  Close #1

  '' Display the text
  Print "Text read from " + f + ":"
  Print s
  Print
End Scope

Output:
Text to write to sample.txt:
FreeBASIC

Binary contents of sample.txt:
FF FE 46 00 72 00 65 00 65 00 42 00 41 00 53 00 49 00 43 00 0D 00 0A 00

Text read from sample.txt:
FreeBASIC
Platform Differences

  • Unicode (w)strings are not supported in the DOS port of FreeBASIC

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Encoding.

Differences from QB

  • QB had no support for Unicode

See also

  • Open


End (Block)
Indicates the end of a compound statement block.

Syntax

End { Sub | Function | If | Select | Type | Enum | Scope | With | Namespace | Extern | Constructor | Destructor | Operator | Property }

Description

Used to indicate the end of the most recent code block.

The type of the block must be included in the command: one of Sub, Function, If, Select, Type, Enum, Scope, With, Namespace, Extern, Constructor, Destructor, Operator, or Property.

Ending a Sub, Function, If, Select, Scope, Constructor, Destructor, Operator, or Property block also closes the scope for variables defined inside that block. When the scope is closed, variables defined inside the scope are destroyed, calling their destructors as needed.

To end a program, see End (Statement).

Example

Declare Sub checkvalue( n As Integer )

Dim variable As Integer

Input "Give me a number: ", variable
If variable = 1 Then
Print "You gave me a 1"
Else
Print "You gave me a big number!"
End If
checkvalue(variable)

Sub checkvalue( n As Integer )
Print "Value is: " & n
End Sub

Differences from QB

  • none

See also

  • Constructor
  • Destructor
  • End (Statement)
  • Enum
  • Extern
  • Function
  • If...Then
  • Namespace
  • Operator
  • Property
  • Scope
  • Select Case
  • Sub
  • Type
  • With


End (Statement)
Control flow statement to end the program.

Syntax

Declare Sub End ( ByVal retval As Long = 0 )

Usage

End [ retval ]

Parameters

retval
Error code returned to system.

Description

Used to exit the program, and return to the operating system. An optional integer return value can be specified to indicate an error code to the system. If no return value is given, a value of 0 is automatically returned at the end of the program.

Usage of this statement does not cleanly close scope. Local variables will not have their destructors called automatically, because FreeBASIC does not do stack unwinding. Only the destructors of global variables will be called in this case.

For this reason, it is discouraged to use End simply to mark the end of a program; the program will come to an end automatically, and in a cleaner fashion, when the last line of module-level code has executed.

Example

'' This program requests a string from the user, and returns an error
'' code to the OS if the string was empty

Function main() As Integer

    '' assign input to text string
    Dim As String text
    Line Input "Enter some text ( try ""abc"" ): " , text

    '' If string is empty, print an error message and
    '' return error code 1 (failure)
    If( text = "" ) Then
        Print "ERROR: string was empty"
        Return 1
    End If

    '' string is not empty, so print the string and
    '' return error code 0 (success)
    Print "You entered: " & text
    Return 0

End Function

'' call main() and return the error code to the OS
End main()

Platform Differences

  • In Linux, the retval parameter is a Byte.

Differences from QB

  • The END statement supports specifying a custom return value to be returned to the operating system.

See also

  • End (Block)
  • Return (From Procedure)
  • Return (From Gosub)
  • System


End If
Control flow statement for conditional branching.

Syntax

If expression Then [statement(s)] [Else [statement(s)]] [End If]
or
If expression Then : [statement(s)] [Else [statement(s)]] : End If
or
If expression Then
[statement(s)]
[ ElseIf expression Then ]
[statement(s)]
[ Else ]
[statement(s)]
End If

Remark: EndIf (without blank) is also supported like in QB for backward compatibility.

Example

See example at If...Then.

Differences from QB

  • END IF was not supported in single-line IFs in QBASIC.

See also

  • If...Then


Enum
Declares an enumerated type.

Syntax

Enum [typename [ Explicit ] ]
symbolname [= expression] [, ...]
...
End Enum

Parameters

typename
Name of the Enum
symbolname
Name of the constant
expression
A constant expression
Explicit
Requires that symbols must be explicitly referred to by typename.symbolname

Description

Enum, short for enumeration, declares a list of symbol names that correspond to discrete values. If no initial value is given, the first item will be set to 0. Each subsequent symbol has a value one more than the previous unless expression is given.

Symbols may be each on their own line, or separated on a single line by commas.

An Enum is a useful way of grouping together a set of related Constants. A symbol can be accessed like a constant, e.g: a = symbolname. But if the name clashes with another symbol, it must be resolved using typename.symbolname. This resolution method is always required if you make the enum Explicit.

A non-Explicit Enum declared inside an Extern ... End Extern block will add its constants to the parent namespace directly, as in C, instead of acting as a namespace on its own. It disallows the typename.symbolname style of access, and the constants may conflict with other symbols from the parent namespace.

Enum can not contain any member procedure or member data (only symbols), but it can be included (named or unnamed) in a Type by having.

An Enum instance can be passed, as any user defined instance, to a procedure (including for the definition of Overloaded operators).
The size of an Enum instance will be always that of an Integer (no matter how many defined symbols are just declarations for the compiler assignment).

Example

Enum MyEnum
    option1 = 1
    option2
    option3
End Enum

Dim MyVar As MyEnum

MyVar = option1

Select Case MyVar
    Case option1
        Print "Option 1"
    Case option2
        Print "Option 2"
    Case option3
        Print "Option 3"
End Select

You can reference formerly declared symbol names within the same enum:
Enum MyEnum
    option1 = 1
    option2
    option3
    __
    MAX_VALUE = __ -1
End Enum

Print "Option #1:", MyEnum.option1
Print "Option #2:", MyEnum.option2
Print "Option #3:", MyEnum.option3
Print "Max Value:", MyEnum.MAX_VALUE

Output:
Option #1:     1
Option #2:     2
Option #3:     3
Max Value:     3
Dialect Differences

  • Explicit Enum not available in the -lang qb dialect unless referenced with the alias __Explicit.

Differences from QB

  • New to FreeBASIC

See also

  • Const
  • Operator
  • Table with variable types overview, limits and suffixes


SetEnviron
Sets a system environment variable

Syntax

Declare Function SetEnviron ( ByRef varexpression As String ) As Long

Usage

result = SetEnviron( varexpression )

Parameters

varexpression
Name and setting of an environment variable in the following (or equivalent) form: varname=varstring.
(varname being the name of the environment variable, and varstring being its text value to set)

Return Value

Return zero (0) if successful, non-zero otherwise.

Description

Modifies system environment variables. There are several variables available for editing other than the default ones on your system. An example of this would be fbgfx, where you can choose the form of graphics driver the FreeBASIC graphics library will use.

Example

'e.g. to set the system variable "path" to "c:":

Shell "set path" 'shows the value of path
SetEnviron "path=c:"
Shell "set path" 'shows the new value of path

  '' WINDOWS ONLY EXAMPLE! - We just set the graphics method to use
  '' GDI rather than DirectX (or Direct2D added on new systems).
  '' You may note a difference in FPS.
SetEnviron("fbgfx=GDI")

  '' Desktop width/height
Dim As Long ScrW, ScrH, BPP
ScreenInfo ScrW, ScrH, BPP

  '' Create a screen at the half width/height of your monitor.
  '' Normally this would be slow, but GDI is fairly fast for this kind
  '' of thing.
ScreenRes ScrW/2, ScrH/2, BPP

  '' Start our timer/
Dim As Double T = Timer

  '' Lock our page
ScreenLock
Do

    '' Print time since last frame
  Locate 1, 1
  Print "FPS: " & 1 / ( Timer - T )
  T = Timer

    '' Flip our screen
  ScreenUnlock
  ScreenLock
    '' Commit a graphical change to our screen.
  Cls

Loop Until Len(Inkey)

  '' unlock our page.
ScreenUnlock

Platform Differences

  • In Linux, varexpression must be permanent (a literal, a variable declared in the main code or a static variable declared in a procedure), because Linux does not memorize the string but only a pointer to its data characters.

Differences from QB

  • In QB, SetEnviron was called Environ.

See also

  • Environ
  • Shell


Environ
Returns the value of a system environment variable

Syntax

Declare Function Environ ( ByRef varname As Const String ) As String

Usage

result = Environ[$]( varname )

Parameters

varname
The name of an environment variable.

Return Value

Returns the text value of the environmental variable, or the empty string ("") if the variable does not exist.

Description

Environ returns the text value of a system environment variable.

Example

'e.g. to show the system variable "path":

Print Environ("path")

Differences from QB

  • The QB ENVIRON statement is now called SetEnviron.
  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

See also

  • SetEnviron
  • Shell


ENVELOPE
Defines an ADSR envelope slot.

Syntax

Envelope id, attack, decay, sustain, release

Description

ENVELOPE defines how a generated instrument changes volume over the life of a note. This is what makes a sound feel plucked, struck, held, or faded instead of simply turning on and off.

Attack is how long the sound takes to rise from silence. Decay is how long it takes to fall from the attack peak to the sustain level. Sustain is the level held while the sound continues. Release is how long the sound takes to fade out at the end.

Like WAVE, ENVELOPE stores a definition in a numbered slot. INSTRUMENT combines a waveform slot and an envelope slot into something NOTE and PLAY can use.

Example

envelope 1, 0.01, 0.10, 0.50, 0.20
wave 1, 0
instrument 1, 1, 1

Notes

  • Short attack and release values make percussive sounds. Longer values make softer fades.

See also

  • Sound Library Reference
  • Generated Sound Commands


Eoc
Checks whether a TCP connection has been closed by the peer.

Syntax

Declare Function Eoc ( ByVal filenum As Long ) As Long

Usage

result = Eoc( filenum )

Parameters

filenum
A file number bound to an Open Tcp connection or an accepted TCP client.

Return Value

Returns true (-1) if the connection has ended, zero (0) otherwise.

Description

Eoc means "end of connection". It is intended for TCP file numbers, where EOF only says that no byte is immediately available to read. A TCP program can poll Eof to decide whether a read is ready and Eoc to decide whether the remote endpoint has closed the socket.

Calling Eoc on a non-TCP file number is an illegal function call.

Example

See examples/network/opentcp/http-get.bas.

Differences from QB

  • New to FreeBASIC.

See also

  • Open Tcp
  • Tcp Accept
  • EOF


EOF
Checks to see if the end of an open file has been reached

Syntax

Declare Function EOF ( ByVal filenum As Long ) As Long

Usage

result = EOF( filenum )

Parameters

filenum
File number of an open file.

Return Value

Returns true (-1) if end-of-file has been reached, zero (0) otherwise.

Description

When reading from files opened for Input (File Mode), it is useful to know when the end of the file has been reached, thus avoiding errors caused by reading past the ends of files. Use EOF to determine this. EOF expects a valid file number from an already opened file. Use FreeFile to retrieve an available file file number.

For file numbers bound to files opened for Output, EOF always returns 0.

For TCP file numbers opened by Open Tcp or returned by Tcp Accept, EOF indicates whether a byte is immediately available to read. Use Eoc to test whether the remote endpoint has closed the connection.

Example

'' This code finds a free file number to use and attempts to open the file
'' "file.ext" and if successful, binds our file number to the opened file. It
'' reads the file line by line, outputting it to the screen. We loop until eof()
'' returns true, in this case we ignore the loop if file is empty.

Dim As String file_name
Dim As Long file_num

file_name = "file.ext"
file_num = FreeFile( )                 '' retrieve an available file number

'' open our file and bind our file number to it, exit on error
If( Open( file_name For Input As #file_num ) ) Then
   Print "ERROR: opening file " ; file_name
   End -1
End If

Do Until EOF( file_num )               '' loop until we have reached the end of the file
   Dim As String text
   Line Input #file_num, text               '' read a line of text ...
   Print text                             '' ... and output it to the screen
Loop

Close #file_num                        '' close file via our file number

End 0

Differences from QB

  • In QB the comm port signaled an EOF when there were no chars waiting to be read.
  • In QB, for files opened in RANDOM or BINARY mode, EOF returned non-zero only after a read past the end of file has been attempted. In FreeBASIC, EOF returns true after the last item is read.

See also

  • LOF
  • LOC
  • FreeFile
  • Eoc


Operator Eqv (Equivalence)
Returns the bitwise-and (equivalence) of two numeric values

Syntax

Declare Operator Eqv ( ByRef lhs As T1, ByRef rhs As T2 ) As Ret

Usage

result = lhs Eqv rhs

Parameters

lhs
The left-hand side expression.
T1
Any numeric or boolean type.
rhs
The right-hand side expression.
T2
Any numeric or boolean type.
Ret
A numeric or boolean type (varies with T1 and T2).

Return Value

Returns the bitwise-equivalence of the two operands.

Description

This operator returns the bitwise-equivalence of its operands, a logical operation that results in a value with bits set depending on the bits of the operands (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value).

The truth table below demonstrates all combinations of a boolean-equivalence operation:

Lhs Bit | Rhs Bit | Result
0 | 0 | 1
1 | 0 | 0
0 | 1 | 0
1 | 1 | 1

No short-circuiting is performed - both expressions are always evaluated.

The return type depends on the types of values passed. Byte, UByte and floating-point type values are first converted to Integer. If the left and right-hand side types differ only in signedness, then the return type is the same as the left-hand side type (T1), otherwise, the larger of the two types is returned. Only if the left and right-hand side types are both Boolean, the return type is also Boolean.

This operator can be overloaded for user-defined types.

Example

Dim As UByte a = &b00110011
Dim As UByte b = &b01010101, c
c = a Eqv b '' c = &b10011001

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Operator Truth Tables


Operator Eqv= (Equivalence And Assign)
Performs a bitwise-eqv (equivalence) and assigns the result to a variable

Syntax

Declare Operator Eqv= ( ByRef lhs As T1, ByRef rhs As T2 )

Usage

lhs Eqv= rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric or boolean type.
rhs
The value to perform a bitwise-eqv (equivalence) with lhs.
T2
Any numeric or boolean type.

Description

This operator performs a bitwise-eqv and assigns the result to a variable (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value). It is functionally equivalent to:
lhs = lhs Eqv rhs

Eqv= compares each bit of its operands, lhs and rhs, and if both bits are the same (either both 0 or both 1), then the corresponding bit in the first operand, lhs, is set to 1, otherwise it is set to 0.

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol 'Eqv=>' can be also used.

Example

Dim As UByte a = &b00110011
Dim As UByte b = &b01010101
a Eqv= b
'' Result    a = &b10011001
Print Bin(a)

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Eqv


Erase
Statement to erase arrays

Syntax

Declare Sub Erase ( array As Any [, ... ] )

Usage

Erase( array0 [, array1 ... arrayN ] )

Parameters

array
An array to be erased.

Description

Using Erase on a fixed-length array resets all elements without freeing the allocated memory.
In case of objects, there is destruction then re-construction.

Using Erase on a variable-length array (array already sized) frees the memory allocated for the array elements, but the array remains declared at its same scope level (with the same datatype and number of dimensions), only the high/low bounds values of each dimension are reset (-1/0).
In case of objects, there is destruction before freeing memory.

Example

Dim MyArray1(1 To 10) As Integer
ReDim MyArray2(1 To 10) As Integer

Erase MyArray1, MyArray2

Example showing the before and after results of single-dimension arrays:
Dim MyArray1(1 To 10) As Integer
ReDim MyArray2(1 To 10) As Integer

Print "MyArray1", LBound( MyArray1 ), UBound( MyArray1 ) ' prints: MyArray1       1             10
Print "MyArray2", LBound( MyArray2 ), UBound( MyArray2 ) ' prints: MyArray2       1             10

Erase MyArray1, MyArray2

Print "MyArray1", LBound( MyArray1 ), UBound( MyArray1 ) ' prints: MyArray1       1             10
Print "MyArray2", LBound( MyArray2 ), UBound( MyArray2 ) ' prints: MyArray2       0            -1

Example showing the before and after results of multi-dimension arrays:
Dim MyArray1(1 To 3, 4 To 9) As Integer
ReDim MyArray2(1 To 3, 4 To 9) As Integer

Print , "LOWER", "UPPER"
Print "MyArray1", _
      LBound( MyArray1, 1 ); ", "; LBound( MyArray1, 2 ), _
      UBound( MyArray1, 1 ); ", "; UBound( MyArray1, 2 )
Print "MyArray2", _
      LBound( MyArray2, 1 ); ", "; LBound( MyArray2, 2 ), _
      UBound( MyArray2, 1 ); ", "; UBound( MyArray2, 2 )

Erase MyArray1, MyArray2

Print
Print "MyArray1", _
      LBound( MyArray1, 1 ); ", "; LBound( MyArray1, 2 ), _
      UBound( MyArray1, 1 ); ", "; UBound( MyArray1, 2 )
Print "MyArray2", _
      LBound( MyArray2, 1 ); ", "; LBound( MyArray2, 2 ), _
      UBound( MyArray2, 1 ); ", "; UBound( MyArray2, 2 )

The above example will output:
              LOWER         UPPER
MyArray1       1,  4         3,  9
MyArray2       1,  4         3,  9

MyArray1       1,  4         3,  9
MyArray2       0,  0        -1, -1

Differences from QB

  • None

See also

  • Common
  • Dim
  • Extern
  • LBound
  • ReDim
  • Static
  • UBound
  • Var


Erfn
Error reporting function

Syntax

Declare Function Erfn ( ) As ZString Ptr

Usage

result = Erfn ( )

Return Value

Returns a pointer to the string identifying the function where the error occurred.

Returns NULL if the source is not compiled with the -exx compiler option.

Description

An error reporting function returning a pointer to the name of the function.

Example

'' test.bas
'' compile with fbc -exx -lang fblite test.bas

#lang "fblite"

Sub Generate_Error
  On Error Goto Handler
  Error 1000
  Exit Sub
Handler:
  Print "Error Function: "; *Erfn()
  Print "Error Module  : "; *Ermn()
  Resume Next
End Sub

Generate_Error

Output:
Error Function: GENERATE_ERROR
Error Module  : test.bas

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Erfn.

Differences from QB

  • New to FreeBASIC

See also

  • Erl
  • Ermn
  • On...Error


Erl
Error handling function to return the line where the error occurred

Syntax

Declare Function Erl ( ) As Long

Usage

result = Erl

Return Value

An Long return value containing the line number where the last error occurred.

Description

Erl will return the line number where the last error occurred. If no error has occurred, Erl will return 0.

Erl cannot always be used effectively -- QB-like error handling must be enabled.

Erl is reset by RESUME and RESUME NEXT

Example

' compile with -lang fblite or qb

#lang "fblite"

' note: compilation with '-ex' option is required

On Error Goto ErrorHandler

' Generate an explicit error
Error 100

End

ErrorHandler:
  Dim num As Long = Err
  Print "Error "; num; " on line "; Erl
  Resume Next

' Expected output is
' Error  100 on line  6

Differences from QB

  • FreeBASIC returns the source code line number and ignores the values of all explicit line numbers, where as QB returns the last encountered explicit line number, and will return zero (0) when explicit line numbers are not used.

See also

  • Error Handling
  • Err


Ermn
Error reporting function

Syntax

Declare Function Ermn ( ) As ZString Ptr

Usage

result = Ermn ( )

Return Value

Returns a pointer to the string identifying the module where the error occurred.

Returns NULL if the source is not compiled with the -exx compiler option.

Description

An error reporting function returning a pointer to the name of the module.

Example

'' test.bas
'' compile with fbc -exx -lang fblite test.bas

#lang "fblite"

Sub Generate_Error
  On Error Goto Handler
  Error 1000
  Exit Sub
Handler:
  Print "Error Function: "; *Erfn()
  Print "Error Module  : "; *Ermn()
  Resume Next
End Sub

Generate_Error

Output:
Error Function: GENERATE_ERROR
Error Module  : test.bas

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Ermn.

Differences from QB

  • New to FreeBASIC

See also

  • Erfn
  • Erl
  • On...Error


Err
Get or set the run-time error number

Usage

result = Err( )
or
Err = number

Description

The Err() function returns the FreeBASIC run-time error number (a 32 bit Long) which can be set by the built-in statements and functions, or by the program through Err = number or Error. Unlike Error, Err = number sets the error number without invoking an error handler.

See Runtime Error Codes for a listing of the predefined runtime error numbers and their associated meaning. The program may use additional custom error numbers.

Err can always be used, even if QB-like error handling is not enabled. Err is reset by Resume and Resume Next.

Note: Care should be taken when calling an internal function (such as Print) after an error occurred, because it will reset the error value with its own error status. To preserve the Err value, it is a good idea to store it in a variable as soon as the error handler is entered.

Remark: Some procedures used in their function version return directly the error code (a 32 bit Long).
That is the case for: BLoad, BSave, Close, FileCopy, GetJoystick, GetMouse, ImageInfo, Kill, Open, Open Com, Open Cons, Open Err, Open Lpt, Open Pipe, Open Scrn, ScreenRes, ScreenSync, SetDate, SetMouse, SetTime.

Example

An example using QBasic style error handling (compile with -ex option)
'' Compile with -lang fblite or qb

#lang "fblite"

On Error Goto Error_Handler
Error 150
End

Error_Handler:
  n = Err()
  Print "Error #"; n
  Resume Next

An example using inline error handling (note: Open can also return its own error status when called as a function)
'' compile without -e switch

Dim filename As String

Do
    Line Input "Input filename: ", filename
    If filename = "" Then End
    Open filename For Input As #1
Loop Until Err() = 0

Print Using "File '&' opened successfully"; filename
Close #1

Differences from QB

  • Error numbers are not the same as in QB.

See also

  • On Error
  • Error
  • Error Handling
  • Runtime Error Codes


Error
Error handling statement to force an error to be generated

Syntax

Declare Sub Error ( errno As Long )

Usage

Error number

Parameters

number
The error number to generate

Description

Error invokes the error handler specified with On Error or, in case there was none set, aborts the program, printing an error message similar to those generated by the compiler's -exx run-time error checking. It's possible to use the built-in run-time error numbers and/or other custom error numbers for number. This can be used to simulate custom error numbers.

Example

To send an error alert of error 150 (just some arbitrary error code) one would do the following:
Error 150

Differences from QB

  • Error numbers are not the same as in QB.

See also

  • Err
  • Error Handling
  • Runtime Error Codes


Event (Message Data From Screenevent)
Pre-defined structure (UDT) from fbgfx.bi used by ScreenEvent to return event data

Syntax

#include once "fbgfx.bi"
using fb
Dim variable As Event

Description

Here we report the EVENT structure for clarity:

Type Event Field = 1
    Type As Long
    Union
        Type
            scancode As Long
            ascii As Long
        End Type
        Type
            x As Long
            y As Long
            dx As Long
            dy As Long
        End Type
        button As Long
        z As Long
        w As Long
    End Union
End Type

The Type field will contain the event type ID, while the remaining 4 integers will hold sensitive data to the event type.

Event types
The event type is identified by an ID number returned into the first integer of the event buffer (the .type field in the EVENT structure). Known event type IDs - and their values at time of writing - are:
    • EVENT_KEY_PRESS (1) A key was pressed on the keyboard. The .scancode field contains the platform independent scancode value for the key; if the key has an ascii representation, it is held into the .ascii field, which otherwise has a value of 0.
    • EVENT_KEY_RELEASE (2) A key was released on the keyboard. The .scancode and .ascii fields have the same meaning as with the EVENT_KEY_PRESS event.
    • EVENT_KEY_REPEAT (3) A key is being held down repeatedly. The .scancode and .ascii fields have the same meaning as with the EVENT_KEY_PRESS event.
    • EVENT_MOUSE_MOVE (4) The mouse was moved while it was on the program window. The .x and .y fields contain the new mouse position relative to the upper-left corner of the screen, while the .dx and .dy fields contain the motion deltas.
    • EVENT_MOUSE_BUTTON_PRESS (5) One of the mouse buttons was pressed. The .button field has one bit set identifying the button that was pressed; bit 0 identifies the left mouse button, bit 1 the right mouse button and bit 2 the middle mouse button.
    • EVENT_MOUSE_BUTTON_RELEASE (6) One of the mouse buttons was released. The .button field has the same meaning as with the EVENT_MOUSE_BUTTON_PRESS event.
    • EVENT_MOUSE_DOUBLE_CLICK (7) One of the mouse buttons was double clicked. The .button field has the same meaning as with the EVENT_MOUSE_BUTTON_PRESS event.
    • EVENT_MOUSE_WHEEL (8) The mouse wheel was used; the new wheel position is returned into the .z field.
    • EVENT_MOUSE_ENTER (9) The mouse was moved into the program window.
    • EVENT_MOUSE_EXIT (10) The mouse was moved out of the program window.
    • EVENT_WINDOW_GOT_FOCUS (11) The program window has got focus.
    • EVENT_WINDOW_LOST_FOCUS (12) The program window has lost focus.
    • EVENT_WINDOW_CLOSE (13) The user attempted to close the program window.
    • EVENT_MOUSE_HWHEEL (14) The horizontal mouse wheel was used; the new horizontal wheel position is returned into the .w field.

The fbgfx.bi header file contains a definition of the EVENT user data type, so it is not necessary to declare it manually.

Example

See example at ScreenEvent.

Dialect Differences

  • In lang fb, the structure and constants are stored in the FB Namespace. This is not the case in other dialects.

Differences from QB

  • New to FreeBASIC

See also

  • ScreenEvent
  • Event Handling
  • Keyboard scancodes
  • Keyboard Input


Exec
Temporarily transfers execution to an external program

Syntax

Declare Function Exec ( ByRef program As Const String, ByRef arguments As Const String ) As Long

Usage

result = Exec( program, arguments )

Parameters

program
The file name (including file path) of the program (executable) to transfer control to.
arguments
The command-line arguments to be passed to the program.

Return Value

The exit status of the program, or negative one (-1) if the program could not be executed.

Description

Transfers control over to an external program. When the program exits, execution resumes immediately after the call to Exec.

Example

'A Windows based example but the same idea applies to Linux
Const exename = "NoSuchProgram.exe"
Const cmdline = "arg1 arg2 arg3"
Dim result As Long
result = Exec( exename, cmdline )
If result = -1 Then
    Print "Error running "; exename
Else
    Print "Exit code:"; result
End If

Platform Differences

  • Linux requires the program case matches the real name of the file. Windows and DOS are case insensitive. The program being executed may be case sensitive for its command line parameters.
  • Path separators in Linux are forward slashes / . Windows uses backward slashes \ but it allows for forward slashes . DOS uses backward \ slashes.
  • Exit code is limited to 8 bits in DOS.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Exec.

Differences from QB

  • New to FreeBASIC

See also

  • Chain transfer temporarily, without arguments
  • Run one-way transfer
  • Command pick arguments


ExePath
Returns the path of the running program

Syntax

Declare Function ExePath ( ) As String

Usage

result = ExePath

Return Value

A String variable set to the path of the running program.

Description

Returns the path (the location) of the calling program. This is not necessarily the same as CurDir.

Example

Dim pathname As String = ExePath
Print "This program's initial directory is: " & pathname

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Exepath.

Differences from QB

  • New to FreeBASIC

See also

  • CurDir


Exit
Control flow statement to exit a compound statement block

Syntax

Exit {Do | For | While | Select }
Exit {Sub | Function | Operator | Constructor | Destructor | Property }

Exit {Do [, Do [ , ...] ] |
For [, For [ , ...] ] |
While [, While, [...] ] |
Select [, Select [ , ...] ] }

Description

Leaves a code block such as a Sub, Function, Operator, Constructor, Destructor, Property, Do...Loop, For...Next, While...Wend, or a Select Case block. The execution skips the rest of the block and goes to the line after its end.

Where there are multiple Do / For / While / Select blocks nested, it will skip to the end of the innermost block of that type. You can skip to the end of multiple blocks of that type by giving the word multiple times, separated by commas.
For example: Exit While, While

Example

'e.g. the print command will not be seen

Do
    Exit Do ' Exit the Do...Loop and continues to run the code after Loop
    Print "I will never be shown"
Loop

Dim As Integer i, j
For i = 1 To 10

    For j = 1 To 10

        Exit For, For

    Next j

    Print "I will never be shown"

Next i

Differences from QB

  • EXIT OPERATOR, EXIT CONSTRUCTOR, EXIT DESTRUCTOR, EXIT PROPERTY, EXIT WHILE and EXIT SELECT are new to FreeBASIC.

See also

  • Sub
  • Function
  • Do...Loop
  • For...Next
  • While...Wend
  • Continue


Exp
Returns e raised to the power of a given number

Syntax

Declare Function Exp cdecl ( ByVal number As Double ) As Double

Usage

result = Exp( number )

Parameters

number
The Double number that e is raised to the power of.

Return Value

Returns the Double value of e raised to power of number.

Description

The mathematical constant e, also called Euler's constant, is the base of the Exp and Log and is an irrational and transcendental number. The value of e to twenty significant figures is: 2.7182818284590452354. The required number argument can be any valid numeric expression within range of the function. If number is too large, Exp returns infinity. If number is too small, Exp returns zero (0.0). If number is zero, 1.0 is returned. The exact limit on number is based on the math processor.

Exp can be overloaded as operator to accept user-defined types.

Example

'Compute Continuous Compound Interest
Dim r As Double
Dim p As Double
Dim t As Double
Dim a As Double

Input "Please enter the initial investment (principal amount): "; p
Input "Please enter the annual interest rate (as a decimal): "; r
Input "Please enter the number of years to invest: "; t

a = p * Exp ( r * t )
Print ""
Print "After";t;" years, at an interest rate of"; r * 100; "%, your initial investment of"; p; " would be worth";a

The output would look like:
Please enter the initial investment (principal amount): 100
Please enter the annual interest rate (As a decimal): .08
Please enter the number of years To invest: 20
After 20 years, at an interest rate of 8%, your initial investment of 100 would be worth 495.3032424395115

Differences from QB

  • None

See also

  • Log
  • Operator ^ (Exponentiate)


Export
Definition specifier to indicate that a procedure in a DLL should be visible from other programs

Syntax

{ Sub | Function } proc_name ( argumentlist ) [ [ ByRef ] As datatype ] Export

Description

If a function is defined with this clause in a DLL, it is added to the public export table, so external programs can dynamically link to it using DyLibSymbol.
(specifier to be put at the level of the first line of procedure definition, forbidden at procedure declaration line level)

Example

See the examples on the Shared Libraries page.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Export.

Platform Differences

  • Export has no effect in DOS dynamic link libraries (DXE's); all public procedures are exported.

Differences from QB

  • New to Freebasic

See also

  • DyLibLoad
  • DyLibSymbol
  • Alias


Extends
Specifies a base type from which to derive a new type

Syntax

Type|Union typename Extends base_typename
...
End Type|Union

Description

Extends declares typename to be derived from base_typename. The derived user-defined type, typename, inherits fields and methods of the base_typename base type. typename objects may be used in place of base_typename objects. Fields and methods inherited from base_typename will be implicitly accessible like members of typename.
However, a member will shadow an inherited member if they have the same identifier. The Base (Member Access) keyword can be used to explicitly access members of the base type shadowed by local members.

User-defined types that extend another type will include the base type structure at their beginning, and their size as reported by Sizeof() is the sum of their base type's size plus the size needed for any regular data fields (non-static data fields). Since the inherited members make sure that the structure is not empty, a derived type is not required to have regular data fields of its own.

In typename (the derived user-defined type), the fields can share the same memory space than the base_typename only if typename is a Union. Here it does not matter whether base_typename is a Union or not.
If only base_typename is a Union, then it will not be affected by fields from typename (the derived user-defined type).
As a Union is not allowed to have complex fields (i.e. user-defined types with constructor/destructor, or dynamic strings), a derived Union cannot be allowed to have (contain) a complex base_typename.

The Base (Initializer) keyword can be used at the top of constructor of derived user-defined type. It allows to specify an initializer or constructor call for the base type.

Extending the built-in Object type allows a user-defined type to be used with Operator Is to perform run-time type checks, to support Virtual and Abstract methods, and to use the Override method attribute.

Note: UDT pointer can only be cast to pointer types of "widened compatibility" (up or even down in the inheritance hierarchy), or to Any Ptr. Otherwise, cast to Any Ptr first (or to Object Ptr first if both types are directly or indirectly derived from Object).

Example

Type SchoolMember 'Represents any school member'
    Declare Constructor ()
    Declare Sub Init (ByRef _name As String, ByVal _age As Integer)
    As String Name
    As Integer age
End Type

Constructor SchoolMember ()
    Print "Initialized SchoolMember"
End Constructor

Sub SchoolMember.Init (ByRef _name As String, ByVal _age As Integer)
    This.name = _name
    This.age = _age
    Print "Name: "; This.name; "   Age:"; This.age
End Sub

Type Teacher Extends SchoolMember 'Represents a teacher derived from SchoolMember'
    Declare Constructor (ByRef _name As String, ByVal _age As Integer, ByVal _salary As Integer)
    As Integer salary
    Declare Sub Tell ()
End Type

Constructor Teacher (ByRef _name As String, ByVal _age As Integer, ByVal _salary As Integer)
    Print "Initialized Teacher"
    This.Init(_name, _age) 'implicit access to base member procedure'
    This.salary = _salary
End Constructor

Sub Teacher.Tell ()
    Print "Salary:"; This.salary
End Sub

Type Student Extends SchoolMember 'Represents a student derived from SchoolMember'
    Declare Constructor (ByRef _name As String, ByVal _age As Integer, ByVal _marks As Integer)
    As Integer marks
    Declare Sub Tell ()
End Type

Constructor Student (ByRef _name As String, ByVal _age As Integer, ByVal _marks As Integer)
    Print "Initialized Student"
    This.Init(_name, _age) 'implicit access to base member procedure'
    This.marks = _marks
End Constructor

Sub Student.Tell ()
    Print "Marks:"; This.marks
End Sub

Dim As Teacher t = Teacher("Mrs. Shrividya", 40, 30000)
t.Tell()
Print
Dim As Student s = Student("Swaroop", 22, 75)
s.Tell()

' Example using all eight keywords of inheritance:
'   'Extends', 'Base.', 'Base()', 'Object', 'Is' operator, 'Virtual', 'Abstract', 'Override'

Type root Extends Object ' 'Extends' to activate RTTI by inheritance of predefined Object type
  Declare Function ObjectHierarchy () As String
  Declare Abstract Function ObjectRealType () As String ' 'Abstract' declares function without local body
                                                        '    which must be overriden
  Dim Name As String
  Declare Virtual Destructor () ' 'Virtual' declares destructor with body ('Abstract' forbidden)
Protected:
  Declare Constructor () ' to avoid user construction from root
  Declare Constructor (ByRef rhs As root) '' to avoid user copy-construction from root
End Type ' derived type may be member data empty

Constructor root ()
End Constructor

Function root.ObjectHierarchy () As String
  Return "Object(forRTTI) <- root"
End Function

Virtual Destructor root ()
  Print "root destructor"
End Destructor

Type animal Extends root ' 'Extends' to inherit of root
  Declare Constructor (ByRef _name As String = "")
  Declare Function ObjectHierarchy () As String
  Declare Virtual Function ObjectRealType () As String Override ' 'Virtual' declares function with local
                                                                '    body which can be overriden
                                                                ' 'Override' to check if the function is
                                                                '    well an override
  Declare Virtual Destructor () Override ' 'Virtual' declares destructor with local body
                                         ' 'Override' to check if the destructor is well an override
End Type

Constructor animal (ByRef _name As String = "")
  This.name = _name
End Constructor

Function animal.ObjectHierarchy () As String
  Return Base.ObjectHierarchy & " <- animal" ' 'Base.' allows to access to parent member function
End Function

Virtual Function animal.ObjectRealType () As String
  Return "animal"
End Function

Virtual Destructor animal ()
  Print "  animal destructor: " & This.name
End Destructor

Type dog Extends animal ' 'Extends' to inherit of animal
  Declare Constructor (ByRef _name As String = "")
  Declare Function ObjectHierarchy () As String
  Declare Function ObjectRealType () As String Override ' 'Override' to check if the function is well an
                                                        '    override
  Declare Destructor () Override ' 'Override' to check if the destructor is well an override
End Type ' derived type may be member data empty

Constructor dog (ByRef _name As String = "")
  Base(_name) ' 'Base()' allows to call parent constructor
End Constructor

Function dog.ObjectHierarchy () As String
  Return Base.ObjectHierarchy & " <- dog" ' 'Base.' allows to access to parent member function
End Function

Function dog.ObjectRealType () As String
  Return "dog"
End Function

Destructor dog ()
  Print "    dog destructor: " & This.name
End Destructor

Type cat Extends animal ' 'Extends' to inherit of animal
  Declare Constructor (ByRef _name As String = "")
  Declare Function ObjectHierarchy () As String
  Declare Function ObjectRealType () As String Override ' 'Override' to check if the function is well an
                                                        '    override
  Declare Destructor () Override ' 'Override' to check if the destructor is well an override
End Type ' derived type may be member data empty

Constructor cat (ByRef _name As String = "")
  Base(_name) ' 'Base()' allows to call parent constructor
End Constructor

Function cat.ObjectHierarchy () As String
  Return Base.ObjectHierarchy & " <- cat" ' 'Base.' allows to access to parent member function
End Function

Function cat.ObjectRealType () As String
  Return "cat"
End Function

Destructor cat ()
  Print "    cat destructor: " & This.name
End Destructor

Sub PrintInfo (ByVal p As root Ptr) ' must be put after definition of animal type, dog type and cat type
  Print "  " & p->Name, "  " & p->ObjectRealType, "           ";
  If *p Is dog Then ' 'Is' allows to check compatibility with type symbol
    Print  Cast(dog Ptr, p)->ObjectHierarchy
  ElseIf *p Is cat Then ' 'Is' allows to check compatibility with type symbol
    Print Cast(cat Ptr, p)->ObjectHierarchy
  ElseIf *p Is animal Then ' 'Is' allows to check compatibility with type symbol
    Print Cast(animal Ptr, p)->ObjectHierarchy
  End If
End Sub

Print "Name:", "Object (real):         Hierarchy:"
Dim a As root Ptr = New animal("Mouse")
PrintInfo(a)
Dim d As root Ptr = New dog("Buddy")
PrintInfo(d)
Dim c As root Ptr = New cat("Tiger")
Printinfo(c)
Print
Delete a
Delete d
Delete c

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Extends.

Differences from QB

  • New to FreeBASIC

See also

  • Type
  • Union
  • Base (Initializer)
  • Base (Member Access)
  • Object
  • Operator Is
  • Virtual
  • Abstract
  • Override
  • Extends Zstring
  • Extends Wstring


Extends Wstring
Specifies a type which inherits a Wstring behavior

Syntax

Type|Union typename Extends Wstring [, base_typename]
...
End Type|Union

Description

Extends Wstring declares typename to inherit properties and behaviors of a WString. Purpose is to allow users to create custom string types (with i.e. dynamic memory management) that can integrate well in to existing fbc compiler built ins (good interoperability with fbc's WString type).

This declaration of such a UDT with a suitable Cast operator will instruct compiler to convert the UDT to a WString (in addition, other suitable operators as Let, [] (Pointer Index), Len, ..., can be also declared).

WString behaviour can be inherited directly, or indirectly and singly from a base-type.
WString behaviour can be inherited by a UDT also extending base_typename (a kind of pseudo multiple-inheritance).

By declaring a type (directly or indirectly) as Extends Wstring (in addition to defining a suitable Cast operator only), this promotes it fully WString type compatible, even with StrPtr/SAdd, LSet/RSet, and Select Case.

Example

See Extends Zstring for similar examples.

Version

  • Before fbc 1.09.0, this promotion was not yet fully WString type compatible with the built in functions Val/ValInt/ValLng/ValUInt/Valunlg and Left/Right.
  • Since fbc 1.07.0

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Extends __Wstring, but unusable because no member procedure is allowed in this dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Type
  • Union
  • Extends
  • WString
  • Extends Zstring


Extends Zstring
Specifies a type which inherits a Zstring behavior

Syntax

Type|Union typename Extends Zstring [, base_typename]
...
End Type|Union

Description

Extends Zstring declares typename to inherit properties and behaviors of a ZString. Purpose is to allow users to create custom string types (with i.e. dynamic memory management) that can integrate well in to existing fbc compiler built ins (good interoperability with fbc's ZString type).

This declaration of such a UDT with a suitable Cast operator will instruct compiler to convert the UDT to a ZString (in addition, other suitable operators as Let, [] (Pointer Index), Len, ..., can be also declared).

ZString behaviour can be inherited directly, or indirectly and singly from a base-type.
ZString behaviour can be inherited by a UDT also extending base_typename (a kind of pseudo multiple-inheritance).

By declaring a type (directly or indirectly) as Extends Zstring (in addition to defining a suitable Cast operator only), this promotes it fully ZString type compatible, even with StrPtr/SAdd, LSet/RSet, and Select Case.

Example

Type myZstring Extends ZString
  Public:
    Declare Constructor (ByRef z As Const ZString = "")
    Declare Operator Cast () ByRef As Const ZString
    Declare Operator Let (ByRef z As Const ZString)
  Private:
    Dim As String s
End Type

Constructor myZstring (ByRef z As Const ZString = "")
  This.s = z
End Constructor

Operator myZstring.Cast () ByRef As Const ZString
  Return *StrPtr(This.s)
End Operator

Operator myZstring.Let (ByRef z As Const ZString)
  This.s = z
End Operator

Dim As myZstring z = "FreeBASIC"
Print "'" & z & "'"

z &= " compiler"
Print "'" & z & "'"

Sleep

Type vZstring Extends ZString
  Public:
    Declare Constructor (ByVal pz As Const ZString Ptr = 0)
    Declare Operator Cast () ByRef As ZString
    Declare Operator Let (ByVal pz As Const ZString Ptr)
    Declare Operator [] (ByVal index As Integer) ByRef As UByte
    Declare Destructor ()
  Private:
    Dim As ZString Ptr p
    Dim As UInteger l
End Type

Constructor vZstring (ByVal pz As Const ZString Ptr = 0)
  This.l = Len(*pz)
  This.p = CAllocate(This.l + 1, SizeOf(ZString))
  *This.p = *pz
End Constructor

Operator vZstring.Cast () ByRef As ZString
  Return *This.p
End Operator

Operator vZstring.Let (ByVal pz As Const ZString Ptr)
  If This.l < Len(*pz) Then
    Deallocate(This.p)
    This.l = Len(*pz)
    This.p = CAllocate(This.l + 1, SizeOf(ZString))
  End If
  *This.p = *pz
End Operator

Operator vZstring.[] (ByVal index As Integer) ByRef As UByte
  Return This.p[index]
End Operator

Destructor vZstring ()
  Deallocate(This.p)
End Destructor

Operator Len (ByRef v As vZstring) As Integer
  Return Len(Type<String>(v))        '' found nothing better than this
End Operator                         ''     (or: 'Return Len(Str(v))')

Dim As vZstring v = "FreeBASIC"
Print "'" & v & "'", Len(v)

Dim As ZString * 256 z
z = *StrPtr(v)                       '' 'error 24: Invalid data types' without 'Extends Zstring'
Print "'" & z & "'", Len(z)

v &= Space(2)
Print "'" & v & "'", Len(v)
RSet v, "FreeBASIC"                  '' 'error 24: Invalid data types' without 'Extends Zstring'
Print "'" & v & "'", Len(v)          ''     ('Cast' must return a modifiable reference)

Select Case v                        '' 'error 24: Invalid data types' without 'Extends Zstring'
Case Type<vZstring>(Trim(v) & "  ")
  Print "Left justified"
Case Type<vZstring>("  " & Trim(v))
  Print "Right justified"
End Select

v[0] = Asc("-")
Print "'" & v & "'", Len(v)

Print "'" & Right(v, 5) & "'"        '' since fbc 1.09.0, 'Right' supports types with 'Extends Zstring'
'Print "'" & Right(Str(v), 5) & "'"  '' before fbc 1.09.0, use this workaround (or: 'Right(Type<String>(v), 5)')

Sleep

Version

  • Before fbc 1.09.0, this promotion was not yet fully ZString type compatible with the built in functions Val/ValInt/ValLng/ValUInt/Valunlg and Left/Right.
  • Since fbc 1.07.0

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Extends __Zstring, but unusable because no member procedure is allowed in this dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Type
  • Union
  • Extends
  • ZString
  • Extends Wstring


Extern
Declares a variable, array or object having external linkage

Syntax

Extern [ Import ] symbolname[ (subscripts) ] [ Alias "aliasname" ] As DataType [, ...]

Parameters

symbolname
The name of the variable, array or object.
aliasname
An alternate external name for the variable, array or object.

Description

Declares symbolname as an external name, meaning it is global to external modules including those to be compiled as static and dynamic libraries (DLLs).
Extern only declares variables, arrays and objects, and does not define them (different from Common or Dim). It also has the effect of making symbolname a shared name, meaning it is visible within procedures (see Shared). A symbolname declared as external name can be (re)defined (using Dim or Redim) only in a single external module.

If Alias is used, aliasname will be used as the external name rather than symbolname, and its case will be preserved.

Extern was added in order to support the C libraries.

If Import is used, the name will be added to the dynamic library import list so its address can be fixed at run-time.

Example

'' extern1.bas

Extern Foo Alias "foo" As Integer

Sub SetFoo
    foo = 1234
End Sub

'' extern2.bas

Declare Sub SetFoo

Extern Foo Alias "foo" As Integer

Dim foo As Integer = 0

SetFoo

Print Foo

Output:
 1234
Platform Differences

  • Windows does not support Extern with a dynamic library (compiled with -dll or -dylib).

Dialect Differences

  • Not available in the -lang qb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Extern...End Extern
  • Common
  • Dim
  • Shared
  • Alias (Name)
  • Alias (Modifier)


Extern...End Extern
Statement block to allow calling of functions compiled for specific languages or platforms.

Syntax

Extern { "C" | "C++" | "Windows" | "Windows-MS" | "rtlib" } [ Lib "libname" ]
declarative statements
End Extern

Description

Extern blocks provide default calling conventions for procedures and mandate a certain name decoration.

Extern "C" blocks provide a default cdecl calling convention to procedures, and also preserve the case of all names declared within them. The same effect can be achieved without the EXTERN block by using cdecl together with an Alias string containing the exact procedure name.

Extern "C++" blocks are exactly like Extern "C" blocks but they also mangle the names declared within them in a way compatible to that of g++-4.x.

Extern "Windows" blocks provide a default stdcall calling convention to procedures, preserve the case of all names declared within them, and on the Windows platform, append an "@N" suffix to procedure names, where N is the total size in bytes of any procedure parameters. Similar to the Extern "C" block, the same effect can be achieved by using stdcall and Alias.

Extern "Windows-MS" blocks are exactly like Extern "Windows" blocks but do not append the "@N" suffix to procedure names on Windows.

Extern "rtlib" blocks combine the name decoration of Extern "c" and the default calling convention of fbc. When used in a namespace, the symbol respects the scope of the namespace, however, the name decoration (name mangling) links the symbol to a regular C library. This behaviour may be desired when declaring procedures that exist in the fb run-time library; where calling convention is based on the target, but we would like to have the compile time name respect the namespace.

Lib "libname" can be used to specify a library which will be linked in as if #Inclib "Libname" or -l libname had been used. Additionally, all procedure declarations inside the Extern block will use the specified Lib "libname" as if it was specified as part of their declarations (but it can still be overridden with an explicit Lib "libname").

Example

'' This procedure uses the default calling convention for the system, which is
'' STDCALL on Win32 and CDECL on Linux/DOS/*BSD, and is seen externally as
'' "MYTEST1@4" on Win32 and "MYTEST1" on Linux/DOS/*BSD (following FB's default
'' ALL-UPPER-CASE name mangling).
Sub MyTest1 ( ByVal i As Integer )
End Sub

Extern "C"
    '' This procedure uses the CDECL convention and is seen externally
    '' as "MyTest2".
    Sub MyTest2 ( ByVal i As Integer )
    End Sub
End Extern

Extern "C++"
    '' This procedure uses the CDECL convention and its name is mangled
    '' compatible to g++-4.x, specifically: "_Z7MyTest3i"
    Sub MyTest3 ( ByVal i As Integer )
    End Sub
End Extern

Extern "Windows"
    '' This procedure uses the STDCALL convention and is seen externally
    '' as "MyTest4@4" on Windows, and "MyTest4" on Linux, *BSD and DOS.
    Sub MyTest4 ( ByVal i As Integer )
    End Sub
End Extern

Extern "Windows-MS"
    '' This procedure uses the STDCALL convention and is seen externally
    '' as "MyTest5".
    Sub MyTest5 ( ByVal i As Integer )
    End Sub
End Extern

MyTest1( 0 )
MyTest2( 0 )
MyTest3( 0 )
MyTest4( 0 )

Dialect Differences

  • Extern blocks are only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

Platform Differences

  • On Linux, *BSD and DOS platforms, Extern "Windows" blocks never append a "@N" suffix to procedure names, and thus are equal to Extern "Windows-MS".

See also

  • cdecl
  • stdcall
  • Extern


False
Intrinsic constant set by the compiler

Syntax

Const False As Boolean

Description

Gives the False Boolean value where used.

Example

Dim b As Boolean = False
If b Then
    Print "b is True"
Else
    Print "b is False"
End If

b is False

Version

  • Since fbc 1.04.0

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __False.

Differences from QB

  • New to FreeBASIC

See also

  • True
  • Boolean


__Fastcall
Specifies the Fastcall calling convention in a procedure declaration

Syntax

declare Sub name __Fastcall [Overload] [Alias "alias"] ( parameters )
declare Function name __Fastcall [Overload] [Alias "alias"] ( parameters ) [ ByRef ] As return_type

Sub name [__Fastcall] [Overload] [Alias "alias"] ( parameters )
Function name [__Fastcall] [Overload] [Alias "alias"] ( parameters ) [ ByRef ] As return_type

Description

Fastcall is a calling convention for x86 targets where the first and second integer arguments are passed in the in ECX and EDX registers respectively instead of on the stack. All other arguments are passed right to left and callee cleans up the stack (like Stdcall).

Fastcall can be specified at both the declaration and the definition.
If a procedure definition has a declaration (with calling convention explicit or by default) and the definition does not explicitly specify a calling convention, then the calling convention is implied by the declaration.

Note: Default calling convention on win32 x86 inside an extern "c++" block for non-static member procedures is Thiscall.

Example

Extern "c++"
    Declare Function Example __Fastcall (param1 As Long, param2 As Byte, param3 As LongInt, param4 As String) As Integer
End Extern

Version

  • Since fbc 1.10.0.

Differences from QB

  • New to FreeBASIC

See also

  • cdecl, stdcall, Thiscall
  • Declare
  • Sub, Function


Fb_Memcopy
Copies a block of memory from a location to another

Syntax

Declare Function fb_memcopy cdecl ( ByRef dst As Any, ByRef src As Any, ByVal bytes As UInteger ) As Any Ptr

Usage

[result =] fb_memcopy( dst, src, bytes )

Parameters

dst
starting address of destination memory
src
starting address of source memory
bytes
number of bytes to copy

Return Value

The starting address of destination memory is returned.

Description

fb_memcopy copies a given number of bytes from the memory location src to the memory location dst.
Each starting address is taken from a reference to a variable or array element.
The memory areas must not overlap (otherwise, the copying is not guaranteed to work properly, especially depending on the platform). Use Fb_Memmove preferably when the memory areas do overlap (safer approach).
To avoid overflows, the valid memory areas pointed to by both src and dst must be at least equal in size to the number of bytes to be copied.

The underlying type of the objects pointed to by both the source and destination pointers are irrelevant for this function.
The function does not check for any terminating null character in the source area. It always copies exactly the given number of bytes.
The result is a binary copy of the data.

Note: In order to copy from/to memory referenced by a Pointer, it must be dereferenced first (or else specify in argument term the ByVal keyword in front of the pointer name). Otherwise, fb_memcopy will try to copy the bytes from/to the pointer variable's memory location.

Example

Type Person
    Dim As ZString * 40 Name
    Dim As Integer age
End Type

Dim As ZString Ptr mynameptr = @"Pierre de Fermat"

Dim As Person person1, person2

' using fb_memcopy to copy string
fb_memcopy(person1.name, *mynameptr, Len(*mynameptr) + 1)
person1.age = 46

' using fb_memcopy to copy structure
fb_memcopy(person2, person1, SizeOf(Person))

Print person2.name, person2.age

Sleep

Output:
Pierre de Fermat             46
Version

  • Since fbc 1.08.0

Differences from QB

  • The behavior and usage is new to FreeBASIC.

See also

  • fb_MemCopyClear
  • Fb_Memmove


fb_MemCopyClear
Copies the first part of a block of memory from a location to another and clears the rest

Syntax

Declare Sub fb_MemCopyClear ( ByRef dst As Any, ByVal dstlen As UInteger, ByRef src As Any, ByVal srclen As UInteger )

Usage

fb_memcopy( dst, dstlen, src, srclen )

Parameters

dst
starting address of destination memory
dstlen
number of bytes to write
src
starting address of source memory
srclen
number of first bytes to copy (other are cleared)

Description

fb_memcopycopy copies a given number of bytes (dstlen) from the memory location src to the memory location dst, but only the first srclen bytes are really copied and the rest is cleared ((dstlen - srclen) bytes).
Each starting address is taken from a reference to a variable or array element.
The memory areas must not overlap (otherwise, the copying is not guaranteed to work properly, especially depending on the platform).
To avoid overflows, the valid memory areas pointed to by both src and dst must be at least equal in size to the number of bytes to be copied (including the bytes cleared).

The underlying type of the objects pointed to by both the source and destination pointers are irrelevant for this function.
The function does not check for any terminating null character in the source area. It always copies exactly the given number of bytes.
The result is a binary copy of the data for the first srclen bytes and a zeroing for the rest ((dstlen - srclen) bytes).

Note: In order to copy from/to memory referenced by a Pointer, it must be dereferenced first (or else specify in argument term the ByVal keyword in front of the pointer name). Otherwise, fb_MemCopyClear will try to copy the bytes from/to the pointer variable's memory location.

Example

Dim src As ZString * 10 = "FreeBASIC"
Dim dst As ZString * 10 = "012345678"

Print "before:"
Print "src = " & src
Print "dst = " & dst
Print

'' copy first 4 bytes and clear the rest
fb_MemCopyClear(dst, SizeOf(dst), src, 4)

Print "after:"
Print "src = " & src
Print "dst = " & dst

Sleep

Output:
before:
src = FreeBASIC
dst = 012345678

after:
src = FreeBASIC
dst = Free
Version

  • Since fbc 1.08.0

Differences from QB

  • The behavior and usage is new to FreeBASIC.

See also

  • Fb_Memcopy
  • Fb_Memmove


Fb_Memmove
Copies a block of memory from a location to another

Syntax

Declare Function fb_memmove cdecl ( ByRef dst As Any, ByRef src As Any, ByVal bytes As UInteger ) As Any Ptr

Usage

[result =] fb_memmove( dst, src, bytes )

Parameters

dst
starting address of destination memory
src
starting address of source memory
bytes
number of bytes to copy

Return Value

The starting address of destination memory is returned.

Description

fb_memmove copies a given number of bytes from the memory location src to the memory location dst. Each starting address is taken from a reference to a variable or array element.
Copying takes place as if an intermediate buffer were used, allowing the destination and source areas to overlap in any way (safer approach, and for any platform). Using Fb_Memcopy is sufficient (and may induce greater speed) when the memory areas do not overlap.
To avoid overflows, the valid memory areas pointed to by both src and dst must be at least equal in size to the number of bytes to be copied.

The underlying type of the objects pointed to by both the source and destination pointers are irrelevant for this function.
The function does not check for any terminating null character in the source area. It always copies exactly the given number of bytes.
The result is a binary copy of the data.

Note: In order to copy from/to memory referenced by a Pointer, it must be dereferenced first (or else specify in argument term the ByVal keyword in front of the pointer name). Otherwise, fb_memmove will try to copy the bytes from/to the pointer variable's memory location.

Example

Dim As ZString * 33 z = "memmove can be very useful......"

Print z

fb_memmove(z[20], z[15], 11)

Print z

Sleep

Output:
memmove can be very useful......
memmove can be very very useful.
Version

  • Since fbc 1.08.0

Differences from QB

  • The behavior and usage is new to FreeBASIC.

See also

  • Fb_Memcopy
  • fb_MemCopyClear


Fbarray (Array Descriptor Structure And Access)
Pre-defined structure (UDT) and procedure declarations from the fbc-int/array.bi include file, usable to access the array descriptor data fields.

Syntax

From ./inc/fbc-int/array.bi:
# If __FB_LANG__ = "fb"
Namespace FBC
# endif

Const FB_MAXDIMENSIONS As Integer = 8

Type FBARRAYDIM
    Dim As UInteger elements     '' number of elements
    Dim As Integer LBound        '' dimension lower bound
    Dim As Integer UBound        '' dimension upper bound
End Type

Const FBARRAY_FLAGS_DIMENSIONS = &h0000000f    '' number of entries allocated in dimTb()
Const FBARRAY_FLAGS_FIXED_DIM  = &h00000010    '' array has fixed number of dimensions
Const FBARRAY_FLAGS_FIXED_LEN  = &h00000020    '' array points to fixed-length memory
Const FBARRAY_FLAGS_RESERVED   = &hffffffc0    '' reserved, do not use

Type FBARRAY
    Dim As Any Ptr index_ptr     '' @array(0, 0, 0, ... )
    Dim As Any Ptr base_ptr      '' start of memory at array lowest bounds
    Dim As UInteger size         '' byte size of allocated contents
    Dim As UInteger element_len  '' byte size of single element
    Dim As UInteger dimensions   '' number of dimensions
    Dim As UInteger flags        '' FBARRAY_FLAGS_*

    '' take care with number of dimensions; fbc may allocate
    '' a smaller descriptor with fewer than FB_MAXDIMENSIONS
    '' in dimTb() if it is known at compile time that they
    '' are never needed.  Always respect number of
    '' dimensions when accessing dimTb()

    Dim As FBARRAYDIM dimTb(0 To FB_MAXDIMENSIONS-1)
End Type

Extern "rtlib"
    Declare Function ArrayDescriptorPtr Alias "fb_ArrayGetDesc" _
        ( array() As Any ) As FBC.FBARRAY Ptr
    Declare Function ArrayConstDescriptorPtr Alias "fb_ArrayGetDesc" _
        ( array() As Const Any ) As Const FBC.FBARRAY Ptr
End Extern

# If __FB_LANG__ = "fb"
End Namespace
# endif

Usage

#include once "fbc-int/array.bi"
using FBC

' then:
Dim pd As FBARRAY Ptr
...
pd = ArrayDescriptorPtr ( array() )

' or safer:
Dim pd As Const FBARRAY Ptr
...
pd = ArrayConstDescriptorPtr ( array() )

Parameters

pd
The name of a pointer to the array descriptor
array
The name of the array for which one want to access its descriptor

Description

At compile time, fbc allocates an array descriptor to store and track information about the array.

If the number of dimensions is unknown at compile time, then the full FB_MAXDIMENSIONS is allocated in the dimTb() field. Otherwise, if the number dimensions is known at compile time, then only the number of dimensions needed are allocated.
Therefore the allocated FBARRAY data may be smaller than the declared FBARRAY structure:
allocated FBARRAY data size (in bytes) = Sizeof(FBARRAY) - (FB_MAXDIMENSIONS - (FBARRAY.flags And FBARRAY_FLAGS_DIMENSIONS)) * Sizeof(FBARRAYDIM)

If an array is passed as argument to a procedure, an array descriptor is allocated. However, if the array is static, fixed length, and never passed as an argument, then all information about the array is known at compile time, including memory locations, and the allocation of a descriptor is optimized out, since all expressions involving the array are compile time constant.

The array descriptor may also be allocated at run time, as would be in the case of allocating a new UDT containing a variable-length array field member.

WARNING: It is inadvisable (especially for a non advanced user) to change the data values ​​of the array descriptor (internal structure of the compiler).
For that, it is safer to use rather the function ArrayConstDescriptorPtr() initializing a pointer declared As Const FBARRAY Ptr (or implicitly declared in this way by Var).

FBARRAY.index_ptr

Pointer to the array data @array(0, 0, ...). This pointer may be outside of the actual array data as a kind of virtual pointer to use when calculating offsets using indexes in to the array.

FBARRAY.base_ptr

Pointer to the array's memory at the array's lowest bound. For variable-length arrays allocated at run time, this points to the allocated memory region (i.e. malloc)

FBARRAY.size

Total size in bytes of the array data. Size is equal to total number of elements in the array (all dimensions) multiplied by element length. i.e. size = dimTb(0).elements * element_len + dimTb(1).elements * element_len + ...

FBARRAY.element_len

Size in bytes of an individual element. Must be set to non-zero value.

FBARRAY.dimensions

Number of valid dimensions in the dimTb() table. A value of zero (0) indicates that dimTb() has FB_MAXDIMENSIONS avaiable, but the array does not yet have number of dimensions defined. On first REDIM, the number of dimensions will be set.

FBARRAY.flags

The flags field contains information about the array descriptor that needs to be known at run time.

FBARRAY_FLAGS_DIMENSIONS : a 4 bit field to indicate the number of elements allocated in dimTb(). If fbc can determine at compile time that less than FB_MAXDIMENSIONS are needed to represent the array, then only the number of dimensions needed are allocated in dimTb().
The real size allocated for the array descriptor can be calculated by:
Sizeof(FBC.FBARRAY) - (FBC.FB_MAXDIMENSIONS - (FBC.ArrayDescriptorPtr(array())->flags And FBC.FBARRAY_FLAGS_DIMENSIONS)) * Sizeof(FBC.FBARRAYDIM)

FBARRAY_FLAGS_FIXED_DIM : if this bit is set, indicates that the number of dimensions are set and are given in dimTb() and must not be changed.

FBARRAY_FLAGS_FIXED_LEN : if this bit is set, indicates that the array data is fixed length and must not be resized or reallocated

FBARRAY_FLAGS_RESERVED : all other bits are reserved for future use

FBARRAY.dimTb()

dimTb() is an array of FBARRAYDIM to indicate the bounds of each dimension.

If the number of dimensions is unknown at compile time, then the full FB_MAXDIMENSIONS is allocated in the dimTb() field. Otherwise, if the number dimensions is known at compile time, then only the number of dimensions needed are allocated. Therefore the allocated FBARRAY data may be smaller than the declared FBARRAY structure.

FBARRAYDIM.elements

Number of elements in the dimension. i.e. (ubound-lbound+1)

FBARRAYDIM.lbound

Lower bound is the lowest valid index in this dimension.

FBARRAYDIM.ubound

Upper bound is the highest valid index in this dimension.

ArrayDescriptorPtr( array() as any ) as FBC.FBARRAY ptr

Retrieves a pointer to the array descriptor, returning a pointer to FBC.ARRAY that can be modified (the array descriptor thus referenced is valid until the end of the scope where it is created).

ArrayConstDescriptorPtr( array() as const any ) as const FBC.FBARRAY ptr

Retrieves a pointer to the array descriptor, returning a pointer to FBC.ARRAY that is read only (the array descriptor thus referenced is valid until the end of the scope where it is created).

Example

Very simple syntaxic example highlighting the access capabilities to the data fields of an array descriptor:
#include "fbc-int/array.bi"

Sub printArrayDescriptor (ByVal p As Any Ptr, ByVal tabulation As Integer = 0, ByRef title As String = "")
    Dim As FBC.FBARRAY Ptr pd = p
    Dim As Integer t = 0
    If title <> "" Then
        Print title
        t = 1
    End If
    Print Space((t    ) * tabulation) & "[@array descriptor: @&h"; Hex(pd, 2 * SizeOf(Any Ptr)) & " / "; _
                                                                   SizeOf(FBC.FBARRAY) - (8 - pd->dimensions) * 3 * SizeOf(Integer) & " bytes]"'
    Print Space((t + 1) * tabulation) & "@array(all_null_indexes)      =&h"; Hex(pd->index_ptr, 2 * SizeOf(Any Ptr))
    Print Space((t + 1) * tabulation) & "@array(all_min_indexes)       =&h"; Hex(pd->base_ptr, 2 * SizeOf(Any Ptr))
    Print Space((t + 1) * tabulation) & "array_total_size_in_bytes     ="; pd->size
    Print Space((t + 1) * tabulation) & "array_element_size_in_bytes   ="; pd->element_len
    Print Space((t + 1) * tabulation) & "number_of_array_dimensions    ="; pd->dimensions
    Print Space((t + 1) * tabulation) & "fixed_len/fixed_dim/dimensions="; (pd->flags And FBC.FBARRAY_FLAGS_FIXED_LEN) Shr 5 & "/"; _
                                                                           (pd->flags And FBC.FBARRAY_FLAGS_FIXED_DIM) Shr 4 & "/"; _
                                                                           (pd->flags And FBC.FBARRAY_FLAGS_DIMENSIONS)
    For i As Integer = 0 To pd->dimensions - 1
        Print Space((t + 1) * tabulation) & "[dimension number:"; i + 1; "]"
        Print Space((t + 2) * tabulation) & "number_of_elements="; pd->dimTb(i).elements
        Print Space((t + 2) * tabulation) & "min_index         ="; pd->dimTb(i).LBound
        Print Space((t + 2) * tabulation) & "max_index         ="; pd->dimTb(i).UBound
    Next i
End Sub

Screen 0
Width , 35

Dim As LongInt test1(0 To 9, 1 To 100)
printArrayDescriptor(FBC.ArrayDescriptorPtr(test1()), 4, "'Dim As Longint test1(0 to 9, 1 to 100)':")
Sleep
Cls
Dim As LongInt test2()
printArrayDescriptor(FBC.ArrayDescriptorPtr(test2()), 4, "'Dim As Longint test2()':")
Print
ReDim test2(0 To 9, 1 To 100)
printArrayDescriptor(FBC.ArrayDescriptorPtr(test2()), 4, "'Redim test2(0 to 9, 1 to 100)':")
Sleep
Cls
Dim As LongInt test3(Any, Any)
printArrayDescriptor(FBC.ArrayDescriptorPtr(test3()), 4, "'Dim As Longint test3(Any, Any)':")
Print
ReDim test3(0 To 9, 1 To 100)
printArrayDescriptor(FBC.ArrayDescriptorPtr(test3()), 4, "'Redim test3(0 to 9, 1 to 100)':")

Sleep

Output example (32-bit):
'Dim As Longint test1(0 to 9, 1 to 100)':
    [@array descriptor: @&h0019DE70 / 48 bytes]
        @array(all_null_indexes)      =&h0019DE98
        @array(all_min_indexes)       =&h0019DEA0
        array_total_size_in_bytes     =8000
        array_element_size_in_bytes   =8
        number_of_array_dimensions    =2
        fixed_len/fixed_dim/dimensions=1/1/2
        [dimension number: 1]
            number_of_elements=10
            min_index         = 0
            max_index         = 9
        [dimension number: 2]
            number_of_elements=100
            min_index         = 1
            max_index         = 100

'Dim As Longint test2()':
    [@array descriptor: @&h0019DDF8 / 24 bytes]
        @array(all_null_indexes)      =&h00000000
        @array(all_min_indexes)       =&h00000000
        array_total_size_in_bytes     =0
        array_element_size_in_bytes   =8
        number_of_array_dimensions    =0
        fixed_len/fixed_dim/dimensions=0/0/8

'Redim test2(0 to 9, 1 to 100)':
    [@array descriptor: @&h0019DDF8 / 48 bytes]
        @array(all_null_indexes)      =&h01FD2AB8
        @array(all_min_indexes)       =&h01FD2AC0
        array_total_size_in_bytes     =8000
        array_element_size_in_bytes   =8
        number_of_array_dimensions    =2
        fixed_len/fixed_dim/dimensions=0/0/8
        [dimension number: 1]
            number_of_elements=10
            min_index         = 0
            max_index         = 9
        [dimension number: 2]
            number_of_elements=100
            min_index         = 1
            max_index         = 100

'Dim As Longint test3(Any, Any)':
    [@array descriptor: @&h0019DDC8 / 48 bytes]
        @array(all_null_indexes)      =&h00000000
        @array(all_min_indexes)       =&h00000000
        array_total_size_in_bytes     =0
        array_element_size_in_bytes   =8
        number_of_array_dimensions    =2
        fixed_len/fixed_dim/dimensions=0/1/2
        [dimension number: 1]
            number_of_elements=0
            min_index         = 0
            max_index         = 0
        [dimension number: 2]
            number_of_elements=0
            min_index         = 0
            max_index         = 0

'Redim test3(0 to 9, 1 to 100)':
    [@array descriptor: @&h0019DDC8 / 48 bytes]
        @array(all_null_indexes)      =&h01FD4C20
        @array(all_min_indexes)       =&h01FD4C28
        array_total_size_in_bytes     =8000
        array_element_size_in_bytes   =8
        number_of_array_dimensions    =2
        fixed_len/fixed_dim/dimensions=0/1/2
        [dimension number: 1]
            number_of_elements=10
            min_index         = 0
            max_index         = 9
        [dimension number: 2]
            number_of_elements=100
            min_index         = 1
            max_index         = 100
Version

  • Since fbc 1.08.0

Dialect Differences

  • Only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC.

See also

  • Arrays
  • Arraylen
  • Arraysize


Fb_No_Gfxlib
User-defined compiler control symbol

Syntax

#define FB_NO_GFXLIB

Description

Defining FB_NO_GFXLIB tells the compiler not to treat selected graphics-library command words as built-in gfxlib command keywords.

This is useful in larger programs or libraries that do not use the legacy graphics command surface and need names such as Screen, Paint, or Window to be available as normal identifiers.

The affected gfxlib command words are PSET, PRESET, POINT, CIRCLE, WINDOW, PALETTE, SCREEN, SCREENQB, PAINT, DRAW, and IMAGECREATE.

FB_NO_GFXLIB affects parsing only after the symbol has been defined. It does not remove libraries that are explicitly requested through other code, and it does not change the behavior of already-parsed source.

Example

#define FB_NO_GFXLIB

Type Window
    As Integer id
End Type

Dim As Window main_window
main_window.id = 1

Version

  • Since fbc 1.20.1

Differences from QB

  • New to FreeBASIC

See also

  • #define
  • Compiler Option: -d
  • Graphics Keyword List


Fb_No_Sfxlib
User-defined compiler control symbol

Syntax

#define FB_NO_SFXLIB

Description

Defining FB_NO_SFXLIB tells the compiler not to treat selected sound-library command words as built-in sfxlib command keywords.

This is useful in larger programs or libraries that do not use the sound command surface and need names such as Music, Sfx, or Device to be available as normal identifiers.

The affected sfxlib command words are MUSIC, SFX, MIDI, DEVICE, and CAPTURE.

FB_NO_SFXLIB affects parsing only after the symbol has been defined. It does not remove libraries that are explicitly requested through other code, and it does not change the behavior of already-parsed source.

Example

#define FB_NO_SFXLIB

Type Music
    As Integer id
End Type

Dim As Music song
song.id = 1

Version

  • Since fbc 1.20.1

Differences from QB

  • New to FreeBASIC

See also

  • #define
  • Compiler Option: -d
  • Sound Library Reference


Field
Specifies field alignment.

Syntax

Type|Union typename Field = { 1 | 2 | 4 }
...
End Type|Union

Description

Field can be used to pack Types or Unions more tightly than the default layout. The most commonly used value is Field = 1, which causes the Type or Union to be packed as tightly as possible, without any padding bytes being added between the fields or at the end of the Type. Field can only be used to decrease field alignment, but it cannot be used to increase it. In order to add padding bytes, a Union with appropriate members could be used instead.

Example

Type bitmap_header Field = 1
    bfType          As UShort
    bfsize          As ULong
    bfReserved1     As UShort
    bfReserved2     As UShort
    bfOffBits       As ULong
    biSize          As ULong
    biWidth         As ULong
    biHeight        As ULong
    biPlanes        As UShort
    biBitCount      As UShort
    biCompression   As ULong
    biSizeImage     As ULong
    biXPelsPerMeter As ULong
    biYPelsPerMeter As ULong
    biClrUsed       As ULong
    biClrImportant  As ULong
End Type

Dim bmp_header As bitmap_header

'Open up bmp.bmp and get its header data:
'Note: Will not work without a bmp.bmp to load . . .
Open "bmp.bmp" For Binary As #1

    Get #1, , bmp_header

Close #1

Print bmp_header.biWidth, bmp_header.biHeight

Sleep

Dialect Differences

  • In the -lang qb dialect, the compiler assumes Field = 1 by default, if no other Field was specified, causing all structures to be tightly packed, without added padding, as in QB.

Differences from QB

  • In QB Field was used to define fields in a file buffer at run time. This feature is not implemented in FB, so the keyword has been redefined. To define fields in a file buffer, Types must be used.

See also

  • Type
  • Union
  • Structure packing/field alignment


FileAttr
Returns information about an open file number

Syntax

Declare Function FileAttr ( ByVal filenum As Long, ByVal returntype As Long = 1 ) As Integer

Usage

#include "file.bi"
result = FileAttr( filenum, [ returntype ] )

or

#include "vbcompat.bi"
result = FileAttr( filenum, [ returntype ] )

Parameters

filenum
The file number of a file or device opened with Open
returntype
An integer value indicating the type of information to return.

Return Value

A value associated with the return type, otherwise 0 on error.

Description

Information about the file number is returned based on the supplied returntype
Value | Description | constant
1 | File Mode | fbFileAttrMode
2 | File Handle | fbFileAttrHandle
3 | Encoding | fbFileAttrEncoding

For File Mode, returntype = 1 (fbFileAttrMode) the return value is the sum of one or more of the following values:
Value | File Mode | Constant
1 | Input | fbFileModeInput
2 | Output | fbFileModeOutput
4 | Random | fbFileModeRandom
8 | Append | fbFileModeAppend
32 | Binary | fbFileModeBinary

For File Handle, returntype = 2 (fbFileAttrHandle), the return value is the file handle as supplied by the C Runtime for file-type devices.

On Windows only: For File Handle, returntype = 2 (fbFileAttrHandle), the value returned for COM devices is the handle returned by CreateFile() when the device was first opened. The value returned for LPT devices is the handle returned by OpenPrinter() when the device was first opened. This handle value can be passed to other Windows API functions.

On Linux only: For File Handle, returntype = 2 (fbFileAttrHandle), the value returned for COM devices is the file descriptor returned by open() when the device was first opened.

For Encoding, returntype = 3 (fbFileAttrEncoding), the return value is one of the following values:
Value | Encoding | Constant
0 | Ascii | fbFileEncodASCII
1 | UTF-8 | fbFileEncodUTF8
2 | UTF-16 | fbFileEncodUTF16
3 | UTF-32 | fbFileEncodUTF32

Example

#include "vbcompat.bi"
#include "crt.bi"

Dim f As FILE Ptr, i As Integer

'' Open a file and write some text to it

Open "test.txt" For Output As #1
f = Cast( FILE Ptr, FileAttr( 1, fbFileAttrHandle ))
For i = 1 To 10
  fprintf( f, !"Line %i\n", i )
Next i
Close #1

'' re-open the file and read the text back

Open "test.txt" For Input As #1
f = Cast( FILE Ptr, FileAttr( 1, fbFileAttrHandle ))
While feof(f) = 0
  i = fgetc(f)
  Print Chr(i);
Wend
Close #1

Differences from QB

  • None for returntype = 1
  • QBasic and 16-bit Visual Basic returned DOS file handle for returntype = 2
  • returntype = 3 is new to FreeBASIC

See also

  • Open


FileCopy
Copies a file

Syntax

Declare Function FileCopy ( ByVal source As ZString Ptr, ByVal destination As ZString Ptr ) As Long

Usage

#include "file.bi"
FileCopy source, destination

or

#include "file.bi"
result = FileCopy( source, destination )

Parameters

source
A String argument specifying the filename of the file to copy from. This file must exist.
destination
A String argument specifying the filename of the file to copy to. This file will be overwritten if it exists. This file should not be currently referenced by any open file handles.

Return Value

Returns 0 on success, or 1 if an error occurred.

Description

Copies the contents of the source file into the destination file, overwriting the destination file if it already exists.
It is necessary to #include either "file.bi" or "vbcompat.bi" in order to gain access to this function.

The error code returned by FileCopy can be checked using Err in the next line. The function version of FileCopy returns directly the error code as a 32 bit Long.

Example

#include "file.bi"
FileCopy "source.txt", "destination.txt"

Platform Differences

  • Linux requires the filename case matches the real name of the file. Windows and DOS are case insensitive.
  • Path separators in Linux are forward slashes /. Windows uses backward slashes \ but it allows forward slashes. DOS uses backward slashes \.

Differences from QB

  • New to FreeBASIC. Existed in Visual Basic.

See also


FileDateTime
Returns the last modified date and time of a file as Date Serial

Syntax

Declare Function FileDateTime ( ByVal filename As ZString Ptr ) As Double

Usage

#include "file.bi"
result = FileDateTime( filename )

or

#include "vbcompat.bi"
result = FileDateTime( filename )

Parameters

filename
Filename to retrieve date and time for.

Return Value

Returns a Date Serial.

Description

Returns the file's last modified date and time as Date Serial.

Example

#include "vbcompat.bi"

Dim filename As String, d As Double

Print "Enter a filename: "
Line Input filename

If FileExists( filename ) Then

  Print "File last modified: ";

  d = FileDateTime( filename )

  Print Format( d, "yyyy-mm-dd hh:mm AM/PM" )

Else

  Print "File not found"

End If

Platform Differences

  • Linux requires the filename case matches the real name of the file. Windows and DOS are case insensitive.
  • Path separators in Linux are forward slashes / . Windows uses backward slashes \ but it allows forward slashes. DOS uses backward slashes \.

Differences from QB

  • New to FreeBASIC

See also

  • Date Serials


FileExists
Tests the existence of a file

Syntax

Declare Function FileExists ( ByVal filename As ZString Ptr ) As Long

Usage

#include "file.bi"
result = FileExists( filename )

or

#include "vbcompat.bi"
result = FileExists( filename )

Parameters

filename
Filename to test for existence.

Return Value

Returns non-zero (-1) if the file exists, otherwise returns zero (0).

Description

FileExists tests for the existence of a file.
Internally, it may issue an Open() and a Close() function, which may have consequences - eg, any existing Lock(s) on the file may be released.
Depending on the exact requirements, alternative methods of checking for file existence may be to use the Dir() function (being careful of attributes and ensuring the path doesn't contain wildcards), or to try Opening the file and checking the return value for success.

Example

#include "vbcompat.bi"

Dim filename As String

Print "Enter a filename: "
Line Input filename

If FileExists( filename ) Then
  Print "File found: " & filename
Else
  Print "File not found: " & filename
End If

Platform Differences

  • Linux requires the filename case matches the real name of the file. Windows and DOS are case insensitive.
  • Path separators in Linux are forward slashes /. Windows uses backward slashes \ but it allows for forward slashes. DOS uses backward \ slashes.

Differences from QB

  • New to FreeBASIC

See also

  • Dir


FileFlush
Flush application stream buffers to system, or system buffers to file

Syntax

Declare Function FileFlush ( ByVal filenum As Long = -1, ByVal systembuffers As Long = 0 ) As Long

Usage

#include "file.bi"
result = FileFlush()
result = FileFlush( filenum )
result = FileFlush( filenum, systembuffers )

Parameters

filenum
File number of bound file or device. If not given, or -1, then flush all open files.
systembuffers
If non-zero, flush system buffers to physical device. Default is zero (0).

Return Value

Returns zero (0) for success or an error code if file buffers could not be flushed.

Description

FileFlush writes application buffered output to the underlying stream, and if systembuffers is non-zero, to the underlying physical device as well.

In the form FileFlush(filenum), the application output buffer for a specific file are written. And in the form FileFlush, all application buffers are written to the system. Typically, when a file is opened, the stream will be block buffered for binary and random files, and line buffered for append and output files. The FileFlush function overrides the normal buffering of the application and immediately writes buffered output to the system. The system may have it's own buffers as well.

In the form FileFlush(filenum,1), both the application buffer is flushed and the underlying system buffer is flushed to physical device. In the form FileFlush(,1), all application buffers are flushed and all underlying system buffers are flushed to physical device.

Example

#include "file.bi"

Dim As Long f1, f2
Dim As String s

Print "File length", "File string"

f1 = FreeFile
Open "fileflushtest.txt" For Output As #f1
Print #f1, "successful file flush"

f2 = FreeFile
Open "fileflushtest.txt" For Input As #f2
Line Input #f2, s
Print FileLen("fileflushtest.txt"), "'" & s & "'"  '' the string is not yet physically written to the file

FileFlush(f1)
Line Input #f2, s
Print FileLen("fileflushtest.txt"), "'" & s & "'"  '' the string is now physically written to the file

Close #f2
Close #f1

Sleep

Output (Windows):
File length   File string
 0            ''
 23           'successful file flush'
Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC.

See also

  • Close


FileLen
Finds the length of a file given its filename

Syntax

Declare Function FileLen ( filename As String ) As LongInt

Usage

#include "file.bi"
result = FileLen(filename)

or

#include "vbcompat.bi"
result = FileLen(filename)

Parameters

filename
A String argument specifying the filename of the file whose length to return.

Description

Returns the size in bytes of the file specified by filename.

Example

#include "file.bi"
Dim length As LongInt
length = FileLen("file.txt")

Platform Differences

  • Linux requires the filename case matches the real name of the file. Windows and DOS are case insensitive.
  • Path separators in Linux are forward slashes / . Windows uses backward slashes \ but it allows for forward slashes . DOS uses backward \ slashes.

Differences from QB

  • New to FreeBASIC. Existed in Visual Basic.

See also

  • LOF


FileSetEof
Sets the length of a open file bound to a file number

Syntax

Declare Function FileSetEof ( ByVal filenum As Long ) As Long

Usage

#include "file.bi"
result = FileSetEof(fnum)

Parameters

filenum
File number of bound file or device.

Return Value

Returns zero (0) for success or an error code if the end of file (file size) could not be set.

Description

FileSetEof Sets the end of file based on the current file position. File position as in Seek is one based.

When the current file position is before the end of the file, the file is truncated. File contents before the the current file position are kept, and file contents on or after the current file position are deleted. When the current position is beyond the end of file, the file is extended with zero value bytes. After FileSetEof completes, the current file position is at the end of the file.

To set a file having a length of N-bytes where the file is opened for binary, output, or append, it is necessary to Seek to position N-bytes + 1. To set a file having a length of N-records where the file is opened for random, it is necessary to Seek to position N-records + 1.

Example

#include "file.bi"

'' create a zero length file
Open "file.dat" For Binary As #1
FileSetEof 1
Close #1

'' open same file and extend to 10000 bytes size
Open "file.dat" For Binary As #1
Seek #1, (10000 + 1)
FileSetEof 1
Close #1

'' open same file and truncate to 5000 bytes size
Open "file.dat" For Binary As #1
Seek #1, (5000 + 1)
FileSetEof 1
Close #1

'' clean-up
Kill "file.dat"

Version

  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC.

See also

  • EOF
  • LOF
  • Seek


Fix
Returns the integer part of a number, rounding towards zero

Syntax

Declare Function Fix ( ByVal number As Single ) As Single
Declare Function Fix ( ByVal number As Double ) As Double
Declare Function Fix ( ByVal number As Integer ) As Integer
Declare Function Fix ( ByVal number As UInteger ) As UInteger

Usage

result = Fix( number )

Parameters

number
the floating-point number to truncate

Return Value

Returns the integer part of number, rounding towards zero.

Description

Equivalent to: Sgn(number) * Int(Abs(number)). For example, Fix(1.3) will return 1.0, and Fix(-4.9) will return -4.0. For integer types, the number is returned unchanged.

Note: this function is also equivalent to number - Frac(number).

The Fix unary Operator can be overloaded with user defined types.

Example

Print Fix(1.9)  '' will print  1
Print Fix(-1.9) '' will print -1

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Int
  • Frac
  • CInt
  • Operator


Flip
Changes the current video display page

Syntax

Declare Function Flip ( ByVal frompage As Long = -1, ByVal topage As Long = -1 ) As Long

Usage

Flip [ frompage ] [, topage ]

Parameters

frompage
previous page
topage
new page to display

Return Value

Returns zero (0) if successful, or a non-zero error code to indicate a failure.

Description

In normal graphics mode, Flip is an alias for PCopy and ScreenCopy. See ScreenCopy for details.

In OpenGL mode, Flip does a hardware page flip and displays the contents of the backbuffer. It is recommended that you call Flip regularly while in OpenGL mode, otherwise your app may also become unresponsive.

The error code returned by Flip can be checked using Err in the next line. The function version of Flip returns directly the error code as a 32 bit Long.

Example

ScreenRes 320, 240, 32, 2    'Sets up the screen to be 320x240 in 32-bit color with 2 video pages.
ScreenSet 1,0                'Sets the working page to 1 and the displayed page to 0

For n As Integer = 50 To 270

    Cls
    Circle (n, 50),50 ,RGB(255,255,0) 'Draws a circle with a 50 pixel radius in yellow on page 1
    Flip 1,0    'Copies our circle from page 1 to page 0

    Sleep 25
Next

Print "Now wasn't that neat!"
Print "Push any key."
Flip 1,0    'Copies our text from page 1 to page 0
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Flip.

Differences from QB

  • New to FreeBASIC


For
Control flow statement, open statement clause, or operator depending on context.

Syntax

For iterator = startvalue To endvalue [ Step increment ]
or
Open [ device ] "filename" For filemode As #handle
or
declare operator For ( byref stp as datatype )

Example

See example at For...Next.

See also

  • For...Next
  • Open
  • Operator


For...Next
Control flow statement for looping

Syntax

For iterator [ As datatype ] = startvalue To endvalue [ Step stepvalue ]
[ statement block ]
Next [ iterator ]

Parameters

iterator
a variable identifier that is used to iterate from an initial value to an end value
datatype
If specified, the variable iterator will automatically be declared with the type datatype
startvalue
an expression that denotes the starting value of the iterator
endvalue
an expression used to compare with the value of the iterator
stepvalue
an expression that is added to the iterator after every iteration

Description

A For...Next loop initializes iterator to startvalue, then executes the statement block, incrementing iterator by stepvalue until it exceeds endvalue. If stepvalue is not explicitly given it will set to 1.

The values of stepvalue and endvalue are stored internally immediately following execution of the For statement and thus neither can be changed inside the For loop. Comparison operators such as < and > will not be effective as stepvalue or endvalue because the expressions will not be re-evaluated while the loop is running. (The results of the expressions used to define them may be changed, but these changes will not affect the execution of the For loop.) See examples.

Note: In some dialects, the temporary variables holding stepvalue and endvalue go out of scope at the end of the loop, and their values are not guaranteed to remain unchanged once any code following the loop has been executed. For this reason, it is recommended never to branch out of a For...Next loop (using Goto or similar), and then jump back into the middle of it later when in the -lang fb or -lang deprecated dialect.

The iterator must be an intrinsic scalar: only Static/Shared variables and local variables can be used; no other kind can be used, including array elements, UDT members, ByRef parameters or any kind of dereferenced address.

The iterator may be defined having the same scope as the For statement by using the As datatype syntax. With this syntax, iterator is created and destroyed within the For...Next scope. See dialect differences below.

If endvalue is less than startvalue then a negative stepvalue must be specified or the statement block will not execute at all, since startvalue compares greater than endvalue.

The For statement causes the execution of the statements in the statement block until iterator compares greater than endvalue (or less than endvalue if stepvalue < 0). iterator will be incremented the amount of stepvalue following each execution of the statement block. If an increment is not given, iterator will be implicitly incremented by 1.

If an Exit For statement is encountered inside the statement block, the loop is terminated, and execution resumes immediately following the enclosing Next statement. If a Continue For statement is encountered, the rest of the statement block is skipped until the block's corresponding Next statement. The counter's value is incremented and the loop restarted if it is still within the bounds given by endvalue.

Note: for integer data types, it is not possible to loop up to the highest possible value (or down to the lowest possible value) that can be stored in the variable type, because the loop only breaks when the incremented variable exceeds endvalue, which can never happen. For example, if you try to iterate an variable from 0 to 255, the loop will only break once the variable reaches 256 or more. Using a UByte variable for the counter wouldn't work, because although it can hold the numbers 0 to 255, it cannot hold 256. See Standard Data Type Limits to find the upper and lower limits for the standard data types.

Like all control flow statements, the For statement can be nested, that is, it can be used in a statement block of another For statement.

For, Next, and Step are operators that can be overloaded inside user defined types. See Operator For, Operator Next, Operator Step

Example

Print "counting from 3 to 0, with a step of -0.5"
For i As Single = 3 To 0 Step -0.5
    Print "i is " & i
Next i

Dim As Integer i, j, k, toTemp, stepTemp
j = 9: k = 1

For i = 0 To j Step k

    j = 0: k = 0 '' Changing j and k has no effect on the current loop.
    Print i;

Next i
Print

' Internally, this is what the above example does:
j = 9: k = 1

i = 0: toTemp = j: stepTemp = k
Do While IIf(stepTemp >= 0, i <= toTemp, i >= toTemp)

    j = 0: k = 0 '' Changing j and k has no effect on the current loop.
    Print i;

    i += stepTemp
Loop
Print

Example of infinite For...Next loop in case of loop up to the highest value of an Ubyte iterator (255):
For ub As UByte = 240 To 255 '' Infinite loop because the end criterion value (255+1=256) can never be reached by the UByte iterator
    Print ub
    If Inkey <> "" Then Exit For
    Sleep 10
Next ub

The same applies to lowest value of an UByte iterator (0) in case of loop down (negative stepvalue).

Dialect Differences

  • In the -lang fb and -lang deprecated dialects, variables declared inside a For..Next block are visible only inside the block, and cannot be accessed outside it. To access duplicated symbols defined as global outside this block, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).
  • In the -lang qb and -lang fblite dialects, variables declared inside a For..Next block (including the counter if declared, and any temporary variables used to hold endvalue or stepvalue), have a procedure-wide scope as in QB

Differences from QB

  • ByRef arguments cannot be used as counters.

See also

  • Continue
  • Do...Loop
  • Exit


Format
Formats a number in a specified format

Syntax

Declare Function Format ( ByVal numerical_expression As Double, ByRef formatting_expression As Const String = "" ) As String

Usage

#include "string.bi"
result = Format[$]( numerical_expression, formatting_expression )

Parameters

numerical_expression
number to format
formatting_expression
formatting pattern

Return Value

Format returns a string with the result of the numerical expression formatted as indicated in the formatting expression.
The formatting expression is a string that can yield numeric or date-time values.

Description

To recover meaningful date-time values the numerical expression must be a date serial obtained from the appropriate functions.
This function is part of FreeBASIC, however it is not recognized by the compiler unless vbcompat.bi is included.

"Numeric Formats"
Symbol | Description
Null string | General format (no formatting)
0 | Digit placeholder: If the number has fewer digits than there are zeros (on either side of the decimal) in the format expression, leading or trailing zeros are displayed. If there are more digits to the right of the decimal than zeros in the format, the number is rounded. If there are more digits to the left of the decimal than zeros in the format the digits are all displayed
# | Digit placeholder: Follows the same rules as for the 0 digit except the leading or trailing zeros are not displayed
. | Placeholder for decimal point.If the format contains only #'s to the left of . then numbers smaller than 1 are begun with a decimal point.
% | Percentage :The expression is multiplied by 100 and the % character is inserted.
, | Thousands separator. Two adjacent commas, or a comma immediately to the left of the decimal point location (whether there is a decimal specified or not) means 'Omit the three digits that fall between these commas, or between the comma and the decimal point, rounding as needed.'
E- E+ e- e+ | Scientific format: If a format contains one digit placeholder (0 or #) to the right of an E-, E+, e-, or e+, the number is displayed in scientific format and an E or e is inserted between the number and its exponent.The number of 0's or #'s to the right determines the number of digits in the exponent. Use E- or e- to place a minus sign next to negative exponents. Use a E+ or e+ to place a minus sign next to negative exponents and a plus sign next to positive exponents.
: ? + $ () space | Display literal character To display a character other than one of these, precede the character with a backslash (\) or enclose the character(s) in double quotation marks
\ | Display next character in format string as it is
text between double quotes | Displays the text inside the double quotes.
: | Time separator is used to separate hours, minutes, and seconds when time values are formatted.
/ | The date separator is used to separate day,month, and year when date values are formatted.

"Date-Time formats:"
Symbol | Description
d, dd | Display the day as a one-digit/two-digit number (1-31/01-31)
ddd | Display the day as an abbreviation (Sun-Sat)
dddd | Display the day as a full name (Sunday-Saturday)
ddddd | Display a serial date number as a complete date (including day, month and year)
m, mm | Display the month as a one-digit/two-digit number (1-12/01-12). If immediately following h or hh, the minute rather than the month is displayed
M, MM | Display the month as a one-digit/two-digit number (1-12/01-12), even if immediately following h or hh
mmm | Display the month as an abbreviation (Jan-Dec)
mmmm | Display the month as a full name (January-December)
y, yy | Display the year as a two-digit number (00-99)
yyyy | Display the year as a four-digit number (1900-2040)
h, hh | Display the hour as a one-digit/two-digit number (0-23/00-23)
m, mm | Display the minute as a one-digit/two-digit number (0-59/00-59). If not immediately following h or hh, the month rather than the minute is displayed
n, nn | Display the minute as a one-digit/two-digit number (0-59/00-59), even if not immediately following h or hh
s, ss | Display the second as a one-digit/two-digit number (0-59/00-59)
ttttt | Display a time serial number as a complete time, including hour, minute and second
AM/PM (Default), am/pm | Use the 12-hour clock displaying AM or am with any hour before noon, PM or pm with any hour between noon and 11:59
A/P, a/p | Use the 12-hour clock displaying A or a with any hour before noon, P or p with any hour between noon and 11:59

The representation of time and date separators may be determined from the operating system if regional or localized settings are supported by the target platform. This determination is made at run-time so that output may be localized to the system the program is running on.

Example

Numeric formats
#include "string.bi"

Dim As Double numberVal(...) = {5, -5, .5}
Dim As String formatStr(...) = {"","0","0.00","#,##0","#,##0.00","0%", "0.00%", "0.00E+00", "0.00E-00"}

? "Format string",, Str(numberVal(0)), Str(numberVal(1)), Str(numberVal(2))
?
For iFormat As Integer = 0 To UBound(formatStr)
    ? formatStr(iFormat),,
    For iNumber As Integer = 0 To UBound(numberVal)
        ? Format(numberVal(iNumber), formatStr(iFormat)),
    Next
    ?
Next

Output:
Format string               5             -5            0.5

'                           5             -5            .5
0                           5             -5            0
0.00                        5.00          -5.00         0.50
#,##0                       5             -5            0
#,##0.00                    5.00          -5.00         0.50
0%                          500%          -500%         50%
0.00%                       500.00%       -500.00%      50.00%
0.00E+00                    5.00E+00      -5.00E+00     5.00E-01
0.00E-00                    5.00E00       -5.00E00      5.00E-01
Example

Date and time formats
#include "vbcompat.bi"

Dim As Double dateTimeVal = DateValue("12/05/1958") + TimeValue("20:50:35")
Dim As String formatStr(...) = {"yyyy-mm-dd","d-mmmm-yy","d-mmm","mmmm-yyyy","h:mm AM/PM","h:mm:ss AM/PM","h:mm","h:mm:ss","m/d/yy h:mm"}

? "Format",, "Display"
?
For iFormat As Integer = 0 To UBound(formatStr)
    ? formatStr(iFormat),, Format(dateTimeVal, formatStr(iFormat))
Next

Output:
Format                      Display

yyyy-mm-dd                  1958-12-05
d-mmmm-yy                   5-December-58
d-mmm                       5-Dec
mmmm-yyyy                   December-1958
h:mm AM/PM                  8:50 PM
h:mm:ss AM/PM               8:50:35 PM
h:mm                        20:50
h:mm:ss                     20:50:35
m/d/yy h:mm                 12/5/58 20:50

Dialect Differences

None

Differences from QB

  • Does not exist in QB 4.5. This function appeared first in PDS 7.1

See also

  • Print Using
  • ? Using
  • Str


Frac
Returns the decimal part of a number

Syntax

Declare Function Frac ( ByVal number As Double ) As Double
Declare Function Frac ( ByVal number As Integer ) As Integer
Declare Function Frac ( ByVal number As UInteger ) As UInteger

Usage

result = Frac( number )

Parameters

number
the number or expression to get the fraction part of.

Return Value

Returns the fractional part of a number or expression.

Description

Equivalent to: (number - Fix(number)).
For example, Frac(4.25) will return 0.25, and Frac(-1.75) will return -0.75. For integer types, the value 0 is always returned.

The Frac unary Operator can be overloaded with user defined types.

Example

Print Frac(10.625)  '' will print  0.625
Print Frac(-10.625) '' will print -0.625

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Fix
  • Operator


Fre
Returns the amount of free memory available

Syntax

Declare Function Fre ( ByVal value As Long = 0 ) As UInteger

Usage

result = Fre( [ value ] )

Parameters

value
Unused dummy parameter kept for backward compatibility; can be ignored.

Return Value

Returns the amount of free memory, in bytes.

Description

Returns the free memory (ram) available, in bytes.

Example

Dim mem As UInteger = Fre

Print "Free memory:"
Print
Print mem; " bytes"
Print mem  \ 1024; " kilobytes"
Print mem  \ (1024 * 1024); " megabytes"

Differences from QB

  • The "value" argument is not checked, Fre will always return the free physical memory available

See also

  • Dim
  • ReDim
  • Allocate


FreeFile
Returns a free file number

Syntax

Declare Function FreeFile ( ) As Long

Usage

result = FreeFile

Return Value

The next available file number, if any, otherwise zero (0).

Description

Returns the number of the next free file number with valid values 1 to 255, or 0 if there are already 255 files opened. This value is a required argument to Open a file. FreeFile is useful when opening files in complex programs where the programmer can't keep track of the used file numbers.

Make sure to always close files when no longer needed, otherwise you will get a file number leak, and won't be able to open any files anymore after 255 filenumbers are exhausted while your program is running.

FreeFile will always return the smallest free file number. The file number returned by FreeFile will not change until that file number is Opened, or until a smaller file number is Closed:
- For this reason, it is wise to use FreeFile immediately before its corresponding Open, to ensure that the same file number is not returned and opened elsewhere first.
- In case of potential conflict with other threads, this non-breaking 'FreeFile...Open' sequence must additionally be considered as a critical section of code and therefore must be protected, for example by mutual exclusion (using a mutex locking).

Example

' Create a string and fill it.
Dim buffer As String, f As Long
buffer = "Hello World within a file."

' Find the first free file number.
f = FreeFile

' Open the file "file.ext" for binary usage, using the file number "f".
Open "file.ext" For Binary As #f

' Place our string inside the file, using file number "f".
Put #f, , buffer

' Close the file.
Close #f

' End the program. (Check the file "file.ext" upon running to see the output.)
End

When using multiple FreeFile statements, FreeFile should be used immediately before the Open statement:
Dim As Long fr, fs
' The CORRECT way:
fr = FreeFile
Open "File1" For Input As #fr

fs = FreeFile
Open "File2" For Input As #fs

As opposed to:
Dim As Long fr, fs
' The WRONG way:
fr = FreeFile
fs = FreeFile '' fs has taken the same file number as fr

Open "file1" For Input As #fr
Open "file2" For Input As #fs '' error: file number already opened

Platform Differences

  • On Windows, a file number used in a dynamic link library is not the same as an identical file number used in the main program. File numbers can not be passed or returned and then used between a DLL and an executable.
  • Besides FreeBASIC's limit of 255 files per program opened at same time, there is an OS limit of total amount of opened files, but usually you won't touch it except in DOS, where the limit may be as low as 15 files total.

Differences from QB

  • None

See also

  • Open
  • Put (File I/O)
  • Get (File I/O)
  • FileAttr


Function
Defines a procedure returning a value

Syntax

[Public|Private] Function identifier [cdecl|pascal|stdcall] [Overload] [Alias external_identifier] [([parameter_list])] [ ByRef ] As return_type [Static] [Export]
statements
...
{ {Return [return_value]}|{Function = return_value}|{identifier = return_value} }
...
End Function

Parameters

identifier: the name of the function
external_identifier: externally visible (to the linker) name enclosed in quotes
parameter_list: parameter[, parameter[, ...]]
parameter: [ByRef|ByVal] identifier [As type] [= default_value]
identifier: the name of the variable referenced in the function. If the argument is an array then the identifier must be followed by an empty parenthesis.
type: the type of variable
default_value: the value of the argument if none is specified in the call (default_value can be a literal, or a constant, or a shared variable)
return_type: the type of variable returned by the function
statements: one or more statements that make up the function body
return_value: the value returned from the function

Description

A function defines a block of code which can be executed with a single statement (a function call), and provide a value back to the caller when finished (a return value). There are several reasons to use functions:
  • Reduces redundancy in your program
  • Enables reuse of code in many programs
  • Improves readability of the program
  • Improves maintainability of the program
  • Makes it easy to extend your program

Access Rights : The Public and Private keywords specify public or private intra module-level access rights, respectively. If neither is given, the function defaults to public access (Public).

Calling Convention : Calling convention, or the order in which arguments are pushed and popped from the stack during function calls, is specified with the cdecl, pascal and stdcall keywords. If none is given, the function uses the standard convention by default (stdcall).

Passing Arguments : Functions may receive one or more variables, or arguments, when called. These arguments are listed as parameters in the parameter_list. The ByRef and ByVal keywords specify whether the argument will be passed by reference or by value, respectively. The argument's type is given by "As type" following the parameter. If a parameter in the declaration is given a default value, the parameter is optional. Array parameters are specified by following an identifier with an empty parenthesis. Note that array parameters are always ByRef and the ByRef keyword is neither required nor allowed for array parameters. When calling a function with an array argument the parenthesis must be supplied there too; see the examples.

Overloaded Functions : An overloaded function may share the same name (identifier) as another with a different signature. The Overload keyword specifies that a function may be overloaded. A function must be defined - or declared - using the Overload keyword prior to any functions that overload them.

Returning values : return_type specifies the data type returned by a function upon exit. If no data type is specified, then the function will return the default data type, which will be Integer unless set to another data type using DefSng, DefDbl, DefStr, etc. Functions can return values using three methods: the Return keyword followed by a value exits the function immediately, and returns that value to the caller. Functions can also return values by assigning the Function keyword or the function's identifier to the desired return value (but Function keyword or function's identifier does not allow to evaluate the current assigned value). The latter two methods do not cause the function to exit, however. Return keyword mixed with Function= keyword or function's identifier= or Exit Function keyword in a same function is unsupported when returning objects with constructors. Since functions return values, function calls evaluate to expressions. Thus, function calls can be made wherever an expression is expected, like in Assignments or If statements. Functions can also return references by specifying Byref As return_type.
Warning: Whatever the output branch used, the return value must be always defined, otherwise an unexpected behavior may occur.

Local Variable Preservation : The Static keyword specifies that a function's locally declared variables are preserved between function calls. Upon entering a function defined with Static, local variables have the same value as when the function was last called.

Global Variable Access: To access duplicated symbols defined as global outside the function body, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

When calling a function, parentheses surrounding the argument list (if any) are required only for function calls in expressions. If there is no argument to pass, the parentheses become optional, but it is a common convention to place empty parentheses '()' after the function name, to signify a function call.

Warning for 64-bit compiler only: See the Identifier Rules page for the choice of user procedure identifier names (and specially the 'Platform Differences' paragraph).

Example

'' This program demonstrates the declaration of a function
'' and returning a value using Return command

Declare Function ReturnTen () As Integer

Print ReturnTen () '' ReturnTen returns an integer by default.

Function ReturnTen() As Integer
    Return 10
End Function

'' This program demonstrates the declaration of a function
'' and returning a value using assignment to function name

Declare Function ReturnTen () As Integer

Print ReturnTen () '' ReturnTen returns an integer by default.

Function ReturnTen() As Integer
    ReturnTen = 10
End Function

'' This program demonstrates function overloading.

'' The overloaded functions must be FIRST.
Declare Function ReturnTen Overload (a As Single) As Integer
Declare Function ReturnTen Overload (a As String) As Integer
Declare Function ReturnTen (a As Integer) As Integer

Print ReturnTen (10.000!) '' ReturnTen will take a single and return an integer
Print ReturnTen (10)      '' ReturnTen will take an integer and return an integer
Print ReturnTen ("10")    '' ReturnTen will take a string and return an integer

Function ReturnTen Overload (a As Single) As Integer
    Return Int(a)
End Function

Function ReturnTen Overload (a As String) As Integer
    Return Val(a)
End Function

Function ReturnTen (a As Integer) As Integer
    Return a
End Function

'' The following example demonstrates optional parameters.

Function TestFunc(P As String = "Default") As String
    Return P
End Function

Print TestFunc("Testing:")
Print TestFunc

'' This example shows how to declare and call
'' functions taking array arguments.

Function x(b() As Double) As Integer
  x = UBound(b)-LBound(b)+1
End Function

Dim a(1 To 10) As Double
Print x(a())
Dim c(10 To 20) As Double
Print x(c())

Dialect Differences

  • In the -lang fb dialect, ByVal is the default parameter passing convention for all built-in types except String; String and user-defined Types are passed ByRef by default.
  • In the -lang qb and -lang fblite dialects, ByRef is the default parameter passing convention.
  • In the -lang qb dialect, the name of the function must be used in an assignment to specify the return value. Using Function = ..." to specify the return value may not be used.
  • In the -lang qb and -lang fblite dialects, Return may only be used to return a value when Option Gosub is off. In -lang qb, this must be done explicitly using the Option Nogosub statement.

Differences from QB

  • Parameters can be optional in FreeBASIC.
  • In QBASIC, the return type could only specified with a suffix, not with AS TYPE, and only allowed functions to return a built-in type.
  • Return value can now be specified by a Return statement.
  • Function Overloading is supported in FreeBASIC.
  • The return value of functions can be ignored in the calling code.

See also

  • Sub
  • Exit
  • Return (From Procedure)
  • Declare
  • Public
  • Private


Function (Member)
Declares or defines a member procedure returning a value

Syntax

{ Type | Class | Union } typename
Declare [ Static | Const ] Function fieldname [calling convention specifier] [ Alias external_name ] ( [ parameters ] ) [ ByRef ] As datatype [ Static ]
End { Type | Class | Union }

Function typename.fieldname ( [ parameters ] ) [ ByRef ] As datatype [ Export ]
statements
End Function

Parameters

typename
name of the Type, Class, or Union
fieldname
name of the procedure
external_name
name of field as seen when externally linked
parameters
the parameters to be passed to the procedure
calling convention specifier
can be one of: cdecl, stdcall or pascal

Description

Function members are accessed with Operator . (Member Access) or Operator -> (Pointer To Member Access) to call a member procedure that returns a value (a reference can also be returned by specifying Byref As return_type). The procedure may optionally accept parameters either ByVal or ByRef. typename be overloaded without explicit use of the Overload keyword.

typename is the name of the type for which the Function method is declared and defined. Name resolution for typename follows the same rules as procedures when used in a Namespace.

A hidden This parameter having the same type as typename is passed to non-static member procedures. This is used to access the fields of the Type, Class, or Union.
To access duplicated symbols defined as global outside the Type, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

A Static (Member) may be declared using the Static specifier. A Const (Member) may be declared using the Const specifier.

As for a normal Function, the return value of a Function member can be ignored in the calling code.

Example

#include "vbcompat.bi"

Type Date

  value As Double

  Declare Static Function Today() As Date

  Declare Function Year() As Integer
  Declare Function Month() As Integer
  Declare Function Day() As Integer

End Type

Function Date.Today() As Date
  Return Type(Now())
End Function

Function Date.Year() As Integer
  Return ..Year(value)
End Function

Function Date.Month() As Integer
  Return ..Month(value)
End Function

Function Date.Day() As Integer
  Return ..Day(value)
End Function

Dim d As Date = Date.Today

Print "Year  = "; d.Year
Print "Month = "; d.Month
Print "Day   = "; d.Day

Dialect Differences

  • Only available in the -lang fb dialect.

See also

  • Class
  • Function
  • Sub (Member)
  • Type


Function Pointer
Data type that stores a pointer to a Function procedure returning a value

Syntax

Dim variable As Function [cdecl|pascal|stdcall] [( [parameter_list] )] [ ByRef ] [As return_type] [= initializer]

Parameters

parameter_list: parameter[, parameter[, ...]]
parameter: [ByRef|ByVal] identifier [As type] [= default_value]
identifier: the name of the variable referenced in the function
type: the type of variable
default_value: the value of the argument if none is specified in the call
return_value: the value returned from the function
intializer: address of a function to set as the initial value

Description

A Function pointer is a procedure pointer that stores the memory location of compiled code that returns a value. If no intializer is given the default initial value is zero (0).

The memory address for the Function procedure can be assigned to the variable by taking the address of a function with ProcPtr or Operator @ (Address Of).

The procedure must match the same Function declaration as the declared Function pointer.

To call the function assigned, use the variable name as if it were a normal declared Function, always with parentheses around the parameter list even empty (without parentheses, only the pointer value, ie the address of the function, would be accessed).

One of the primary uses for Function pointers is to create callback procedures:
- A callback Function is a Function that is passed through an argument (a Function pointer) to another procedure which is expected to call back (execute) the "argument" at a convenient time.
- If the callback Function is completely executed before the invocation returns to the caller code, then the callback process is said to be "synchronous".
- If the invocation immediately returns to the caller code, and the callback Function and the caller's next code are running in parallel, then the callback process is said to be "asynchronous".

Example

Function ConcatSelf( x As String ) As String
    Return x & x
End Function

Dim x As Function( x As String ) As String = ProcPtr( ConcatSelf )

Print x( "Hello" )

Function x2 (ByVal i As Integer) As Integer
  Return i * 2
End Function

Function x3 (ByVal i As Integer) As Integer
  Return i * 3
End Function

Function operation (ByVal i As Integer, ByVal op As Function (ByVal As Integer) As Integer) As Integer
  Return op(i)
End Function

Print operation(4, @x2)
Print operation(4, @x3)

' Example of basic callback Function mechanism (asynchronous) to implement a key pressed event:
' (the user callback Function address cannot be modified while the event thread is running)
'   - An asynchronous thread tests the keyboard in a loop, and calls a user callback Function each time a key is pressed.
'   - The callback Function address is passed to the thread.
'   - The callback Function prints the character of the key pressed,
'       but if the key pressed is <escape> it orders the thread to finish by using the function return value.
'   - As the user callback address is passed to the thread as argument, it cannot be modified while the thread is running.

'' thread Sub definition
  Sub threadInkey (ByVal p As Any Ptr)
    If p > 0 Then                                                '' test condition callback Function defined
      Dim As Function (ByRef As String) As Integer callback = p  '' convert the any ptr to a callback Function pointer
      Do
        Dim As String s = Inkey
        If s <> "" Then                                          '' test condition key pressed
          If callback(s) Then                                    '' test condition to finish thread
            Exit Do
          End If
        End If
        Sleep 50, 1
      Loop
    End If
  End Sub

'' user callback Function definition
  Function printInkey (ByRef s As String) As Integer
    If Asc(s) = 27 Then                                        '' test condition key pressed = <escape>
      Print
      Return -1                                                '' order thread to finish
    Else
      Print s;
      Return 0                                                 '' order thread to continue
    End If
  End Function

'' user main code
  Dim As Any Ptr p = ThreadCreate(@threadInkey, @printInkey)   '' launch the thread, passing the callback Function address
  ThreadWait(p)                                                '' wait for the thread finish

Differences from QB

  • New to FreeBASIC

See also

  • Function
  • ProcPtr
  • Operator @ (Address Of)


Get (Graphics)
Gets a copy of a portion of the current work page or an image buffer

Syntax

Get [source,] [STEP](x1, y1) - [STEP](x2, y2), dest

Parameters

source
the address of an image buffer. If it's omitted, the current work page is copied.
STEP
indicates that the following co-ordinates are not absolute co-ordinates.
[STEP](x1, y1)
co-ordinates of the upper-left corner of the sub-image to copy. STEP indicates that (x1, y1) offsets are relative to the current graphics cursor position.
[STEP](x2, y2)
co-ordinates of the lower-right corner of the sub-image to copy. STEP indicates that x2 and y2 are relative to x1 and y1, respectively.
dest
the address of a previously allocated buffer to store the image data.

Description

Get copies a rectangular portion of the current work page specified by the co-ordinates (x1, y1) and (x2, y2), which represent the upper-left and lower-right corners of the rectangle, respectively. STEP specifies that the upper-left co-ordinates are relative to the current graphics pen location, and/or that the lower-right co-ordinates are relative to the upper-left co-ordinates. The new image buffer is formatted to match the current screen mode pixel format.

dest can be an address, an array (name not followed by empty parentheses), or a reference to the first element in an array that will receive the new image buffer. This memory must be sufficiently allocated to hold the image buffer (the number of bytes required varies with the -lang dialect used to compile the program). Valid image buffers can be created simply by using preferably the ImageCreate statement.

source can be an address, an array (name not followed by empty parentheses), or a reference to the first element in an array that holds an image buffer to retrieve a portion of. x1, y1, x2, y2, Step and dest have the same meaning in this case.

The co-ordinates of the rectangle are affected by the most recent Window and View (Graphics) statements, and must both be within the current clipping region set by View (Graphics) (or within the default viewport), otherwise an illegal function call runtime error will be triggered, and the function will have no effect.

Runtime errors:
Get throws one of the following runtime errors:

(1) Illegal function call
    • dest is an array, but is not big enough to hold the image buffer.
    • The upper-left or lower-right co-ordinates of the rectangle are outside the current clipping region (set or default). See View (Graphics).

Dialect Differences

There are 2 types of buffers (details see GfxInternalFormats) depending from FB dialect used:

  • In the -lang fb dialect, dest receives a new-style image buffer, which consists of a 32-byte image header followed by pixel data which is row-padded to the next paragraph boundary (16 bytes). Use the following formula to calculate the total size, in bytes, required to store the image buffer, where w and h are the respective width and height of the rectangular portion of the current work page or source image buffer, and bpp is the number of bytes per pixel of the current screen mode:
size = 32 + (((w * bpp + &hF) and not &hF) * h)

  • In the -lang qb and -lang fblite dialects, dest receives a QB-style image buffer, which consists of a 4-byte image header followed by pixel data which is not row-padded. Use the following formula to calculate the total size, in bytes, required to store the image buffer, where w and h are the respective width and height of the rectangular portion of the current work page or source image buffer, and bpp is the number of bytes per pixel of the current screen mode:
size = 4 + (w * h * bpp)

Example

#include Once "fbgfx.bi"

'' Setup a 400x300 32bit screen
ScreenRes 400, 300, 32

'' First draw funny stuff...
Line (10,10)-(140,30), RGB(255,255,0), bf
Draw String (30, 20), "Hello there!", RGB(255,0,0)

'' Now capture a 150x50 block from the top-left of the screen into an image
'' buffer with GET...
Dim As fb.Image Ptr image = ImageCreate(150, 50)
Get (0,0)-(150-1,50-1), image

'' And duplicate it all over the place!
Put (0,50), image
Put (0,100), image
Put (0,150), image
Put (0,200), image
Put (0,250), image
Put (150,0), image
Put (150,50), image
Put (150,100), image
Put (150,150), image
Put (150,200), image
Put (150,250), image

ImageDestroy(image)

'' And a frame around a whole screen..
Line (0,0)-(400-1,300-1), RGB(255,255,0), b

'' Now get the whole screen...
Dim As fb.Image Ptr big = ImageCreate(400, 300)
Get (0,0)-(400-1,300-1), big

'' And display that "screenshot" as if it was scrolling by...
Dim As Integer x = -350
While ((Inkey() = "") And (x < 350))
    ScreenLock
        Cls
        Put (x,0), big
    ScreenUnlock

    Sleep 100, 1

    x += 10
Wend

ImageDestroy(big)

See also

  • Put (Graphics)
  • ImageCreate
  • Get (File I/O)
  • Screen (Graphics)
  • Window
  • View (Graphics)
  • Internal graphics formats


Get (File I/O)
Reads data from a file to a buffer

Syntax

Get #filenum As Long, [position As LongInt], ByRef data As Any [, [amount As UInteger] [, ByRef bytesread As UInteger] ]
Get #filenum As Long, [position As LongInt], data As String [, , ByRef bytesread As UInteger ]
Get #filenum As Long, [position As LongInt], data() As Any [, , ByRef bytesread As UInteger ]

Usage

Get #filenum, position, data [, [amount] [, bytesread ] ]
varres = Get (#filenum, position, data [, [amount] [, bytesread ] ] )

Parameters

filenum
The value passed to Open when the file was opened.
position
The position where the read must start. If the file was opened For Random, the position is in records; otherwise, it is in bytes. If omitted, reading starts at the present file pointer position. The position is 1-based: i.e. first record or byte of a file is at position 1.
If position is omitted or zero (0), file reading will start from the current file position.
data
The buffer where data is written. It can be a numeric variable, a string, an array, a user defined type (including referenced by This), or a dereferenced pointer. The read operation will try to fill completely the variable, unless the EOF is reached. For a user-defined type instance, the data impacted is only the non-static data members.
When getting arrays, data should be followed by an empty pair of brackets: "()". Get will read data for all of the values in the array. amount is not allowed.
When getting Strings, the number of bytes read is the same as the number of bytes in the string data. amount is not allowed.
Note: If you want to read values into a buffer, you should NOT pass a pointer to the buffer; instead you should pass the first variable in the buffer (this can be done by dereferencing the pointer with Operator * (Value Of)). If you pass a pointer directly, then Get will overwrite the pointer variable, not the memory it points to.
amount
Makes Get read amount consecutive variables from file to memory, i.e. it reads (amount * SizeOf(data) ) bytes of data from file into the memory starting at data's memory location. If amount is omitted it defaults to 1, meaning that Get just reads a single variable.
bytesread
An unsigned integer variable to accept the result of the number of bytes read successfully from the file.

Return Value

Get() returns a 32 bit Long: a zero (0) on success; non-zero on error.
Note: if EOF (end of file) is reached while reading, Get will return success. The amount of bytes actually read can be checked by passing a bytesread variable.

Description

Reads binary data from a file to a buffer variable

Get can be used as a function, and will return 0 on success or an error code on failure.

For files opened in Random mode, the size in bytes of the data to read must match the specified record size.

Note:
- If a real [w/z]string variable is passed to Get, the amount parameter should be forbidden as it is when passing a string. Do not use. Otherwise, it is ignored (except for the '0' value).
- If a dereferenced [w/z]string pointer is passed to Get, the amount parameter is not taken into account as it is when passing a dereferenced numeric pointer. Do not use. But instead of respecting the amount parameter, the pointed buffer must begin with at least as many non-zero elements as the number of elements to read.
- For finer granularity, any [w/z]string variable can be safely passed to Get as a numeric buffer by providing the first numeric element (an indexed [w/z]string variable, or a dereferenced [w/z]string pointer then indexed) and the number of numeric elements to be processed.

Note:
- Using Get # is naturally dedicated to Binary/Random Access file mode.
- It is also allowed in Input Access file mode, but this was never well tested and results may vary.

Example

Dim Shared f As Integer

Sub get_long()

    Dim buffer As Long ' Long variable

    ' Read a Long (4 bytes) from the file into buffer, using file number "f".
    Get #f, , buffer

    ' print out result
    Print buffer
    Print

End Sub

Sub get_array()

    Dim an_array(0 To 10-1) As Long ' array of Longs

    ' Read 10 Longs (10 * 4 = 40 bytes) from the file into an_array, using file number "f".
    Get #f, , an_array()

    ' print out result
    For i As Integer = 0 To 10-1
        Print an_array(i)
    Next
    Print

End Sub

Sub get_mem

    Dim pmem As Long Ptr

    ' allocate memory for 5 Longs
    pmem = Allocate(5 * SizeOf(Long))

    ' Read 5 Longs (5 * 4 = 20 bytes) from the file into allocated memory
    Get #f, , *pmem, 5 ' Note pmem must be dereferenced (*pmem, or pmem[0])

    ' print out result using [] Pointer Indexing
    For i As Integer = 0 To 5-1
        Print pmem[i]
    Next
    Print

    ' free pointer memory to prevent memory leak
    Deallocate pmem

End Sub

' Find the first free file file number.
f = FreeFile

' Open the file "file.ext" for binary usage, using the file number "f".
Open "file.ext" For Binary As #f

  get_long()

  get_array()

  get_mem()

' Close the file.
Close #f

' Load a small text file to a string

Function LoadFile(ByRef filename As String) As String

    Dim h As Integer
    Dim txt As String

    h = FreeFile

    If Open( filename For Binary Access Read As #h ) <> 0 Then Return ""

    If LOF(h) > 0 Then

        txt = String(LOF(h), 0)
        If Get( #h, ,txt ) <> 0 Then txt = ""

    End If

    Close #h

    Return txt

End Function

Dim ExampleStr As String
ExampleStr = LoadFile("smallfile.txt")
Print ExampleStr

' 'THIS' can be used as argument for writing/filling all non-static data of an UDT instance to/from a file

Type UDT
    Dim As String * 32 s
    Dim As Double d
    Declare Sub Save(ByRef filename As String)
    Declare Sub Load(ByRef filename As String)
End Type

Sub UDT.Save(ByRef filename As String)
    Dim As Integer f
    f = FreeFile()
    Open filename For Binary As #f
    Put #f, , This  '' writes all non-static data of the UDT instance to the file
    Close #f
End Sub

Sub UDT.Load(ByRef filename As String)
    Dim As Integer f
    f = FreeFile()
    Open filename For Binary As #f
    Get #f, , This  '' fills all non-static data of the UDT instance from the file
    Close #f
End Sub

Dim As UDT u1
u1.s = "PI number"
u1.d = 3.14159
u1.Save("file.ext")

Dim As UDT u2
u2.Load("file.ext")
Print u2.s
Print u2.d

Differences from QB

  • Get in FB can read full arrays as in VB or, alternatively, read a multiple of the data size into the memory.
  • Get can be used as a function in FB, to find the success/error code returned without having to use error handling procedures.
  • FB allows the bytesread parameter, to check how many bytes have been successfully read in.

See also

  • Get (Graphics) different usage of same keyword
  • Put (File I/O)
  • Open
  • Close
  • Binary
  • Random
  • FreeFile
  • File I/O methods comparison


GetJoystick
Reads buttons and axis information from attached gaming devices

Syntax

Declare Function GetJoystick ( ByVal id As Long, ByRef buttons As Integer = 0, ByRef a1 As Single = 0, ByRef a2 As Single = 0, ByRef a3 As Single = 0, ByRef a4 As Single = 0, ByRef a5 As Single = 0, ByRef a6 As Single = 0, ByRef a7 As Single = 0, ByRef a8 As Single = 0 ) As Long

Usage

result = GetJoystick( id[, buttons[, a1[, a2[, a3[, a4[, a5[, a6[, a7[, a8]]]]]]]]] )

Parameters

id
the device id number (0 - 15)
buttons
the button status
a1
first axis value
a2
second axis value
a3
third axis value
a4
fourth axis value
a5
fifth axis value
a6
sixth axis value
a7
seventh axis value
a8
eighth axis value

Return Value

0 on success or 1 on failure. All of the axis positions are returned in floating point format.

Description

GetJoystick will retrieves the button state, and the axis positions for up to 8 axes, for the joystick determined by id, a number between 0 and 15. Buttons are stored in a similar manner to GetMouse, with each bit in buttons representing a button.

A single precision value between -1.0 and 1.0 is returned for each valid axis. If the axis does not exist for the controller, a value of -1000.00 is returned.

GetJoystick will return 0 upon successful completion; It will return 1 upon failure. Failure can be caused by specifying an illegal joystick number, specifying a joystick which doesn't exist, or a failure in the joystick API.

The error code returned by GetJoystick can be checked using Err in the next line. The function version of Getjoystick returns directly the error code as a 32 bit Long.

Example

Screen 12

Dim x As Single
Dim y As Single
Dim buttons As Integer
Dim result As Long
Dim a As Integer

Const JoystickID = 0

'This line checks to see if the joystick is ok.

If GetJoystick(JoystickID,buttons,x,y) Then
    Print "Joystick doesn't exist or joystick error."
    Print
    Print "Press any key to continue."
    Sleep
    End
End If

Do
    result = GetJoystick(JoystickID,buttons,x,y)

    Locate 1,1
    Print ;"result:";result;" x:" ;x;" y:";y;" Buttons:";buttons,"","",""

    'This tests to see which buttons from 1 to 27 are pressed.
    For a = 0 To 26
        If (buttons And (1 Shl a)) Then
            Print "Button ";a;" pressed.    "
        Else
            Print "Button ";a;" not pressed."
        End If
    Next a
Loop

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Getjoystick.

Differences from QB

  • New to FreeBASIC

See also

  • Screen (Graphics)
  • SetMouse
  • GetMouse
  • MultiKey
  • Event
  • ScreenEvent


GetKey
Returns the ascii code of the first key in the keyboard buffer

Syntax

Declare Function GetKey ( ) As Long

Usage

result = GetKey

Return Value

The value of the ascii code returned.

Description

It returns the ascii code of the first key in the keyboard buffer. The key is removed from the buffer. If no key is present, GetKey waits for it.

For extended keys (returning two characters), the extended code is returned in the first byte (255), and the scancode for keyboard is returned in the second byte (the third and fourth bytes always being null at least in console mode).

For FB's built-in functionality of getting keyboard input, see Keyboard Input (Basics).

WARNING: In graphics mode and depending on the key pressed, Getkey may not always return the exact same value as in console mode (for a non-extended key, the most significant bit of the ascii code byte may be propagated to the higher 3 bytes of the return value, such as a sign bit).
For a compatible code of the 2 screen modes, see the example below.

The key read is not echoed to the screen.

For a keyword not stopping the program if no key is at the buffer see Inkey or MultiKey.

Example

Dim As Long foo
Do
    foo = GetKey
    Print "total return: " & foo

    If( foo > 255 ) Then
        Print "extended code: " & (foo And &hff)
        Print "regular code: " & (foo Shr 8)
    Else
        Print "regular code: " & (foo And &hff)
    End If
    Print
Loop Until foo = 27

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Getkey.

Differences from QB

  • New to FreeBASIC

See also

  • GetMouse
  • Inkey
  • Input()
  • MultiKey
  • Keyboard scancodes
  • Keyboard Input


GetMouse
Retrieves the status of the mouse pointing device

Syntax

Declare Function GetMouse ( ByRef x As Long, ByRef y As Long, ByRef wheel As Long = 0, ByRef buttons As Long = 0, ByRef clip As Long = 0 ) As Long
Declare Function GetMouse ( ByRef x As LongInt, ByRef y As LongInt, ByRef wheel As LongInt = 0, ByRef buttons As LongInt = 0, ByRef clip As LongInt = 0 ) As Long

Usage

result = GetMouse (x, y [, [ wheel ] [, [ buttons ] [, [ clip ]]]])

Parameters

x
x coordinate value
y
y coordinate value
wheel
scroll wheel value
buttons
button status
clip
clip status

Return Value

0 on success, or 1 on error (for example because the mouse is outside the graphic window) or on failure. (sets a runtime error)

Description

GetMouse retrieves the mouse position and buttons status; information is returned in the variables passed to this function by reference. If a mouse is not available, all variables will contain the -1 value.

If in console mode, the x and y coordinates are the character cell coordinates the mouse is currently on; the upper left corner of the screen is at coordinates 0, 0. If the mouse moves out of the console window, GetMouse returns the last coordinate on the window that the mouse was on. If in console mode and fullscreen, the scroll wheel value is not returned.

If in graphics mode, x and y will always be returned in pixel coordinates still relative to the upper left corner of the screen, which is at 0,0 in this case; custom coordinates system set via View or Window do not affect the coordinates returned by GetMouse.
If the mouse runs off the graphic window, all values are set to -1 and the return value of the function is set to 1. This may result in misinterpretation for the buttons and wheel if the return value of the function is not also tested.

Wheel is the mouse wheel counter; rotating the wheel away from you makes the count to increase, rotating the wheel toward you makes it to decrease. At program startup or when a new graphics mode is set via Screen (Graphics), wheel position is reset to 0. FreeBASIC may not always support mouse wheels for a given platform, in which case 0 is always returned.

Buttons stores the buttons status as a bitmask: bit 0 is set if left mouse button is down; bit 1 is set if right mouse button is down; bit 2 is set if middle mouse button / wheel is down.

Clip stores the mouse clipping status; if 1, the mouse is currently clipped to the graphics window; if 0, the mouse is not clipped.

The error code returned by GetMouse can be checked using Err in the next line. The function version of GetMouse returns directly the error code as a 32 bit Long.

Warning: When the flag GFX_SHAPED_WINDOW is set (see ScreenRes), GetMouse is only active in any colored area, ie there where color <> RGBA(255, 0, 255, alpha).

Example

Dim As Long x, y, buttons, res
' Set video mode and enter loop
ScreenRes 640, 480, 8
Do
    ' Get mouse x, y and buttons. Discard wheel position.
    res = GetMouse (x, y, , buttons)
    Locate 1, 1
    If res <> 0 Then '' Failure

#ifdef __FB_DOS__
        Print "Mouse or mouse driver not available"
#else
        Print "Mouse not available or not on window"
#endif

    Else
        Print Using "Mouse position: ###:###  Buttons: "; x; y;
        If buttons And 1 Then Print "L";
        If buttons And 2 Then Print "R";
        If buttons And 4 Then Print "M";
        Print "   "
    End If
Loop While Inkey = ""
End

'Example 2: type-union-type structure
Type mouse
    As Long res
    As Long x, y, wheel, clip
    Union
        buttons As Long
        Type
            Left:1 As Long
            Right:1 As Long
            middle:1 As Long
        End Type
    End Union
End Type

Screen 11
Dim As mouse m

Do
    m.res = GetMouse( m.x, m.y, m.wheel, m.buttons, m.clip )
    ScreenLock
    Cls
    Print Using "res = #"; m.res
    Print Using "x = ###; y = ###; wheel = +###; clip = ##"; m.x; m.y; m.wheel; m.clip
    Print Using "buttons = ##; left = #; middle = #; right = #"; m.buttons; m.left; m.middle; m.right
    ScreenUnlock
    Sleep 10, 1
Loop While Inkey = ""

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Getmouse. The variables passed must also be of type Long instead of Integer.

Platform Differences

  • On Win32, scroll wheel changes are not guaranteed to be detected in full-screen console mode. Confirmed on WinXP: In windowed mode, mouse wheel changes are seen, but in full-screen console mode they are not.
  • In DOS, the "clip" value has no relevance. Additionally the wheel and middle button will not work unless supported and enabled by the mouse driver. See also FaqDOS.

Differences from QB

  • New to FreeBASIC

See also

  • ScreenRes setting graph mode by resolution
  • Screen (Graphics) setting mode the QB-like way
  • SetMouse
  • MultiKey
  • GetJoystick
  • Event
  • ScreenEvent


Getxpad
Reads Xbox-style controller state from the graphics library.

Syntax

Declare Function Getxpad ( ByVal id As Long, ByRef buttons As Integer = 0, ByRef leftx As Single = 0, ByRef lefty As Single = 0, ByRef rightx As Single = 0, ByRef righty As Single = 0, ByRef lefttrigger As Single = 0, ByRef righttrigger As Single = 0, ByRef dpad As Integer = 0 ) As Long

Usage

status = Getxpad( id[, buttons[, leftx[, lefty[, rightx[, righty[, lefttrigger[, righttrigger[, dpad]]]]]]]] )

Parameters

id
The controller number to query. The valid range is platform dependent.
buttons
A bit mask of XPAD_BUTTON_* values.
leftx, lefty, rightx, righty
Analog stick positions in the range -1.0 to 1.0.
lefttrigger, righttrigger
Trigger positions in the range 0.0 to 1.0.
dpad
A bit mask of XPAD_DPAD_* values.

Return Value

Returns one of the XPAD_STATUS_* constants from fbgfx.bi:
    • XPAD_STATUS_MISSING (0)
    • XPAD_STATUS_CONNECTED (1)
    • XPAD_STATUS_DISCONNECTED (2)

Description

Getxpad provides a stable Xbox-style controller layout across supported backends. It is separate from GetJoystick because modern gamepads have a conventional button, trigger, stick, and d-pad shape that maps poorly to a generic axis list.

The named XPAD_BUTTON_*, XPAD_DPAD_*, and XPAD_STATUS_* constants are declared in fbgfx.bi under the FB namespace. The function itself is an intrinsic and does not require an include file to call.

Example

See examples/xpad/first-pad.bas, examples/xpad/active-state.bas, and examples/xpad/stick-view.bas.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Getxpad.

Differences from QB

  • New to FreeBASIC.

See also

  • GetJoystick
  • MultiKey
  • GetMouse


Generated Sound Commands
Overview of the generated-tone, PLAY-string, mix, waveform, envelope, and instrument commands.

Description

Generated sound commands synthesize audio directly through sfxlib. They are useful for simple tones, retro-style PLAY strings, game sound effects that do not need external files, and quick diagnostics that prove the sound path works.

Command pages

  • SOUND: generated tone.
  • TONE: channel-specific tone.
  • NOISE: generated noise.
  • NOTE: named musical note.
  • PLAY: BASIC PLAY strings.
  • TEMPO: PLAY tempo.
  • CHANNEL: default channel.
  • OCTAVE: default octave.
  • VOICE: default instrument.
  • VOL: compact master volume.
  • VOLUME: master or channel volume.
  • BALANCE: master stereo balance.
  • PAN: channel stereo pan.
  • WAVE: waveform definition.
  • ENVELOPE: ADSR envelope definition.
  • INSTRUMENT: instrument definition or assignment.

Example

tempo 140
octave 4
channel 0
volume 0.5
play "cdefgab"


GoSub
Control flow statement to use a section of code and return.

Syntax

GoSub label

Description

Execution jumps to a subroutine marked by a line label. Always use Return to exit a GoSub, execution will continue on next statement after GoSub.

The line label where GoSub jumps must be in the same main/function/sub block as GoSub. All the variables in the subroutine are shared with the block, no arguments can be used. For this reason Gosub is considered bad programming practice as it can generate unreadable and untraceable code. It is better to use Sub or Function instead.

Example

'' Compile with -lang qb

'$lang: "qb"

GoSub message
End

message:
Print "Welcome!"
Return

Dialect Differences

  • Only available in the -lang qb and -lang fblite dialects.
  • GoSub support is disabled by default in the -lang fblite unless the Option Gosub statement is used.

Differences from QB

  • None when using the -lang qb dialect.

See also

  • Goto
  • Return (From Gosub)
  • Sub
  • Function
  • Option Gosub
  • Labels


Goto
Control flow statement to jump to another part of the program

Syntax

Goto label

Description

Jumps code execution to a line label.

When using Goto label to exit a scope, any local variables defined in that scope are destroyed (destructors are called).

Usage of Goto label may be disallowed when it skips a variable definition but not the end of the variable's scope. If the variable requires construction, a compiler error is shown. For other variables, a compiler warning is shown. This is intended to prevent potential accesses to uninitialized variables, and ensures that automatic destruction never happens to an uninitialized variable.

For better source code readability, overuse of Goto should be avoided in favor of more modern structures such as Do...Loop, For...Next, Sub, and Function.

Example

    Goto there

backagain:
    End

there:
    Print "Welcome!"
    Goto backagain

'' Compile with -lang qb or fblite

'$lang: "qb"

1 Goto 3
2 End
3 Print "Welcome!"
4 Goto 2

Dialect Differences

  • Line numbers are allowed only in the -lang qb and -lang deprecated dialects.
  • In the -lang qb and -lang fblite dialects, Goto label is allowed to skip any variable definitions, because nested scopes are not supported and all variable definitions are moved to the top of their procedure.

Differences from QB

  • None

See also

  • GoSub
  • Sub
  • Function
  • Labels


Hex
Returns the hexadecimal of the given number

Syntax

Declare Function Hex ( ByVal number As UByte ) As String
Declare Function Hex ( ByVal number As UShort ) As String
Declare Function Hex ( ByVal number As ULong ) As String
Declare Function Hex ( ByVal number As ULongInt ) As String
Declare Function Hex ( ByVal number As Const Any Ptr ) As String

Declare Function Hex ( ByVal number As UByte, ByVal digits As Long ) As String
Declare Function Hex ( ByVal number As UShort, ByVal digits As Long ) As String
Declare Function Hex ( ByVal number As ULong, ByVal digits As Long ) As String
Declare Function Hex ( ByVal number As ULongInt, ByVal digits As Long ) As String
Declare Function Hex ( ByVal number As Const Any Ptr, ByVal digits As Long ) As String

Usage

result = Hex[$]( number [, digits ] )

Parameters

number
A number or expression evaluating to a number. A floating-point number will be converted to a LongInt.
digits
Optional number of digits to return.

Return Value

A String containing the unsigned hexadecimal representation of number.

Description

Returns the unsigned hexadecimal string representation of the integer number. Hexadecimal digits range from 0-9, or A-F.

If you specify digits > 0, the result string will be exactly that length. It will be truncated or padded with zeros on the left, if necessary.

The length of the string will not go longer than the maximum number of digits required for the type of number (8 for a Long, 16 for a LongInt).

If you want to do the opposite, i.e. convert a hexadecimal string back into a number, the easiest way to do it is to prepend the string with "&H", and convert it to an integer type, using a function like CInt, similarly to a normal numeric string. E.g. CInt("&HFF")

Example

'54321 is D431 in hex
Print Hex(54321)
Print Hex(54321, 2)
Print Hex(54321, 5)

will produce the output:

D431
31
0D431

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • In QBASIC, there was no way to specify the number of digits returned.
  • The size of the string returned was limited to 32 bits, or 8 hexadecimal digits.

See also

  • Bin
  • Oct
  • ValInt
  • ValLng


HiByte
Gets the second byte of the operand.

Syntax

#define HiByte( expr ) ((Cast(UInteger, expr) And &h0000FF00) Shr 8)

Usage

result = HiByte( expr )

Parameters

expr
A numeric expression, converted to an UInteger value.

Return Value

Returns the value of the high byte of the low 16bit word of expr.

Description

This macro converts the numeric expression expr to an UInteger value, then expands to an UInteger representing the value of its second byte - that is the most-significant (high) byte of the least-significant (low) 16bit word of expr.

Example

Dim N As UInteger

'Note there are 16 bits
N = &b1010101110000001
Print "N is                                       "; N
Print "The binary representation of N is          "; Bin(N)
Print "The most significant byte (MSB) of N is    "; HiByte(N)
Print "The least significant byte (LSB) of N is   "; LoByte(N)
Print "The binary representation of the MSB is    "; Bin(HiByte(N))
Print "The binary representation of the LSB is    "; Bin(LoByte(N))
Sleep

The output would look like:
N Is                                       43905
The Binary representation of N Is          1010101110000001
The most significant Byte (MSB) of N Is    171
The least significant Byte (LSB) of N Is   129
The Binary representation of the MSB Is    10101011
The Binary representation of the LSB Is    10000001

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __HIBYTE.

Differences from QB

  • New to FreeBASIC

See also

  • LoByte
  • LoWord
  • HiWord


HiWord
Gets the second 16bit word of the operand.

Syntax

#define HiWord( expr ) ((Cast(UInteger, expr) and &hFFFF0000) Shr 16)

Usage

result = HiWord( expr )

Parameters

expr
A numeric expression, converted to an UInteger value.

Return Value

Returns the value of the high 16bit word of the low 32bit dword of expr.

Description

This macro converts the numeric expression expr to an UInteger value, then expands to an UInteger representing the value of its second 16bit word - that is the most-significant (high) 16bit word of the least-significant (low) 32bit dword of expr.

Example

Dim N As UInteger

'Note there are 32 bits
N = &b10000000000000011111111111111111

Print "N is                                       "; N
Print "The binary representation of N is          "; Bin(N)
Print "The most significant word (MSW) of N is    "; HiWord(N)
Print "The least significant word (LSW) of N is   "; LoWord(N)
Print "The binary representation of the MSW is    "; Bin(HiWord(N))
Print "The binary representation of the LSW is    "; Bin(LoWord(N))

Sleep

The output would look like:
N Is                                       2147614719
The Binary representation of N Is          10000000000000011111111111111111
The most significant word (MSW) of N Is    32769
The least significant word (LSW) of N Is   65535
The Binary representation of the MSW Is    1000000000000001
The Binary representation of the LSW Is    1111111111111111

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __HIWORD.

Differences from QB

  • New to FreeBASIC

See also

  • LoByte
  • HiByte
  • LoWord


Hour
Gets the hour of day from a Date Serial

Syntax

Declare Function Hour ( ByVal date_serial As Double ) As Long

Usage

#include "vbcompat.bi"
result = Hour( dateserial )

Parameters

date_serial
the date serial

Return Value

Returns the hour from a variable containing a date in Date Serial format.

Description

The compiler will not recognize this function unless vbcompat.bi is included.

Example

#include "vbcompat.bi"

Dim ds As Double = DateSerial(2005, 11, 28) + TimeSerial(7, 30, 50)

Print Format(ds, "yyyy/mm/dd hh:mm:ss "); Hour(ds)

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials


If...Then
Control flow statement for conditional branching

Syntax

If expression Then [statement(s)] [Else [statement(s)]] [End If]
or
If expression Then : [statement(s)] [Else [statement(s)]] : End If
or
If expression Then
[statement(s)]
[ ElseIf expression Then ]
[statement(s)]
[ Else ]
[statement(s)]
End If

Remark: EndIf (without blank) is also supported like in QB for backward compatibility.

Description

If...Then is a way to make decisions.
It is a mechanism to execute code only if a condition is true, and can provide alternative code to execute based on more conditions:
    • Execute code (just behind Then) if a condition is true.
    • Execute certain code (just behind Then) if a condition is true and execute other (just behind Else) if it's false.
    • Test other conditions (with ElseIf) if the first condition is false.

expression can be one of several forms:
    • a conditional expression, for example:
x = 5
    • multiple conditions separated by logical bit-wise operators, for example:
x >= 5 And x <= 10
    • multiple conditions separated by logical short-circuit operators, for example:
y <> 0 AndAlso x \ y = 1
(in this case, "x \ y = 1" will only be evaluated if "y <> 0" is True)
    • any numerical expression, in which case a value of zero (0) represents False, and a non-zero value represents True

Both multi-line and single-line Ifs can be nested. In the latter case, the optional End Ifs can be useful to control where nested Ifs begin and end.

The multi-line syntax allows several Elseifs (but none after a Else) and tests can be nested (there must be in this case as many End Ifs as Ifs).
If the condition of the If is not true, those of Elseifs blocks are tested in succession:
    • If either of these is true, the corresponding code is executed, then the program skips the following blocks to continue after the End If.
    • If none are true, the code following the Else (if exists) is executed alone.

In the -lang fb and -lang fblite dialects, colons (:) can be used instead of newlines to construct multi-line If blocks on a single line.

Note: The single-line syntax If...Goto, as shortcut for If...Then Goto, is deprecated and it only exists for compatibility with QB.

Example

'' Here is a simple "compute the square root" code using a single-line if...then for the decision,
'' but with multiple statements extended with colons (:)

Dim As Double d , r
r = -1
d = 2
'd = -3

If d > 0 Then r = Sqr(d) : Print "square root computed:" Else r = 0 : Print "square root not computed:"
Print r

Sleep

'' Here is a simple "guess the number" game using a multi-line if...then for a decision.

Dim As Integer num, guess

Randomize
num = Int(Rnd * 10) + 1 'Create a random number between 1 and 10...

Print "guess the number between 1 and 10 (or CTRL-C to abort)"

Do 'Start a loop

    Input "Guess"; guess 'Input a number from the user

    If guess > 10 OrElse guess < 1 Then  'The user's guess is out of range
        Print "The number can't be greater then 10 or less than 1!"
    ElseIf guess > num Then  'The user's guess is too high
        Print "Too high"
    ElseIf guess < num Then  'The user's guess is too low
        Print "Too low"
    Else                     'The user guessed the right number!
        Print "Correct!"
        Exit Do   'Exit the loop
    End If

Loop 'Go back to the start of the loop

Sleep

Dialect Differences

  • In the -lang qb and -lang fblite dialects, variables declared inside an If..Then block have a procedure-wide scope as in QB
  • In the -lang fb and -lang deprecated dialects, variables declared inside an If..Then block are visible only inside the block, and cannot be accessed outside it. To access duplicated symbols defined as global outside this block, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).
  • In the -lang qb dialect, if there is a new line or a single-line comment (') directly after THEN, then the IF will be multi-line. A colon, a Rem or any other statement will result in a single-line IF.
  • In the -lang fb and -lang fblite dialects, if there is a new line, a single-line comment ('), a colon (:), or a Rem statement directly after THEN, then the IF will be multi-line. Any other statement will result in a single-line IF.

Differences from QB

  • END IF was not supported in single-line IFs in QBASIC.

See also

  • ElseIf
  • Do...Loop
  • #if
  • Select Case


IIf
Conditional function that returns one of two values.

Syntax

IIf ( condition, expr_if_true, expr_if_false )

Parameters

condition
The condition to test.
A non-zero value evaluates as true, while a value of zero evaluates as false.
expr_if_true
An expression to evaluate and return if condition is true.
It must return:
      • a numeric value, which can be an integer, floating point number or a pointer, including Boolean,
      • or a string value,
      • or an UDT value.
expr_if_false
An expression to evaluate and return if condition is false.
It must be same type as expr_if_true (either numeric, either string or UDT).

Return Value

if condition is non-zero, expr_if_true, otherwise expr_if_false

Description

IIf returns a different numeric or string or UDT value (not a reference) depending of the result of a conditional expression evaluated at run-time.
Its typical use is in the middle of an expression; it avoids splitting it to put a conditional in the middle.

IIf only evaluates the expression that it needs to return. This saves time, and can also be useful to prevent evaluating expressions that might be invalid depending on the condition.

When IIf treats expressions of mixed numeric types (conditional expression evaluated at run-time):
    • if at least one expression is of floating-point type, the result type is the floating-point type (the bigger in case of two floating-point types),
    • if the two expressions are of integer types, the result type is the bigger type of both (see Coercion and Conversion for the precise ranking of integer types).
Note: If the condition evaluation can be done at compile time (if the comparison expression is a constant), the usage rules are a bit more flexible and IIf can for example return a reference or even return a function call (but not a sub call).

Example

Dim As Integer a, b, x, y, z
a = (x + y + IIf(b > 0, 4, 7)) \ z

is equivalent to:
Dim As Integer a, b, x, y, z, temp
If b > 0 Then temp = 4 Else temp = 7
a = (x + y + temp) \ z

Dim As Integer I
I = -10
Print I, IIf(I>0, "positive", IIf(I=0, "null", "negative"))
I = 0
Print I, IIf(I>0, "positive", IIf(I=0, "null", "negative"))
I = 10
Print I, IIf(I>0, "positive", IIf(I=0, "null", "negative"))
Sleep

Type UDT1
  Dim As Integer I1
End Type

Type UDT2 Extends UDT1
  Dim As Integer I2
End Type

Dim As UDT1 u1, u10 = (1)
Dim As UDT2 u2, u20 = (2, 3)

u1 = IIf(0, u10, u20)
Print u1.I1
u1 = IIf(1, u10, u20)
Print u1.I1

u2 = IIf(0 , u10, u20)
Print u2.I1; u2.I2
'u2 = Iif(1, u10, u20) ''Invalid assignment/conversion
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Iif.

Differences from QB

  • New to FreeBASIC

See also

  • If...Then


ImageConvertRow
Converts a row of image data into another color depth

Syntax

Declare Sub ImageConvertRow ( ByVal src As Any Ptr, ByVal src_bpp As Long, ByVal dst As Any Ptr, ByVal dst_bpp As Long, ByVal width As Long, ByVal isrgb As Long = 1 )

Usage

ImageConvertRow( src, src_bpp, dst, dst_bpp, width [, isrgb ] )

Parameters

src
The address of the start of the source row. The source can either be a full-color image with a bit depth of 24 or 32 bits per pixel, or a paletted image with a bit depth of 1-8 bits per pixel. Converting a paletted image will only work properly if you are in a screen mode that is using the correct palette for the image when you do the conversion.
src_bpp
The number of bits per pixel in the source row. 1-8, 24 and 32.
dst
The address of the start of the destination row. The image can be a full-color image of 16 or 32 bits per pixel. If the source is a paletted image, the destination can also be a paletted image of 1 to 8 bits per pixel.
dst_bpp
The number of bits per pixel in the destination row. Valid values for this are 1-8, 16 and 32.
width
The length of the row in pixels.
isrgb
A value of zero indicates that the Red and Blue channels are the other way round in the source image. Use this switch if you want the Red and Blue channels to be swapped in the conversion.

Description

Copies the row of an image from one memory location to another, converting the color information in each pixel to match the destination image.

Example

#include "fbgfx.bi"
#if __FB_LANG__ = "fb"
Using FB
#endif

Const As Long w = 64, h = 64
Dim As IMAGE Ptr img8, img32
Dim As Integer x, y

'' create a 32-bit image, size w*h:
ScreenRes 1, 1, 32, , GFX_NULL
img32 = ImageCreate(w, h)

If img32 = 0 Then Print "Imagecreate failed on img32!": Sleep: End

'' create an 8-bit image, size w*h:
ScreenRes 1, 1, 8, , GFX_NULL
img8 = ImageCreate(w, h)

If img8 = 0 Then Print "Imagecreate failed on img8!": Sleep: End

'' fill 8-bit image with a pattern
For y = 0 To h - 1
    For x = 0 To w - 1
        PSet img8, (x, y), 56 + (x + y) Mod 24
    Next x
Next y

'' open a graphics window in 8-bit mode, and PUT the image into it:
ScreenRes 320, 200, 8
WindowTitle "8-bit color mode"
Put (10, 10), img8

Sleep

'' copy the image data into a 32-bit image
Dim As Byte Ptr p8, p32
Dim As Long pitch8, pitch32

#ifndef ImageInfo '' older versions of FB don't have the ImageInfo feature
#define GETPITCH(img_) IIf(img_->Type=PUT_HEADER_NEW,img_->pitch,img_->old.width*img_->old.bpp)
#define GETP(img_) CPtr(Byte Ptr,img_)+IIf(img_->Type=PUT_HEADER_NEW,SizeOf(PUT_HEADER),SizeOf(_OLD_HEADER))
pitch8 = GETPITCH(img8): p8 = GETP(img8)
pitch32 = GETPITCH(img32): p32 = GETP(img32)
#else
ImageInfo( img8,  , , , pitch8,  p8  )
ImageInfo( img32, , , , pitch32, p32 )
#endif

For y = 0 To h - 1
    ImageConvertRow(@p8 [ y * pitch8 ],  8, _
                    @p32[ y * pitch32], 32, _
                    w)
Next y

'' open a graphics window in 32-bit mode and PUT the image into it:
ScreenRes 320, 200, 32
WindowTitle "32-bit color mode"
Put (10, 10), img32

Sleep

'' free the images from memory:
ImageDestroy img8
ImageDestroy img32

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __ImageConvertRow.

Differences from QB

  • New to FreeBASIC

See also

  • ScreenRes
  • Get (Graphics)
  • Put (Graphics)
  • ImageCreate
  • ImageDestroy
  • ImageInfo


ImageCreate
Allocates and initializes storage for an image

Syntax

Declare Function ImageCreate ( ByVal width As Long, ByVal height As Long, ByVal color As ULong = transparent_color ) As Any Ptr
Declare Function ImageCreate ( ByVal width As Long, ByVal height As Long, ByVal color As ULong = transparent_color, ByVal depth As Long ) As Any Ptr

Usage

result = ImageCreate( width, height [, [ color ][, depth ]] )

Parameters

width
The desired width, in number of pixels.
height
The desired height, in number of pixels.
color
The pixel value to fill the area of the image.
depth
The desired color depth, in bits per pixel.

Return Value

If the image could not be created, NULL (0) is returned, otherwise, the address of the image is returned. ImageCreate must be called after graphic mode initialization, else it returns 0.

Consequently, in case of Shared variable declaration, ImageCreate cannot be used as integrated initializer, even inside an Udt (in member field or constructor), because the initialization value (of shared variable) is set at the start of the program before any user code is run. The image allocation call must be in a separated executable instruction, and after the graphic mode initialization.

Description

Both procedures allocate memory for an image of the specified width and height. If not successful, NULL (0) is returned. Otherwise, an image of that size is created and initialized by filling the entire area of pixels with the value color. If not specified, color assumes the value of the transparent color for the current graphics screen, which can be found by calling ScreenControl. In any case, the address of the image is returned, which is then controlled by the user, and must be destroyed using ImageDestroy.

The first procedure creates an image with a color depth matching that of the current graphics screen, which can be found by calling ScreenControl. The second procedure creates an image with a color depth of depth, in bits per pixel. For both procedures, the resulting image can be used in drawing procedures while in any screen mode -- and across mode changes -- as long as the color depth of the image matches that of the graphics screen.

ImageCreate is the recommended way to allocate memory for new images. The memory layout -- size, structure, etc. -- while documented, may change from version to version, making manual calculation of the sizes involved error-prone. However, ImageInfo can be used to retrieve, among other things, the size, in bytes, of an existing image, allowing memory to be manually allocated for a copy of an image, or to be read from or written to a file or device.

Get (Graphics) can be used to initialize an image using pre-allocated memory.

Example

'' Create a graphics screen.
ScreenRes 320, 200, 32

'' Create a 64x64 pixel image with a darkish green background.
Dim image As Any Ptr = ImageCreate( 64, 64, RGB(0, 128, 0) )

If image = 0 Then
    Print "Failed to create image."
    Sleep
    End -1
End If

'' Draw a semi-transparent, red circle in the center of the image.
Circle image, (32, 32), 28, RGBA(255, 0, 0, 128),,, 1.0, f

'' Draw the image onto the screen using various blitting methods.
Put (120, 60), image, PSet
Put (140, 80), image, Alpha

'' Destroy the image.
ImageDestroy image

Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Imagecreate.

Differences from QB

  • New to FreeBASIC

See also

  • ImageDestroy
  • ImageInfo
  • Get (Graphics)
  • Internal pixel formats


ImageDestroy
Destroys and deallocates storage for an image

Syntax

Declare Sub ImageDestroy ( ByVal image As Any Ptr )

Usage

ImageDestroy( image )

Parameters

image
The address of the image to destroy.

Description

Destroys the image pointed to by image, which must be an address returned from a call to ImageCreate.

Calling ImageDestroy on a null pointer induces no action.

Example

See ImageCreate for an example on using ImageDestroy.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Imagedestroy.

Differences from QB

  • New to FreeBASIC

See also

  • ImageCreate


ImageInfo
Retrieves information about an image

Syntax

Declare Function ImageInfo ( ByVal image As Const Any Ptr, ByRef width As Long = 0, ByRef height As Long = 0, ByRef bypp As Long = 0, ByRef pitch As Long = 0, ByRef pixdata As Any Ptr = 0, ByRef size As Long = 0 ) As Long
Declare Function ImageInfo ( ByVal image As Const Any Ptr, ByRef width As LongInt, ByRef height As LongInt, ByRef bypp As LongInt = 0, ByRef pitch As LongInt = 0, ByRef pixdata As Any Ptr = 0, ByRef size As LongInt = 0 ) As Long

Usage

in the LONG (or INTEGER<32>) version of the function:
result = ImageInfo( image [, [ width ] [, [ height ] [, [ bypp ] [, [ pitch ] [ , [ pixdata ] [, size ]]]]]] )
in the LONGINT (or INTEGER<64>) version of the function:
result = ImageInfo( image , width , height [, [ bypp ] [, [ pitch ] [ , [ pixdata ] [, size ]]]] )

Parameters

image
The address of the image.
width
Stores the width of the image, in pixels.
height
Stores the height of the image, in pixels.
bypp
Stores the bytes per pixel of the image - i.e. the size of a single pixel, in bytes.
pitch
Stores the pitch of the image - i.e. the size of each scanline (row), in bytes. Note that this may be more than just width * bypp, because the scanlines may be padded to allow them to be aligned better in memory.
pixdata
Stores the address of the start of the first scanline of the image.
size
Stores the size of the image in memory, in bytes.

Return Value

If image doesn't point to a valid image, one (1) is returned. Otherwise, width, height, bypp, pitch, pixdata and size are assigned appropriate values, and zero (0) is returned.

Description

ImageInfo provides various information about an image, such as its dimensions and color depth, but also provides you with the information you need to directly access all the pixel data in the pixel buffer.

It can also provide the size of the image in memory, which is useful for allocating memory to copy the existing image, or to write the image to a file.

ImageInfo is an alternative way to access the main characteristics of an image, rather than directly accessing the internal FB.IMAGE structure (defined in fbgfx.bi) through a typed pointer to member data.

The error code returned by ImageInfo can be checked using Err in the next line. The function version of ImageInfo returns directly the error code as a 32 bit Long.

Example

'' pixelptr(): use imageinfo() to find the pointer to a pixel in the image
'' returns null on error or x,y out of bounds
Function pixelptr(ByVal img As Any Ptr, ByVal x As Integer, ByVal y As Integer) As Any Ptr

    Dim As Long w, h, bypp, pitch
    Dim As Any Ptr pixdata
    Dim As Long result

    result = ImageInfo(img, w, h, bypp, pitch, pixdata)

    If result = 0 Then '' seems like a valid image
        If x < 0 Or x >= w Then Return 0
        If y < 0 Or y >= h Then Return 0
        Return pixdata + y * pitch + x * bypp
    Else
        Return 0
    End If

End Function

'' usage example:

'' 320*200 graphics screen, 8 bits per pixel
ScreenRes 320, 200, 8

Dim As Any Ptr ip '' image pointer

Dim As Byte Ptr pp '' pixel pointer (use byte for 8 bits per pixel)

ip = ImageCreate(32, 32) '' create an image (32*32, 8 bits per pixel)

If ip <> 0 Then

    '' draw a pattern on the image
    For y As Integer = 0 To 31

        For x As Integer = y - 5 To y + 5 Step 5

            '' find the pointer to pixel at x,y position
            '' note: this is inefficient to do for every pixel!
            pp = pixelptr(ip, x, y)

            '' if success, plot a value at the pixel
            If (pp <> 0) Then *pp = 15

        Next x

    Next y

    '' put the image and draw a border around it
    Put (10, 10), ip, PSet
    Line (9, 9)-Step(33, 33), 4, b

    '' destroy the image to reclaim memory
    ImageDestroy ip

Else
    Print "Error creating image!"
End If

Sleep

'' Create 32-bit graphics screen and image.
ScreenRes 320, 200, 32
Dim image As Any Ptr = ImageCreate( 64, 64 )

Dim pitch As Long
Dim pixels As Any Ptr

'' Get enough information to iterate through the pixel data.
If 0 <> ImageInfo( image, ,,, pitch, pixels ) Then
    Print "unable to retrieve image information."
    Sleep
    End
End If

'' Draw a pattern on the image by directly manipulating pixel memory.
For y As Integer = 0 To 63
    Dim row As ULong Ptr = pixels + y * pitch

    For x As Integer = 0 To 63
        row[x] = RGB(x * 4, y * 4, (x Xor y) * 4)
    Next x
Next y

'' Draw the image onto the screen.
Put (10, 10), image

ImageDestroy( image )

Sleep

Version

  • Before fbc 1.08.0:
Syntax:
Declare Function ImageInfo ( ByVal image As Any Ptr, ByRef width As Integer = 0, ByRef height As Integer = 0, ByRef bypp As Integer = 0, ByRef pitch As Integer = 0, ByRef pixdata As Any Ptr = 0, ByRef size As Integer = 0 ) As Long
Usage:
result = ImageInfo( image [, [ width ] [, [ height ] [, [ bypp ] [, [ pitch ] [, [ pixdata ] [, size ]]]]]] )

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Imageinfo.

Differences from QB

  • New to FreeBASIC

See also

  • ImageCreate
  • ImageDestroy
  • ImageConvertRow
  • Get (Graphics)
  • Put (Graphics)
  • Internal pixel formats


Operator Imp (Implication)
Returns the bitwise-and (implication) of two numeric values

Syntax

Declare Operator Imp ( ByRef lhs As T1, ByRef rhs As T2 ) As Ret

Usage

result = lhs Imp rhs

Parameters

lhs
The left-hand side expression.
T1
Any numeric or boolean type.
rhs
The right-hand side expression.
T2
Any numeric or boolean type.
Ret
A numeric or boolean type (varies with T1 and T2).

Return Value

Returns the bitwise-implication of the two operands.

Description

This operator returns the bitwise-implication of its operands, a logical operation that results in a value with bits set depending on the bits of the operands (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value).

The truth table below demonstrates all combinations of a boolean-implication operation:

Lhs Bit | Rhs Bit | Result
0 | 0 | 1
1 | 0 | 0
0 | 1 | 1
1 | 1 | 1

No short-circuiting is performed - both expressions are always evaluated.

The return type depends on the types of values passed. Byte, UByte and floating-point type values are first converted to Integer. If the left and right-hand side types differ only in signedness, then the return type is the same as the left-hand side type (T1), otherwise, the larger of the two types is returned. Only if the left and right-hand side types are both Boolean, the return type is also Boolean.

This operator can be overloaded for user-defined types.

Example

Dim As UByte a, b, c
a = &b00001111
b = &b01010101
c = a Imp b '' c = &b11110101

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Operator Truth Tables


Implements
Specifies an interface to be implemented by a user-defined type
Note: Stub page. Even though this keyword is reserved already, interfaces are not implemented yet.

Syntax

Type typename Implements interface
...
End Type

Description

Example

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Implements.

Differences from QB

  • New to FreeBASIC

See also

  • Type
  • Extends
  • Extends Zstring
  • Extends Wstring


Import
External linkage attribute for public data located in DLL's

Syntax

Extern Import symbolname[( subscripts)] [ Alias "aliasname"] [ As DataType] [, ...]

Description

Is used only (with the Extern keyword) in external modules to access global variables from Win32 DLLs: the variable names will be added to the dynamic library import list so that their addresses can be fixed at run-time.
This is due to the level of indirection on any such access: an implicit pointer dereference.

Example

/* mydll.c :
    compile With
      gcc -Shared -Wl,--strip-all -o mydll.dll mydll.c
*/
__declspec( dllexport ) Int MyDll_Data = 0x1234;

/'  import.bas :
 Compile with
   fbc import.bas
'/
#inclib "mydll"

Extern Import MyDll_Data Alias "MyDll_Data" As Integer

Print "&h" + Hex( MyDll_Data )

' Output:
' &h1234

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Import.

Differences from QB

  • New to FreeBASIC

See also

  • Extern


Inkey
Returns a string representing the first key waiting in the keyboard buffer

Syntax

Declare Function Inkey ( ) As String

Usage

result = Inkey[$]

Return Value

The first character found in the keyboard buffer, or an empty string ("") if none found.

Description

Peeks into the keyboard buffer and returns a String representation of the first character, if any, found. The key is then removed from the buffer, and is not echoed to the screen. If the keyboard buffer is empty, an empty string ("") is immediately returned without waiting for keys.

If the key is in the ASCII character set, a one-character String consisting of that character is returned.
If the key is an "extended" one (numeric pad, cursors, functions) a two-character String is returned, the first of which is the extended character (See dialect differences below), and the second is the raw scancode for the keyboard.

For FB's built-in functionality of getting keyboard input, see Keyboard Input (Basics).

The Shift, Ctrl, Alt, and AltGr keys can't be read independently by this function as they never enter the keyboard buffer (although, perhaps obviously, Shift-A will be reported by Inkey differently than Control-A et cetera; Alt-A is an extended key a la the above).

See also Input() or GetKey, or Sleep to wait for a key press if the keyboard buffer is empty.

Example

Print "press q to quit"
Do
    Sleep 1, 1
Loop Until Inkey = "q"

'' Compile with -lang fblite or qb

#lang "fblite"

#if __FB_LANG__ = "qb"
#define EXTCHAR Chr$(0)
#else
#define EXTCHAR Chr(255)
#endif

Dim k As String

Print "Press a key, or Escape to end"
Do

    k = Inkey$

    Select Case k

        Case "A" To "Z", "a" To "z": Print "Letter: " & k
        Case "1" To "9":             Print "Number: " & k

        Case Chr$(32): Print "Space"

        Case Chr$(27): Print "Escape"

        Case Chr$(9): Print "Tab"

        Case Chr$(8): Print "Backspace"

        Case Chr$(32) To Chr$(127)
            Print "Printable character: " & k

        Case EXTCHAR & "G": Print "Up Left / Home"
        Case EXTCHAR & "H": Print "Up"
        Case EXTCHAR & "I": Print "Up Right / PgUp"

        Case EXTCHAR & "K": Print "Left"
        Case EXTCHAR & "L": Print "Center"
        Case EXTCHAR & "M": Print "Right"

        Case EXTCHAR & "O": Print "Down Left / End"
        Case EXTCHAR & "P": Print "Down"
        Case EXTCHAR & "Q": Print "Down Right / PgDn"

        Case EXTCHAR & "R": Print "Insert"
        Case EXTCHAR & "S": Print "Delete"

        Case EXTCHAR & "k": Print "Close window / Alt-F4"

        Case EXTCHAR & Chr$(59) To EXTCHAR & Chr$(68)
            Print "Function key: F" & Asc(k, 2) - 58

        Case EXTCHAR & Chr$(133) To EXTCHAR & Chr$(134)
            Print "Function key: F" & Asc(k, 2) - 122

        Case Else
            If Len(k) = 2 Then
                Print Using "Extended character: chr$(###, ###)"; Asc(k, 1); Asc(k, 2)
            ElseIf Len(k) = 1 Then
                Print Using "Character chr$(###)"; Asc(k)
            End If

    End Select

    If k = Chr$(27) Then Exit Do

    Sleep 1, 1

Loop

Dialect Differences

  • The extended character is Chr(255) in the -lang fb and -lang fblite dialects.
    • In the -lang qb dialect, the extended character depends on how the keyword is written. If the QB form Inkey$ is used, the extended character is Chr(0). If it is referenced as __Inkey, the extended char is Chr(255).
    • In all other dialects, the extended char is always Chr(255).
  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • None in the -lang qb dialect.
  • QBasic returned a Chr(0) as the first character for an extended key, but FreeBASIC returns Chr(255) as the first character in the -lang fb and -lang fblite dialects.

See also

  • Sleep
  • GetKey
  • Input()
  • MultiKey
  • Keyboard scancodes
  • Keyboard Input


Inp
Returns a value at a hardware port.

Syntax

Declare Function Inp ( ByVal port As UShort ) As Long

Usage

value = Inp(port)

Parameters

port
Port number to read.

Return Value

The value at the specified port.

Description

This function retrieves the value at 'port' and returns immediately.

Example

'' Turn off PC speaker
Out &h61,Inp(&h61) And &hfc

Platform Differences

  • In the Windows and Linux versions three port numbers (&H3C7, &H3C8, &H3C9) are hooked by the graphics library when a graphics mode is in use to emulate QB's VGA palette handling. This use is deprecated; use Palette to retrieve and set palette colors.

  • Using true port access in the Windows version requires the program to install a device driver for the present session. For that reason, Windows executables using hardware port access should be run with administrator permits each time the computer is restarted. Further runs don't require admin rights as they just use the already installed driver. The driver is only 3K in size and is embedded in the executable.

See also

  • Out
  • Wait
  • Palette


Operator Imp= (Implication And Assign)
Performs a bitwise-imp (implication) and assigns the result to a variable

Syntax

Declare Operator Imp= ( ByRef lhs As T1, ByRef rhs As T2 )

Usage

lhs Imp= rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric or boolean type.
rhs
The value to perform a bitwise-imp (implication) with lhs.
T2
Any numeric or boolean type.

Description

This operator performs a bitwise-imp and assigns the result to a variable (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value). It is functionally equivalent to:
lhs = lhs Imp rhs

Imp is a bitwise operator which is the same as (Not lhs) Or rhs. Imp= compares each bit of its operands, lhs and rhs, and if the bit in lhs is 0 or the bit in rhs is 1, then the corresponding bit in the first operand, lhs, is set to 1, otherwise it is set to 0.

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol 'Imp=>' can be also used.

Example

Dim As UByte a = &b00110011
Dim As UByte b = &b01010101
a Imp= b
'' Result    a = &b11011101
Print Bin(a)

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Imp
  • Assignment Operators


Input
Reads a list of values from the keyboard

Syntax

Input [;] ["prompt" ,|; ] variable_list

Parameters

prompt
an optional string literal that is written to the screen as a prompt. If it is followed by a semicolon (;), a question mark ("? ") will be appended to the prompt. If it is followed by a comma, nothing will be appended.
variable_list
a list of comma-separated variables used to hold the values read from the user.

Description

Reads a list values from the keyboard up until the first carriage return. Numerical values are converted from their string representation into the corresponding types in the variable list. Characters are echoed to the screen as they are typed.

If there is more than one value in the input list, then the input line will be split up by scanning for delimiters - commas (,) after strings, or commas and whitespace after numbers. Surrounding whitespace will be trimmed from string values. If an input string has a comma in it, it must be wrapped in quotes ("...") to prevent it being split up.
For inputting to a single string without delimiting, Line Input should be used instead.

The prompt - if any - is written to the screen at the current cursor location, and characters read are echoed to the screen immediately following the prompt. If no prompt is specified, characters are echoed at the current cursor location.

The optional leading semicolon (;) after Input is similar to the optional trailing semicolon in a Print statement: the cursor will remain on the same line after all of the characters have been echoed, otherwise, the cursor will move to the beginning of the next line.

If more values are read than are listed in the variable list, extra values will be ignored; if fewer values are read (i.e. the user presses enter before inputting all values), the remaining variables will be initialized (numeric variables to zero (0), and string variables to the empty string ("")).

Numeric values are converted using methods similar to the procedures Val and ValLng, using the most appropriate function for the number format, converting as many numeric characters as possible.

Input has a limited edit capacity: it allows to use the left and right cursor keys to navigate the text, and to erase or insert characters. If a better user interface is needed, a custom input routine should be used.

Example

Example #1
Dim user_name As String, user_age As Integer

Input "Enter your name and age, separated by a comma: ", user_name, user_age

Print "Your name is " & user_name & ", and you are " & user_age & " years old."

Example #2
Dim As Double a, b
Dim As String yn

Do

    Input   "Please enter a number: ", a
    Input ; "And another: ", b
    Print , "Thank you"
    Sleep 500
    Print
    Print "The total is "; a + b
    Print

    Do
        Input "Would you like to enter some more numbers"; yn
        yn = LCase(yn)
    Loop Until yn = "y" Or yn = "n"

Loop While LCase(yn) = "y"

Differences from QB

  • If the user inputs the wrong number of values, or if it expects a number for a value and gets a string that is not a valid number, then QBASIC issues the message "Redo from start", and does not continue further until it receives a valid input.
  • QB does not treat space as a delimiter when inputting a number from the console.

See also

  • Input #
  • Input()
  • Line Input


Input (File Mode)
Specifies text file to be opened for input mode

Syntax

Open filename for Input [Encoding encoding_type] [Lock lock_type] as [#]filenum

Parameters

filename
file name to open for input
encoding_type
indicates encoding type for the file
lock_type
locking to be used while the file is open
filenum
unused file number to associate with the open file

Description

A file mode used with Open to open a text file for reading.

This mode allows to read sequentially lines of text with Line Input #, or to read comma separated values with Input #.

Text files can't be simultaneously read and written in FreeBASIC, so if both functions are required on the same file, it must be opened twice.

filename must be a string expression resulting in a legal file name in the target OS, without wildcards. The file will be sought for in the present directory, unless the filename contains a path . If the file does not exist, an error is issued. The pointer is set at the first character of the file.

Encoding_type indicates the Unicode Encoding of the file, so characters are correctly read. If omitted, "ascii" encoding is defaulted. Only little endian character encodings are supported at the moment.
    • "utf8",
    • "utf16"
    • "utf32"
    • "ascii" (the default)

Lock_type indicates the way the file is locked for other processes, it is one of:
    • Read - the file can be opened simultaneously by other processes, but not for reading
    • Write - the file can be opened simultaneously by other processes, but not for writing
    • Read Write - the file cannot be opened simultaneously by other processes (the default)

filenum is a valid FreeBASIC file number (in the range 1..255) not being used for any other file presently open. The file number identifies the file for the rest of file operations. A free file number can be found using the FreeFile function.

Example

Dim ff As UByte
Dim randomvar As Integer
Dim name_str As String
Dim age As Integer

'' collect the test data and output to file with Write #
Input "What is your name? ", name_str
Input "What is your age? ", age
Randomize
Print

ff = FreeFile
Open "testfile" For Output As #ff
Write #ff, Int(Rnd*42), name_str, age
Close #ff

'' clear variables
randomvar = 0
name_str = ""
age = 0

'' input the variables, using Input #
ff = FreeFile
Open "testfile" For Input As #ff
Input #ff, randomvar, name_str, age
Close #ff

Print "Random Number was: " & randomvar
Print "Your name is: " & name_str
Print "Your age is: " & age

'File outputted by this sample will look something like this
'(not including the leading comment marker):
'23,"Your Name",19

Differences from QB

See also

  • Input (Console I/O)
  • Input #
  • Input()
  • Append
  • Open
  • Output


Input #
Reads a list of values from a text file

Syntax

Input # filenum, variable_list

Parameters

filenum
a file number of a file or device opened for Input
variable_list
a list of variables used to hold the values read

Description

Reads from a text file through a bound file number a delimiter-separated set of values and writes them in reading order into the variables in variable_list. If a variable is numeric the read value is converted from its string representation into the corresponding type.

Numeric values are converted in a similar way to the procedures Val and ValLng, using the most appropriate function for the number format.

Delimiters may be commas or line breaks. Whitespace is also treated as a separator after numbers. A string including a comma or a whitespace must be surrounded by double quotes.

To read an entire line into a string, use Line Input instead.

Write # can be used to create a file readable with Input #.

Note:
- Using Input # is naturally dedicated to Input Access file mode.
- It is also allowed in Binary/Random Access file mode, but this was never well tested and results may vary.

Example

Dim a As Integer
Dim b As String
Dim c As Single

Open "myfile.txt" For Output As #1
Write #1, 1, "Hello, World", 34.5
Close #1

Open "myfile.txt" For Input As #1
Input #1, a, b, c
Close #1
Print a, b, c

Differences from QB

  • QB has a bug in INPUT # that causes it to read past the end of the line if it does not find a matching end-quote when reading a string. If you are porting QB code that relies upon this bug, you may need to edit your data files to remove newlines from inside quoted strings, or to use a custom function to piece back together the multiline string.

See also

  • Input
  • Input()
  • Line Input #
  • Write #
  • Open
  • Input (File Mode)


Input()
Reads a number of characters from console or file

Syntax

Declare Function Input ( n As Integer ) As String
Declare Function Input ( n As Integer, filenum As Integer ) As String

Usage

result = Input[$]( n [, [#]filenum ] )

Parameters

n
Number of bytes to read.
filenum
File number of a bound file or device.

Return Value

Returns a String of the characters read.

Description

Reads a number of characters from the console, or a bound file/device specified by filenum.

The first version waits for and reads n characters from the keyboard buffer. Extended keys are not read. The characters are not echoed to the screen.

The second version waits for and reads n characters from a file or device. The file position is updated.

Note: Using Input() is dedicated to Input Access file mode, but it is also allowed in Binary/Random Access file mode with some restrictions on the values of n (for random mode, n must be equal to the record length set by the LEN clause (or implicit value) in the OPEN statement).

Example

Print "Select a color by number"
Print "1. blue"
Print "2. red"
Print "3. green"
Dim choice As String
Do
   choice = Input(1)
Loop Until choice >= "1" And choice <= "3"

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • None in the -lang qb dialect.

See also

  • Winput()
  • GetKey
  • Inkey


INSTRUMENT
Defines an instrument or assigns one to a channel.

Syntax

Instrument id, wave_id, envelope_id
Instrument channel, instrument_id

Description

INSTRUMENT gives generated sound a reusable voice. The definition form combines a WAVE slot and an ENVELOPE slot into an instrument id. That instrument can then be selected with VOICE or assigned to a specific channel.

The assignment form changes the instrument used by a channel. After that, NOTE and PLAY commands on that channel use the selected instrument, so the same melody can sound smooth, buzzy, plucked, or noisy depending on the waveform and envelope.

This is the command to use when SOUND and NOTE are working but the default tone is too plain for the program you are writing.

Example

wave 1, 0
envelope 1, 0.01, 0.10, 0.50, 0.20
instrument 1, 1, 1
instrument 0, 1
note 0, "C", 4, 0.25

Notes

  • Use VOICE to set the default instrument without naming a channel every time.

See also

  • Sound Library Reference
  • Generated Sound Commands


InStr
Locates the first occurrence of a substring or character within a string

Syntax

Declare Function InStr ( ByRef str As Const String, [ Any ] ByRef substring As Const String ) As Integer
Declare Function InStr ( ByRef str As Const WString, [ Any ] ByRef substring As Const WString ) As Integer
Declare Function InStr ( ByVal start As Integer, ByRef str As Const String, [ Any ] ByRef substring As Const String ) As Integer
Declare Function InStr ( ByVal start As Integer, ByRef str As Const WString, [ Any ] ByRef substring As Const WString ) As Integer

Usage

first = InStr( [ start, ] str, [ Any ] substring )

Parameters

str
The string where to search.
substring
The substring to find.
start
The position in str at which the search will begin. The first character starts at position 1.

Return Value

The position of the first occurrence of substring in str.

Description

Locates the position of the first occurrence of a substring or character within a string. In the first form of InStr (without start parameter), the search begins at the first character.

Zero (0) is returned if: either substring is not found, either str or substring are empty strings, or start < 1.

If the Any keyword is specified, InStr returns the first occurrence of any character in substring.

Example

' It will return 4
Print InStr("abcdefg", "de")

' It will return 0
Print InStr("abcdefg", "h")

' It will search for any of the characters "f", "b", "c", and return 2 as "b" is encountered first
Print InStr("abcdefg", Any "fbc")

Dim test As String
Dim idx As Integer

test = "abababab"
idx = InStr(test, "b")

Do While idx > 0 'if not found loop will be skipped
    Print """b"" at " & idx
    idx = InStr(idx + 1, Test, "b")
Loop

Platform Differences

  • The wide-character string version of InStr is not supported for DOS target.

Differences from QB

  • QB returns start if search is a zero length string.
  • QB does not support Unicode.

See also

  • InStrRev
  • Mid (Function)

'A Unicode example:
dim text as wstring*20
text = "Привет, мир!"
print instr(text,"ет") ' displays 5


InStrRev
Locates the last occurrence of a substring or character within a string

Syntax

Declare Function InStrRev ( ByRef str As Const String, [ Any ] ByRef substring As Const String, ByVal start As Integer = -1 ) As Integer
Declare Function InStrRev ( ByRef str As Const WString, [ Any ] ByRef substring As Const WString, ByVal start As Integer = -1 ) As Integer

Usage

last = InStrRev( str, [ Any ] substring [, start ] )

Parameters

str
The string where to search.
substring
The substring to find.
start
The position in str at which the search will begin. The first character starts at position 1.

Return Value

The position of the last occurrence of substring in str.

Description

Locates the position of the last occurrence of a substring or character within a string. If start parameter is not given or is less than zero, the search begins at the last character.

Zero (0) is returned if:
    • substring is not found, or
    • either str or substring is an empty strings, or
    • start is zero, or
    • start is greater than the length of str.

If the Any keyword is specified, InStrRev returns the last occurrence of any character in substring.

Example

' It will return 4
Print InStrRev("abcdefg", "de")

' It will return 0
Print InStrRev("abcdefg", "h")

Dim test As String
Dim idx As Integer

test = "abababab"
idx = InStrRev(test, "b")

Do While idx > 0 'if not found loop will be skipped
    Print """b"" at " & idx
    idx = InStrRev(Test, "b", idx - 1)
Loop

Platform Differences

  • The wide-character string version of InStrRev is not supported for DOS target.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Instrrev.

Differences from QB

  • New to FreeBASIC

See also

  • InStr
  • Mid (Function)

'A Unicode example:
dim text as wstring*20
text = "Привет, мир!"
print instrrev(text,"ет") ' displays 5


Int
Returns the floor of a number

Syntax

Declare Function Int ( ByVal number As Single ) As Single
Declare Function Int ( ByVal number As Double ) As Double
Declare Function Int ( ByVal number As Integer ) As Integer
Declare Function Int ( ByVal number As UInteger ) As UInteger

Usage

result = Int( number )

Parameters

number
the floating-point number to round

Return Value

Returns the floor of number, i.e. the largest integer that is less than or equal to it.

Description

Int returns the floor of number. For example, Int(4.9) will return 4.0, and Int(-1.3) will return -2.0. For integer types, the number is returned unchanged.

Note: To return the ceiling of number, i.e. the smallest integer that is greater than or equal to it, one can use -Int( -number ).

The Int unary Operator can be overloaded with user defined types.

Example

Print Int(1.9)  '' will print  1
Print Int(-1.9) '' will print -2

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Fix
  • CInt
  • Operator


Integer
Standard data type: 32-bit or 64-bit signed, same size as SizeOf(Any Ptr)

Syntax

Dim variable As Integer
Dim variable As Integer<bits>

Parameters

bits
A numeric constant expression indicating the size in bits of integer desired. The values allowed are 8, 16, 32 or 64.

Description

Integer is the main data type FreeBASIC uses for integer math and bitwise operations. It is the default type for number literals.

In the first form Integer is a 32-bit or 64-bit signed whole-number data type, depending on the target platform.

If an explicit bit size is given (the second form), a data type is provided that can hold values from -1LL Shl (bits-1) up to (1LL Shl (bits-1)) - 1. The selected data type is Byte for Integer<8>, Short for Integer<16>, Long for Integer<32> and LongInt for Integer<64>.

Example

#ifdef __FB_64BIT__
    Dim x As Integer = &H8000000000000000
    Dim y As Integer = &H7FFFFFFFFFFFFFFF
    Print "Integer Range = "; x; " to "; y
#else
    Dim x As Integer = &H80000000
    Dim y As Integer = &H7FFFFFFF
    Print "Integer Range = "; x; " to "; y
#endif

Dialect Differences

  • In the -lang fb and -lang fblite dialects, the Integer data type is 32-bit or 64-bit depending on target platform
  • In the -lang qb dialect, the Integer data type is 16-bit, regardless of platform.

Differences from QB

  • The ability to select a bit size is new to FreeBASIC
  • The INTEGER type is always 16 bits wide in QB.

See also

  • Long
  • LongInt
  • UInteger
  • CInt
  • Table with variable types overview, limits and suffixes


Is
Clause in the Select Case statement block.

Syntax

Case Is expression

Description

Is specifies that a particular case inside a Select Case block will be evaluated based on an expression including the greater than (>) or less than (<) operator and a value.

Example

See example at Select Case.

See also

  • Select Case
  • Operator Is


Operator Is (Run-Time Type Information)
Check if an object is compatible to a type derived from its compile-time type
(in the context of inheritance)

Syntax

result = expression Is typename

Parameters

expression
The expression to check, an object of a type that is directly or indirectly derived from Object using Extends.
typename
The child type to check for. This type must be directly or indirectly derived from the type of expression (the compile-time type of the object).

Return Value

Returns negative one (-1) if the expression is an object of real-type typename or one of its base-types derived from the expression type, or zero (0) if it's an object of an incompatible type.

Description

The Is operator must be used in conjunction with inheritance in order to check compatibility between objects and types from an inheritance structure extending the built-in Object type.

The Is operator is a binary operator that checks whether an object is compatible to its derived types at run-time. Because Is relies on Run-Time Type Information (RTTI), it can only be used with types that are derived from the built-in Object type using Extends. The compiler disallows using Is for checks that can be solved at compile-time.

The Is operator is successful not only for the real-type (the "lowest"), but also for its base-types, as long as they are still below the type of expression (the compile-time type). In order to determine the real-type, all possibilities from lowest to highest must be checked.

Extending the built-in Object type allows to add an extra hidden vtable pointer field at the top of the Type. The vtable is used to access information for run-time type identification used by the Is operator.

This operator cannot be overloaded.

Example

Type Vehicle Extends Object
    As String Name
End Type

Type Car Extends Vehicle
End Type

Type Cabriolet Extends Car
End Type

Type Bike Extends Vehicle
End Type

Sub identify(ByVal p As Object Ptr)
    Print "Identifying:"

    '' Not a Vehicle object?
    If Not (*p Is Vehicle) Then
        Print , "unknown object"
        Return
    End If

    '' The cast is safe, because we know it's a Vehicle object
    Print , "name: " & CPtr(Vehicle Ptr, p)->Name

    If *p Is Car Then
        Print , "It's a car"
    End If

    If *p Is Cabriolet Then
        Print , "It's a cabriolet"
    End If

    If *p Is Bike Then
        Print , "It's a bike"
    End If
End Sub

Dim As Car ford
ford.name = "Ford"
identify(@ford)

Dim As Cabriolet porsche
porsche.name = "Porsche"
identify(@porsche)

Dim As Bike mountainbike
mountainbike.name = "Mountain Bike"
identify(@mountainbike)

Dim As Vehicle v
v.name = "some unknown vehicle"
identify(@v)

Dim As Object o
identify(@o)

Differences from QB

  • New to FreeBASIC

See also

  • Extends
  • Extends Zstring
  • Extends Wstring
  • Object
  • Is (Select Case)
  • TypeOf


IsDate
Tests if a string can be converted to a Date Serial

Syntax

Declare Function IsDate ( ByRef stringdate As Const String ) As Long

Usage

#include "vbcompat.bi"
result = IsDate( stringdate )

Parameters

stringdate
the string to test

Return Value

Returns non-zero (-1) if the date string can be converted to a Date Serial, otherwise returns zero (0).

Description

Date strings must be in the format set in the regional settings of the OS to be considered valid dates.

IsDate(Date) will return non-zero (-1) only if the regional settings specify the same date format that QB used.

The compiler will not recognize this function unless vbcompat.bi or datetime.bi is included.

Example

#include "vbcompat.bi"

Dim s As String, d As Long

Do
  Print
  Print "Enter a date: "

  Line Input s

  If s = "" Then Exit Do

  If IsDate( s ) = 0 Then
    Print "'"; s; "' is not a valid date"
  Else
    d = DateValue( s )
    Print "year  = "; Year( d )
    Print "month = "; Month( d )
    Print "day   = "; Day( d )
  End If

Loop

Differences from QB

  • New to FreeBASIC

See also

  • Date Serials
  • DateSerial
  • TimeValue
  • DateValue


IsRedirected
Checks whether stdin or stdout is redirected to a file

Syntax

Declare Function IsRedirected ( ByVal is_input As Long = 0 ) As Long

Usage

#include "fbio.bi"
result = IsRedirected( is_input )

Parameters

is_input
A Long indicating the type of information to return.

Return Value

Returns non-zero (-1) if stdin or stdout is redirected, otherwise returns zero (0).

Description

IsRedirected checks whether stdin or stdout is redirected to a file, instead of being connected to the console/terminal as usual.

If is_input is equal to non-zero (-1), IsRedirected checks stdin.
If is_input is equal to zero (0), IsRedirected checks stdout.

Example

'' A Windows based example, just for the use principle
'' Self-sufficient example, using his own .exe file as dummy input file for stdin redirection

#include "fbio.bi"

'' Quotation marks wrapping for compatibility with spaces in path name
Dim As String pathExe = """" & ExePath & """"
Dim As String fileExe = Mid(Command(0), InStrRev(Command(0), "\") + 1)
Dim As String redirection = " < """ & Command(0)
If LCase(Right(Command(0), 4)) = ".exe" Then
  redirection &= """"
Else
  redirection &= ".exe"""
End If

If Command() = "" Then  '' First process without stdin redirection
  '' Check stdin redirection
  Print "First process without stdin redirection: IsRedirected(-1) = "; IsRedirected(-1)
  '' Creation of asynchronous second process with stdin redirected from file.exe
  Shell("start /d " & pathExe & " /b " & fileExe & redirection & " secondprocess")
  '' Waiting for termination of asynchronous second process
  Sleep
ElseIf Command() = "secondprocess" Then  '' Second process with stdin redirection
  '' Check stdin redirection
  Print "Second process with stdin redirection  : IsRedirected(-1) = "; IsRedirected(-1)
End If

Differences from QB

  • New to FreeBASIC.

See also

  • Reset(Streamno)


Kill
Deletes a file from disk / storage media.

Syntax

Declare Function Kill ( ByRef filename As Const String ) As Long

Usage

result = Kill( filename )

Parameters

filename
The filename is the name of the disk file to delete. If the file is not in the current directory, the path must also be given as path/file.

Return Value

Returns zero (0) on success, or non-zero on error.

Description

Kill deletes a file from disk / storage media.

The error code returned by Kill can be checked using Err in the next line. The function version of Kill returns directly the error code as a 32 bit Long.

Example

Dim filename As String = "file.ext"
Dim result As Long = Kill( filename )

If result <> 0 Then Print "error trying to kill " ; filename ; " !"

Platform Differences

On some platforms, Kill may be able to remove folders and read-only files. Whether it succeeds or fails here is not currently defined. It may be necessary to check the attributes of the file you are deleting, and decide accordingly whether you want to try Killing it.

Differences from QB

  • KILL can optionally be used as function in FreeBASIC.

See also

  • Shell
  • RmDir


LBound
Returns the lower bound of an array's dimension

Syntax

Declare Function LBound ( array() As Any, ByVal dimension As Integer = 1 ) As Integer

Usage

result = LBound( array [, dimension ] )

Parameters

array
an array of any type
dimension
the dimension to get lower bound of

Return Value

Returns the lower bound of an array's dimension.

Description

LBound returns the lowest value that can be used as an index into a particular dimension of an array.

Array dimensions are numbered from one (1) to n, where n is the total number of dimensions. If dimension is not specified, LBound will return the lower bound of the first dimension.

If dimension is zero (0), LBound returns 1, corresponding to the lower bound of the array dimensions 1..n. UBound returns n, the number of dimensions, in this case. This can be used to detect the array's number of dimensions.

For any other (non-zero) dimension values outside of the valid range 1..n, LBound returns 0. UBound returns -1 in this case. This can be used to detect whether a certain dimension exists in the array, and also works when used on an empty array which does not have any valid dimensions.

Thus, for empty dynamic arrays, we get:
    • Lbound(array) = 0 and Ubound(array) = -1 (dimension 1 does not exist)
    • Lbound(array, 0) = 1 and Ubound(array, 0) = 0 (zero dimensions)
    • @array(Lbound(array)) = 0 (no data buffer allocated) - Note: No longer supported since fbc 1.20.0

Example

Dim array(-10 To 10, 5 To 15, 1 To 2) As Integer

Print LBound(array) 'returns -10
Print LBound(array, 2) 'returns 5
Print LBound(array, 3) 'returns 1

See also

  • UBound
  • Static
  • Dim
  • ReDim


LCase
Returns a lower case copy of a string

Syntax

Declare Function LCase ( ByRef str As Const String, ByVal mode As Long = 0 ) As String
Declare Function LCase ( ByRef str As Const WString, ByVal mode As Long = 0 ) As WString

Usage

result = LCase[$]( str [ , mode ] )

Parameters

str
String to convert to lowercase.
mode
The conversion mode: 0 = current locale, 1 = ASCII only

Return Value

Lowercase copy of str.

Description

Returns a copy of str with all of the letters converted to lower case.

If str is empty, the null string ("") is returned.

Example

Print LCase("AbCdEfG")

Output:
abcdefg

Platform Differences

  • The wide-character string version of LCase is not supported for DOS target.

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • QB does not support Unicode.

See also

  • UCase


Left
Returns the leftmost substring of a string

Syntax

Declare Function Left ( ByRef str As Const String, ByVal n As Integer ) As String
Declare Function Left ( ByRef str As Const WString, ByVal n As Integer ) As WString

Usage

result = Left[$]( str, n )

Parameters

str
The source string.
n
The number of characters to return from the source string.

Return Value

Returns the leftmost substring from str.

Description

Returns the leftmost n characters starting from the left (beginning) of str. If str is empty, then the null string ("") is returned. If n <= 0 then the null string ("") is returned. If n > len(str) then the entire source string is returned.

Example

Dim text As String = "hello world"
Print Left(text, 5)

will produce the output:

hello
An Unicode example:

Platform Differences

  • DOS does not support the wide-character string version of Left.

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • QB does not support Unicode.

See also

  • Right
  • Mid (Function)

dim text as wstring*20
text = "Привет, мир!"
print left(text, 6) 'displays "Привет"


Len
Returns the length of an expression or data type

Syntax

Declare Function Len ( ByRef expression As String ) As Integer
Declare Function Len ( ByRef expression As ZString ) As Integer
Declare Function Len ( ByRef expression As WString ) As Integer

Declare Operator Len ( ByRef expression As datatype ) As datatype

Declare Function Len ( datatype ) As Integer

Usage

result = Len( expression )
or
result = Len( DataType )

Parameters

expression
An expression of any type.
datatype
A DataType.

Return Value

Returns the size of an expression or DataType (including the data fields of a UDT) in bytes.

Description

Len returns the length of an expression or the size of a DataType, in bytes.

In the first form, if expression is of type String, WString or ZString, the length of the string in characters will be returned. If the expression is of a user defined type, an Operator Len compatible with that data type is called. Otherwise, the size of the expression's data type in bytes is returned.

In the second form, if expression is ZString or WString, the size in bytes of an ASCII or Unicode character is returned, respectively. If datatype is String, the size of the string descriptor type is returned.

If there is both a user defined type and a variable visible with the same name in the current scope, the user defined type takes precedence over the variable. To ensure that the Len takes the variable instead of the user defined type, wrap the argument to Len with parentheses to force it to be seen as an expression. For example Len((variable)).

Note: When used with array names, Len returns the length of the array datatype, but not the total number of its elements.
For clarity, it is recommended that you avoid this potential confusion, and use Len on the array datatype doing Len(Typeof(array)), rather than the array name only doing Len(array).

The Len unary Operator can be overloaded with user defined types.

Example

Print Len("hello world") 'returns "11"
Print Len(Integer) ' returns 4

Type xyz
    a As Integer
    b As Integer
End Type

Print Len(xyz) ' returns 8

Version

  • Before fbc 1.08.0:
Len was not returning the size of the data fields of a UDT.
When a variable from a given namespace was accessed with the namespace's name prefix, the argument to Len had to be wrapped with parentheses to force it to be seen as an expression. For example Len((namespace_name.variable)).

Dialect Differences

  • Len only allows expressions in the -lang qb dialect.
  • Can be used with built-in types and user-defined types in the -lang fb and -lang fblite dialects.

Differences from QB

  • Can be used with built-in types and user-defined types in the -lang fb and -lang fblite dialects.
  • None in the -lang qb dialect.

See also

  • SizeOf


Let
Indicates the assignment operator.

Syntax

Let variable = value
or
Let( variable1 [, variable2 [, ... ]] ) = udt
or
Operator typename.Let ( [ ByRef | ByVal ] rhs As datatype )
statements
end operator

Description

Command intended to help the programmer to distinguish an assignment statement (e.g. Let a = 1) from an equality test (e.g. If a = 1 then ...). As the compiler does not require it, it is usually omitted.

Let can be used as a left-hand side operator to assign the members of a user defined type to multiple variables. See Operator Let() (Assignment)

Let is used with operator overloading to refer the assignment operator. See Operator Let (Assignment)

Example

'' Compile with -lang fblite or qb

#lang "fblite"

' these two lines have the same effect:
Let x = 100
x = 100

Dialect Differences

  • The use of Let to indicate an assignment statement (Let variable = expr) is not allowed in the -lang fb dialect.
  • The UDT to multi-variable Let assignment is only available in the -lang fb dialect.
  • Overloading of operators is not available in the -lang qb and -lang fblite dialects.

Differences from QB

  • None in the -lang fb dialect.
  • The Let operator is new to FreeBASIC.
  • The UDT to multi-variable Let assignment is new to FreeBASIC.

See also

  • Operator =[>] (Assignment)
  • Operator Let (Assignment)
  • Operator Let() (Assignment)
  • Operator


Operator Let (Assign)
Indicates the assignment operator when overloading Operator = (Assignment)

Syntax

{ Type | Class | Union } typename
Declare Operator Let ( [ ByRef | ByVal ] rhs As datatype )
End { Type | Class | Union }

Operator typename.Let ( [ ByRef | ByVal ] rhs As datatype )

Usage

lhs = rhs
or
lhs => rhs

Parameters

typename
name of the Type, Class, or Union.
lhs
The variable to assign to.
rhs
The value to assign.

Description

Let is used to overload the Operator =[>] (Assignment) operator and to distinguish it from the comparison operator Operator = (Equal).

lhs =[>] rhs will assign the rhs to lhs by invoking the Let operator procedure defined in typename.
This includes the case of an object returned from a function by value, by using Function =[>] rhs (or function_identifier =[>] rhs) assignment.

An operator Let (assign) must be defined if the shallow implicit copy is not sufficient. This happens in cases when the object manages dynamically allocated memory or other resources which need to be specially copied (for example if a member pointer points to dynamically allocated memory, the implicit assignment operator will simply copy the pointer value instead of allocate memory and then perform the copy of data).
Note: It is safe to do a check for self-assignment at the top of the Let body (by comparing the address of implicit 'this' instance with the address of 'rhs' parameter) to avoid object destruction if previously allocated memory is first deallocated (see example below).

When the operator Let (assign) is defined for copy assignment, its parameter (the object to clone) can not be passed by value.

Example

Type UDT
  Public:
    Declare Constructor (ByVal zp As Const ZString Ptr)  ''constructor with string initializer
    Declare Operator Let (ByRef rhs As UDT)              ''operator Let (assignment)
    Declare Function getString () As String              ''function to get string
    Declare Destructor ()                                ''destructor
  Private:
    Dim zp As ZString Ptr                                ''private pointer to avoid direct access
End Type

Constructor UDT (ByVal zp As Const ZString Ptr)
  This.zp = CAllocate(Len(*zp) + 1)
  *This.zp = *zp
End Constructor

Operator UDT.Let (ByRef rhs As UDT)
  If @This <> @rhs Then  '' check for self-assignment to avoid object destruction
    Deallocate(This.zp)
    This.zp = CAllocate(Len(*rhs.zp) + 1)
    *This.zp = *rhs.zp
  End If
End Operator

Function UDT.getString () As String
  Return *This.zp
End Function

Destructor UDT ()
  Deallocate(This.zp)
End Destructor

Dim u As UDT = UDT("")
u = Type<UDT>("Thanks to the overloading operator Let (assign)")
Print u.getString
Sleep

Output:
Thanks to the overloading operator Let (assign)
Version

  • Before fbc version 1.20.0, assigning one array was not supported.

Dialect Differences

  • In the -lang qb and -lang fblite dialects, this operator cannot be overloaded.
  • In the -lang qb and -lang fblite dialects, an assignment expression can be preceded by the Let keyword.

Differences from QB

  • None.

See also

  • Let
  • Operator Let() (Assignment)
  • Operator =[>] (Assignment)
  • Operator = (Equal)
  • Coercion and Conversion


Operator Let() (Assignment)
Assigns fields of a user defined type to a list of variables

Syntax

Let( variable1 [, variable2 [, ... ]] ) = UDT_var
or
Let( variable1 [, variable2 [, ... ]] ) => UDT_var

Parameters

variable1 [, variable2 [, ... ]]
Comma separated list of variables to receive the values of the UDT variable's fields.
UDT_var
A user defined type variable.

Description

Assigns the values from the UDT_var variable's fields to the list of variables.

When the UDT Extends a Base, the first variable (variable1) assigned by the operator then corresponds to a Base instance (only the other variables are those to receive the values of the data fields of the UDT).

Union is not supported.

Example

Type Vector3D
    x As Double
    y As Double
    z As Double
End Type

Dim a As Vector3D = ( 5, 7, 9 )

Dim x As Double, y As Double

'' Get the first two fields only
Let( x, y ) = a

Print "x = "; x
Print "y = "; y

Output:
x =  5
y =  7

Type Parent
    Dim As Integer p1, p2
End Type

Type Child Extends Parent
    Dim As Integer c1, c2
End Type

Type GrandChild Extends Child
    Dim As Integer gc1, gc2
End Type

Dim As GrandChild gc = Type(1, 2, 3, 4, 5, 6)

Dim As Integer i1, i2
Dim As Integer j1, j2
Dim As Parent p
Dim As Child c

Let(c, i1, i2) = gc
Print c.p1, c.p2, c.c1, c.c2, i1, i2

Let(p, j1, j2) = gc
Print p.p1, p.p2, j1, j2

Output:
 1             2             3             4             5             6
 1             2             5             6

Dialect Differences

  • Only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Let
  • Operator =[>] (Assignment)
  • Operator Let (Assignment)


Lib
Specifies the library where a sub or function can be found as part of a declaration

Syntax

Declare { Sub | Function } proc_name Lib "libname" [ Alias "symbol_name" ] ( arguments list ) As return_type

Extern "mangling" lib "libname"
declarative statements
end Extern

Type T
As Integer dummy
Declare Constructor Lib "libname" [ Alias "symbol_name" ] ( arguments list )
end Type

Description

In Sub or Function declarations, and also in class method declarations (including constructors and destructors), Lib indicates the library containing the function. Libraries specified in this way are linked in as if #Inclib "Libname" or -l libname had been used.

Lib can also be used with Extern ... End Extern Blocks to specifiy a Lib for all declarations inside.

Example

'' mydll.bas
'' compile with:
''   fbc -dll mydll.bas

Public Function GetValue() As Integer Export
  Function = &h1234
End Function

Declare Function GetValue Lib "mydll" () As Integer

Print "GetValue = &h"; Hex(GetValue())

' Expected Output :
' GetValue = &h1234

Differences from QB

  • New to FreeBASIC

See also

  • Declare
  • #inclib


Line (Graphics)
Draws a line

Syntax

Line [target,] [[STEP]|(x1, y1)]-[STEP] (x2, y2) [, [color][, [B|BF][, style]]]
or
Line - (x2, y2) [, [color][, [B|BF][, style]]]

Parameters

target
specifies buffer to draw on
STEP
indicates that the starting coordinates are relative
(x1, y1)
starting coordinates of the line
STEP
indicates that ending coordinates are relative
(x2, y2)
ending coordinates of the line
color
the color attribute.
B|BF
specifies box or box filled mode
style
line style

Description

Graphics statement that draws a straight line or a box between two points. The action will take place on the current work page set via ScreenSet, or onto the buffer Get/Put buffer if specified.

Line coordinates are affected by custom coordinates system set via Window and View (Graphics) statements, and respect clipping rectangle set by View (Graphics). If a pair of coordinates is preceded by the STEP keyword, the coordinates are assumed to be relative to the last graphics cursor position. If the B flag is specified, a rectangle will be drawn instead of a line, with (x1,y1)-(x2,y2) as the coordinates of the opposite rectangle corners. If BF is specified, a filled rectangle will be drawn.

Color denotes the color attribute, which is mode specific (see Color and Screen (Graphics) for details). If omitted, the current foreground color as set by the Color statement is used.

Style, if specified, allows styled line drawing; its value is interpreted as a 16-bit bitmask, and Line will use it to skip pixel drawing. Starting at (x1,y1), the most significant bit of the style mask is checked: if 1, the pixel is drawn, if 0, it's skipped. This repeats for all the line pixels with the other bits, with the mask being reused when the 16 bits are all checked.

When Line is used as Line - (x2, y2), a line is drawn from the current cursor position to the (x2,y2) coordinates specified by Line. Alternatively, Point can be used to get the current cursor position.

Note: Either chained use or boxes drawn with Line can induce pixels overdrawn at some locations. Thus, the resultant (blended) color of these overdrawn pixels is affected if a transparent color (in conjunction with the GFX_ALPHA_PRIMITIVES option flag) is used.

Example

'' draws a diagonal red line with a white box, and waits for 3 seconds
Screen 13
Line (20, 20)-(300, 180), 4
Line (140, 80)-(180, 120), 15, b
Line - ( 200, 200 ), 15
Sleep 3000

' Draws 2 lines with 2 different line styles in 2 different colors
ScreenRes 320, 240

Line (10, 100)-(309, 140),  4, B, &b1010101010101010 ' red box with dashed border

Line (20, 115)-(299, 115),  9,  , &b1111000011111111 ' blue dashed line
Line (20, 125)-(299, 125), 10,  , &b0000000011110000 ' green dashed line

Sleep

Differences from QB

  • target is new to FreeBASIC

See also

  • Circle
  • Window
  • View (Graphics)


Line Input
Reads one line of input from the keyboard

Syntax

Line Input [;] [promptstring {;|,} ] stringvariable1
or
Line Input [;] promptstring {;|,} stringvariable2 , maxlength

Parameters

promptstring
prompt to display before waiting for input. If it is followed by a semicolon (;), a question mark ("? ") will be appended to the prompt. If it is followed by a comma, nothing will be appended.
stringvariable1
variable-length or fixed_length (and known) string, to receive the line of text
stringvariable2
dereferenced {z|w}string pointer or {z|w}string variable passed by reference (string buffer size unknown for both), to receive the line of text
maxlength
maximum number of characters allowed to be written to the string buffer, including the NULL terminator

Description

Reads a line of text from the keyboard and stores it in a string variable.

The promptstring - if any - is written to the screen at the current cursor location, and characters read are echoed to the screen immediately following the prompt. If no prompt is specified, characters are echoed at the current cursor location.

The optional leading semicolon (;) after Line Input is similar to the optional trailing semicolon in a Print statement: the cursor will remain on the same line after all of the characters have been echoed, otherwise, the cursor will move to the beginning of the next line.

Line Input has a limited edit capacity: it allows to use the left and right cursor keys to navigate the text, and to erase or insert characters. If a better user interface is needed, a custom input routine should be used.

Two syntaxes are available:
    • The first syntax is only allowed when the string buffer size (provided by stringvariable1) is either variable or fixed (and known). For this first syntax, the promptstring parameter is optional.
    • The second syntax with maxlength parameter is only allowed when the string buffer size (provided by stringvariable2) is unknown. This happens for dereferenced ZString/WString pointers, or ZString/WString variables passed by reference. This can used to truncate the text line to be read, or avoid overflowing beyond allocated data of the provided string buffer. For this second syntax, the promptstring parameter is mandatory even if it is empty (use "").

Example

Dim s As String
Line Input "Enter a line"; s
Print "Full line that you entered:"
Print "'"; s; "'"
Print

Const maxlength = 11  '' max 10 characters plus 1 null terminal character
Dim pz As ZString Ptr = CAllocate(maxlength, SizeOf(ZString))
Line Input "Enter a line"; *pz, maxlength
Print "First " & maxlength - 1 & " characters that you entered:"
Print "'"; *pz; "'"
Deallocate(pz)

Version

  • Before fbc 1.10.0, the second syntax (with maxlength parameter) was not supported.

Differences from QB

  • QBASIC only allowed literal strings for the prompt text. FreeBASIC allows any variable or constant string expression.

See also

  • Line Input #
  • Input


Line Input #
Reads one line of text from a file

Syntax

Line Input #file number, string_variable_1
or
Line Input #file number, string_variable_2 , max_length

Parameters

file number
file number of an file opened for Input
string_variable_1
variable-length or fixed_length (and known) string, to receive the line of text
string_variable_2
dereferenced {z|w}string pointer or {z|w}string variable passed by reference (string buffer size unknown for both), to receive the line of text
max_length
maximum number of characters allowed to be written to the string buffer, including the NULL terminator

Description

Reads a line from an open text file (opened for Input through a bound file number) and stores it in a string variable.

A line of text ends at, but does not include the end of line characters. An end of line character may be the LF character (Chr(10)) or the CRLF character pair (Chr(13,10)).

Two syntaxes are available:
    • The first syntax is only allowed when the string buffer size (provided by string_variable_1) is either variable or fixed (and known).
    • The second syntax with max_length parameter is only allowed when the string buffer size (provided by string_variable_2) is unknown. This happens for dereferenced ZString/WString pointers, or ZString/WString variables passed by reference. This can used to truncate the text line to be read, or avoid overflowing beyond allocated data of the provided string buffer.
Note:
- Using Line Input # is naturally dedicated to Input Access file mode.
- It is also allowed in Binary/Random Access file mode, but this was never well tested and results may vary.

Example

Open "myfile.txt" For Output As #1
Print #1, "Hello, World"
Close #1

Dim s As String
Open "myfile.txt" For Input As #1
Line Input #1, s
Close #1
Print "'" & s & "'"

Const maxlength = 6  '' max 5 characters plus 1 null terminal character
Dim pz As ZString Ptr = CAllocate(maxlength, SizeOf(ZString))
Open "myfile.txt" For Input As #1
Line Input #1, *pz, maxlength
Close #1
Print "'" & *pz & "'"
Deallocate(pz)

Version

  • Before fbc 1.10.0, the second syntax (with max_length parameter) was not supported.

Differences from QB

  • None

See also

  • Line Input
  • Input #
  • Open
  • Input (File Mode)


LoByte
Gets the lowest byte of the operand.

Syntax

#define LoByte( expr ) (Cast(UInteger, expr) And &h000000FF)

Usage

result = LoByte( expr )

Parameters

expr
A numeric expression, converted to an UInteger value.

Return Value

Returns the value of the low byte of expr.

Description

This macro converts the numeric expression expr to an UInteger value, then expands to an UInteger representing the value of its least-significant (low) byte.

Example

Dim N As UInteger

'Note there are 16 bits
N = &b1010101110000001
Print "N is                                       "; N
Print "The binary representation of N is          "; Bin(N)
Print "The most significant byte (MSB) of N is    "; HiByte(N)
Print "The least significant byte (LSB) of N is   "; LoByte(N)
Print "The binary representation of the MSB is    "; Bin(HiByte(N))
Print "The binary representation of the LSB is    "; Bin(LoByte(N))
Sleep

The output would look like:
N Is                                       43905
The Binary representation of N Is          1010101110000001
The most significant Byte (MSB) of N Is    171
The least significant Byte (LSB) of N Is   129
The Binary representation of the MSB Is    10101011
The Binary representation of the LSB Is    10000001

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __LOBYTE.

Differences from QB

  • New to FreeBASIC

See also

  • HiByte
  • LoWord
  • HiWord


LOC
Returns the file position where the last file read/write was performed

Syntax

Declare Function LOC ( ByVal filenum As Long ) As LongInt

Usage

result = LOC( filenum )

Parameters

filenum
The file number of an open file.

Return Value

The file position where the last read/write was performed.

Description

Returns the position where the last file read/write was performed.

The position is indicated in records:
In files opened FOR RANDOM the record length specified when file was opened is used
In text files (FOR INPUT|OUTPUT|APPEND, a record length of 128 bytes is supposed.
In files opened for BINARY a 1 byte record length is used.

In FreeBASIC the file position is 1 based, the first record of a file is record 1 (LOC=1 after reading or writing the first record, LOC=0 for the start position in the file).

When used with a serial device, LOC returns the number of bytes waiting to be read from the serial device's input buffer.

Example

Dim b As String

If Open Com ("com1:9600,n,8,1,cs,rs,ds,bin" For Binary As #1) <> 0 Then
  Print "unable to open serial port"
  End
End If

Print "Sending command: AT"

Print #1, "AT" + Chr(13, 10);

Sleep 500,1

Print "Response:"

While( LOC(1) > 0 )
  b = Input(LOC(1), 1)
  Print b;
Wend

Close #1

Differences from QB

  • None

See also

  • LOF
  • EOF
  • Seek (Function)
  • Open


Local
Error handling statement to set the current error handler

Syntax

On Local Error Goto label

Description

The Local clause in an On Error construction allows to define an error handler in the same Sub or Function the On Local Error is in.

Remark: Presently, the Local clause (authorized only inside Sub/Function) is ignored by the compiler, and the error handler should be in the scope of the same procedure the On [Local] Error is in.

Example

'' compile with -lang fblite or qb

#lang "fblite"

Declare Sub foo

foo
Print "ok"
Sleep

Sub foo
  Dim errno As Integer
  On Local Error Goto fail
  Open "xzxwz.zwz" For Input As #1
  On Local Error Goto 0
  Exit Sub
fail:                  ' here starts the error handler
  errno = Err
  Print "Error "; errno      ' just print the error number
  Sleep
End Sub

Differences from QB

  • The LOCAL clause comes from PDS 7.1. QB 4.5 does not allow local error handling.

See also

  • On Error
  • Labels


Locate
Sets the current cursor position

Syntax

Declare Function Locate( row As Long = 0, column As Long = 0, state As Long = -1, start As Long = 0, stop As Long = 0 ) As Long

Usage

Locate [row], [column], [state]

result = Locate( [row], [column], [state] )
new_column = LoByte( result )
new_row = HiByte( result )
new_state = HiWord( result )

Parameters

row
the 1-based vertical character position in the console.
column
the 1-based horizontal character position in the console.
state
the state of the cursor in console-mode only: 0 is off, 1 is on; text cursor is never visible in graphics mode.
start
Ignored. Allowed for -lang qb dialect compatibility only.
stop
Ignored. Allowed for -lang qb dialect compatibility only.

Return Value

Returns a 32 bit Long containing the current cursor position and state. The Low Byte Of The Low Word contains the column, the High Byte Of The Low Word contains the row, and the High Word contains the cursor state.

If any of the row, column or state parameters were just set by the call to Locate, then the return value will reflect these new values, not the previous ones. If any of the parameters were omitted in the call to Locate, then the return value will reflect the current values, which are the same as before the call to Locate.

Locate will attempt to position the cursor at the specified row and column. If the position is beyond the screen extents, cursor will not reposition. And next Print to the screen will continue at last valid cursor position. When printing to the last line of the screen, and the Print statement has a new line character, the screen will scroll and reposition the cursor automatically to the last line, column 1.

Description

Sets the text cursor in both graphics and console modes.

Example

Locate 10
Print "Current line:"; CsrLin

'' Text cursor + mouse tracking
Dim As Long x = 0, y = 0, dx, dy

Cls
Locate , , 1

While Inkey <> Chr(27)
    GetMouse dx, dy
    If( dx <> x Or dy <> y ) Then
        Locate y+1, x+1: Print " ";
        x = dx
        y = dy
        Locate 1, 1: Print x, y, ""
        Locate y+1, x+1: Print "X";
    End If
Wend

Differences from QB

  • The start and stop arguments have no effect in FreeBASIC.
  • QB will raise an error if row or column are beyond the screen extents.

See also

  • CsrLin
  • Pos
  • Print
  • ?


Lock
Restricts read/write access to a file or portion of a file

Syntax

Lock #filenum, record
Lock #filenum, start To end

Parameters

filenum
The file number used to Open the file.
record
The record (Random files) to lock.
start
The first byte position (Binary files) to lock from.
end
The last byte position (Binary files) to lock to.

Description

Lock temporarily restricts access by other threads or programs to a file, or portion of a file, usually to allow safe writing to it.

After modifying the data, an Unlock with the same parameters as the Lock should be issued.

Note: This command does not always work, neither as documented nor as expected. It appears to be broken at the moment.

Example

'' e.g. locking a file, reading 100 bytes, and unlocking it.
'' To run, make sure there exists a file called 'file.ext'
'' in the current directory that is at least 100 bytes.

Dim array(1 To 100) As Integer
Dim f As Integer, i As Integer
f = FreeFile
Open "file.ext" For Binary As #f
Lock #f, 1 To 100
For i = 1 To 100
    Get #f, i, array(i)
Next
Unlock #f, 1 To 100
Close #f

Differences from QB

  • Currently, FB cannot implicitly lock the entire file
  • In Random mode, FB cannot lock a range of records

See also

  • Open
  • Unlock
  • ScreenLock


LOF
Returns the length of an open disk file

Syntax

Declare Function LOF ( ByVal filenum As Long ) As LongInt

Usage

result = LOF( filenum )

Parameters

filenum
The file number of an open disk file.

Return Value

The length in bytes of an open disk file.

Description

Returns the length, in bytes, of a file opened previously with Open using the given filenum.

With Open Com it returns the length of the data pending to be read in the receive buffer.

Example

Dim f As Long
f = FreeFile
Open "file.ext" For Binary As #f
Print LOF(f)
Close #f

Differences from QB

  • None

See also

  • LOC
  • EOF
  • Open


Log
Returns the natural logarithm of a given number

Syntax

Declare Function Log cdecl ( ByVal number As Double ) As Double

Usage

result = Log( number )

Parameters

number
The number to calculate the natural log.

Return Value

Returns the logarithm with the base e (also know as the natural logarithm) of number.

Description

There can be some confusion with this notation given that in mathematics the natural logarithm function is usually denoted LN, while the logarithm of base 10 is often denoted as LOG. FreeBASIC, like most computer programming languages, uses LOG to denote the natural logarithm. The required number argument can be any valid numeric expression greater than zero. If number is zero, FreeBASIC returns a special value representing "-infinity", printing like "-Inf". If number is less than zero, Log returns a special value representing "not defined", printing like "NaN" or "IND", exact text is platform dependent. If number is an uninitialized variable, -infinity is returned.

Log can be overloaded as operator to accept user-defined types.

Example

'Find the logarithm of any base
Function LogBaseX (ByVal Number As Double, ByVal BaseX As Double) As Double
    LogBaseX = Log( Number ) / Log( BaseX )
    'For reference:   1/log(10)=0.43429448
End Function

Print "The log base 10 of 20 is:"; LogBaseX ( 20 , 10 )
Print "The log base 2 of 16 is:"; LogBaseX ( 16 , 2 )

Sleep

The output would look like:
The log base 10 of 20 is: 1.301029995663981
The log base 2 of 16 is: 4

Differences from QB

  • None

See also

  • Exp


Long
Standard data type: 32-bit signed integer

Syntax

Dim variable As Long

Description

32-bit signed whole-number data type. Can hold values from -2147483648 to 2147483647. Corresponds to a signed DWORD.

Example

  Dim x As Long = &H80000000
  Dim y As Long = &H7FFFFFFF
  Print "Long Range = "; x; " to "; y

Output:
Long Range = -2147483648 to  2147483647

See also

  • Integer
  • LongInt
  • ULong
  • Table with variable types overview, limits and suffixes


LongInt
Standard data type: 64 bit signed

Syntax

Dim variable As LongInt

Description

A 64-bit signed whole-number data type. Can hold values from -9 223 372 036 854 775 808 to 9 223 372 036 854 775 807. Corresponds to a signed QWORD.

Example

  Dim x As LongInt = &H8000000000000000
  Dim y As LongInt = &H7FFFFFFFFFFFFFFF
  Print "LongInt Range = "; x; " to "; y

Output:
LongInt Range = -9223372036854775808 to  9223372036854775807

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Longint.

Differences from QB

  • New to FreeBASIC

See also

  • ULongInt
  • CLngInt
  • Table with variable types overview, limits and suffixes


Loop
Control flow statement for looping.

Syntax

Do
[ statement block ]
Loop [ { Until | While } condition ]

Example

See example at Do...Loop.

See also

  • Do...Loop


LoWord
Gets the lowest 16bit word of the operand.

Syntax

#define LoWord( expr ) (Cast(UInteger, expr) And &h0000FFFF)

Usage

result = LoWord( expr )

Parameters

expr
A numeric expression, converted to an UInteger value.

Return Value

Returns the value of the low word of expr.

Description

This macro converts the numeric expression expr to an UInteger value, then expands to an UInteger representing the value of its least-significant (low) 16bit word.

Example

Dim N As UInteger

'Note there are 32 bits
N = &b10000000000000011111111111111111

Print "N is                                       "; N
Print "The binary representation of N is          "; Bin(N)
Print "The most significant word (MSW) of N is    "; HiWord(N)
Print "The least significant word (LSW) of N is   "; LoWord(N)
Print "The binary representation of the MSW is    "; Bin(HiWord(N))
Print "The binary representation of the LSW is    "; Bin(LoWord(N))

Sleep

The output would look like:
N Is                                       2147614719
The Binary representation of N Is          10000000000000011111111111111111
The most significant word (MSW) of N Is    32769
The least significant word (LSW) of N Is   65535
The Binary representation of the MSW Is    1000000000000001
The Binary representation of the LSW Is    1111111111111111

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __LOWORD.

Differences from QB

  • New to FreeBASIC

See also

  • LoByte
  • HiByte
  • HiWord


LPos
Returns the number of characters sent to the printer port in the last LPrint statement.

Syntax

Declare Function LPos ( ByVal printer As Long ) As Long

Usage

result = LPOS(printer)

Parameters

printer
Either 0, 1, 2 or 3. Represents the printer port (LPT#)

Return Value

Returns the number of characters sent.

Description

Used to determine, from the last LPrint, how many characters were sent to the printer port.

Example

' compile with -lang fblite or qb

#lang "fblite"

Dim test As String = "LPrint Example test"

Print "Sending '" + test + "' to LPT1 (default)"
LPrint test
Print "LPT1 last recieved " + Str(LPos(1)) + " characters"
Print "String sent was " + Str(Len(test)) + " characters long"

Sleep

Differences from QB

  • None

See also

  • LPrint


LPrint
Writes text to the default printer.

Syntax

LPrint [ Using formatstring,] [expressionlist] [(, | ;)] ...

Parameters

formatstring
String specifying the output format.
expressionlist
List of variables to output according to the specified format.

Description

Prints expressionlist to the printer attached to the parallel port LPT1, or if it does not exist, to the default printer. To print to a printer different from the default one, use Open Lpt.

The Using clause formats expressionlist according to formatstring. Except an UDT, any data type can be passed to LPrint expressionlist, expressions do not need to be first converted to strings.

Using a comma (,) as separator or in the end of the expressionlist will place the cursor in the next column (every 14 characters), using a semi-colon (;) won't move the cursor. If neither of them are used in the end of the expressionlist, then a new-line will be printed.

Some printers will not print at all until a Chr(12) (End of Page) character is printed.

Internally, FreeBASIC uses the special file number -1 for printing using LPrint. This file number may be safely closed using Close -1. The next use of LPrint will automatically reopen it as needed.

Example

'' Compile with -lang fblite or qb

#lang "fblite"

'' new-line
LPrint "Hello World!"

'' no new-line
LPrint "Hello"; "World"; "!";

LPrint

'' column separator
LPrint "Hello!", "World!"

'' end of page
LPrint Chr$(12)

Differences from QB

  • None

Dialect Differences

  • LPrint is not supported in the -lang fb dialect. In this dialect the printer must be properly opened with Open Lpt and Print # must be used to print.

See also

  • Open Lpt
  • Print
  • ?
  • Print #
  • ? #
  • Write


LSet
Left-justifies a string

Syntax

Declare Sub LSet ( ByRef dst As String, ByRef src As Const String )
Declare Sub LSet ( ByVal dst As WString Ptr, ByVal src As Const WString Ptr )

Usage

LSet dst, src
LSet dst_udt, src_udt

Parameters

dst
String String to receive the data.
src
Source String to get the data.
dst_udt
User defined Type to receive the data.
src_udt
User defined Type to copy the data from.

Description

LSet left justifies text into the string buffer dst, filling the left part of the string with src and the right part with spaces. The string buffer size is not modified.
If text is too long for the string buffer size, LSet truncates characters from the right.

For compatibility with QBasic, LSet can also copy a user defined type variable into another one. The copy is made byte for byte, without any care for fields or alignment. It's up to the programmer to take care for the validity of the result.

Example

Dim buffer As String
buffer = Space(10)
LSet buffer, "91.5"
Print "-[" & buffer & "]-"

Output:
-[91.5      ]-

Type mytype1
    x As Integer
    y As Integer
End Type

Type mytype2
    z As Integer
End Type

Dim a As mytype1 , b As mytype2
b.z = 1234

LSet a, b
Print a.x

Differences from QB

  • In QB, the syntax was LSet dst = src. That syntax is also supported by FB.

See also

  • RSet
  • Space
  • Put (File I/O)
  • MKD
  • MKI
  • MKL
  • MKS


LTrim
Removes surrounding substrings or characters on the left side of a string

Syntax

Declare Function LTrim ( ByRef str As Const String, [ Any ] ByRef trimset As Const String = " " ) As String
Declare Function LTrim ( ByRef str As Const WString, [ Any ] ByRef trimset As Const WString = WStr(" ") ) As WString

Usage

result = LTrim[$]( str [, [ Any ] trimset ] )

Parameters

str
The source string.
trimset
The substring to trim.

Return Value

Returns the trimmed string.

Description

This procedure trims surrounding characters from the left (beginning) of a source string:
    • Substrings matching trimset will be trimmed if specified, otherwise spaces (ASCII code 32) are trimmed.
    • If the Any keyword is used, any character matching a character in trimset will be trimmed.
All comparisons are case-sensitive.

Example

Dim s1 As String = "  101 Things to do."
Print "'" + LTrim(s1) + "'"
Print "'" + LTrim(s1, " 01") + "'"
Print "'" + LTrim(s1, Any " 01") + "'"

Dim s2 As String = "BaaBaaBAA Test Pattern"
Print "'" + LTrim(s2, "Baa") + "'"
Print "'" + LTrim(s2, Any "BaA") + "'"

will produce the output:

'101 Things to do.'
'  101 Things to do.'
'Things to do.'
'BAA Test Pattern'
' Test Pattern'

Platform Differences

  • DOS version/target of FreeBASIC does not support the wide-character version of LTrim.

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • QB does not support specifying a trimset string or the ANY clause.

See also

  • RTrim
  • Trim


MIDI
Controls MIDI output through sfxlib where the target backend supports it.

Syntax

result = Midi Open(device)
Midi Close
result = Midi Close()
result = Midi Play(filename)
Midi Stop
Midi Pause
Midi Resume
result = Midi Send(status, data1, data2)

Description

MIDI is not recorded audio. It is a stream of musical instructions sent to a MIDI device or synthesizer. Those instructions can say things like note on, note off, change instrument, or change controller value.

Midi Open selects a MIDI output device by index. Midi Send sends one short MIDI message using a status byte and two data bytes. For example, &h90 is a common note-on status byte on channel 1, the first data byte is the note number, and the second is velocity.

Midi Play attempts to play a MIDI file where the backend supports file playback. Stop, Pause, Resume, and Close control that MIDI path.

MIDI support varies a lot by operating system and backend. Programs should check return values and continue gracefully if no MIDI device exists.

Example

if midi open(0) >= 0 then
midi send &h90, 60, 100 ' note on: middle C
sleep 250, 1
midi send &h80, 60, 0 ' note off
midi close
end if

Notes

  • Use MUSIC for recorded music files such as WAV or decoded audio.
  • Use PLAY or NOTE for generated music that does not need a MIDI device.

See also

  • DEVICE
  • MUSIC
  • Sound Library Reference


Mid (Statement)
Overwrites a substring of a string with another

Syntax

Declare Sub Mid ( ByRef text As String, ByVal start As Integer, ByVal length As Integer, ByRef expression As Const String )
Declare Sub Mid ( ByVal text As WString Ptr, ByVal start As Integer, ByVal length As Integer, ByVal expression As Const WString Ptr )

Usage

Mid( text, start ) = expression
Or
Mid( text, start, length ) = expression

Parameters

text
The string to work with.
start
The start position in text of the substring to overwrite. The first character starts at position 1.
length
The number of characters to overwrite.

Description

Copies a maximum of length characters of expression into text, starting at start.

If length is not specified, all of expression is copied. The size of the string text is unchanged; if expression is too big, as much of it is copied up to the end of text.

Mid can also be used as a function to return part of another string. See Mid (Function).

Example

Dim text As String

text = "abc 123"
Print text 'displays "abc 123"

' replace part of text with another string
Mid(text, 5, 3) = "456"
Print text 'displays "abc 456"

Differences from QB

  • None

See also

  • Mid (Function)


Mid (Function)
Returns a substring of a string

Syntax

Declare Function Mid ( ByRef str as Const String, ByVal start as integer ) as String
Declare Function Mid ( ByVal str as Const WString Ptr, ByVal start as integer ) as WString
Declare Function Mid ( ByRef str as Const String, ByVal start as integer, ByVal n as integer ) as String
Declare Function Mid ( ByVal str as Const WString Ptr, ByVal start as integer, ByVal n as integer ) as WString

Usage

result = Mid[$]( str, start [, n ] )

Parameters

str
The source string.
start
The start position in str of the substring. The first character starts at position 1.
n
The substring length, in characters.

Description

Returns a substring starting from start in str. If str is empty then the null string ("") is returned. If start <= 0 or start > len(str) then the null string ("") is returned.

In the first form of Mid, all of the remaining characters are returned. In the second form, if n < 0 or n > len(str) - start then all of the remaining characters are returned.

Example

Print Mid("abcdefg", 3, 2)
Print Mid("abcdefg", 3)
Print Mid("abcdefg", 2, 1)

will produce the output:
cd
cdefg
b
A Unicode example:
Wiki: code rendered this way to allow display of the Unicode characters.

Platform Differences

  • DOS does not support the wide-character string versions of Mid.

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • QB does not support Unicode.

See also

  • InStr
  • Mid (Statement)
  • Left
  • Right
  • Asc

dim text as wstring * 20
text = "Привет, мир!"
print mid(text, 6, 4) ' displays "т, м"


Minute
Gets the minute of the hour from a Date Serial

Syntax

Declare Function Minute ( ByVal date_serial As Double ) As Long

Usage

#include "vbcompat.bi"
result = Minute( date_serial )

Parameters

date_serial
the date serial

Return Value

Returns the minute from a variable containing a date in Date Serial format.

Description

The compiler will not recognize this function unless vbcompat.bi is included.

Example

#include "vbcompat.bi"

Dim ds As Double = DateSerial(2005, 11, 28) + TimeSerial(7, 30, 50)

Print Format(ds, "yyyy/mm/dd hh:mm:ss "); Minute(ds)

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials


MKD
Does a binary copy from a Double variable to a String, setting its length to 8 bytes

Syntax

Declare Function MKD ( ByVal number As Double ) As String

Usage

result = MKD[$]( number )

Parameters

number
A Double variable to binary copy to a String.

Return Value

Returns a String with a binary copy of the Double.

Description

Does a binary copy from a Double variable to a String, setting its length to 8 bytes. The resulting string can be read back to a Double by CVD.

This function is useful to write numeric values to buffers without using a Type definition.

Example

Dim n As Double, e As String
n = 1.2345
e = MKD(n)
Print n, CVD(e)

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • None

See also

  • MKI
  • MKL
  • MKS
  • CVD
  • CVI
  • CVL
  • CVS


MkDir
Makes a folder/directory on the local file system

Syntax

Declare Function MkDir ( ByRef folder As Const String ) As Long

Usage

result = MkDir( folder )

Parameters

folder
The new default folder/directory to be created, or a path to the sub-folder/sub-directory to be created.

Return Value

Returns zero (0) on success, and negative one (-1) on failure.

Description

Creates a folder/directoy on the local file system.

If folder is not a simple folder/directory name, but a path to a sub-folder/sub-directory, the parent folder(s)/directory(s) specified in the path must already exist.
(only one folder/directory can be created for each request)

Example

Dim pathname As String = "foo\bar\baz"
Dim result As Long = MkDir( pathname )

If 0 <> result Then Print "error: unable to create folder " & pathname & " in the current path."

Platform Differences

  • Linux requires the filename case matches the real name of the file. Windows and DOS are case insensitive.
  • Path separators in Linux are forward slashes / . Windows uses backward slashes \ but it allows for forward slashes . DOS uses backward \ slashes.

Differences from QB

  • None

See also

  • Shell
  • CurDir
  • ChDir
  • RmDir


MKI
Does a binary copy from an integer variable to a String of the same length as the size of the input variable

Syntax

Declare Function MKI ( ByVal number As Integer ) As String
Declare Function MKI<bits> ( ByVal number As Integer<bits> ) As String

Usage

result = MKI[$]( number )
result = MKI[$]<bits>( number )

Parameters

number
A Integer or Integer<bits> variable to binary copy to a String.

Return Value

Returns a String containing a binary copy of number.

Description

Does a binary copy from an Integer or Integer<bits> variable to a String, setting its length to the number of bytes in the type. The resulting string can be read back to an integer type using CVI or CVI<bits>.

This function is useful to write numeric values to buffers without using a Type definition.

MKI supports an optional <bits> parameter before the argument. If bits is 16, MKShort will be called instead; if bits is 32, MKL will be called; if bits is 64, MKLongInt will be called. The length of the return value and the required number argument type will depend on which function is called. See each function's page for more information.

Example

Dim a As Integer, b As String
a=4534
b=MKI(a)
Print a, CVI(b)

Dialect Differences

  • In the -lang qb dialect, MKI returns a 2-byte-string, since a QB integer is only 16 bits.
  • In the -lang qb dialect, <bits> parameter is not supported.
  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

See also

  • CVI
  • MKShort
  • MKL
  • MKLongInt
  • Integer


MKL
Does a binary copy from a Long variable to a String, setting its length to 4 bytes

Syntax

Declare Function MKL ( ByVal number As Long ) As String

Usage

result = MKL( number )

Parameters

number
A Long variable to binary copy to a String.

Return Value

Returns a String with a binary copy of the Long.

Description

Does a binary copy from a Long variable to a String, setting its length to 4 bytes. The resulting string can be read back to a Long by CVL.

This function is useful to write numeric values to buffers without using a Type definition.

Example

Dim a As Long, b As String
a = 4534
b = MKL(a)
Print a, CVL(b)
Sleep

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • None

See also

  • MKD
  • MKI
  • MKS
  • CVD
  • CVI
  • CVL
  • CVS


MKLongInt
Does a binary copy from a LongInt variable to a String, setting its length to 8 bytes

Syntax

Declare Function MKLongInt ( ByVal number As LongInt ) As String

Usage

result = MKLongInt[$]( number )

Parameters

number
A LongInt variable to binary copy to a String.

Return Value

Returns a String with a binary copy of the LongInt.

Description

Does a binary copy from a LongInt variable to a string, setting its length to 8 bytes. The resulting string can be read back to a longint by CVLongInt

This function is useful to write numeric values to buffers without using a Type definition.

Example

Dim a As LongInt, b As String
a = 4534
b = MKLongInt(a)
Print a, CVLongInt(b)
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Mklongint.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • New to FreeBASIC

See also

  • CVLongInt


MKS
Does a binary copy from a Single variable to a String, setting its length to 4 bytes

Syntax

Declare Function MKS ( ByVal number As Single ) As String

Usage

result = MKS[$]( number )

Parameters

number
A Single variable to binary copy to a String.

Return Value

Returns a String with a binary copy of the Single.

Description

Does a binary copy from a Single variable to a String, setting its length to 4 bytes. The resulting string can be read back to a Single by CVS.

This function is useful to write numeric values to buffers without using a Type definition.

Example

Dim n As Single, e As String
n = 1.2345
e = MKS(n)
Print n, CVS(e)

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • None

See also

  • MKI
  • MKL
  • MKD
  • CVD
  • CVI
  • CVL
  • CVS


MKShort
Does a binary copy from a Short variable to a String, setting its length to 2 bytes

Syntax

Declare Function MKShort ( ByVal number As Short ) As String

Usage

result = MKShort[$](number)

Parameters

number
A Short variable to binary copy to a String.

Return Value

Returns a String with a binary copy of the Short.

Description

Does a binary copy from a SHORT variable to a string, setting its length to 2 bytes. The resulting string can be read back to a Short by CVShort

This function is useful to write numeric values to buffers without using a Type definition.

Example

Dim a As Short, b As String
a = 4534
b = MKShort(a)
Print a, CVShort(b)
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Mkshort.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • In QBasic this function is called MKI.

See also

  • CVShort


Operator Mod (Modulus)
Finds the remainder from a division operation

Syntax

Declare Operator Mod ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer

Usage

result = lhs Mod rhs

Parameters

lhs
The left-hand side dividend expression.
rhs
The right-hand side divisor expression.

Return Value

Returns the remainder of a division operation.

Description

Operator Mod (Modulus) divides two Integer expressions and returns the remainder. Float numeric values are converted to Integer by rounding up or down.

Neither of the operands are modified in any way.

This operator can be overloaded for user-defined types.

Example

Print 47 Mod 7
Print 5.6 Mod 2.1
Print 5.1 Mod 2.8

Output:
5
0
2

This is because:
  • 47 divided by 7 gives a remainder of 5
  • 5.6 is rounded to 6 while 2.1 is rounded to 2. This makes the problem 6 MOD 2 which means 6 divided by 2 which gives a remainder of 0
  • 5.1 is rounded to 5 while 2.8 is rounded to 3. This makes the problem 5 MOD 3 which means 5 divided by 3 which gives a remainder of 2

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Mathematical Functions


Operator Mod= (Modulus And Assign)
Divides a value and assigns the remainder to a variable

Syntax

Declare Operator Mod= ( ByRef lhs As Integer, ByRef rhs As Integer )

Usage

lhs Mod= rhs

Parameters

lhs
The variable to assign to.
rhs
The value to divide lhs by.

Description

This operator divides two values of Integer type and assigns the remainder to its left-hand side (lhs) variable. It is functionally equivalent to:
lhs = lhs Mod rhs

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol 'Mod=>' can be also used.

Example

Dim n As Integer
n = 11
n Mod= 3
'' The result is 2
Print n
Sleep

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Operator Mod (Modulus)
  • Mathematical Functions


Month
Gets the month of the year from a Date Serial

Syntax

Declare Function Month ( ByVal date_serial As Double ) As Long

Usage

#include "vbcompat.bi"
result = Month( date_serial )

Parameters

date_serial
the date

Return Value

Returns the month number from a variable containing a date in Date Serial format.

The month values are in the range 1-12 being 1 for January and 12 for December.

Description

The compiler will not recognize this function unless vbcompat.bi is included.

Example

#include "vbcompat.bi"

Dim a As Double = DateSerial(2005,11,28) + TimeSerial(7,30,50)

Print Format(a, "yyyy/mm/dd hh:mm:ss "); Month(a)

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials


MonthName
Gets the name of a month from its integral representation

Syntax

Declare Function MonthName ( ByVal month As Long, ByVal abbreviate As Long = 0 ) As String

Usage

#include "vbcompat.bi"
result = MonthName( month_number [, abreviate ] )

Parameters

month
the number of the month of the year - 1:January through 12:December
abbreviate
flag to indicate that name should be abbreviated

Return Value

Returns the local operating system language month name from month value 1 to 12.

Description

If abbreviate is true, the month name abbreviation is returned. If omitted or false, the whole name is returned.

The compiler will not recognize this function unless vbcompat.bi or datetime.bi is included.

Example

#include "vbcompat.bi"

Dim ds As Double = DateSerial(2005, 11, 28) + TimeSerial(7, 30, 50)

Print Format(ds, "yyyy/mm/dd hh:mm:ss "); MonthName(Month(ds))

Differences from QB

  • Did not exist in QB. This function appeared in Visual Basic.

See also

  • Date Serials


MUSIC
Loads and controls the current music stream through sfxlib.

Syntax

result = Music Load(filename)
Music Play
result = Music Play()
result = Music Play(filename)
Music Loop
result = Music Loop()
result = Music Loop(filename)
Music Pause
Music Resume
Music Stop
status = Music Status()
position = Music Position()

Description

MUSIC is for longer audio that behaves like a current song or background track. It is different from SFX, which is meant for short reusable sound effects. A program normally has one current music stream at a time.

Music Load reads a file and makes it the current track. Music Play starts the current track, or loads and starts a filename in one step. Music Loop does the same thing but repeats the track. Pause, Resume, and Stop control playback after it has started.

Music Status lets a program ask whether the music is stopped, playing, or paused. Music Position reports the current playback position in frames, which can be useful for diagnostics or simple progress displays.

File playback depends on the decoders and audio backend available on the target. A program should check return values and have a fallback path when a file cannot be opened or decoded.

Example

if music play("theme.wav") < 0 then
print "could not play music"
end if

sleep 2000, 1
music pause
sleep 500, 1
music resume
sleep 500, 1
music stop

Notes

  • Use SFX for short overlapping samples such as clicks, shots, or impacts.
  • Use generated commands such as PLAY or NOTE when you do not want to ship an audio file.

See also

  • SFX
  • DEVICE
  • Sound Library Reference


MultiKey
Detects the status of keys by keyboard scancode.

Syntax

Declare Function MultiKey ( ByVal scancode As Long ) As Long

Usage

result = MultiKey(scancode)

Parameters

scancode
The scan code of the key to check.

Return Value

Returns -1 if the key for the specified scan code is pressed, otherwise returns 0.

Description

MultiKey is a function which will detect the status of any key, determined by scancode, at any time. It will return -1 if the key is pressed, otherwise it will return 0.

The keyboard input buffer is not disabled while you use MultiKey; that is, pressed keys will be stored and subsequently returned by your next call to Inkey or GetKey or Input. This means you have to empty the keyboard input buffer manually when you finish using MultiKey, using something like the following method:
While Inkey <> "": Wend  '' loop until the keyboard input buffer is empty

For FB's built-in functionality of getting keyboard input, see Keyboard Input (Basics).

Keeping Inkey to work while you use MultiKey allows more flexibility and can be useful to detect Chr(255)+"k" combo returned on window close button click, if a windowed graphics mode has been set via the Screen (Graphics) statement. For a list of accepted scancodes, see DOS keyboard scancodes; these are guaranteed to be valid for all FreeBASIC supported platforms.
MultiKey should always work in graphics mode, as long as the screen is Unlocked. Support in the console depends on the platform the program is run on though, and cannot be guaranteed.

Example

#include "fbgfx.bi"
#if __FB_LANG__ = "fb"
Using FB '' Scan code constants are stored in the FB namespace in lang FB
#endif

Dim As Integer x, y

ScreenRes 640, 480

Color 2, 15

x = 320: y = 240
Do
    ' Check arrow keys and update the (x, y) position accordingly
    If MultiKey(SC_LEFT ) And x >   0 Then x = x - 1
    If MultiKey(SC_RIGHT) And x < 639 Then x = x + 1
    If MultiKey(SC_UP   ) And y >   0 Then y = y - 1
    If MultiKey(SC_DOWN ) And y < 479 Then y = y + 1

    ' Lock the page while we work on it
    ScreenLock
        ' Clear the screen and draw a circle at the position (x, y)
        Cls
        Circle(x, y), 30, , , , ,F
    ScreenUnlock

    Sleep 15, 1

    ' Run loop until user presses Escape
Loop Until MultiKey(SC_ESCAPE)

' Clear Inkey buffer
While Inkey <> "": Wend

Print "Press CTRL and H to exit..."

Do
    Sleep 25

    '' Stay in loop until user holds down CTRL and H at the same time
    If MultiKey(SC_CONTROL) And MultiKey(SC_H) Then Exit Do
Loop

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Multikey.

Differences from QB

  • New to FreeBASIC

See also

  • Keyboard scancodes
  • GetMouse
  • GetJoystick
  • Screen (Graphics)
  • Inkey
  • Event
  • ScreenEvent
  • Keyboard Input


MutexCreate
Creates a mutex used for synchronizing the execution of threads

Syntax

Declare Function MutexCreate ( ) As Any Ptr

Usage

result = MutexCreate

Return Value

The Any Ptr handle of the mutex created, or the null pointer (0) on failure.

Description

Mutexes, short for "Mutually Exclusive", are a way of synchronizing shared data within threads. If there is a global variable used by multiple threads (or a local static variable used by a single thread called multiple times), it should be "locked" during its use with a mutex. This halts all threads using MutexLock with that mutex (including the implicit main thread executing main program), until it is unlocked with MutexUnlock.

Mutexcreate creates a mutex, returning a handle which is to be referred to when locking, unlocking, or destroying the mutex. Mutexes created with Mutexcreate should be destroyed when no longer needed or before the end of the program with MutexDestroy.

A mutex is a lock that guarantees three things:
1. Atomicity - Locking a mutex is an atomic operation, meaning that the operating system (or threads library) assures you that if you locked a mutex, no other thread succeeded in locking this mutex at the same time.
2. Singularity - If a thread managed to lock a mutex, it is assured that no other thread will be able to lock the thread until the original thread releases the lock.
3. Non-Busy Wait - If a thread attempts to lock a thread that was locked by a second thread, the first thread will be suspended (and will not consume any CPU resources) until the lock is freed by the second thread. At this time, the first thread will wake up and continue execution, having the mutex locked by it.

Example

See also the ThreadCreate examples.

'Visual example of mutual exclusion between 2 threads by using Mutex:
'the "user-defined thread" computes the points coordinates on a circle,
'and the "main thread" plots the points.
'
'Principle of mutual exclusion
'          Thread#A                XOR                  Thread#B
'.....                                         .....
'MutexLock(mut)                                MutexLock(mut)
'  Do_something#A_with_exclusion                 Do_something#B_with_exclusion
'MutexUnlock(mut)                              MutexUnlock(mut)
'.....                                         .....
'
'Behavior:
'- The first point must be pre-calculated.
'- Nothing prevents that a same calculated point could be plotted several times
'(depends on execution times of the loops between main thread and user thread).
'- Nothing prevents that a calculated point could be not plotted
'(same remark on the loop times).
'
'If you comment out the lines containing "MutexLock" and "MutexUnlock"
'(inside "user-defined thread" or/and "main thread"),
'there will be no longer mutual exclusion between computation of coordinates and plotting of points,
'and many points will not be plotted on circle (due to non coherent coordinates).

'-----------------------------------------------------------------------------------------------------

Type ThreadUDT                                   'Generic user thread UDT
    Dim handle As Any Ptr                        'Any Ptr handle to user thread
    Dim sync As Any Ptr                          'Any Ptr handle to mutex
    Dim quit As Byte                             'Boolean to end user thread
    Declare Static Sub Thread (ByVal As Any Ptr) 'Generic user thread procedure
    Dim procedure As Sub (ByVal As Any Ptr)      'Procedure(Any Ptr) to be executed by user thread
    Dim p As Any Ptr                             'Any Ptr to pass to procedure executed by user thread
    Const False As Byte = 0                      'Constante "false"
    Const True As Byte = Not False               'Constante "true"
End Type

Static Sub ThreadUDT.Thread (ByVal param As Any Ptr) 'Generic user thread procedure
    Dim tp As ThreadUDT Ptr = param                  'Casting to generic user thread UDT
    Do
        Static As Integer I
        MutexLock(tp->sync)                          'Mutex (Lock) for user thread
        tp->procedure(tp->p)                         'Procedure(Any Ptr) to be executed by user thread
        I += 1
        Locate 30, 38
        Print I;
        MutexUnlock(tp->sync)                        'Mutex (Unlock) for user thread
        Sleep 5, 1
    Loop Until tp->quit = tp->True                   'Test for ending user thread
End Sub

'-----------------------------------------------------------------------------------------------------

Type Point2D
    Dim x As Integer
    Dim y As Integer
End Type

Const x0 As Integer = 640 / 2
Const y0 As Integer = 480 / 2
Const r0 As Integer = 200
Const pi As Single = 4 * Atn(1)

Sub PointOnCircle (ByVal p As Any Ptr)
    Dim pp As Point2D Ptr = p
    Dim teta As Single = 2 * pi * Rnd
    pp->x = x0 + r0 * Cos(teta)
    Sleep 5, 1                         'To increase possibility of uncorrelated data occurrence
    pp->y = y0 + r0 * Sin(teta)
End Sub

Screen 12
Locate 30, 2
Print "<any_key> : exit";
Locate 30, 27
Print "calculated:";
Locate 30, 54
Print "plotted:";

Dim Pptr As Point2D Ptr = New Point2D
PointOnCircle(Pptr)                   ' Computation for a first point valid on the circle

Dim Tptr As ThreadUDT Ptr = New ThreadUDT
Tptr->sync = MutexCreate
Tptr->procedure = @PointOnCircle
Tptr->p = Pptr
Tptr->handle = ThreadCreate(@ThreadUDT.Thread, Tptr)

Do
    Static As Integer I
    Sleep 5, 1
    MutexLock(Tptr->sync)   'Mutex (Lock) for main thread
    PSet (Pptr->x, Pptr->y) 'Plotting one point
    I += 1
    Locate 30, 62
    Print I;
    MutexUnlock(Tptr->sync) 'Mutex (Unlock) for main thread
Loop Until Inkey <> ""

Tptr->quit = Tptr->True
ThreadWait(Tptr->handle)
MutexDestroy(Tptr->sync)
Delete Tptr
Delete Pptr

Sleep

See also the similar CondCreate example

Dialect Differences

  • Threading is not allowed in the -lang qb dialect.

Platform Differences

  • The DOS version of FreeBASIC does not allow for threads, as the OS does not support them.
  • In Linux the threads are always started in the order they are created, this can't be assumed in Win32. It's an OS, not a FreeBASIC issue.

Differences from QB

  • New to FreeBASIC

See also

  • MutexDestroy
  • MutexLock
  • MutexUnlock
  • ThreadCreate
  • ThreadWait


MutexDestroy
Destroys a mutex

Syntax

Declare Sub MutexDestroy ( ByVal id As Any Ptr )

Usage

MutexDestroy( id )

Parameters

id
The Any Ptr handle of the mutex to be destroyed.

Description

Mutexdestroy discards a mutex created by MutexCreate. This call should be executed after any threads using the mutex are no longer in use.

See MutexCreate for more general information on mutexes.

Example

See the examples in MutexCreate and also ThreadCreate.

Dialect Differences

  • Threading is not allowed in the -lang qb dialect.

Platform Differences

  • The DOS version of FreeBASIC does not allow for threads, as the OS does not support them.
  • In Linux the threads are always started in the order they are created, this can't be assumed in Win32. It's an OS, not a FreeBASIC issue.

Differences from QB

  • New to FreeBASIC

See also

  • MutexCreate
  • MutexLock
  • MutexUnlock
  • ThreadCreate
  • ThreadWait


MutexLock
Acquires a mutex

Syntax

Declare Sub MutexLock ( ByVal id As Any Ptr )

Usage

MutexLock( id )

Parameters

id
The Any Ptr handle of the mutex to be locked.

Description

Mutexlock halts any other threads using a mutex "handle", generated by MutexCreate, until the handle is unlocked with MutexUnlock.
Such a halted thread has its execution suspended and does not consume any CPU time until the mutex is unlocked.

See MutexCreate for more general information on mutexes.

Example

See also the examples in MutexCreate and also ThreadCreate.

'Example of mutual exclusion for synchronization between 2 threads
'by using 2 Mutexes only (by self lock and mutual unlock):
'The Producer works one time, then the Consumer works one time.
'
'Principle of synchronisation by mutual exclusion
'(initial condition: mut#A and mut#B locked)
'
'          Thread#A              XORs              Thread#B
'Do_something#A_with_exclusion          MutexLock(mut#A)
'MutexUnlock(mut#A)                       Do_something#B_with_exclusion
'.....                                  MutexUnlock(mut#B)
'MutexLock(mut#B)                       .....

'----------------------------------------------------------------------

Dim Shared produced As Any Ptr
Dim Shared consumed As Any Ptr
Dim consumer_id As Any Ptr
Dim producer_id As Any Ptr

Sub consumer ( ByVal param As Any Ptr )
    For i As Integer = 0 To 9
        MutexLock produced
        Print , ",consumer gets:" ; i
        MutexUnlock consumed
        Sleep 5, 1
    Next i
End Sub

Sub producer ( ByVal param As Any Ptr )
    For i As Integer = 0 To 9
        Print "Producer puts:" ; i;
        MutexUnlock produced
        MutexLock consumed
    Sleep 5, 1
Next i
End Sub

produced = MutexCreate
consumed = MutexCreate
If ( produced = 0 ) Or ( consumed = 0 ) Then
    Print "Error creating mutexes! Exiting..."
    Sleep
    End
End If

MutexLock produced
MutexLock consumed

consumer_id = ThreadCreate ( @ consumer )
producer_id = ThreadCreate ( @ producer )
If ( producer_id = 0 ) Or ( consumer_id = 0 ) Then
    Print "Error creating threads! Exiting..."
    Sleep
    End
End If

ThreadWait consumer_id
ThreadWait producer_id

MutexDestroy consumed
MutexDestroy produced

Sleep

' 'Threadcreate' launches one time the user-defined Sub in a separate execution thread
'    (which runs simultaneously with the rest of the main code).
' If you want obtain a periodically display from the thread,
'    you must put (into the thread) a [Do...Loop] block with a 'Sleep x, 1' to adjust the display period
'    and a flag to exit the loop (and terminate the thread).
'
' Warning:
' - Each thread has not its own memory of cursor position, so for that and other reasons, it is mandatory
'      to apply an exclusion between displaying from the main code (main thread) and displaying from the user thread,
'      by using a 'Mutex' ([Mutexlock...Mutexunlock] block).
'   At beginning of each display block both into main thread and user thread,
'      the initial cursor position must also be re-initialized.
' - The input keywords (like keyboard, mouse) cannot be safely run when the screen is locked,
'      therefore a such keyword must be outside of any [Screenlock...Screenunlock] block (outside this block in its own thread,
'      and protected of block of another thread by a 'Mutex').
'
' See below a rustic program, but showing all these constraints:

Dim Shared As Any Ptr sync   '' pointer to Mutex
Dim Shared As Byte quit = 0  '' flag to end user thread
Dim As Any Ptr handle        '' pointer to thread handle

Sub ProcedureThread (ByVal param As Any Ptr)  '' param not used in thread body
    Do
        MutexLock(sync)       '' Mutex for exclusion of displaying
            ScreenLock        '' keyword after Mutexlock
                Locate 1, 70  '' re-initialize cursor position
                Print Date
                Locate , 71
                Print Time;
            ScreenUnlock      '' keyword before Mutexunlock
        MutexUnlock(sync)     '' end exclusion
        Sleep 100, 1          '' ajust display period
    Loop Until quit <> 0      '' test for exit thread
End Sub

Screen 12
Locate 1, 62
Print "Date:"
Locate , 62
Print "Time:";
Locate 15, 20
Print "Mouse (position):"
Locate , 20
Print "Mouse (buttons) :";
Locate 30, 2
Print "<any_key> or <click on window close button>: exit";

sync = MutexCreate                          '' create Mutex (before Threadcreate)
handle = ThreadCreate(@ProcedureThread, 0)  '' launch thread

Dim As String s
Do
    MutexLock(sync)                     '' Mutex for exclusion of displaying
        Dim As Long x, y, b
        GetMouse x, y , , b             '' keyword outside [Screenlock...Screenunlock] and protected by Mutex
        ScreenLock                      '' keyword after Mutexlock
            Locate 15, 37               '' re-initialize cursor position
            Print Using "######"; x; y
            Locate , 43
            Print Using "##"; b;
        ScreenUnlock                    '' Keyword before Mutexunlock
        s = Inkey                       '' keyword outside [Screenlock...Screenunlock] and protected by Mutex
    MutexUnlock(sync)                   '' end exclusion
    Sleep 10, 1                         '' ajust display period
Loop Until s <> ""

quit = Not quit     '' order thread end
ThreadWait(handle)  '' wait for thread end
MutexDestroy(sync)  '' free Mutex

Dialect Differences

  • Threading is not allowed in the -lang qb dialect.

Platform Differences

  • The DOS version of FreeBASIC does not allow for threads, as the OS does not support them.
  • In Linux the threads are always started in the order they are created, this can't be assumed in Win32. It's an OS, not a FreeBASIC issue.

Differences from QB

  • New to FreeBASIC

See also

  • MutexCreate
  • MutexDestroy
  • MutexUnlock
  • ThreadCreate
  • ThreadWait


MutexUnlock
Releases a mutex lock

Syntax

Declare Sub MutexUnlock ( ByVal id As Any Ptr )

Usage

MutexUnlock( id )

Parameters

id
The Any Ptr handle of the mutex to be unlocked.

Description

Mutexunlock releases a mutex "handle" created by MutexCreate, and locked with MutexLock. This allows other threads sharing the mutex to continue execution.

See MutexCreate for more general information on mutexes.

Example

See the examples in MutexCreate and also ThreadCreate.

Dialect Differences

  • Threading is not allowed in the -lang qb dialect.

Platform Differences

  • The DOS version of FreeBASIC does not allow for threads, as the OS does not support them.
  • In Linux the threads are always started in the order they are created, this can't be assumed in Win32. It's an OS, not a FreeBASIC issue.

Differences from QB

  • New to FreeBASIC

See also

  • MutexCreate
  • MutexDestroy
  • MutexLock
  • ThreadCreate
  • ThreadWait


Naked
Write functions without prolog/epilog code

Syntax

{Sub | Function} identifier Naked [calling_convention] ( param_list ) [[ ByRef ] As data_type]
asm_statements
End {Sub | Function}

Parameters

identifier - name of the procedure.
calling_convention - calling convention of the procedure - can be cdecl, pascal, or stdcall
asm_statements - the code in the procedure body. The code for handling parameters and returning values must all be done manually. Note that the methods for doing these can change, depending on the calling convention.
param_list - parameters to be passed to the procedure.
data_type - the data type of the function.

Description

Naked allows the programmer to write procedures without the compiler generating any prolog/epilog code.
This is useful when writing small, fast functions in Asm without any unnecessary overhead (so, no register preservation for such Asm blocks).

Example

'' Naked cdecl function (for fbc 32-bit)
Function subtract_c Naked cdecl _   '' parameters pushed onto call stack in reverse order of declaration
    ( _
        ByVal a As Long, _
        ByVal b As Long _        '' parameter pushed onto stack in first
    ) As Long

    Asm
        mov eax, dword Ptr [esp+4]  '' eax = a
        Sub eax, dword Ptr [esp+8]  '' eax -= b
        ret                         '' return result in eax
    End Asm

End Function

Print subtract_c( 5, 1 ) '' 5 - 1

''---------------------------------------------------------------------------------------------------------------------

'' Naked stdcall function (for fbc 32-bit)
Function subtract_s Naked stdcall _ '' parameters pushed onto call stack in reverse order of declaration
                         _          '' called procedure responsible for removing parameters from stack
                         _          ''   (appending constant to RET instruction specifying number of bytes to release)
    ( _
        ByVal a As Long, _
        ByVal b As Long _        '' parameter pushed onto stack in first
    ) As Long

    Asm
        mov eax, dword Ptr [esp+4]  '' eax = a
        Sub eax, dword Ptr [esp+8]  '' eax -= b
        ret 8                       '' return result in eax and 8 bytes (2 integers) to release
    End Asm

End Function

Print subtract_s( 5, 1 ) '' 5 - 1

''---------------------------------------------------------------------------------------------------------------------

'' Naked pascal function (for fbc 32-bit)
Function subtract_p Naked pascal _  '' parameters pushed onto call stack in same order as declaration
                         _          '' called procedure responsible for removing parameters from stack
                         _          ''   (appending constant to RET instruction specifying number of bytes to release)
    ( _
        ByVal a As Long, _       '' parameter pushed onto stack in first
        ByVal b As Long _
    ) As Long

    Asm
        mov eax, dword Ptr [esp+8]  '' eax = a
        Sub eax, dword Ptr [esp+4]  '' eax -= b
        ret 8                       '' return result in eax and 8 bytes (2 longs) to release
    End Asm

End Function

Print subtract_p( 5, 1 ) '' 5 - 1

'' Naked cdecl function (for fbc 32-bit)
'' plus ecx register preserved in asm block by creating user stack
Function subtract_cp Naked cdecl _      '' parameters pushed onto call stack in reverse order of declaration
    ( _
        ByVal a As Long, _
        ByVal b As Long _            '' parameter pushed onto stack in first
    ) As Long

    Asm
        push ebp                        '' push ebp onto stack   => esp -= 4
        mov ebp, esp                    '' ebp = esp
                                        ''    => create user stack 4 bytes above call stack
        push ecx                        '' push ecx onto user stack   => esp -= 4
        mov eax, dword Ptr [(ebp+4)+4]  '' eax = a   (supplementary offset of +4 bytes only due to 'push ebp')
        mov ecx, dword Ptr [(ebp+8)+4]  '' ecx = b   (supplementary offset of +4 bytes only due to 'push ebp')
        Sub eax, ecx                    '' eax -= ecx
        pop ecx                         '' pop ecx from user stack   => esp += 4
        mov esp, ebp                    '' esp = ebp
        pop ebp                         '' pop ebp from stack   => esp += 4
                                        ''    => discard user stack
        ret                             '' return result in eax
    End Asm

End Function

Print subtract_cp( 5, 1 ) '' 5 - 1

Platform Differences

  • The default calling convention depends on the target platform, thus it is best to specify the expected calling convention explicitly when using Naked.

Differences from QB

  • New to FreeBASIC

See also

  • Asm
  • Calling Conventions
  • Function
  • Sub
  • cdecl
  • pascal
  • stdcall


Name
Renames a file on disk

Syntax

Declare Function Name( ByRef oldname As Const String, ByRef newname As Const String ) As Long

Usage

result = Name( oldname, newname )

Parameters

oldname
Name of an existing file.
newname
New name of the file.

Return Value

Returns zero (0) on success and non-zero on failure.

Description

Renames a file or folder originally called oldname to newname.

The function is not guaranteed to succeed if a file/folder exists with the same name. It may succeed, overwriting the original, or it may fail. For greater control, FileExists could be used to test for an existing file, and Kill could be used to delete an existing file beforehand.

Example

Dim OldName As String
Dim NewName As String
Dim result As Long

OldName = "dsc001.jpg"
NewName = "landscape.jpg"

result = Name( OldName, NewName )
If 0 <> result Then
    Print "error renaming " & oldname & " to " & newname & "."
End If

Differences from QB

  • In QB, NAME required AS rather than a comma between the old and new names. This is because NAME was a language keyword rather than a function.

See also

  • Kill
  • FileExists


Namespace
Declares a namespace block.

Syntax

Namespace identifier [ Alias "aliasname" ]
statements
End Namespace

Parameters

identifier
The name of the namespace (including nested names specifier).
aliasname
An alternate external name for the namespace.

Description

Namespaces allow to group entities like objects (predefined data-types and UDTs including Union and Enum) and procedures (including their declarations) under a name. This way the global scope can be divided in "sub-scopes", each one with its own name.

Whether or not explicitly declared a namespace in a source file, the compiler adds a default namespace. This unnamed namespace, called the global namespace, is present in every file.
Any identifier in the global namespace is available for use in a named namespace (even global symbols with the same name as keywords may be declared inside a namespace).

Namespaces implicitly have public access and this is not modifiable.
A variable declared inside a namespace is always implicitly static and visible throughout the entire program even if the declaration modifier Shared is not specified (static and shared are optional, but this may improve code readability).

Namespaces do not have any effect on the visibility of a define.
It is possible to define a namespace in two or more declarations.

Namespaces are commonly used in libraries where you don't want all the symbols from that library to crowd the user's space (called the global namespace).
For example, if you used the "Forms" library, it might define the Point type for describing an X and Y coordinate, and you might also define it for another purpose. This can be resolved by creating the namespace Forms for the library, and then referring to its Point type as Forms.Point, and yours as just Point.

To access from outside a defined symbol in a namespace, add the namespace identifier followed by a dot as a prefix of the symbol, or bring the namespace symbols into the current scope by means of the Using (Namespaces) statement.
To access duplicated symbols defined as global outside the namespace, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

Note: The parser allows to define anonymous Namespaces (without identifier term), but this is the only similarity with the actual C++ capability: The FB compiler automatically generates multiple separate anonymous Namespaces instead of one only per module in such a case.
The FB anonymous Namespaces are almost unusable because all their declarations are inaccessible, even from the body of the module that contains them. Apart from encapsulating module constructors/destructors also inside, nothing else can be done with them.

Example

Namespace Forms
    Type Point '' A 2D point
        As Integer x
        As Integer y
    End Type
    '' Since we are inside of the namespace, Point resolves to Forms.Point.
    Sub AdjustPoint( ByRef pt As Point, ByVal newx As Integer, ByVal newy As Integer )
        pt.x = newx
        pt.y = newy
    End Sub
End Namespace

Type Point '' A 3D point
    As Integer x
    As Integer y
    As Integer z
End Type

Sub AdjustPoint( ByRef pt As Point, ByVal newx As Integer, ByVal newy As Integer, ByVal newz As Integer )
    pt.x = newx
    pt.y = newy
    pt.z = newz
End Sub

Dim pt1 As Point
AdjustPoint( pt1, 1, 1, 1 )
Dim pt2 As Forms.Point
Forms.AdjustPoint( pt2, 1, 1 )

Namespaces are GCC C++ compatible, the following code aims to test that.
(cpp)
// mylib.cpp
// To compile:
//	g++ -c mylib.cpp -o mylib.o
//	ar rcs libmylib.a mylib.o

#include
#include

namespace mylib
{
    int test()
    {
        return 123;
    }
}
'' test.bas

Extern "c++" Lib "mylib"
    Namespace mylib Alias "mylib"
        Declare Function test() As Integer
    End Namespace
End Extern

Print mylib.test()

Dialect Differences

  • Namespaces are not supported in the -lang qb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Using (Namespaces)


Operator New Expression
Operator to dynamically allocate memory and construct data of a specified type.

Usage

result = New datatype
or
result = New datatype ( initializers, ... )
or
result = New datatype[ count ]

Parameters

datatype
Name of the data type to create.
initializers
Initial value(s) for the variable.
count
Exact number of elements to allocate.

Return Value

A pointer of type datatype to the newly allocated data, or null pointer if the memory allocation failed.

Description

The New Expression operator dynamically allocates memory and constructs a specified data type.

For simple types, like integers, an initial value can be given. For types without constructors, initial values can be specified for each field (either with default initializer at data-field declaration, or with initializer list as in New datatype (initializers, ..) if all type data-fields are numeric primitives only and without any default initializers). For types with at least one constructor, the initialize list (if any) must match an existing constructor. If no initializers are given, the default values for those types will be set.

New[] Expression operator is the (one-dimensional) array-version of the New Expression operator and allocates enough memory for the specified number of objects. The default constructor for the type will be used to set the initial values for each item.

Objects created with New Expression operator must be freed with Delete Statement operator. Object array created with New[] Expression operator must be freed with Delete[] Statement operator, the array-version of Delete Statement operator. You cannot mix and match the different versions of the operators.

Specifying an initial value of Any, as in New datatype (Any) will allocate memory for the type, but not initialize the data. This is only valid on data types that do not have constructors (otherwise for data types with constructors, syntax of simple memory allocation with pointer conversion, like Cptr(datatype Ptr, Allocate(Sizeof(datatype))), can be substituted to the invalid use of New...Any).

Specifying an initial value of Any, as in New datatype[count] {Any} will allocate memory for the array, but not initialize the data. This is only valid on data types that do not have constructors (otherwise for data types with constructors, syntax of simple memory allocation with pointer conversion, like Cptr(datatype Ptr, Allocate(count * Sizeof(datatype))), can be substituted to the invalid use of New...Any).

The total memory, in bytes, to be allocated with New datatype[count] expression is calculated as sizeof(datatype) * count, plus sizeof(uinteger) if there is an implicit or explicit Destructor. The total memory requested in bytes to be allocated must not overflow the value that can be held by a UInteger. The extra uinteger, if allocated, stores the number of elements as part of the allocation, so that Delete Statement can determine the count of destructors to call.

If the memory allocation fails, a null pointer is returned and no constructors are called.

The dynamic memory allocation process part provided by the New Expression operator can be overloaded for user-defined types as a member operator New Overload. The following process part for data construction can never be modified.

Note: Using pointer = New datatype[count] may be unsafe if pointer was declared with a type different from datatype (for sub-type polymorphism purpose for example), because the pointer arithmetic fails to access the elements if the pointer type size is different from the size of datatype (when using Operator [] (Pointer Index) or adding an offset (element number) to the pointer, or even when Delete[] Statement itself (the array-version of Delete Statement) must destroy the elements).

Example

Type Rational
    As Integer numerator, denominator
End Type

' Create and initialize a "rational" and store its address.
Dim p As Rational Ptr = New Rational(3, 4)

' Test if null return pointer
If (p = 0) Then
    Print "Error: unable to allocate memory"
Else
    Print p->numerator & "/" & p->denominator
    ' Destroy the rational and give its memory back to the system.
    Delete p
End If

Sleep

' Allocate memory for 100 integers and store the address of the first one.
Dim p As Integer Ptr = New Integer[100]

' Test if null return pointer
If (p = 0) Then
    Print "Error: unable to allocate memory"
Else
    ' Assign some values to the integers in the array.
    For i As Integer = 0 To 99
        p[i] = i
    Next
    ' Free the entire integer array.
    Delete[] p
End If

Print "Done."
Sleep

'' Example of nested New [] to get a 2-dimentional object array (4*3)

Type UDT
    Dim As Integer N
    Declare Constructor ()
    Declare Destructor ()
End Type

Constructor UDT ()
    Print "Constructor",
End Constructor

Destructor UDT ()
    Print "Destructor",
End Destructor

Dim As UDT Ptr Ptr p = New UDT Ptr [4]  '' New [] allocation for the first dimension:
                                        ''   no internal allocation of extra uinteger because
                                        ''   allocation of array of pointers (to UDT objects with destructor)
For I As Integer = 0 To 3
    p[I] = New UDT [5]                  '' New [] allocations for the last dimension:
                                        ''   internal allocation of an extra uinteger for each New [],
                                        ''   because allocation of an array of UDT objects with destructor
    Print
Next I

For I As Integer = 0 To 3
    For J As Integer = 0 To 4
        p[I][J].N = I * 10 + J  '' assignment of each object array element
    Next J
Next I

Print
For I As Integer = 0 To 3
    For J As Integer = 0 To 4
        Print p[I][J].N,        '' display of each object array element
    Next J
    Print
Next I
Print

For I As Integer = 0 To 3
    Delete [] p[I]  '' Delete [] deallocations for the last dimension
    Print
Next I
Delete [] p         '' Delete [] deallocation for the first dimension)
Print

Sleep

Output example:
Constructor   Constructor   Constructor   Constructor   Constructor
Constructor   Constructor   Constructor   Constructor   Constructor
Constructor   Constructor   Constructor   Constructor   Constructor
Constructor   Constructor   Constructor   Constructor   Constructor

 0             1             2             3             4
 10            11            12            13            14
 20            21            22            23            24
 30            31            32            33            34

Destructor    Destructor    Destructor    Destructor    Destructor
Destructor    Destructor    Destructor    Destructor    Destructor
Destructor    Destructor    Destructor    Destructor    Destructor
Destructor    Destructor    Destructor    Destructor    Destructor
Dialect Differences

  • Only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Delete Statement
  • Placement New
  • New Overload


Operator Placement New
Operator to construct an object at a specified memory address.

Syntax

result = New(address) datatype
or
result = New(address) datatype ( initializers, ... )
or
result = New(address) datatype[ count ]

Parameters

address
the location in memory to construct. the parenthesis are not optional.
initializers
Initial value(s) for the variable.
datatype
name of the data type to construct.
count
Number of elements to construct.

Return Value

A pointer of type datatype to the newly constructed data.

Description

The Placement New operator constructs a specified data type at the specified memory location.

For simple types, like integers, an initial value can be given. For types without Constructors, initial values can be specified for each field (either with default initializer at data-field declaration, or with initializer list as in New datatype (initializers, ..) if all type data-fields are numeric primitives only and without any default initializers). For types with at least one constructor, the initialize list (if any) must match an existing constructor. If no initializers are given, the default values for those types will be set.

Memory is not allocated when using the Placement New operator. Instead, the memory at the specified address is used (the provided memory size must be large enough to contain all the placement).
It is incorrect to call Delete Statement on the address. The proper way is to only call the destructor if one exists (implicitly or explicitly), with syntax as for a member method by using member access operator.
See examples below for proper Placement New operator usage.

Placement New[] operator is the (one-dimensional) array-version of the Placement New operator and constructs the specified number of objects from the specified memory location. The default constructor for the type will be used to set the initial values for each item.

Specifying an initial value of Any, as in New(address)datatype (Any) or New(address)datatype[count] {Any} will not initialize the data. This is only valid on data types that do not have constructors (otherwise for data types with constructors, syntax of simple pointer conversion, like Cptr(datatype Ptr, address), can be substituted to the invalid use of New...Any).

Because it does not provide any dynamic memory allocation process, the Placement New operator (unlike the New Expression operator) does not allow any overloading by a member operator for user-defined types.

Note: Using pointer = New(address)datatype[count] may be unsafe if pointer was declared with a type different from datatype (for sub-type polymorphism purpose for example), because the pointer arithmetic fails to access the elements if the pointer type size is different from the size of datatype (when using Operator [] (Pointer Index) or adding an offset (element number) to the pointer).

Example

'' "placement new" example

Type Rational
    As Integer    numerator, denominator
    Declare Constructor ( ByVal n As Integer, ByVal d As Integer )
    As String ratio = "/"
End Type

Constructor Rational ( ByVal n As Integer, ByVal d As Integer )
    This.numerator = n
    This.denominator = d
End Constructor

Scope

    '' allocate some memory to construct as a Rational
    Dim As Any Ptr ap = CAllocate(Len(Rational))

    '' make the placement new call
    Dim As Rational Ptr r = New (ap) Rational( 3, 4 )

    '' you can see, the addresses are the same, just having different types in the compiler
    Print ap, r

    '' confirm all is okay
    Print r->numerator & r->ratio & r->denominator

    '' delete must not be used with placement new
    '' destroying must be done explicitly if a destructor exists (implicitly or explicitly)
    ''   (in this example, the var-string member induces an implicit destructor)
    r->Destructor( )

    '' we explicitly allocated, so we explicitly deallocate
    Deallocate( ap )

End Scope

Dialect Differences

  • Only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Destructor
  • New Expression


Next
Control flow statement to mark the end of a For...Next loop.

Syntax

Next [ identifier_list ]

Description

Indicates the end of a statement block associated with a matching For statement.

When Next is used on its own without an identifier_list, it closes the most recent For statement block.

identifier_list is optional and may be one or more variable names separated by commas. This form of the Next statement is retained for compatibility with QB. identifier_list, if given, must match the identifiers used in the associated For statements in reverse order, from inner to outer.

Example

For i As Integer = 1 To 10
    For j As Integer = 1 To 2
        ' ...
    Next
Next

For i As Integer = 1 To 10
    For j As Integer = 1 To 2
        ' ...
    Next j
Next i

For i As Integer = 1 To 10
For j As Integer = 1 To 2
    ' ...
Next j,i

Differences from QB

  • ByRef arguments cannot be used as counters.

See also

  • For...Next


Resume Next
Error handling statement to resume execution after a jump to an error handler

Syntax

Resume Next

Description

Resume Next is used in the traditional QB error handling mechanism within an error handler (called by On Error) to return execution to the line after the one that caused the error. Usually this is used to avoid executing the same line and causing the error again.

Resume Next resets the Err value to 0

Example

'' Compile with -lang fblite or qb

#lang "fblite"

Dim As Single i, j

On Error Goto ErrHandler

i = 0
j = 5
j = 1 / i ' this line causes a divide-by-zero error; execution jumps to ErrHandler label

Print "ending..."

End ' end the program so that execution does not fall through to the error handler again

ErrHandler:

Resume Next ' execution jumps to 'Print "ending..."' line, but j is now in an undefined state

Dialect Differences

  • RESUME NEXT is not supported in the -lang fb dialect. Statements can be used in its function form to return an error code
If Open( "text" For Input As #1 ) <> 0 Then
  Print "Unable to open file"
End If

Differences from QB

  • Must compile with -ex or -exx option

See also

  • Err
  • Resume
  • Error Handling


NOISE
Plays generated noise through sfxlib.

Syntax

Noise channel, duration, volume
Noise channel, frequency, duration, volume

Description

NOISE creates an unpitched or partly pitched noisy sound instead of a clean musical tone. It is meant for effects such as hiss, static, wind, simple percussion, explosions, and retro game noise.

The short form selects a channel, duration, and volume. The four-argument form also supplies a frequency or color value, which changes the character of the noise so it can sound brighter, lower, rougher, or more focused depending on the backend and generator.

Because noise is generated by sfxlib, it does not need an external audio file. That makes it useful for quick effects and tests.

Example

noise 0, 0.25, 0.4
noise 1, 1200, 0.25, 0.4
sleep 300, 1

Notes

  • Use SFX when you need a recorded sample instead of generated noise.

See also

  • Sound Library Reference
  • Generated Sound Commands


NOTE
Plays a named musical note.

Syntax

Note name, octave, duration
Note channel, name, octave, duration

Description

NOTE is for musical code where note names are clearer than raw frequencies. Instead of writing SOUND 440, you can write a note name and octave and let sfxlib calculate the pitch.

The name is a string such as "C" or "D#". The octave controls the register: higher octave values produce higher notes, and lower octave values produce lower notes. The duration controls how long the note plays.

The channel form sends the note to a specific sfxlib channel. The shorter form uses the current default channel.

Example

note "C", 4, 0.25
note 1, "D#", 5, 0.25
sleep 300, 1

Notes

  • Use PLAY when you want to write a phrase as one music string instead of one NOTE command per note.

See also

  • Sound Library Reference
  • Generated Sound Commands


Operator Not (Complement)
Returns the bitwise-not (complement) of a numeric value

Syntax

Declare Operator Not ( ByRef rhs As Byte ) As Integer
Declare Operator Not ( ByRef rhs As UByte ) As Integer
Declare Operator Not ( ByRef rhs As Single ) As Integer
Declare Operator Not ( ByRef rhs As Double ) As Integer

Declare Operator Not ( ByRef rhs As T ) As T

Usage

result = Not rhs

Parameters

rhs
The right-hand side expression.
T
Any numeric or boolean type.

Return Value

Returns the bitwise-complement of its operand.

Description

This operator returns the bitwise-complement of its operand, a logical operation that results in a value with bits set depending on the bits of the operand.
(for a boolean type, 'Not false' returns 'true' and 'Not true' returns 'false')

The truth table below demonstrates all combinations of a boolean-complement operation:

Rhs Bit | Result
0 | 1
1 | 0

This operator can be overloaded for user-defined types.

Example

' Using the NOT operator on a numeric value

Dim numeric_value As Byte
numeric_value = 15 '00001111

'Result = -16 =     11110000
Print Not numeric_value

' Using the NOT operator on conditional expressions
Dim As UByte numeric_value1, numeric_value2
numeric_value1 = 15
numeric_value2 = 25

If Not numeric_value1 = 10 Then Print "Numeric_Value1 is not equal to 10"
If Not numeric_value2 = 25 Then Print "Numeric_Value2 is not equal to 25"

' This will output "Numeric_Value1 is not equal to 10" because
' the first IF statement is false.
' It will not output the result of the second IF statement because the
' condition is true.

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Operator Truth Tables


Now
Gets the current system time as a Date Serial

Syntax

Declare Function Now ( ) As Double

Usage

#include "vbcompat.bi"
result = Now

Return Value

Returns a date serial containing the system's date and time at execution time.

Description

As the time is the decimal part of a date serial, if the value of Now is saved to an integer, the time in it will be reset to 00:00:00

The compiler will not recognize this function unless vbcompat.bi is included.

Example

#include "vbcompat.bi"

Dim a As Double = Now()

Print Format(a, "yyyy/mm/dd hh:mm:ss")

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials


Object
Built-in type providing run-time type information

Syntax

Type Object
As fb_BaseVT Ptr vtable_ptr
Declare Constructor()
End Type

Usage

Type typename Extends Object
End Type

Dim variable As Object

Description

Object is a built-in type which provides Run-Time Type Information (RTTI) for all types derived from it using Extends, allowing them to be used with Operator Is, and to support Virtual and Abstract methods.

Extending the built-in Object type allows to add an extra hidden vtable pointer field at the top of the Type. The vtable is used to dispatch Virtual and Abstract methods and to access information for run-time type identification used by Operator Is.

Example

See the Operator Is page, the Virtual and Abstract pages.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Object.

Differences from QB

  • New to FreeBASIC

See also

  • Extends
  • Extends Zstring
  • Extends Wstring
  • Operator Is
  • Virtual
  • Abstract


Oct
Converts a number to octal representation

Syntax

Declare Function Oct ( ByVal number As UByte ) As String
Declare Function Oct ( ByVal number As UShort ) As String
Declare Function Oct ( ByVal number As ULong ) As String
Declare Function Oct ( ByVal number As ULongInt ) As String
Declare Function Oct ( ByVal number As Const Any Ptr ) As String

Declare Function Oct ( ByVal number As UByte, ByVal digits As Long ) As String
Declare Function Oct ( ByVal number As UShort, ByVal digits As Long ) As String
Declare Function Oct ( ByVal number As ULong, ByVal digits As Long ) As String
Declare Function Oct ( ByVal number As ULongInt, ByVal digits As Long ) As String
Declare Function Oct ( ByVal number As Const Any Ptr, ByVal digits As Long ) As String

Usage

result = Oct[$]( number [, digits ] )

Parameters

number
A number or expression evaluating to a number. A floating-point number will be converted to a LongInt.
digits
Desired number of digits in the returned string.

Return Value

A string containing the unsigned octal representation of number.

Description

Returns the unsigned octal string representation of number. Octal digits range from 0 to 7.

If you specify digits > 0, the result string will be exactly that length. It will be truncated or padded with zeros on the left, if necessary.

The length of the returned string will not be longer than the maximum number of digits required for the type of number (3 characters for Byte, 6 for Short, 11 for Long, and 22 for LongInt)

If you want to do the opposite, i.e. convert an octal string back into a number, the easiest way to do it is to prepend the string with "&O", and convert it to an integer type, using a function like CInt, similarly to a normal numeric string. E.g. CInt("&O77")

Example

Print Oct(54321)
Print Oct(54321, 4)
Print Oct(54321, 8)

will produce the output:

152061
2061
00152061

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • In QBASIC, there was no way to specify the number of digits returned.
  • The size of the string returned was limited to 32 bits, or 11 octal digits.

See also

  • Bin
  • Hex
  • ValInt
  • ValLng


OCTAVE
Sets or returns the default musical octave.

Syntax

Octave octave
octave = Octave()

Description

OCTAVE sets the default octave for generated musical notes. Higher octave numbers produce higher notes; lower octave numbers produce lower notes.

This is mainly useful with NOTE and PLAY. If a note command or PLAY string does not provide its own octave change, it can use the current default set by OCTAVE.

Calling OCTAVE as a function returns the current default octave.

Example

octave 4
note "C", octave(), 0.25
play "c d e f"

Notes

  • Use NOTE with an explicit octave when a single note should not depend on the current default.

See also

  • Sound Library Reference
  • Generated Sound Commands


OffsetOf
Returns the offset of a field within a type.

Syntax

#define OffsetOf(typename, fieldname) CInt( @Cast( typename Ptr, 0 )->fieldname )

Usage

result = OffsetOf( typename, fieldname )

Parameters

typename
Name of the type as defined using the Type...End Type statements.
fieldname
Name of the field as defined within the type (or within the base types for a derived type).

Description

For a non-derived type, OffsetOf will return the location fieldname as offset in bytes from the beginning of typename.

For a derived type, OffsetOf will return the location fieldname as offset in bytes from the beginning of its highest base type.
Note: if a member of the base type is overridden by a new member, the offset of the old member cannot be accessed from the derived type.

Example

Type MyType
  x As Single
  y As Single
  Union
    b As Byte
    i As Integer
  End Union
End Type

Print "OffsetOf x = "; OffsetOf(MyType, x)
Print "OffsetOf y = "; OffsetOf(MyType, y)
Print "OffsetOf b = "; OffsetOf(MyType, b)
Print "OffsetOf i = "; OffsetOf(MyType, i)

Output
OffsetOf x =  0
OffsetOf y =  4
OffsetOf b =  8
OffsetOf i =  8

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Offsetof.

Differences from QB

  • New to FreeBASIC

See also

  • Type...End Type
  • SizeOf


On Error
Error handling statement to set the current error handler

Syntax

On [Local] Error Goto label

Parameters

label
Label to jump to when an error occurs

Description

On Error triggers a jump to an error handler when an error occurs. Such errors can be triggered by built-in statements such as Open, or when the Error statement is used.

Note: The error checking for built-in statements is only enabled if the program is compiled with one of the -e, -ex or -exx options. On Error remains working with Error even when none of these options are used.

On Local Error can be used to specify a local error handler inside a procedure. This allows for specialized per-procedure error handling and will override the global error handler, if any.
Without Local, the handler must be in the main part of the module.
Remark: Presently, the Local clause is ignored by the compiler.

On Error Goto 0 deactivates the current error handler.

Example

'' Compile with QB (-lang qb) dialect

'$lang: "qb"

On Error Goto errorhandler
Error 24 '' simulate an error
Print "this message will not be seen"

errorhandler:
n = Err
Print "Error #"; n; "!"
End

'' compile as: fbc onerror.bas -ex

#lang "fblite"

Function hFileExists( filename As String ) As Integer Static
    Dim f As Integer

    hFileExists = 0

    On Local Error Goto exitfunction

    f = FreeFile
    Open filename For Input As #f

    Close #f

    hFileExists = -1

exitfunction:
    Exit Function
End Function

    Print "File exists (0=false): "; hFileExists( Command )

    On Error Goto errhandler
    Error 1234
    Print "back from resume next"
    End 0

errhandler:
    Print "error number: " + Str( Err ) + " at line: " + Str( Erl )
    Resume Next

Differences from QB

  • QB has no LOCAL clause and requires the label to be in the main part of the module.

See also

  • Error
  • Local
  • Err
  • Runtime Error Codes
  • Error Handling
  • Labels


On...Gosub
Calls a label based on an expression

Syntax

On expression GoSub label1[, ...]

Description

Branches to different labels depending on the value of expression. An expression value of 1 will branch to the first label, a value of 2 to the second, etc. If the value of expression is zero (0) or greater than the number of items in the list, execution continues on the next statement following the On...Gosub.

This statement behaves exactly like GoSub and execution may return to the statement following the On...Gosub using Return.

It is recommended that the structured Select Case conditional statement be used instead of On...Gosub.

Example

'' Compile with -lang qb

'$lang: "qb"

choice = 3
On choice GoSub labela, labelb, labelc
Print "Good bye."
End

labela:
Print "choice a"
Return

labelb:
Print "choice b"
Return

labelc:
Print "choice c"
Return

Dialect Differences

  • Only available in the -lang qb and -lang fblite dialects.
  • On Gosub support is disabled by default in the -lang fblite unless the Option Gosub statement is used.

Differences from QB

  • FreeBASIC does not generate a run-time error if expression is negative or greater than 255.

See also

  • Select Case
  • On...Goto
  • GoSub
  • Return (From Gosub)
  • Option Gosub
  • Labels


On...Goto
Jumps to a label based on an expression.

Syntax

On expression Goto label1[, ...]

Description

Branches to different labels depending on the value of expression. An expression value of 1 will branch to the first label, a value of 2 to the second, etc. If the value of expression is zero (0) or greater than the number of items in the list, execution continues on the next statement following the On...Goto.

It is recommended that the structured Select Case conditional statement be used instead of On...Goto.

Example

Dim choice As Integer

Input "Enter a number: ", choice

On choice Goto labela, labelb, labelc

labela:
Print "choice a"
End

labelb:
Print "choice b"
End

labelc:
Print "choice c"
End

Differences from QB

  • FreeBASIC does not generate a run-time error if expression is negative or greater than 255.

See also

  • Select Case
  • On...Gosub
  • Goto
  • Labels


Open
Opens a disk file for reading or writing using file operations

Syntax

Open filename For Input [encoding_type] [lock_type] As [#]filenumber
Open filename For Output [encoding_type] [lock_type] As [#]filenumber
Open filename For Append [encoding_type] [lock_type] As [#]filenumber

Open filename For Binary [access_type] [lock_type] As [#]filenumber
Open filename For Random [access_type] [lock_type] As [#]filenumber [Len = record_length]

Usage

result = Open( filename[,] For {Input|Output|Append}[,] As filenumber )
or
result = Open( filename[,] For Binary[,] Access {Read|Write}[,] As filenumber )
or
result = Open( filename[,] For Random[,] Access {Read|Write}[,] As filenumber [[,] Len = record_length] )
or
Open filename For {Input|Output|Append} As filenumber
or
Open filename For Binary Access {Read|Write} As filenumber
or
Open filename For Random Access {Read|Write} As filenumber [Len = record_length]

Parameters

filename
A string value of the name of the disk file to open. Relative file paths are relative to the current directory (see CurDir).
encoding_type
The encoding to be used when reading or writing text, can be one of:
      • Encoding "ascii" (ASCII encoding is used, default)
      • Encoding "utf8" (8-bit Unicode encoding is used)
      • Encoding "utf16" (16-bit Unicode encoding is used)
      • Encoding "utf32" (32-bit Unicode encoding is used)
access_type
The type of access requested by the calling process.
      • Access [Read] [Write] (both read and write access can be used, which is the default)
lock_type
Imposes restrictions on disk file access from other processes (threads or programs), can be either:
      • Shared (the file can be freely accessed by other processes)
      • Lock [Read] [Write] (both read and write access can be denied to other processes)
filenumber
An available file number to bind to the disk file, which can be found with FreeFile.
record_length
The size, in bytes, of each record read from or written to the disk file. The default is 128.

Return Value

In the first usage, Open() returns a 32 bit Long value: zero (0) on success and a non-zero error code otherwise.

Description

Opens a disk file for reading and/or writing. The file number file_num is bound to the file on disk, for use in subsequent file operations, such as Input # and Lock. The next available file number can be retrieved with FreeFile.

The Input, Output and Append file modes open disk files for sequential text I/O, useful for reading or writing plain text files:
    • When the Input mode is specified, only reading file operations can be used, like Line Input # and Get #. If the disk file does not exist a runtime error will be thrown.
    • The Append mode specifies that only writing operations can be used, like Print # and Put #. Writing operations will take place at the end of the disk file if it exists, preserving the existing data.
    • The Output mode is like the Append mode, except that if the file exists then its contents are deleted and its length reset to zero before writing.

The Input, Output and Append file modes also allow selection of a character encoding to be used when reading from or writing text to the disk file. ASCII or a Unicode encoding may be specified (see the description of the encoding_type parameter above).

The Binary and Random file modes open disk files for random-access reading or writing of arbitrary sized binary data:
    • The Binary file mode allows reading and writing of simple data type values, like Byte or LongInt, using binary read or write operations, like Get #. LOC and Seek are among the procedures used for reading and writing to arbitrary locations in the disk file.
    • The Random file mode is similar to Binary, except that file I/O operations always use a constant data size when reading or writing.

By default, the Binary and Random file modes allow both reading and writing operations on the opened disk file, but this can be changed by specifying an access type (see the description for the access_type parameter above). When opening an existing file in Binary or Random file mode, and with the only Write access type specified, all data in the file are previously deleted at opening, before any other operation.

For any file mode, access to the opened disk file can be restricted or granted to other threads or programs by specifying a lock type (see the description for the lock_type parameter above). If no lock type is specified, other threads of the current program can freely open the disk file (Shared), while other programs cannot (Lock Read Write). Lock and Unlock can be used to temporarily restrict access to parts of a file.

The error code returned by Open can be checked using Err in the next line. The function version of Open returns directly the error code as a 32 bit Long.

Example

' Create a string and fill it.
Dim buffer As String, f As Integer
buffer = "Hello World within a file."

' Find the first free file number.
f = FreeFile

' Open the file "file.ext" for binary usage, using the file number "f".
Open "file.ext" For Binary As #f
If Err>0 Then Print "Error opening the file":End

' Place our string inside the file, using number "f".
Put #f, , buffer

' Close all open files.
Close

' End the program. (Check the file "file.ext" upon running to see the output.)
End

'OPEN A COM PORT
Open Com "COM1:9600,N,8,1" As #1
If Err>0 Then Print "The port could not be opened."

'COM1, 9600 BAUD, NO PARITY BIT, EIGHT DATA BITS, ONE STOP BIT

'function version of OPEN
If Open("file.ext" For Binary Access Read As #1) = 0 Then

    Print "Successfully opened file"

    '' ...

    Close #1

Else

    Print "Error opening file"

End If

Platform Differences

  • Linux requires the filename case matches the real name of the file. Windows and DOS are case insensitive.
  • Path separators in Linux are forward slashes /. Windows uses backward slashes \ but it allows for forward slashes /. DOS uses backward slashes \.
  • On Windows, a file number used in a dynamic link library is not the same as an identical file number used in the main program. File numbers can not be passed or returned and then used between a DLL and an executable.
  • If you try to open a directory on Linux, the Open command will succeed.

Differences from QB

  • Using MS-DOS device names to open streams or hardware devices ("LPT:", "SCR:", etc.) is supported only in the -lang qb dialect; for other modes FreeBASIC's new composite keywords must be used: see Open Com, Open Cons, Open Err, Open Pipe, Open Lpt, Open Scrn, Open Tcp, and Open Tcp Server.
  • Open can be called as a function that returns an error code.

Dialect Differences

  • The -lang qb dialect supports the old GW-BASIC-style syntax OPEN mode_string, #filenumber, filename [length] with mode_string being "I" for input, "O" for output, "A" for append, "R" for random, "B" for binary.

See also

  • Err (and a list of error codes)
  • Close
  • FreeFile
  • Open Cons, Open Err, Open Pipe, Open Lpt, Open Com, Open Scrn, Open Tcp, Open Tcp Server


Open Com
Opens a serial port for input and output, using it as if it were file operations.

Syntax

Declare Function Open Com ( byref options As String, As filenumber As Long ) As Long

Usage

result = Open Com( options[,] As[#] filenumber )

Parameters

options
A String containing options used in controlling the port.
filenumber
The file number to bind to the port.

Return Value

Open Com() returns a 32 bit Long: a zero (0) on success and a non-zero error code otherwise.

Description

This command opens a serial port of the PC, allowing to send and receive data by using the normal file commands as Print #, Input #, Get #, ...

The main parameter is a String that describes, at the very least, which communications port to open. It has the format:

"Comn: [ baudrate ][ , [ parity ][ , [ data_bits ][ , [ stop_bits ][ , [ extended_options ]]]]]"
where,
n
Com port to open. "1", "2", "3", "4", etc. Some platforms will support more serial ports depending on how the operating system is configured. Where n is not given, "COM:" will map to "COM1:", except on Linux where "COM:" maps to "/dev/modem"
baudrate
"300" (default), "1200", ..., etc.
parity
"N" (none), "E" (even, default), "O" (odd), "S" (space), "M" (mark), "PE" (QB-quirk: checked, even parity)
data_bits
"5", "6", "7" (default) or "8".
stop_bits
"1", "1.5" or "2". (default value depends on baud rate and data bits, see table below)
Condition | Default number of stop bits
baud rate <= 110 and data bits = 5 | 1.5
baud rate <= 110 and data bits >= 6 | 2
baud rate > 110 | 1

extended_options
Miscellaneous options. (See table below)
Option | Action
'CSn' | Set the CTS duration (in ms) (n>=0), 0 = turn off, default = 1000
'DSn' | Set the DSR duration (in ms) (n>=0), 0 = turn off, default = 1000
'CDn' | Set the Carrier Detect duration (in ms) (n>=0), 0 = turn off
'OPn' | Set the 'Open Timeout' (in ms) (n>=0), 0 = turn off
'TBn' | Set the 'Transmit Buffer' size (n>=0), 0 = default, depends on platform
'RBn' | Set the 'Receive Buffer' size (n>=0), 0 = default, depends on platform
'RS' | Suppress RTS detection
'LF' | Communicate in ASCII mode (add LF to every CR) - Win32 doesn't support this one
'ASC' | same as 'LF'
'BIN' | The opposite of LF and it'll always work
'PE' | Enable 'Parity' check
'DT' | Keep DTR enabled after CLOSE
'FE' | Discard invalid character on error
'ME' | Ignore all errors
'IRn' | IRQ number for COM (only supported (?) on DOS)

All items except for the COM port are optional. The order of baudrate, parity, data_bits, stop_bits is fixed. Any skipped fixed item ( baudrate, etc...) must be empty.

The error code returned by Open Com can be checked using Err in the next line. The function version of Open Com returns directly the error code as a 32 bit Long.

Example

Open Com "COM1:9600,N,,2" As 1

Opens COM1 with 9600 baud, no parity, 7 data bits and 2 stop bits.

Open Com "COM1:115200" As 1

Opens COM1 with 115200 baud, "even" parity, 7 data bits and 1 stop bits.

Platform Differences

  • On the Windows platform
"COM:" maps to "COM1:"
baud rate must be an unsigned 32-bit integer, and is passed on directly to the operating system.
  • On the Linux platform
"COM:" maps to "/dev/modem"
"COM1:" maps to "/dev/ttyS0"
"COM2:" maps to "/dev/ttyS1", etc
"/dev/xyz:" maps to "/dev/xyz", etc
baud rate must be one of 50, 150, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000
in fbc >= 1.09.0, baud rate can also be one of 1500000, 2000000, 2500000, 3000000, 3500000, 4000000
the baud rates supported depend on the system where the fbc compiler itself was compiled and built
  • The DOS serial driver is experimental and can access COM ports 1 to 4
It uses the following base io and IRQ's as default:
COM1 - &h3f8 - IRQ4
COM2 - &h2f8 - IRQ3
COM3 - &h3e8 - IRQ4
COM4 - &h2e8 - IRQ3
An alternate IRQ can be specified using the the "IRn" protocol option where n is 3 through 7 (first PIC). Since fbc 1.10.0, n can also be 8 through 15 (second PIC).
Currently not supported: IRQ's on the slave PIC, alternate base I/O addresses, Timeouts and most errors as detected in QB, hardware flow control, FIFO's.
"COM:" maps to "COM1:"
baud rate must be between 50 and 115200
Dialect Differences

  • In the -lang qb dialect the old syntax OPEN "COMx:... is supported.

Differences from QB

  • In QB the syntax was OPEN "COMx:[baudrate] [,parity, [data_bits, [stop_bits, [extended_options]]]]" FOR INPUT|OUTPUT|RANDOM AS [#] n
  • In QB, only "COM1:" and "COM2:" are supported. In FreeBASIC, any correctly configured serial port may be used.

See also

  • Open


Open Cons
Opens the console's standard input (stdin) or output (stdout) streams, using it as if it were file operations.

Syntax

Open Cons As [#]filenumber
Open Cons For Input As [#]filenumber
Open Cons For Output As [#]filenumber

Usage

result = Open Cons( [For {Input|Output}[,]] As filenumber )
(or using the QB-like syntax,)
Open Cons [For {Input|Output}] As filenumber

Parameters

filenumber
An available file number to bind to the stdin or stdout stream, which can be found with FreeFile.

Return Value

In the first usage, Open Cons() returns a 32 bit Long: a zero (0) on success and a non-zero error code otherwise.

Description

Open Cons opens the console's stdin or stdout streams for reading or writing. A file number is bound to the stream, which is used in subsequent file operations, such as Input #. An available file number can be retrieved with FreeFile.

The Input file mode opens the stdin stream for reading file operations, such as Line Input #, while the Output file mode opens the stdout stream for writing file operations, such as Print #. The Output file mode is the default if not specified.

The stdin and stdout streams are the ones used when the calling process' input or output is redirected (piped) by OS commands, or when it is opened with Open Pipe.

To open both the stdin and stdout streams for file operations, a process must use multiple file numbers.

The normal screen commands, such as Color and Locate, do not work in this mode, because they do not accept a file number.
The Tab keyword, regardless of the given column number, is always interpreted as a simple comma (,) (next output will take place at the next 14 column boundary).

The error code returned by Open Cons can be checked using Err in the next line. The function version of Open Cons returns directly the error code as a 32 bit Long.

Warning: Presently, Open Cons does not work as described above. Without any file mode specifier, a runtime error 1 (illegal function call) occurs. With the Input file mode specifier, the only input mode is well supported. But with the Output file mode specifier, input and output modes are simultaneously supported.

Runtime errors:
Open Cons throws one of the following runtime errors:

(1) Illegal function call
    • filenumber was not free at the time. use FreeFile to ensure that filenumber is free.

Example

Dim a As String

Open Cons For Input As #1
Open Cons For Output As #2

Print #2,"Please write something and press ENTER"
Line Input #1,a
Print #2, "You wrote : ";a

Close
Sleep

Differences from QB

  • In QB the syntax was OPEN "CON:" FOR INPUT|OUTPUT AS [#] filenumber

See also

  • Open
  • Open Scrn
  • Open Err
  • FreeFile


Open Err
Opens both the standard input (stdin) and standard error (stderr) streams, using it as if it were file operations.

Syntax

Open Err [for mode] As [#]filenumber As Long

Usage

Open Err [for mode] as [#]filenumber
or
result = Open Err( [for mode[,]] as [#]filenumber )

Parameters

mode
Ignored.
filenumber
An unused file number.

Return Value

A 32 bit Long: a zero is returned if Open Err() completed successfully, otherwise a non-zero value is returned to indicate failure.

Description

This command opens stdin to read from and stderr to write to the console allowing read and write operations with normal file commands.

stderr is an output stream different from stdout allowing error messages to be redirected separately from the main console output.

The normal console commands, such as Color and Locate, do not work in this mode, because they do not accept a file number.
The Tab keyword, regardless of the given column number, is always interpreted as a simple comma (,) (next output will take place at the next 14 column boundary).

The [For Input|Output] mode is allowed for compatibility, but is ignored.

The error code returned by Open Err can be checked using Err in the next line. The function version of Open Err returns directly the error code as a 32 bit Long.

Runtime errors:
Open Err throws one of the following runtime errors:

(1) Illegal function call
    • Filenumber was not free at the time. use FreeFile to ensure that filenumber is free.

Example

Dim a As String
Open Err For Input  As #1
Print #1,"Please write something and press ENTER"
Line Input #1, a
Print #1, "You wrote"; a
Close
Sleep

Differences from QB

  • New to FreeBASIC

See also

  • Open


Open Lpt
Open a printer device, using it as if it were file operations.

Syntax

Open Lpt ["[LPT[x]:][Printer_Name][,TITLE=Doc_Title][,EMU=TTY]"] [For Input|Output] As #filenumber

Usage

Open Lpt "LPT..." As [#]filenumber
or
result = Open Lpt( "LPT..."[,] As [#]filenumber )

Parameters

x
Specifies a port number. If omitted, output is sent to the system print spooler.
Printer_Name
Name of printer to open. This parameter is ignored on DOS.
TITLE=Doc_Title
Title of the print job as seen by the printer spooler. This parameter is ignored on DOS.
EMU=TTY
Emulation of TTY output on a windows GDI printer, using driver text imaging. This parameter is ignored on DOS and Linux.
For Input|Output
clause is allowed for compatibility, but it is ignored.
filenumber
An unused file number to assign to the device.

Return Value

A 32 bit Long: 0 is returned if Open Lpt() completed successfully, otherwise a non-zero value is returned to indicate failure.

Description

Open Lpt opens a connection to a printer device. The connection is treated like a file, so data may be written to the printer using Print and Put # commands.

Any printer attached to the system may be opened with Open Lpt

Open Lpt "LPT:" ... will try to open the default printer on Windows and Linux, and "LPT1:" on DOS.

LPrint will automatically try to open the default printer on Windows and Linux, and "LPT1:" on DOS.

The error code returned by Open Lpt can be checked using Err in the next line. The function version of Open Lpt returns directly the error code as a 32 bit Long.

Platform specific notes:

Windows
The argument EMU=TTY assumes printable ASCII or Unicode text, and applies printer driver text imaging to the input. EMU=TTY also allows the usage of CR, LF, BS, TAB, FF, etc., for virtual print-head movement...even when the printer is a GDI printer and therefore doesn't itself understand these special characters. If ",EMU=TTY" is omitted, the data must be sent in the printer's language (ESC/P, HPGL, PostScript, etc...). Other useful emulation modes aren't supported yet.

Linux
A printer spooler available through lp must be installed to access printers by name or a default printer. Spooler access was tested only with CUPS, but other spoolers may work that are invoked through lp. Port are zero-based on Linux. "LPT1:" corresponds with "/dev/lp0".

The data must be sent in the printer's language (ESC/P, HPGL, PostScript, etc...). Emulation modes aren't supported yet.

DOS
FreeBASIC does not support print spoolers on DOS. Printers must be accessible through "LPTx:".

The data must be sent in the printer's language (ESC/P, HPGL, PostScript, etc...). Emulation modes aren't supported yet.

Example

' Send some text to the Windows printer on LPT1:, using driver text imaging.
Open Lpt "LPT1:EMU=TTY" For Output  As #1
Print #1, "Testing!"
Close

' Sends contents of text file test.txt to Windows printer named "ReceiptPrinter"
Dim RptInput As String
Dim PrintFileNum As Long, RptFileFileNum As Long

RptFileFileNum = FreeFile
Open "test.txt" For Input As #RptFileFileNum

PrintFileNum = FreeFile
Open Lpt "LPT:ReceiptPrinter,TITLE=ReceiptWinTitle,EMU=TTY" As _
    #PrintFilenum

While (EOF(RptFileFileNum) = 0)
        Line Input #RptFileFileNum, RptInput
        Print #PrintFileNum, RptInput
Wend

Close #PrintFileNum  ' Interestingly, does not require CHR(12).  But if pagination is desired, CHR(12) is the way.

Close #RptFileFileNum

Print "Press any key to end program..."
GetKey

End

'This simple program will print a PostScript file to a PostScript compatible printer.
Dim As Long FFI, PPO
Dim As String temp

FFI = FreeFile()
Open "sample.ps" For Input Access Read As #FFI
PPO = FreeFile()
Open Lpt "LPT1:" For Output As #PPO
While (EOF(FFI) = 0)
Line Input #FFI, temp
Print #PPO, temp
Wend

Close #FFI
Close #PPO

Print "Printing Completed!"

Dialect Differences

  • In the -lang qb dialect the old syntax is supported OPEN "LPT:..." . This syntax used in the other dialects will open a regular file.

See also

  • Open
  • LPrint


Open Pipe
Opens an external process' standard input (stdin) or output (stdout) stream, using it as if it were file operations.

Syntax

Open Pipe shell_command For Input As [#]filenumber
Open Pipe shell_command For Output As [#]filenumber
Open Pipe shell_command For Binary access_type [#]filenumber

Usage

result = Open Pipe( command[,] For {Input|Output}[,] As filenumber )
or,
result = Open Pipe( command[,] For Binary[,] access_type[,] As filenumber )
(or in the QB-like syntax,)
Open Pipe filename For {Input|Output} As filenumber
(or,)
Open Pipe filename For Binary access_type As filenumber

Parameters

shell_command
The external process to execute in the operating system command shell. Relative file paths are relative to the current directory (see CurDir). When opening a pipe for a process that requires double quotes in either its executable path, or its arguments, the entire pipe string should be nested inside of double quotes.
access_type
The type of read or write access requested by the calling process.
      • Access {Read|Write} (either the stdin or stdout stream of the external process can be opened)
filenumber
An available file number to bind to the external process' stdin or stdout stream.

Return Value

In the first usage, Open Pipe() returns a 32 bit Long: a zero (0) on success and a non-zero error code otherwise.

Description

Open Pipe executes another process in the command shell and opens either its stdin or stdout streams for reading or writing. A file number is bound to the stream, which is used in subsequent file operations, such as Input #. An available filenumber can be retrieved with FreeFile. If the external process does not exist, a runtime error is thrown.

The Input and Output file modes open the external process' stdin and stdout streams, respectively, for sequential text I/O, useful for reading or writing plain text. Characters, words or whole lines can then be read or written using text-mode file operations, such as Line Input # and Print #.

The Binary file mode opens the external process' stdin or stdout streams - depending on the access type specified (see description of the access_type parameter above) - for random-access reading or writing of arbitrarily sized and interpreted raw data. Simple data type values, like Byte and LongInt, and whole chunks of memory can be read from or written to the streams with binary-mode file operations like Get # and Put #.
Bidirectional pipes are not supported by FB and must be implemented using the OS' API functions.

The error code returned by Open Pipe can be checked using Err in the next line. The function version of Open Pipe returns directly the error code as a 32 bit Long.

Runtime errors:
Open Pipe throws one of the following runtime errors:

(1) Illegal function call
    • filenumber was not free at the time. use FreeFile to ensure that filenumber is free.

Example

'' This example uses Open Pipe to run a shell command and retrieve its output.
#ifdef __FB_UNIX__
Const TEST_COMMAND = "ls *"
#else
Const TEST_COMMAND = "dir *.*"
#endif

Open Pipe TEST_COMMAND For Input As #1

Dim As String ln
Do Until EOF(1)
    Line Input #1, ln
    Print ln
Loop

Close #1

Platform Differences

  • The Binary file mode is not supported on all platforms; Open Pipe will throw an error if it is unable to open the external process' stdin or stdout streams in binary mode.

Differences from QB

  • New to FreeBASIC

See also

  • Shell
  • Open
  • Open Cons
  • Open Err
  • FreeFile


Open Scrn
Opens the screen directly for input and output, using it as if it were file operations.

Syntax

Open Scrn [for mode] As [#]filenumber As Long

Usage

Open Scrn [for mode] as [#]filenumber
or
result = Open Scrn( [for mode[,]] as [#]filenumber )

Parameters

mode
Either Input or Output. If omitted, Output is assumed.
filenumber
An unused file number.

Return Value

A 32 bit Long: a zero (0) is returned if Open Scrn() completed successfully, otherwise a non-zero value is returned to indicate failure.

Description

This command opens the screen (in text or graphics screen mode) for both input and output as a file, allowing to read/write from/to it with normal file commands.

This command may use direct access to the screen for speed in some implementations, so it should not be used when the input / output is required to be redirected or piped with OS commands.

The normal screen commands, such as Color and Locate, do not work in this mode, because they do not accept a file number.

The [For Input|Output] clause is allowed for compatibility, but is ignored.

filenumber is an unused file number.

An unused file number can be found using FreeFile.

The error code returned by Open Scrn can be checked using Err in the next line. The function version of Open Scrn returns directly the error code as a 32 bit Long.

Runtime errors:
Open Scrn throws one of the following runtime errors:

(1) Illegal function call
    • filenumber was not free at the time. use FreeFile to ensure that filenumber is free.

Example

Dim a As String
Open Scrn For Input  As #1
Print #1,"Please write something and press ENTER"
Line Input #1,a
Print #1, "You wrote";a
Close
Sleep

Differences from QB

  • QB used OPEN "SCRN:" ...

See also

  • Open
  • Open Cons


Open Tcp
Opens a TCP client connection and binds it to a file number.

Syntax

Declare Function Open Tcp ( options As String, As [#]filenumber As Long ) As Long

Usage

result = Open Tcp( options[,] As [#]filenumber )
Open Tcp options As [#]filenumber

Parameters

options
A comma-separated string describing the connection.
filenumber
The file number to bind to the connected socket.

Return Value

Returns zero (0) on success, or a non-zero error code on failure.

Description

Open Tcp connects to a TCP server and exposes the socket through FreeBASIC's normal file I/O statements. Once opened, the file number can be used with Print #, Line Input #, Input(), Get #, and Put #.

The options string accepts:
    • host=name-or-address, required for client connections
    • port=number, required
    • timeout=milliseconds, optional socket timeout

Unknown options are ignored so that future TCP options can be added without breaking existing programs.

For sockets, EOF means that no byte is currently available to read. Use Eoc to test whether the peer has closed the connection.

Example

See examples/network/opentcp/http-get.bas.

Platform Differences

  • TCP support is not available on all targets. Programs that need to be portable should use target defines such as FB_DOS, FB_JS, and FB_XBOX where appropriate.

Differences from QB

  • New to FreeBASIC.

See also

  • Open Tcp Server
  • Tcp Accept
  • Eoc
  • Open


Open Tcp Server
Opens a listening TCP server socket and binds it to a file number.

Syntax

Declare Function Open Tcp Server ( options As String, As [#]filenumber As Long ) As Long

Usage

result = Open Tcp Server( options[,] As [#]filenumber )
Open Tcp Server options As [#]filenumber

Parameters

options
A comma-separated string describing the listening socket.
filenumber
The file number to bind to the server socket.

Return Value

Returns zero (0) on success, or a non-zero error code on failure.

Description

Open Tcp Server creates a listening TCP socket. The server file number is passed to Tcp Accept to accept incoming connections. The accepted client receives its own file number and can then be read from or written to using normal file I/O statements.

The options string accepts:
    • host=name-or-address, optional bind address
    • port=number, required
    • backlog=count, optional listen backlog, default 16
    • timeout=milliseconds, optional socket timeout

Server file numbers are not used for data transfer. Use Tcp Accept and then read or write the returned client file number.

Example

See examples/network/opentcp/http-server.bas and examples/network/opentcp/two-connections-selftest.bas.

Platform Differences

  • TCP support is not available on all targets. Programs that need to be portable should use target defines such as FB_DOS, FB_JS, and FB_XBOX where appropriate.

Differences from QB

  • New to FreeBASIC.

See also

  • Open Tcp
  • Tcp Accept
  • Close


Operator
Declares or defines an overloaded operator.

Syntax

{ Type | Class | Union } typename
Declare Operator Cast () [ ByRef ] As datatype
Declare Operator @ () [ ByRef ] As datatype Ptr
Declare Operator assignment_op ( [ ByRef | ByVal ] rhs As datatype )
Declare Operator [] ( index As datatype ) [ ByRef ] As datatype
Declare Operator New ( size As UInteger ) As Any Ptr
Declare Operator New[] ( size As UInteger ) As Any Ptr
Declare Operator Delete ( buf As Any Ptr )
Declare Operator Delete[] ( buf As Any Ptr )
End { Type | Class | Union }

{ Type | Class | Union } typename
Declare Operator For ()
Declare Operator For ( [ ByRef | ByVal ] stp As typename )
Declare Operator Step ()
Declare Operator Step ( [ ByRef | ByVal ] stp As typename )
Declare Operator Next ( [ ByRef | ByVal ] cond As typename ) As Integer
Declare Operator Next ( [ ByRef | ByVal ] cond As typename, [ ByRef | ByVal ] stp As typename ) As Integer
End { Type | Class | Union }

Declare Operator unary_op ( [ ByRef | ByVal ] rhs As datatype ) As datatype
Declare Operator binary_op ( [ ByRef | ByVal ] lhs As datatype, [ ByRef | ByVal ] rhs As datatype ) As datatype

Operator typename.Cast () [ ByRef ] As datatype [ Export ]
Operator typename.@ () [ ByRef ] As datatype Ptr [ Export ]
Operator typename.assignment_op ( [ ByRef | ByVal ] rhs As datatype ) [ Export ]
Operator typename.[] ( index As datatype ) [ ByRef ] As datatype [ Export ]
Operator unary_op ( [ ByRef | ByVal ] rhs As datatype ) As datatype [ Export ]
Operator binary_op ( [ ByRef | ByVal ] lhs As datatype, [ ByRef | ByVal ] rhs As datatype ) As datatype [ Export ]
Operator typename.New ( size as uinteger ) As Any Ptr [ Export ]
Operator typename.New[] ( size As UInteger ) As Any Ptr [ Export ]
Operator typename.Delete ( buf As Any Ptr ) [ Export ]
Operator typename.Delete[] ( buf As Any Ptr ) [ Export ]

Parameters

typename
Name of the Type, Class, Union, or Enum.
assignment_op
let += -= *= &= /= \= mod= shl= shr= and= or= xor= imp= eqv= ^=
unary_op
- not * -> abs sgn fix frac int exp log sin asin cos acos tan atn len sqr
binary_op
+ - * & / \ mod shl shr and or xor imp eqv ^ = <> < > <= >=

Description

The built in operators like =, +, and cast have predefined behaviors when used in expressions. These operators can be overloaded to do something other than predefined operations when at least one of the arguments to the operator is a Type, Class, Enum, or Union data type.

Operators are just functions. The operator '+' has functionality like Function Plus( A as DataType, B as DataType ) as DataType. See Operator Overloading for more information. Operators can be overloaded to accept different data types as parameters. The Cast Operator is the only operator (or function) that can be declared multiple times when only the return type differs, but not the same as the Type, Class, or Union it is declared in (for not explicit usage, the compiler may decide which cast overload to call based on how the object is used).

Non-static operator members are declared inside the Type, Class, or Union. Global operators are declared outside. All operator definitions (procedure bodies) must appear outside.

Let, Cast, and other assignment operators must be declared inside the Type, Class, or Union. As all non-static member procedures, they have passed a hidden This parameter.

Unary operators must be declared outside the Type, Class, or Union and have a return data type explicitly declared. Unary operators can be overloaded to return any valid data type, except for Operator -> (Pointer To Member Access) which must return a Type, Class, or Union data type.

Binary operators must be declared outside the Type, Class, or Union and have a return data type explicitly declared. Binary operators can be overloaded with valid data types, including for relational operators, which can also return any valid data type.

Let refers to the assignment operator, as in LET a=b. The Let keyword is omitted in common practice, and is not allowed in the -lang fb dialect. However, Let() can be used to assign the fields of a UDT to multiple variables.

See For, Step, and Next for more information on overloading the For..Next statement for use with user defined types.

Although declared inside the Type, Class, or Union, the member operators New, New[], Delete, and Delete[] are always static, even if not explicitly declared (Static keyword is unnecessary but allowed).

Example

'' operator1.bas

Type Vector2D
  As Single x, y

  '' Return a string containing the vector data.
  Declare Operator Cast() As String

  '' Multiply the vector by a scalar.
  Declare Operator *= ( ByVal rhs As Single )
End Type

'' Allow two vectors to be able to be added together.
Declare Operator + ( ByRef lhs As Vector2D, ByRef rhs As Vector2D ) As Vector2D

'' Return the modulus (single) of the vector using the overloaded operator abs().
Declare Operator Abs (  ByRef rhs As Vector2D ) As Single

Operator Vector2D.cast () As String
  Return "(" + Str(x) + ", " + Str(y) + ")"
End Operator

Operator Vector2D.*= ( ByVal rhs As Single )
  This.x *= rhs
  This.y *= rhs
End Operator

Operator + ( ByRef lhs As Vector2D, ByRef rhs As Vector2D ) As Vector2D
  Return Type<Vector2D>( lhs.x + rhs.x, lhs.y + rhs.y )
End Operator

Operator Abs ( ByRef rhs As Vector2D ) As Single
  Return Sqr( rhs.x * rhs.x + rhs.y * rhs.y )
End Operator

Dim a As Vector2D = Type<Vector2D>( 1.2, 3.4 )
Dim b As Vector2D = Type<Vector2D>( 8.9, 6.7 )
Dim c As Vector2D = Type<Vector2D>( 4.3, 5.6 )

Print "a = "; a, "abs(a) ="; Abs( a )
Print "b = "; b, "abs(b) ="; Abs( b )
Print "a + b = "; a + b, "abs(a+b) ="; Abs( a + b )
Print "c = "; c, "abs(c) ="; Abs( c )
Print "'c *= 3'"
c *= 3
Print "c = "; c, "abs(c) ="; Abs( c )

Small use case of the operator "[]": simplest smart pointers for byte buffers.
'' operator3.bas

'' A smart pointer is an object which behaves like a pointer but does more than a pointer:
'' - This object is flexible as a pointer and has the advantage of being an object,
''   like constructor and destructor called automatically.
'' - Therefore, the destructor of the smart pointer will be automatically called
''   when this object goes out of scope, and it will delete the user pointer.

'' Example of simplest smart pointers for byte buffers:
'' - Constructor and destructor allow to allocate, deallocate, and resize the byte buffer.
'' - Pointer index operator allows to access buffer elements.
'' - Copy-constructor and let-operator are just declared in private section,
''   in order to disallow copy construction and any assignment.

Type smartByteBuffer
  Public:
    Declare Constructor (ByVal size As UInteger = 0)
    Declare Operator [] (ByVal index As UInteger) ByRef As Byte
    Declare Destructor ()
  Private:
    Declare Constructor (ByRef rhs As smartByteBuffer)
    Declare Operator Let (ByRef rhs As smartByteBuffer)
    Dim As Byte Ptr psbb
End Type

Constructor smartByteBuffer (ByVal size As UInteger = 0)
  This.destructor()
  If size > 0 Then
    This.psbb = New Byte[size]
    Print "Byte buffer allocated"
  End If
End Constructor

Operator smartByteBuffer.[] (ByVal index As UInteger) ByRef As Byte
  Return This.psbb[index]
End Operator

Destructor smartByteBuffer ()
  If This.psbb > 0 Then
    Delete[] This.psbb
    This.psbb = 0
    Print "Byte buffer deallocated"
  End If
End Destructor

Scope
  Dim As smartByteBuffer sbb = smartByteBuffer(256)
  For I As Integer = 0 To 255
    sbb[I] = I - 128
  Next I
  Print
  For I As Integer = 0 To 255
    Print Using "#####"; sbb[I];
  Next I
  Print
End Scope

Dialect Differences

  • Only available in the -lang fb dialect.

See also

  • Class
  • Union
  • Type
  • Coercion and Conversion


Option()
Specifies additional attributes and/or characteristics of symbols.

Syntax

Option( "literal-text" )

Parameters

literal-text
The literal text specifying the option. See description.

Description

Option() allows the programmer to specify additional attributes or characteristics. Enclosing the string into quotes and parentheses is required in the syntax. Unrecognized options are ignored.

Option can also be used as a statement to specify other compile time options. See Compiler Switches.

Individual options are explained below.

SSE

Option("SSE") indicates that a floating point value (Single or Double) returned from a function is stored in the xmm0 register. Option("Sse") is ignored unless the source is compiled with the -fpu SSE command line option. This option may be used immediately after the return type in a function declaration or function definition. This option is an optimization only and not required to compile programs using the -fpu SSE command line option.

Declare Function ValueInXmm0 () As Double Option("sse")

FPU

Option("FPU") indicates that a floating point value (Single or Double) returned from a function is stored in the st(0) register. This option may be used immediately after the return type in a function declaration or function definition.

Declare Function ValueInStZero () As Double Option("fpu")

Differences from QB

  • New to FreeBASIC

See also

  • Compiler Option: -fpu
  • Compiler Switches


Option Base
Specifies a default lower bound for array declarations

Syntax

Option Base base_subscript

Parameters

base_subscript
an numeric literal value

Description

Option Base is a statement that sets the default lower bound for any following array declarations. This default remains in effect for the rest of the module in which Option Base is used, and can be overridden by declaring arrays with an explicit lower bound, or with another Option Base statement.

Note: initially, the default base is 0.

Example

'' Compile with the "-lang qb" or "-lang fblite" compiler switches

#lang "fblite"

Dim foo(10) As Integer      ' declares an array with indices 0-10

Option Base 5

Dim bar(15) As Integer      ' declares an array with indices 5-15
Dim baz(0 To 4) As Integer  ' declares an array with indices 0-4

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.
  • In -lang fb, Option Base is not allowed, and the default lower bound is always 0.

Differences from QB

  • QBASIC only supported values of 0 or 1 for base_subscript.
  • In QBASIC the word Base was a reserved keyword, and couldn't be used as a variable name.
  • Arrays must always be explicitly created in FreeBASIC. QBASIC would implicitly create an array from base_subscript to 10 if one was used in code without being predefined.

See also

  • Dim
  • ReDim
  • LBound


Option ByVal
Specifies parameters are to be passed by value by default in procedure declarations

Syntax

Option ByVal

Description

Option ByVal is a statement that sets the default passing convention for procedure parameters to by value, as if declared with ByVal. This default remains in effect for the rest of the module in which Option ByVal is used, and can be overridden by specifying ByRef in parameter lists.

Example

'' compile with the "-lang fblite" compiler switch

#lang "fblite"

Sub TestDefaultByref( a As Integer )
  '' change the value
  a = a * 2
End Sub

Option ByVal

Sub TestDefaultByval( a As Integer )
  a = a * 2
End Sub

Dim a As Integer = 1

Print "a = "; a
TestDefaultByref( a )
Print "After TestDefaultByref : a = "; a
Print

Print "a = "; a
TestDefaultByval( a )
Print "After TestDefaultByval : a = "; a
Print

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OPTION_BYVAL__


Option Dynamic
Specifies variable-length array declarations

Syntax

Option Dynamic

Description

Option Dynamic is a statement that specifies that any following array declarations are variable-length, whether they are declared with constant subscript ranges or not. This remains in effect for the rest of the module in which Option Dynamic is used, and can be overridden with Option Static. It is equivalent to the '$Dynamic metacommand.

Example

'' Compile with "-lang fblite" compiler switch

#lang "fblite"

Dim foo(99) As Integer      ' declares a fixed-length array

Option Dynamic

Dim bar(99) As Integer      ' declares a variable-length array
' ...
ReDim bar(199)              ' resize the array

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OPTION_DYNAMIC__
  • '$Dynamic
  • '$Static
  • Option Static
  • Dim
  • ReDim


Option Escape
Specifies that string literals should be processed for C-like escape sequences by default

Syntax

Option Escape

Description

Option Escape is a statement that causes string literals to be processed for C-like escape sequences by default. Normally, escape sequences have no effect in string literals unless the string is prefixed with the ! Operator (Escaped String Literal). This default remains in effect for the rest of the module in which Option Escape is used, and can be overridden by prefixing string literals with the $ Operator (Non-Escaped String Literal).

See Literals in the Programmer's Guide to learn more about escape sequences.

Example

'' Compile with the "-lang fblite" compiler switch

#lang "fblite"

Option Escape

Print "Warning \a\t The path is:\r\n c:\\Freebasic\\Examples"
Print $"This string doesn't have expanded escape sequences: \r\n\t"

#include "crt.bi"

Dim As Integer a = 2, b = 3
printf("%d * %d = %d\r\n", a, b, a * b)

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OPTION_ESCAPE__
  • Operator ! (Escaped String Literal)
  • Operator $ (Non-Escaped String Literal)
  • Literals


Option Explicit
Forces variables, objects and arrays to be declared before they are used

Syntax

Option Explicit

Description

Option Explicit is a statement that forces any following variable, object or array usage to be preceded by a declaration, with, for example, Dim or Static. This rule remains in effect for the rest of the module in which Option Explicit is used, and cannot be overridden.

Example

'' Compile with the "-lang qb" or "-lang fblite" compiler switches

#lang "fblite"

Option Explicit

Dim a As Integer            ' 'a' must be declared..
a = 1                       ' ..or this statement will fail to compile.

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OPTION_EXPLICIT__


Option Gosub
Enables support for GoSub and On Gosub.

Syntax

Option Gosub

Description

Option Gosub enables support for GoSub and Return (from gosub).

Because Return could mean return-from-gosub or return-from-procedure, Option Gosub and Option Nogosub can be used to enable and disable GoSub support. When GoSub support is disabled, Return is then recognized as return-from-procedure.

Example

'' Compile with the "-lang fblite" compiler switch

#lang "fblite"

'' turn on gosub support
Option GoSub

GoSub there
backagain:
    Print "backagain"
    End

there:
    Print "there"
    Return

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OPTION_GOSUB__
  • Option Nogosub
  • GoSub
  • Return (From Gosub)
  • Return (From Procedure)


Option Nogosub
Disables support for GoSub and On Gosub.

Syntax

Option Nogosub

Description

Option Nogosub disables support for GoSub and Return (from gosub).

Because Return could mean return-from-gosub or return-from-procedure, Option Gosub and Option Nogosub can be used to enable and disable GoSub support. When GoSub support is disabled, Return is then recognized as return-from-procedure.

Example

'' Compile with the "-lang qb" compiler switch

'$lang: "qb"

'' turn off gosub support
Option nogosub

Function foo() As Integer
    Return 1234
End Function

Print foo

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OPTION_GOSUB__
  • Option Gosub
  • GoSub
  • Return (From Procedure)
  • Return (From Gosub)


Option NoKeyword
"Undefines" a reserved keyword

Syntax

Option NoKeyword keyword

Parameters

keyword
the keyword to undefine

Description

Option NoKeyword is a statement that undefines a FreeBASIC reserved keyword, meaning it can be used as an identifier for a variable, object, procedure or any other symbol. The keyword is undefined for the rest of the module in which Option NoKeyword is used.

Example

'' Compile with the "-lang fblite" compiler switch

#lang "fblite"

Option NoKeyword Int        ' remove the keyword 'int' from the internal
                            ' symbol table

Dim Int As Integer          ' declare a variable with the name 'int'

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • New to FreeBASIC

See also

  • #undef


Option Private
Specifies internal linkage by default for procedure declarations

Syntax

Option Private

Description

Option Private is a statement that gives any following procedure declarations internal linkage by default, as if declared with Private. This default remains in effect for the rest of the module in which Option Private is used, and can be overridden by declaring procedures with Public.

Example

'' Compile with the "-lang fblite" compiler switch

#lang "fblite"

Sub ProcWithExternalLinkage()
   ' ...
End Sub

Option Private

Sub ProcWithInternalLinkage()
   ' ...
End Sub

Public Sub AnotherProcWithExternalLinkage()
   ' ...
End Sub

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • New to FreeBASIC

See also

  • __FB_OPTION_PRIVATE__
  • Private
  • Public


Option Static
Reverts to default array declaration behavior

Syntax

Option Static

Description

Option Static is a statement that overrides the behavior of Option Dynamic, that is, arrays declared with constant subscript ranges are fixed-length. This remains in effect for the rest of the module in which Option Static is used, and can be overridden with Option Dynamic. It is equivalent to the '$Static metacommand.

Example

'' Compile with the "-lang fblite" compiler switch

#lang "fblite"

Option Dynamic

Dim foo(100) As Integer            ' declares a variable-length array

Option Static

Dim bar(100) As Integer            ' declares a fixed-length array

Dialect Differences

  • Only available in the -lang fblite and -lang qb dialects.

Differences from QB

  • New to FreeBASIC

See also

  • '$Dynamic
  • '$Static
  • Dim
  • Erase
  • ReDim
  • Option Dynamic
  • Static


Operator Or (Inclusive Disjunction)
Returns the bitwise-or (inclusive disjunction) of two numeric values

Syntax

Declare Operator Or ( ByRef lhs As T1, ByRef rhs As T2 ) As Ret

Usage

result = lhs Or rhs

Parameters

lhs
The left-hand side expression.
T1
Any numeric or boolean type.
rhs
The right-hand side expression.
T2
Any numeric or boolean type.
Ret
A numeric or boolean type (varies with T1 and T2).

Return Value

Returns the bitwise-disjunction of the two operands.

Description

This operator returns the bitwise-disjunction of its operands, a logical operation that results in a value with bits set depending on the bits of the operands (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value).

The truth table below demonstrates all combinations of a boolean-disjunction operation:

Lhs Bit | Rhs Bit | Result
0 | 0 | 0
1 | 0 | 1
0 | 1 | 1
1 | 1 | 1

No short-circuiting is performed - both expressions are always evaluated.

The return type depends on the types of values passed. Byte, UByte and floating-point type values are first converted to Integer. If the left and right-hand side types differ only in signedness, then the return type is the same as the left-hand side type (T1), otherwise, the larger of the two types is returned. Only if the left and right-hand side types are both Boolean, the return type is also Boolean.

This operator can be overloaded for user-defined types.

Example

' Using the OR operator on two numeric values
Dim As UByte numeric_value1, numeric_value2
numeric_value1 = 15 '00001111
numeric_value2 = 30 '00011110

'Result =  31  =     00011111
Print numeric_value1 Or numeric_value2
Sleep

' Using the OR operator on two conditional expressions
Dim As UByte numeric_value
numeric_value = 10

If numeric_value = 5 Or numeric_value = 10 Then Print "Numeric_Value equals 5 or 10"
Sleep

' This will output "Numeric_Value equals 5 or 10" because
' while the first condition of the first IF statement is false, the second is true

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • OrElse
  • Operator Truth Tables


Operator Or= (Inclusive Disjunction And Assign)
Performs a bitwise-or (inclusive disjunction) and assigns the result to a variable

Syntax

Declare Operator Or= ( ByRef lhs As T1, ByRef rhs As T2 )

Usage

lhs Or= rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric or boolean type.
rhs
The value to perform a bitwise-or (inclusive disjunction) with lhs.
T2
Any numeric or boolean type.

Description

This operator performs a bitwise-or and assigns the result to a variable (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value). It is functionally equivalent to:
lhs = lhs Or rhs

Or= compares each bit of its operands, lhs and rhs, and if either bits are 1, then the corresponding bit in the first operand, lhs, is set to 1, otherwise it is set to 0.

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol 'Or=>' can be also used.

Example

Dim As UByte a = &b00110011
Dim As UByte b = &b01010101
a Or= b
'' Result    a = &b01110111
Print Bin(a)

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Or


Or (Graphics Put)
Parameter to the Put graphics statement which uses a bit-wise Or as the blitting method

Syntax

Put [ target, ] [ STEP ] ( x,y ), source [ ,( x1,y1 )-( x2,y2 ) ], Or

Parameters

Or
Required.

Description

The Or method combines each source pixel with the corresponding destination pixel, using the bit-wise Or function. The result of this is output as the destination pixel.
This method works in all graphics modes. There is no mask color, although color values of 0 (RGBA(0, 0, 0, 0) in full-color modes) will have no effect, because of the behavior of Or.

In full-color modes, each component (red, green, blue and alpha) is kept in a discrete set of bits, so the operation can be made to only affect some of the channels, by making sure the all the values of the other channels are set to 0.

Example

''open a graphics window
ScreenRes 320, 200, 16

''create 3 sprites containing red, green and blue circles
Const As Long r = 32
Dim As Any Ptr cr, cg, cb
cr = ImageCreate(r * 2 + 1, r * 2 + 1, RGBA(0, 0, 0, 0))
cg = ImageCreate(r * 2 + 1, r * 2 + 1, RGBA(0, 0, 0, 0))
cb = ImageCreate(r * 2 + 1, r * 2 + 1, RGBA(0, 0, 0, 0))
Circle cr, (r, r), r, RGB(255, 0, 0), , , 1, f
Circle cg, (r, r), r, RGB(0, 255, 0), , , 1, f
Circle cb, (r, r), r, RGB(0, 0, 255), , , 1, f

''put the sprite at three different multipier
''levels, overlapping each other in the middle
Put (146 - r, 108 - r), cr, Or
Put (174 - r, 108 - r), cg, Or
Put (160 - r,  84 - r), cb, Or

''free the memory used by the sprites
ImageDestroy cr
ImageDestroy cg
ImageDestroy cb

''pause the program before closing
Sleep

Differences from QB

  • None

See also

  • Or
  • Put (Graphics)


Operator Orelse (Short Circuit Inclusive Disjunction)
Returns the short circuit-or (Inclusive Disjunction) of two numeric values

Syntax

Declare Operator OrElse ( ByRef lhs As T1, ByRef rhs As T2 ) As Ret

Usage

result = lhs OrElse rhs

Parameters

lhs
The left-hand side expression.
T1
Any numeric or boolean type.
rhs
The right-hand side expression.
T2
Any numeric or boolean type.
Ret
A numeric or boolean type (varies with T1 and T2).

Return Value

Returns the short circuit-or (inclusive disjunction) of the two operands.

Description

This operator evaluates the left hand side expression. If the result is nonzero, then -1 (true) is immediately returned. If the result is zero then the right hand side is evaluated, and the logical result from that is returned, returning -1 (true) for a nonzero value or 0 (false) for zero.
(for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value)

The truth table below demonstrates all combinations of a short circuit-or operation, the '-' denotes that the operand is not evaluated.

Lhs Value | Rhs Value | Result
0 | 0 | 0
0 | nonzero | -1
nonzero | (not evaluated) | -1

OrElse equivalent to:
Iif( Lhs = 0, Rhs <> 0 , -1 )

Short-circuiting is performed - only expressions needed to calculate the result are evaluated. The left hand side lhs is evaluated first, and only if it evaluates to zero (false) is the right hand side rhs also evaluated. If the left hand side evaluation lhs returns non-zero (true), it is known that at that point that the overall condition is true, so the right hand side rhs is not evaluated (skipped).

The return type is almost always an Integer, of the value 0 or -1, denoting false and true respectively. Except if the left and right-hand side types are both Boolean, then the return type is also Boolean.

This operator cannot be overloaded for user-defined types.

Example

'' Using the ORELSE operator to test
'' if a value is out of range

Dim As Integer n
Input "Enter a number between 1 and 10: ", n

Function test(ByVal n As Integer) As Integer
    Print "expression evaluated"
    Return n
End Function

'' print value only if n is in range [1, 10]
If test(n) < 1 OrElse test(n) > 10 Then  '' if n < 1, second expression is not evaluated
    Print "   => out of range"
Else
    Print "   => n ="; n
End If
Sleep

Differences from QB

  • This operator was not available in QB.

See also

  • AndAlso
  • Or
  • Operator Truth Tables


Out
Outputs a value to a hardware port.

Syntax

Declare Function Out ( ByVal port As UShort , ByVal data As UByte ) As Long

Usage

Out port,value

Parameters

port
Hardware port to write to.
data
Data value to write.

Description

This function sends value to port and returns immediately.

Example

'speakersound.bas
Sub Sound(ByVal freq As UInteger, dur As UInteger)
  Dim t As Double,f1 As Unsigned Short
    f1 = 1193181 \ freq
    Out &h61,Inp(&h61) Or 3
    Out &h43,&hb6
    Out &h42,LoByte(f1)
    Out &h42,HiByte(f1)
    t=Timer
    While ((Timer - t) * 1000) < dur
      Sleep 0,1
    Wend
    Out &h61,Inp(&h61) And &hfc
End Sub

Sound(523, 60)  'C5
Sound(587, 60)  'D5
Sound(659, 60)  'E5
Sound(698, 60)  'F5
Sound(784, 60)  'G5
Sound(880, 60)  'A5
Sound(988, 60)  'B5
Sound(1046, 60) 'C6

Platform Differences

  • In the Windows and Linux versions three port numbers (&H3C7, &H3C8, &H3C9) are hooked by the graphics library when a graphics mode is in use to emulate QB's VGA palette handling. This use is deprecated; use Palette to retrieve and set palette colors.

  • Using true port access in the Windows version requires the program to install a device driver for the present session. For that reason, Windows executables using hardware port access should be run with administrator permits each time the computer is restarted. Further runs don't require admin rights as they just use the already installed driver. The driver is only 3K in size and is embedded in the executable.

See also

  • Inp
  • Wait
  • Palette


Output
Specifies text file to be opened for output mode

Syntax

Open filename for Output [Encoding encoding_type] [Lock lock_type] as [#]filenum

Parameters

filename
file name to open for output
encoding_type
indicates encoding type for the file
lock_type
locking to be used while the file is open
filenum
unused file number to associate with the open file

Description

A file mode used with Open to open a text file for writing.

This mode is used to write text with Print #, or comma separated values with Write #.

Text files can't be simultaneously read and written in FreeBASIC, so if both functions are required on the same file, it must be opened twice.

filename must be a string expression resulting in a legal file name in the target OS, without wildcards. The file will be sought for in the present directory, unless the filename contains a path . If the file does not exist, it is created. The pointer is set at the first character of the file.

Encoding_type indicates the Unicode Encoding of the file, so characters are correctly read. If omitted, "ascii" encoding is defaulted. Only little endian character encodings are supported at the moment.
    • "utf8"
    • "utf16"
    • "utf32"
    • "ascii" (the default)

Lock_type indicates the way the file is locked for other processes, it is one of:
    • Read - the file can be opened simultaneously by other processes, but not for reading
    • Write - the file can be opened simultaneously by other processes, but not for writing
    • Read Write - the file cannot be opened simultaneously by other processes (the default)

filenum Is a valid FreeBASIC file number (in the range 1..255) not being used for any other file presently open. The file number identifies the file for the rest of file operations. A free file number can be found using the FreeFile function.

Example

Dim ff As UByte
Dim randomvar As Integer
Dim name_str As String
Dim age_ubyte As UByte

ff = FreeFile
Input "What is your name? ",name_str
Input "What is your age? ",age_ubyte
Open "testfile" For Output As #ff
Write #ff, Int(Rnd(0)*42),name_str,age_ubyte
Close #ff
randomvar=0
name_str=""
age_ubyte=0

Open "testfile" For Input As #ff
Input #ff, randomvar,name_str,age_ubyte
Close #ff

Print "Random Number was: ", randomvar
Print "Your name is: " + name_str
Print "Your age is: " + Str(age_ubyte)

'File outputted by this sample will look like this,
'minus the comment of course:
'23,"Your Name",19

Differences from QB

See also

  • Append
  • Input (File Mode)
  • Open


Overload
Specifies that a procedure name can be overloaded

Syntax

Declare [Static] Sub procedure_name [cdecl|stdcall|pascal] Overload [Alias "external_name"] [([parameter_list])] [Constructor [priority]] [Static] [Export]

Declare [Static] Function procedure_name [cdecl|stdcall|pascal] Overload [Alias "external_name"] [([parameter_list])] [ ByRef ] As return_type [Static] [Export]

[Public|Private] Sub procedure_name [cdecl|stdcall|pascal] Overload [Alias "external_name"] [([parameter_list])] [Constructor [priority]] [Static] [Export]
..procedure body..
End Sub

[Public|Private] Function procedure_name [cdecl|stdcall|pascal] Overload [Alias "external_name"] [([parameter_list])] [ ByRef ] As return_type [Static] [Export]
..procedure body..
End Function

Description

In procedure declarations, Overload allows procedure names to be overloaded, that is, other procedures (regardless of whether to be subs or functions) can then be declared with the same name if their parameter lists are unique. Two parameter lists are unique if they contain a different number of parameters, or have parameters of different types. Note that this means that two or more procedures cannot be declared with the same name if they differ in return type alone.
A variadic procedure name can never be overloaded.

Once a procedure name has been declared overloaded, further declarations using the name need not specify Overload, but it is allowed.

Overload is not necessary in member procedure declarations, as they are always implicitly overloaded.

When calling an overloaded procedure name, a match score is calculated comparing the call argument types with the parameter types for each candidate procedure (major resolution on datatypes themselves, minor resolution on datatype sizes).
The highest matching score wins. If this highest score is too low, or if several overloaded procedures have the same highest score, the compiler then generates an error at compile time (no matching procedure, or ambiguous call).

Example

Declare Function SUM Overload (A As Integer,B As Integer) As Integer
Declare Function SUM Overload (A As Single,B As Single) As Single
Function SUM  (A As Integer,B As Integer) As Integer
   Function=A+B
End Function
Function SUM  (A As Single,B As Single) As Single
   Function=A+B
End Function
Dim As Integer A,B
Dim As Single A1,B1
A=2
B=3
A1=2.
b1=3.
Print SUM(A,B)
Print SUM (A1,B1)
Sleep

Differences from QB

  • New to FreeBASIC

See also

  • Declare
  • Sub, Function


Override
Method attribute; specifies that a method must override a virtual

Syntax

Type typename Extends basename
...
Declare Sub|Function|Operator|Property|Destructor ... ( [parameterlist] ) [[ ByRef ] As datatype] Override
...
End Type

Description

In method declarations, Override can be used to indicate that this method is expected to override a Virtual or Abstract method from the base class. Then the compiler will show an error if the method does not override anything (only a non-static method can override a virtual or abstract method).

Use of Override is not mandatory to override a virtual or abstract method, it is highly recommended, as it will help prevent inadvertent errors (name/signature not matching).

Override can only be specified on the method declaration in the UDT block, but not on the method body, because it is just a compile-time check in the context of the inheritance hierarchy, and does not affect the method in any way.

Override is only recognized as a keyword at the end of member procedure declarations. It can still be used as identifier elsewhere.

Example

Type A Extends Object
    Declare Virtual Sub f1( )
    Declare Virtual Function f2( ) As Integer
End Type

Type B Extends A
    Declare Sub f1( ) Override
    Declare Function f2( ) As Integer Override
End Type

Sub A.f1( )
End Sub

Function A.f2( ) As Integer
    Function = 0
End Function

Sub B.f1( )
End Sub

Function B.f2( ) As Integer
    Function = 0
End Function

Differences from QB

  • New to FreeBASIC

See also

  • Virtual, Abstract


Paint
Fills an area delimited by a border of a specified color

Syntax

Paint [target,] [STEP] (x, y)[, [paint][, [border_color]]]

Parameters

target
specifies buffer to draw on.
STEP
indicates that coordinates are relative
(x, y)
coordinates of the pixel on which to start the flood fill (paint)
paint
the color attribute or fill pattern
a numeric value indicates a color, while a string indicates a fill pattern
border_color
boundary color for the fill

Description

Graphics command to fill an area delimited by a border of specified color. Also known as 'flood-fill' or 'paint bucket'.

Paint can operate on the current work page as set by the ScreenSet statement or on the target Get/Put buffer, if specified.

Filling starts at specified (x,y) coordinates; if STEP is specified, these are relative to the last graphics cursor position. Coordinates are also affected by custom coordinates system set up by Window and/or View (Graphics) statements; clipping set by View also applies.

If the paint argument is a number, it is assumed a color in the same format used by the Color statement, and the region is flood-filled using that color. If paint is a String, the region will be filled using a pattern; the pattern is always 8*8 pixels, and the passed string must hold pixels data in a format dependent on the current color depth. The string holds pattern pixels row by row, and its size should be as follows:

For color depths 1, 2, 4 and 8:
size = 8 * 8 = 64
For color depths 15 and 16:
size = (8 * 8) * 2 = 128
For color depths 24 and 32:
size = (8 * 8) * 4 = 256

If the passed string is smaller, missing pixels will be 0. If the paint argument is omitted, normal filling is performed using the current foreground color set by Color. Flood-filling continues until pixels of the specified border color are found; if border_color is omitted, the current background color is assumed.

Warning: If the border is drawn with a transparent color (in conjunction with the GFX_ALPHA_PRIMITIVES option flag) and some pixels are overdrawn on it, the resultant (blended) color of these overdrawn pixels can cause a leak point through which the fill color escapes outside the border. So drawing a border with a transparent color is not recommended.

Example

' draws a white circle painted blue inside
Screen 13
Circle (160, 100), 30, 15
Paint (160, 100), 1, 15
Sleep

' draws a circle and fills it with a checkered pattern

'' choose the bit depth for the Screen
'' try setting this to other values: 8, 16 or 32

Const bit_depth = 8

'' function for returning a pixel color, represented as a string
'' returns a the string in the appropriate format for the current bit depth
Function paint_pixel( ByVal c As ULong, ByVal bit_depth_ As Integer ) As String

    If bit_depth_ <= 8 Then '' 8-bit:
        Function =  Chr( CUByte(c) )

    ElseIf bit_depth_ <= 16 Then '' 16-bit:
        Function = MKShort( c Shr 3 And &h1f Or _
                            c Shr 5 And &h7e0 Or _
                            c Shr 8 And &hf800 )

    ElseIf bit_depth_ <= 32 Then '' 32-bit:
        Function = MKL(c)

    End If

End Function

'' open a graphics window at the chosen bit depth
ScreenRes 320, 200, bit_depth

'' declare variables for holding colors
Dim As ULong c, c1, c2, cb

'' declare string variable for holding the pattern used in Paint
Dim As String paint_pattern = ""

'' set colors
If bit_depth <= 8 Then
    c1 = 7  ''pattern color 1
    c2 = 8  ''pattern color 2
    cb = 15 ''border color
Else
    c1 = RGB(192, 192, 192) '' pattern color 1
    c2 = RGB(128, 128, 128) '' pattern color 2
    cb = RGB(255, 255, 255) '' border color
End If

'' make the pattern to be used in Paint
For y As UInteger = 0 To 7
    For x As UInteger = 0 To 7

        '' choose the color of the pixel (c)
        If (x \ 4 + y \ 4) Mod 2 > 0 Then
            c = c1
        Else
            c = c2
        End If

        '' add the pixel to the pattern
        paint_pattern = paint_pattern + paint_pixel(c, bit_depth)

        '' the following line can be used if you want to draw the
        '' pattern tile in the top left hand corner of the screen:

        ' pset (x, y), c

    Next x
Next y

'' draw a circle with the border color
Circle (160, 100), 50, cb, , , 1.0

'' paint the circle region with paint_pattern, stopping at the border color
Paint (160, 100), paint_pattern, cb

'' pause before ending the program
Sleep

Differences from QB

  • target is new to FreeBASIC
  • In QB, the fill pattern was always 8-bits wide, and the height was the length of the string (up to 64). In FreeBASIC, the fill pattern is 8 pixels wide, independent of the color depth, and the height is always 8
  • The background color parameter supported by QB is not supported by the FreeBASIC version

See also

  • Screen (Graphics)


Palette
Customizes colors in modes with paletted colors

Syntax

Palette [Get] [index, color]
Palette [Get] [index, r, g, b]
Palette [Get] Using arrayname(idx)

Parameters

Get
indicates getting palette information rather than setting palette information
index
palette index
color
color attribute
r
red color component
g
green color component
b
blue color component
Using
indicates using array of color values
arrayname(idx)
array and index to get/set color attributes

Description

The Palette statement is used to retrieve or customize the current palette for graphics modes with a color depth of up to 8bpp; using Palette while in a mode with a higher color depth will have no effect. Calling Palette with no argument restores the default palette for current graphics mode, as set by the Screen (Graphics) statement.
The GfxLib sets a default palette when a Screen mode is initialized.

First form
If you specify index and color, these are dependent on the current mode:
Screen mode | index range | color range
1 | 0-3 | 0-15
2 | 0-1 | 0-15
7,8 | 0-15 | 0-15
9 | 0-15 | 0-63
11 | 0-1 | see below
12 | 0-15 | see below
13 to 21 | 0-255 | see below

In screen modes 1, 2, 7, 8 and 9 you can assign to each color index one of the colors in the available range. In other screen modes, the color must be specified in the form &hBBGGRR, where BB, GG and RR are the blue, green and red components ranging &h0-&h3F in hexadecimal (0-63 in decimal). If you don't like hexadecimal form, you can use the following formula to compute the integer value to pass to this parameter:
color = red Or (green Shl 8) Or (blue Shl 16)
Where red, green and blue must range 0-63. Please note that color values accepted by Palette are not the in the same form as returned by the RGB macro (the red and blue fields are inverted, and the range is different); this is for backward compatibility with QB.

Second form
In the second form, you specify the red, green and blue components for a palette entry directly, by calling Palette with 4 parameters. In this case r, g and b must be in the range 0-255.

Third form
Calling Palette Using allows to set a list of color values all at once; you should pass an array holding enough elements as the color indices available for your current graphics mode color depth (2 for 1bpp, 4 for 2bpp, 16 for 4bpp or 256 for 8bpp). The array elements must be integer color values in the form described above. The colors stored into arrayname starting with given idx index are then assigned to each palette index, starting with index 0.
Form 1 and 3 are for backward compatibility with QB; form 2 is meant to ease palette handling. Any change to the palette is immediately visible on screen.

If the Get option is specified, Palette retrieves instead of setting color values for the current palette. The parameters have the same meaning as specified for the form being used, but in this case color, r, g and b must be variables passed by reference that will hold the color RGB values on function exit.

Example

' Setting a single color, form 1.
Screen 15
Locate 1,1: Color 15
Print "Press any key to change my color!"
Sleep
' Now change color 15 hues to bright red
Palette 15, &h00003F
Sleep

' Getting a single color, form 2.
Dim As ULong r, g, b
Screen 13
Palette Get 32, r, g, b
Print "Color 32 hues:"
Print Using "Red:### Green:### Blue:###"; r; g; b
Sleep

' Getting whole palette, form 3.
Dim pal(0 To 255) As ULong
Screen 13
Palette Get Using pal
For i As Integer = 0 To 15
    Print Using "Color ## = &"; i; Hex(pal(i), 6)
Next i
Sleep

Differences from QB

  • QBasic did not support PALETTE GET to retrieve a palette.
  • QBasic did not allow passing individual red/green/blue values.

See also

  • Screen (Graphics)
  • Color
  • Using
  • Internal Pixel Formats
  • Default Palettes


PAN
Sets or returns channel stereo pan.

Syntax

Pan channel, position
position = Pan(channel)

Description

PAN moves one sfxlib channel left or right in the stereo field. It is useful for simple positional effects, such as making a sound effect come from the left side of the screen while music remains centered.

The first argument is the channel to adjust. The second argument is the pan position. A centered value keeps the channel balanced; values to either side move that channel toward one speaker.

Calling PAN as a function returns the current pan position for the channel.

Example

pan 0, -0.5
pan 1, 0.5
print "channel 0 pan="; pan(0)

Notes

  • Use BALANCE when the whole mix should move left or right.

See also

  • Sound Library Reference
  • Generated Sound Commands


pascal
Specifies a Pascal-style calling convention in a procedure declaration

Syntax

Sub name pascal [Overload] [Alias "alias"] ( parameters )
Function name pascal [Overload] [Alias "alias"] ( parameters ) [ ByRef ] As return_type

Description

In procedure declarations, pascal specifies that a procedure will use the pascal calling convention. In the Pascal calling convention, any parameters are to be passed (pushed onto the stack) in the same order in which they are listed, that is, from left to right. The procedures need not preserve the EAX, ECX or EDX registers, and must clean up the stack (pop any parameters) before it returns.

pascal is not allowed to be used with variadic procedure declarations (those with the last parameter listed as "...").

pascal is the default calling convention for procedures in Microsoft QuickBASIC, and is the standard convention used in the Windows 3.1 API.

If a procedure definition has a declaration (with calling convention explicit or by default) and the definition does not explicitly specify a calling convention, then the calling convention is implied by the declaration.

Example

Declare Function MyFunc pascal Alias "MyFunc" (MyParm As Integer) As Integer

Differences from QB

  • New to FreeBASIC

See also

  • cdecl, stdcall
  • Declare
  • Sub, Function


PCopy
Copies one graphical or text page onto another

Syntax

Declare Function PCopy ( ByVal source As Long = -1, ByVal destination As Long = -1 ) As Long

Usage

PCopy [ source ] [, destination ]

Parameters

source
page to copy from
destination
page to copy to

Return Value

Returns zero (0) if successful, or a non-zero error code to indicate a failure.

Description

Copies one graphical or text video page to another. Useful for drawing all graphics on one invisible page and copying it to the active visible page - creating smooth graphics and animation. Known as 'double buffering' or 'page flipping'.

source and destination refer to page numbers. The 'source' page is copied over the 'destination' page when pcopy is called.

If the source argument is omitted, the current working page is assumed. If the destination page is omitted, the current visible page is assumed.

PCopy is inactive if the destination page is locked.

The error code returned by PCopy can be checked using Err in the next line. The function version of PCopy returns directly the error code as a 32 bit Long.

Example

'Sets up the screen to be 320x200 in 8-bit color with 2 video pages.
ScreenRes 320, 200, 8, 2

'Sets the working page to 1 and the displayed page to 0
ScreenSet 1, 0

'Draws a circle moving across the top of the screen
For x As Integer = 50 To 269
    Cls                    'Clears the screen so we can start fresh
    Circle (x, 50), 50, 14 'Draws a yellow circle with a 50 pixel radius on page 1
    PCopy 1, 0             'Copies our image from page 1 to page 0
    Sleep 25               'Waits for 25 milliseconds.
Next x

'Wait for a keypress before the screen closes
Sleep

'' Compile with -lang fblite or qb

#lang "fblite"

'' Console mode example:

'' Set the working page number to 0, and the visible page number to 1
#if __FB_LANG__ = "QB"
Screen ,, 0, 1
#else
Screen , 0, 1
#endif

Dim As Integer i, frames, fps
Dim As Double t

t = Timer

Do
    '' Fill working page with a certain color and character
    Cls
    Locate 1, 1
    Color (i And 15), 0
    Print String$(80 * 25, Hex$(i, 1));
    i += 1

    '' Show frames per second
    Color 15, 0
    Locate 1, 1
    Print "fps: " & fps,
    If Int(t) <> Int(Timer) Then
        t = Timer
        fps = frames
        frames = 0
    End If
    frames += 1

    '' Copy working page to visible page
    PCopy

    '' Sleep 50ms per frame to free up cpu time
    Sleep 50, 1

    '' Run loop until the user presses a key
Loop Until Len(Inkey$)

Platform Differences

  • Maximum number of text pages in Windows is 4.
  • Maximum number of text pages in DOS is 8.
  • Maximum number of text pages in all other targets is 1.
  • Maximum number of graphics pages depends on what was specified when the Screen (Graphics) or ScreenRes statement was called.

Differences from QB

  • None

See also

  • ScreenCopy
  • Flip
  • Screen (Graphics)


Peek
Gets the value of an arbitrary type at an address in memory

Syntax

Declare Function Peek ( ByVal address As Any Ptr ) ByRef As UByte
Declare Function Peek ( datatype, ByVal address As Any Ptr ) ByRef As datatype

Usage

Peek( [ datatype, ] address )

Parameters

address
The address in memory to get the value from.
datatype
The type of value to get. If omitted, UByte is assumed.

Description

This procedure returns a reference to the value in memory given by a memory address, and is equivalent to:
*cast(ubyte ptr, address)
or
*cast(datatype ptr, address)
thus this keyword can also be used to assign a value to a memory location, similarly to Poke.

Note: When using Peek, the -exx compiler option does not add code for null-pointer checking (no nullity test on the value of address).

Example

Dim i As Integer, p As Integer Ptr
p = @i

Poke Integer, p, 420
Print Peek(Integer, p)

will produce the output:

420

Differences from QB

  • Peek did not support the datatype parameter in QB, and could only return individual bytes.
  • Peek returns a reference in FB, so can be used to set the memory contents of the address, like with Operator * (Value Of).
  • DEF SEG isn't needed anymore because the address space is 32-bit flat in FreeBASIC.

See also

  • Poke
  • Operator * (Value Of)


PLAY
Plays BASIC PLAY strings through sfxlib.

Syntax

Play music
Play channel, music
Play music1, music2
Play music1, music2, music3

Description

PLAY is a compact way to write a short tune or sound pattern as a string. The string uses the traditional BASIC idea of note letters, octave changes, tempo changes, and note lengths instead of one command per note.

A simple PLAY string can contain note letters such as C, D, E, F, G, A, and B. It can also contain controls such as T for tempo and O for octave. That makes it good for short melodies, jingles, alerts, and old-school game music.

The channel form starts the phrase on a chosen sfxlib channel. The multi-string forms are for simple layered playback, letting a program start more than one phrase together without writing a separate PLAY command for each one.

Example

play "t140 o4 cdefgab"
play 1, "o5 c8 e8 g8"
play "o3 c2", "o4 e2", "o4 g2"
sleep 1000, 1

Notes

  • Use TEMPO and OCTAVE to set defaults before PLAY.
  • Use NOTE when one note at a time is clearer than a PLAY string.

See also

  • Sound Library Reference
  • Generated Sound Commands


PMap
Maps coordinates between view and physical mapping.

Syntax

Declare Function PMap ( ByVal coord As Single, ByVal func As Long ) As Single

Usage

result = PMap( coord, func )

Parameters

coord
An expression indicating the coordinate to be mapped.
func
The mapping function number to be applied to given coordinate.

Return Value

The mapped coordinate value.

Description

This function converts a coordinate between view (as defined by the Window statement) and physical (as set by the View (Graphics) statement) mappings. Depending on the value of func, expr is used to compute a different mapping to be returned by PMap:

func value: | return value:
0 | Treats expr as x view coordinate and returns corresponding x physical coordinate.
1 | Treats expr as y view coordinate and returns corresponding y physical coordinate.
2 | Treats expr as x physical coordinate and returns corresponding x view coordinate.
3 | Treats expr as y physical coordinate and returns corresponding y view coordinate.

Example

ScreenRes 640, 480
Window Screen (0, 0)-(100, 100)
Print "Logical x=50, Physical x="; PMap(50, 0)   '' 320
Print "Logical y=50, Physical y="; PMap(50, 1)   '' 240
Print "Physical x=160, Logical x="; PMap(160, 2) '' 25
Print "Physical y=60, Logical y="; PMap(60, 3)   '' 12.5
Sleep

Differences from QB

  • None

See also

  • Window
  • View (Graphics)


Point
Returns the color attribute of a specified pixel coordinate

Syntax

result = Point( coord_x, coord_y [,buffer] )
or
result = Point( function_index )

Usage

coord_x
x coordinate of the pixel
coord_y
y coordinate of the pixel
buffer
the image buffer to read from
function_index
the type of screen coordinate to return: one of the values 0, 1, 2, 3

Return Value

The return datatype is a ULong.

If the x, y coordinates of a pixel are provided Point returns the color attribute at the specified coordinates, as an 8-bit palette index in 8 bpp indexed modes, a 24-bit RGB value in 16 bpp modes (upper 8 bits unused, limited precision of R,G,B), and a 32-bit RGB or RGBA value in 32 bpp modes (upper 8 bits unused or holding Alpha). Note that it does NOT return a 16-bit value (5 bits R + 6 bits G + 5 bits B).

If the argument is a function index, Point returns one of the graphics cursor coordinates set by the last graphics command.

Argument | Value Returned
0 | The current physical x coordinate.
1 | The current physical y coordinate.
2 | The current view x coordinate. This returns the same value as the POINT(0) function if the WINDOW statement has not been used.
3 | The current view y coordinate. This returns the same value as the POINT(1) function if the WINDOW statement has not been used.

Description

GfxLib Function with two different uses.
If supplied with two coordinates it reads the color of the pixel at the coordinate coord_x, coord_y of the screen, or of the buffer, if supplied.
The value return is a color index in a 256 or less color Screen (Graphics), and an RGB value in true color modes. If the coordinates are off-screen or off-buffer, -1 is returned

If supplied with a single value it returns the one of the coordinates of the graphics cursor as set by the last graphics command executed. If the last command was executed in a buffer, the values returned will be coordinates in the buffer. Arguments out of the range 0-3 will return 0.

The function Point does not work in text modes.

Speed note: while Point provides valid results, it is quite slow to call repeatedly due to the overhead of additional calculations and checks. Much better performance can be achieved by using direct memory access using the results obtained from ImageInfo and ScreenInfo/ScreenPtr.

Example

' Set an appropriate screen mode - 320 x 240 x 8bpp indexed color
ScreenRes 320, 240, 8

' Draw a line using color 12 (light red)
Line (20,20)-(100,100), 12

' Print the color of a point on the line
Print Point(20,20)

' Sleep before the program closes
Sleep

Output:
12
Version

Before fbc 1.08.0, the return datatype was an Integer.

Differences from QB

  • buffer is new to FreeBASIC
  • In 16 bpp and 32 bpp modes, a 32-bit value is returned instead of an 8-bit palette index

See also

  • PSet - write pixels
  • PMap
  • Color
  • View (Graphics)
  • Window
  • Internal pixel formats


PointCoord
Queries Draw's pen position in graphics mode

Syntax

Declare Function PointCoord( ByVal func As Long ) As Single

Usage

result = PointCoord( func )

Description

The PointCoord function can be used to query x and y position of the Draw pen in graphics mode. The result value depends on the passed func value:

func value: | return value:
0 | x physical coordinate, same as PMap( PointCoord( 2 ), 0 )
1 | y physical coordinate, same as PMap( PointCoord( 3 ), 1 )
2 | x view coordinate
3 | y view coordinate

Example

Screen 12

Print "--- Default window coordinate mapping ---"
Print "DRAW pen position, at the default (0,0):"
Print "Physical:", PointCoord( 0 ), PointCoord( 1 )
Print "View:", PointCoord( 2 ), PointCoord( 3 )

Draw "BM 50,50"
Print "DRAW pen position, after being moved to (50,50):"
Print "Physical:", PointCoord( 0 ), PointCoord( 1 )
Print "View:", PointCoord( 2 ), PointCoord( 3 )

Print "--- Changing window coordinate mapping ---"
Window Screen (-100, -100) - (100, 100)

Draw "BM 0,0"
Print "DRAW pen position, after being moved to (0,0):"
Print "Physical:", PointCoord( 0 ), PointCoord( 1 )
Print "View:", PointCoord( 2 ), PointCoord( 3 )

Draw "BM 50,50"
Print "DRAW pen position, after being moved to (50,50):"
Print "Physical:", PointCoord( 0 ), PointCoord( 1 )
Print "View:", PointCoord( 2 ), PointCoord( 3 )

Sleep

Differences from QB

  • New to FreeBASIC

See also

  • PMap
  • Window


(Pointer | Ptr)
A variable declaration type modifier

Syntax

... As DataType {Pointer | Ptr}

Description

Declares a pointer variable.
The variable type can be a predefined type or a user-defined type.

Operator @ (Address Of) operator or VarPtr are used to take the address of a variable. The Operator * (Value Of) operator is used to dereference the pointer, that is, access the actual value stored in the memory location the pointer is pointing at.

Example

' Create the pointer.
Dim p As Integer Ptr

' Create an integer value that we will point to using pointer "p"
Dim num As Integer = 98845

' Point p towards the memory address that variable "num" occupies.
p = @num

' Print the value stored in memory pointed to by pointer "p"
Print "Pointer 'p' ="; *p
Print

' Print the actual location in memory that pointer "p" points at.
Print "Pointer 'p' points to memory location:"
Print p

Dim p As ZString Pointer
Dim text As String
text = "Hello World!"
p = StrPtr(text) + 6
Print text
Print *p

'' Output:
'' Hello World!
'' World!

Type mytype
    a As Integer = 12345
End Type

Dim As mytype mt

Dim As mytype Ptr pmt
pmt = @mt

Print (*pmt).a  '' or Print pmt->a

'' Output:
'' 12345

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Pointer or __Ptr.

Differences from QB

  • New to FreeBASIC

See also

  • Allocate


Poke
Assigns a value to a location in memory.

Syntax

Declare Sub Poke ( ByVal address As Any Ptr, ByRef value As UByte )
Declare Sub Poke ( datatype, ByVal address As Any Ptr, ByRef value As datatype )

Usage

Poke [ datatype, ] address, value

Parameters

datatype
The type of data at the specified address. If omitted, UByte is assumed.
address
The location in memory to assign to.
value
The value to assign.

Description

Poke assigns a value to a location in memory. It is equivalent to
*cast(ubyte ptr, address) = value
or
*cast(datatype ptr, address) = value

When datatype is a user-defined type, Poke assigns value using the type's Operator Let.

Note: When using Poke, the -exx compiler option does not add code for null-pointer checking (no nullity test on the value of address).

Example

Dim i As Integer, p As Integer Ptr
p = @i

Poke Integer, p, 420
Print Peek(Integer, p)

Will produce the output:

420

Differences from QB

  • Only the byte form were supported in QB.
  • DEF SEG isn't needed anymore because the address space is 32-bit flat in FreeBASIC.

See also

  • Peek


Pos
Returns the horizontal (left to right) position of the text cursor

Syntax

Declare Function Pos ( ) As Long
Declare Function Pos ( ByVal dummy As Long ) As Long

Usage

result = Pos[ ( dummy ) ]

Parameters

dummy
An unused parameter retained for backward compatibility with QBASIC.

Return Value

Returns the horizontal position of the text cursor.

Description

Returns the horizontal (left to right) position of the text cursor. The leftmost column is number 1.

Example

Dim As Long p

'' print starting column position
p = Pos()
Print "position: "; p

'' print a string, without a new-line
Print "ABCDEF";

'' print new column position:
p = Pos()
Print: Print "position: "; p
Print

''position changes after each Print:
Print "Column numbers: "
Print Pos(), Pos(), Pos(), Pos(), Pos()

Differences from QB

  • The dummy parameter was not optional in QBASIC.

See also

  • CsrLin
  • Tab
  • Locate


Preserve
Used with ReDim to preserve contents will resizing an array

Syntax

ReDim Preserve array(...) [As datatype]

Description

Used with ReDim so that when an array is resized, data is not reset but is preserved. This means when the array is enlarged that only new data is reset, while the old data remains the same (but not necessarily at the same absolute addresses in memory).

NOTE: ReDim Preserve may not work as expected in all cases:
Preserve's current behavior is to keep the original data contiguous in memory, and only expand or truncate the size of the memory (if resizing is not possible, the whole original data block is first shifted to another memory location).
Its behavior (with a single dimension) is well-defined only when the upper bound is changed. If the lower bound is changed, the current result is that the data is in effect shifted to start at the new lower bound.
If there are multiple dimensions, only the upper bound of only the first dimension may be changed safely. If the first dimension is reduced, the existing mappable data may be lost. If lower-order dimensions are resized at all, the effects can be hard to predict (because multidimensional arrays are stored in row-major order : values differing only in the last index are contiguous).

Example

ReDim array(1 To 3) As Integer
Dim i As Integer

array(1) = 10
array(2) = 5
array(3) = 8

ReDim Preserve array(1 To 10)

For i = 1 To 10
    Print "array("; i; ") = "; array(i)
Next

Differences from QB

  • Preserve wasn't supported until PDS 7.1

See also

  • Dim
  • LBound
  • ReDim
  • UBound


PReset
Plots a single pixel

Syntax

PReset [target ,] [STEP] (x, y) [,color]

Parameters

target
specifies buffer to draw on.
STEP
indicates that coordinates are relative
(x, y)
coordinates of the pixel.
color
the color attribute.

Description

target specifies buffer to draw on. target may be an image created with ImageCreate or Get (Graphics). If omitted, target defaults to the screen's current work page.

(x, y) are the coordinates of the pixel. STEP if present, indicates that (x, y) coordinates are relative to the graphics cursor position. If omitted, (x, y) are relative to the upper left-hand corner of target. The x and y coordinates are affected by the last call to the View (Graphics) and Window statements, and respect the current clipping region as set by the View (Graphics) statement.

color specifies the color attribute. If omitted, color defaults to the current background color. See Color. color is graphics mode specific, see Color and Screen (Graphics) for details.

Example

Screen 13

'Set background color to 15
Color , 15

'Draw a pixel with the background color at 10, 10
PReset (10,10)

'Draw a pixel with the background color at Last x cord +10, Last y cord +10
PReset Step (10,10)
Sleep

Differences from QB

  • target is new to FreeBASIC

See also

  • PSet


Private
Specifies a procedure having internal linkage

Syntax

Private Sub procedure_name [cdecl|stdcall|pascal] [Overload] [Alias "external_name"] [([parameter_list])] [Constructor [priority]] [Static] [Export]
..procedure body..
End Sub

Private Function procedure_name [cdecl|stdcall|pascal] [Overload] [Alias "external_name"] [([parameter_list])] [ ByRef ] As return_type [Static] [Export]
..procedure body..
End Function

Description

In procedure definitions (forbidden at declaration line level), Private specifies that a procedure has internal linkage, meaning its name is not visible to external modules.
Therefore among the compiled modules, two procedures with the same identifier, but defined inside different modules, may exist if both are Private.

The compiler removes the Private procedures that are not called, but this does not currently work for Private procedures that are only called by other Private procedures that are not called themselves, because the first one appears as being called.

The Option Private statement allows procedures to be defined with internal linkage by default.

Example

'e.g.

Private Sub i_am_private
End Sub

Sub i_am_public
End Sub

Differences from QB

  • New to FreeBASIC

See also

  • Private: (Access Control)
  • Public
  • Option Private
  • Sub
  • Function


Private: (Access Control)
Specifies private member access control in a Type or Class

Syntax

Type typename
Private:
member declarations
End Type

Parameters

typename
name of the Type or Class
member declarations
declarations for fields, functions, or enumerations

Description

Private: indicates that member declarations following it have private access.
Private members are accessible only from inside a member procedure of their Type or Class. Seen from inside such a member procedure, it is as if the protected member is in fact public, and that regardless of the object on which the access operator is applied.

member declarations following Private: are private until a different access control specifier is given, like Public: or Protected:.

Members in a Type declaration are Public: by default if no member access control specifier is given.

Example

Type testing
  number As Integer
  Private:
    nome As String
  Declare Sub setNome( ByRef newnome As String )
End Type

Sub testing.setnome( ByRef newnome As String )
  '' This is OK. We're inside a member function for the type
  this.nome = newnome
End Sub

Dim As testing myVariable

'' This is OK, number is public
myVariable.number = 69

'' this would generate a compile error
'' - nome is private and we're trying to access it outside any of this TYPE's member functions
'' myVariable.nome = "FreeBASIC"

Dialect Differences

  • Available only in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Private
  • Public: (Access Control)
  • Protected: (Access Control)
  • Type


Operator Procptr (Procedure Pointer And Vtable Index)
Returns the address of any procedure, and the index in the vtable for a virtual/abstract member procedure.

Syntax

Declare Operator ProcPtr ( ByRef identifier As proctype [, Any|user_proctype ] ) As internal_proctype Ptr
Declare Operator ProcPtr ( ByRef identifier As proctype, virtual [ Any|user_proctype ] ) As Integer

Usage

result = ProcPtr ( identifier [, [virtual] [any|user_proctype] ] )

Parameters

identifier
A procedure identifier.
user_proctype
Any user type of procedure (sub/function, static/member, normal/virtual).
(internal_proctype has a supplementary first parameter byref as udt_name only for the member procedures of UDT udt_name)

Return Value

Returns the address of any procedure (first syntax), or the index in the vtable for a virtual/abstract member procedure (second syntax with additional qualifier virtual).
(any, ie any procedure signature, does not induce any particular selection (compared to its non-use), but just allows for writing ProcPtr always with 2 arguments)

Description

First syntax:
- This operator returns the address of a Sub or Function static/member procedure or member operator.
- The type of the return value corresponds to the internal signature of the procedure (user signature, plus a supplementary first parameter byref as udt_name for a member procedure) .
Second syntax (with qualifier virtual):
- This operator returns the zero based index in the vtable for a virtual/abstract member procedure or member operator.
- In case of overridden member procedure (polymorphism), the vtable index allows access, from the vtable of the used instance, to the address of the most derived override member procedure.

When using the user_proctype argument, ProcPtr syntax allows of getting procedure pointer or vtable index for based on parameter types (including sub/function type and return type if any).
This makes it possible to explicitly specify the right procedure to resolve procedure overloads, or make a check for parameter types (including sub/function type and return type if any) on non-overloaded procedures.

Operator @ (Address Of), when used with procedures, behaves the same as the first ProcPtr syntax without second argument.

Note:
- If the procedure member is abstract, then ProcPtr ( identifier [, any|user_proctype ] ) returns a null procedure pointer of the member procedure call signature (internal_proctype).
- If there is no vtable entry (or no vtable at all) then ProcPtr ( identifier , virtual [any|user_proctype] ) returns the special value of -1.

Example

' This example uses ProcPtr to demonstrate function pointers
Declare Function Subtract( x As Integer, y As Integer) As Integer
Declare Function Add( x As Integer, y As Integer) As Integer
Dim myFunction As Function( x As Integer, y As Integer) As Integer

' myFunction will now be assigned to Add
myFunction = ProcPtr( Add )
Print myFunction(2, 3)

' myFunction will now be assigned to Subtract.  Notice the different output.
myFunction = ProcPtr( Subtract )
Print myFunction(2, 3)

Function Add( x As Integer, y As Integer) As Integer
    Return x + y
End Function

Function Subtract( x As Integer, y As Integer) As Integer
    Return x - y
End Function

Sub s Overload()
End Sub

Sub s( ByVal i As Integer )
End Sub

'----- since fbc 1.09.0, ProcPtr supports a second parameter (optional):
Var s1 = ProcPtr( s, Sub() )
Var s2 = ProcPtr( s, Sub( ByVal i As Integer ) )

'----- before fbc 1.09.0, it was only possible with:
'Dim s1 As Sub()
's1 = ProcPtr( s )
'Dim s2 As Sub( Byval i As Integer)
's2 = ProcPtr( s )

' Since fbc 1.10.0, ProcPtr supports the member procedures/operators with various syntaxes

Type UDT Extends Object
    Dim As String s1
    Dim As String s2
    Declare Virtual Sub test()
    Declare Virtual Operator Cast() As String
End Type

Sub UDT.test()
    Print This.s1
End Sub

Operator UDT.Cast() As String
    Return This.s2
End Operator

Var testPtr1 = ProcPtr(UDT.test)
Var testPtr2 = ProcPtr(UDT.test, Any)
Var testPtr3 = ProcPtr(UDT.test, Sub())

Dim As Function(ByRef As UDT) As String castPtr1 = ProcPtr(UDT.cast)
Dim As Function(ByRef As UDT) As String castPtr2 = ProcPtr(UDT.cast, Any)
Dim As Function(ByRef As UDT) As String castPtr3 =  ProcPtr(UDT.cast, Function() As String)

Var testIndex1 = ProcPtr(UDT.test, Virtual)
Var testIndex2 = ProcPtr(UDT.test, Virtual Any)
Var testIndex3 = ProcPtr(UDT.test, Virtual Sub())

Dim As Integer castIndex1 = ProcPtr(UDT.cast, Virtual)
Dim As Integer castIndex2 = ProcPtr(UDT.cast, Virtual Any)
Dim As Integer castIndex3 = ProcPtr(UDT.cast, Virtual Function() As String)

Print testPtr1  '' absolue address value of UDT.test pointer
Print testPtr2  '' absolue address value of UDT.test pointer
Print testPtr3  '' absolue address value of UDT.test pointer
Print

Print castPtr1  '' absolue address value of UDT.Cast pointer
Print castPtr2  '' absolue address value of UDT.Cast pointer
Print castPtr3  '' absolue address value of UDT.Cast pointer
Print

Print testIndex1  '' vtable index of UDT.test
Print testIndex2  '' vtable index of UDT.test
Print testIndex3  '' vtable index of UDT.test
Print

Print castIndex1  '' vtable index of UDT.Cast
Print castIndex2  '' vtable index of UDT.Cast
Print castIndex3  '' vtable index of UDT.Cast
Print

Dim As UDT u
u.s1 = "Virtual Sub test()"
u.s2 = "Virtual Operator Cast() As String"

testPtr1(u)  '' execute u.test() through its procedure pointer
testPtr2(u)  '' execute u.test() through its procedure pointer
testPtr3(u)  '' execute u.test() through its procedure pointer
Print

Print castPtr1(u)  '' execute Cast(UDT, u) through its procedure pointer
Print castPtr2(u)  '' execute Cast(UDT, u) through its procedure pointer
Print castPtr3(u)  '' execute Cast(UDT, u) through its procedure pointer
Print

CPtr(Sub(ByRef As UDT), CPtr(Any Ptr Ptr Ptr, @u)[0][testIndex1])(u)  '' execute u.test() through its vtable index
CPtr(Sub(ByRef As UDT), CPtr(Any Ptr Ptr Ptr, @u)[0][testIndex2])(u)  '' execute u.test() through its vtable index
CPtr(Sub(ByRef As UDT), CPtr(Any Ptr Ptr Ptr, @u)[0][testIndex3])(u)  '' execute u.test() through its vtable index
Print

Print CPtr(Function(ByRef As UDT) As String, CPtr(Any Ptr Ptr Ptr, @u)[0][castIndex1])(u)  '' execute Cast(UDT, u) through its vtable index
Print CPtr(Function(ByRef As UDT) As String, CPtr(Any Ptr Ptr Ptr, @u)[0][castIndex2])(u)  '' execute Cast(UDT, u) through its vtable index
Print CPtr(Function(ByRef As UDT) As String, CPtr(Any Ptr Ptr Ptr, @u)[0][castIndex3])(u)  '' execute Cast(UDT, u) through its vtable index
Print

Sleep

' Since fbc 1.10.0, ProcPtr also allows to access the vtable index of a virtual/abstract member procedure/operator

Type Parent Extends Object
    Declare Abstract Sub VirtualTest()
    Declare Virtual Operator Cast() As String
    Declare Sub NormalTest()
End Type

Operator Parent.Cast() As String
    Return "Parent.Cast() As String"
End Operator

Sub Parent.NormalTest()
    Print "Parent.NormalTest()"
End Sub

Type Child Extends Parent
    Declare Virtual Sub VirtualTest()          '' or Declare Sub test()
    Declare Virtual Operator Cast() As String  '' or Declare Operator Cast() As String
    Declare Sub NormalTest()
End Type

Sub Child.VirtualTest()
    Print "Child.VirtualTest"
End Sub

Operator Child.Cast() As String
    Return "Child.Cast() As String"
End Operator

Sub Child.NormalTest()
    Print "Child.NormalTest()"
End Sub

Dim As Parent Ptr p = New Child

(*p).VirtualTest()      '' or p->VirtualTest()
Print Cast(Parent, *p)  '' or Print *p
(*p).NormalTest()       '' or p->NormalTest()
Print

#define RuntimeProcPtr(instance, procedure, signature...) _   '' pointer to procedure
    __FB_IIF__(ProcPtr(procedure, Virtual signature) >= 0, _  '' (the most derived override if exists)
               CPtr(TypeOf(ProcPtr(procedure, signature)), _
                    CPtr(Any Ptr Ptr Ptr, @(instance)) _
                    [0][ProcPtr(procedure, Virtual signature)]), _
               ProcPtr(procedure, signature))

'' Here, providing the procedure signature to the macro is useless
'' (because there are no procedure overloads to solve in this case)
RuntimeProcPtr(*p, Parent.VirtualTest)(*p)  '' execute (*p).VirtualTest() through its vtable index
Print RuntimeProcPtr(*p, Parent.Cast)(*p)   '' execute Cast(Parent, *p) through its vtable index
RuntimeProcPtr(*p, Parent.NormalTest)(*p)   '' execute (*p).NormalTest() through its compile address
Print

Delete p
Sleep

Version

  • Before fbc 1.10.0, the member procedures/operators were not supported.
  • Before fbc 1.09.0, the second argument (the optional) was not supported.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Procptr (but does not support qualifier virtual).

Differences from QB

  • New to FreeBASIC

See also

  • Sub
  • VarPtr
  • StrPtr
  • Any
  • Virtual
  • Pointers


Property
Declares or defines a property in a type or class

Syntax

{ Type | Class } typename
Declare Property fieldname () [ ByRef ] As datatype
Declare Property fieldname ( [ ByRef | ByVal ] new_value As datatype )
Declare Property fieldname ( [ ByRef | ByVal ] index As datatype ) [ ByRef ] As datatype
Declare Property fieldname ( [ ByRef | ByVal ] index As datatype, [ ByRef | ByVal ] new_value As datatype )
End { Type | Class }

Property typename.fieldname () [ ByRef ] As datatype [ Export ]
statements
End Property

Property typename.fieldname ( [ ByRef | ByVal ] new_value As datatype ) [ Export ]
statements
End Property

Property typename.fieldname ( [ ByRef | ByVal ] index As datatype ) [ ByRef ] As datatype [ Export ]
statements
End Property

Property typename.fieldname ( [ ByRef | ByVal ] index As datatype, [ ByRef | ByVal ] new_value As datatype ) [ Export ]
statements
End Property

Parameters

typename
name of the Type or Class
fieldname
name of the property
new_value
the value passed to property to be assigned
index
the property index value

Description

Property fields are used to get and set values of a Type or Class in the same way as other data fields except instead of a simple assignment to a field or a value retrieved from field, a procedure is executed.

typename is the name of the type for which the Property method is declared and defined. Name resolution for typename follows the same rules as procedures when used in a Namespace.

A Property may optionally have one index parameter. When indexed, properties are accessed as fieldname(Index) [ = value ].
A Property without index parameter must always be used without empty parentheses after fieldname.
A get-Property can also return a reference by specifying ByRef As return_type.

A hidden This parameter having the same type as typename is passed to the property procedure. This is used to access the fields of the Type or Class.

Note: A standard Property (get & set) does not work with combination operators (as "+="). But a result byref get-Property (as more generally any result byref function) works with combination operators.

Note: When a get-Property is defined with one index parameter, the fieldname= syntax can not be used to return a value. For such a get-Property, the property= syntax (in addition to the Return syntax) is only the one allowed.

Example

Type Vector2D
  As Single x, y
  Declare Operator Cast() As String
  Declare Property Length() As Single
  Declare Property Length( ByVal new_length As Single )
End Type

Operator Vector2D.cast () As String
  Return "(" + Str(x) + ", " + Str(y) + ")"
End Operator

Property Vector2D.Length() As Single
  Length = Sqr( x * x + y * y )
End Property

Property Vector2D.Length( ByVal new_length As Single )
  Dim m As Single = Length
  If m <> 0 Then
    '' new vector = old / length * new_length
    x *= new_length / m
    y *= new_length / m
  End If
End Property

Dim a As Vector2D = ( 3, 4 )

Print "a = "; a
Print "a.length = "; a.length
Print

a.length = 10

Print "a = "; a
Print "a.length = "; a.length

Output:
a = (3, 4)
a.length =  5

a = (6, 8)
a.length =  10
Property Indexing:
  '' True/False
Namespace BOOL
  Const False = 0
  Const True = Not False
End Namespace

Type BitNum
  Num As UInteger

    '' Get/Set Properties each with an Index.
  Declare Property NumBit( ByVal Index As Integer ) As Integer
  Declare Property NumBit( ByVal Index As Integer, ByVal Value As Byte )
End Type

  '' Get a bit by it's index.
Property BitNum.NumBit( ByVal Index As Integer ) As Integer
  Return Bit( This.Num, Index )
End Property

  '' Set a bit by it's index.
Property BitNum.NumBit( ByVal Index As Integer, ByVal Value As Byte )

    '' Make sure index is in Integer range.
  If Index >= ( SizeOf(This.Num) * 8 ) Then
    Print "Out of uInteger Range!"
    Exit Property
  Else
    If Index < 0 Then Exit Property
  End If

  If Value = BOOL.FALSE Then
    This.Num = BitReset( This.Num, Index )
  End If

  If Value = BOOL.TRUE Then
    This.Num = BitSet( This.Num, Index )
  End If

End Property

Dim As BitNum Foo

Print "Testing property indexing with data types:"
Print "FOO Number's Value: " & Foo.Num

  '' Set the bit in the number as true.
Foo.NumBit(31) = BOOL.TRUE
Print "Set the 31st bit of FOO"

  '' Print to see if our bit has been changed.
Print "FOO Number's Value: " & Foo.Num
Print "FOO 31st Bit Set? " & Foo.NumBit(31)
Sleep
Print ""

Output:
Testing property indexing with data types:
FOO Number's Value: 0
Set the 31st bit of FOO
FOO Number's Value: 2147483648
FOO 31st Bit Set? -1
See also

  • Class
  • Type


Protected: (Access Control)
Specifies protected member access control in a Type or Class

Syntax

Type typename
Protected:
member declarations
End Type

Parameters

typename
name of the Type or Class
member declarations
declarations for fields, functions, or enumerations

Description

Protected: indicates that member declarations following it have protected access.
Protected members are accessible only from inside a member procedure of their Type or Class, and classes which are derived from this Type or Class. Seen from inside such a member procedure, it is as if the protected member is in fact public, and that regardless of the object on which the access operator is applied.

member declarations following Protected: are protected until a different access control specifier is given, like Private: or Public:.

Members in a Type declaration are Public: by default if no member access control specifier is given.

Example

Type animal
  Dim As String animalName
  Protected:
    Dim As Integer serialNumber
End Type

Type dog Extends animal
  Dim As String masterName
  Declare Sub setSerialNumber ( ByVal number As Integer )
End Type

Sub dog.setSerialNumber ( ByVal number As Integer )
  '' This is OK. We're inside a member function of the derived type
  This.serialNumber = number
End Sub

Dim As dog d

'' This is OK, animalName is public
d.animalName = "Buddy"

'' this would generate a compile error:
'' - serialNumber is protected and we're trying to access it outside its type and the derived type
'' d.serialNumber = 123456789

' Example to illustrate the access control 'Protected' with a token provided by an admin right for an user right:
'    - The 'admin_right' type extends the 'user_right' type.
'    - Create directly an 'user_right' object is forbidden.
'         ('default user_right.constructor' access and 'copy user_right.constructor' access are 'Protected')
'    - The 'user_right' type has only the access right to get the token.
'         ('user_right.token' get-property access is 'Public' and 'user_right.token' set-property access is 'protected')
'    - The 'admin_right' type has the access rights to set and to get the token.
'         ('admin_right.token' get-property access and 'admin_right.token' set-property access are 'Public')
'
' An 'admin_right' object is created, and then a reference of type 'user_right' to this object is defined.
'    (create directly an 'user_right' object is forbidden)

Type user_right
  Public:
    Declare Property token () As String          '' 'Public' to authorize user_right token get
  Protected:
    Declare Constructor ()                       '' 'Protected' to forbid user_right object default-construction
    Declare Constructor (ByRef u As user_right)  '' 'Protected' to forbid user_right object copy-construction
    Declare Property token (ByRef s As String)   '' 'Protected' to forbid user_right token set
  Private:
    Dim As String user_right_token               '' 'Private' to forbid access from outside user_right
End Type

Constructor user_right ()  '' Default-constructor
End Constructor

Constructor user_right (ByRef u As user_right)  '' Protected copy-constructor
  This.user_right_token = u.user_right_token
End Constructor

Property user_right.token () As String  '' Public property user_right token get
  Return This.user_right_token
End Property

Property user_right.token (ByRef s As String)  '' Protected property user_right token set
  This.user_right_token = s
End Property

Type admin_right Extends user_right
  Public:
    Declare Property token () As String         '' 'Public' to authorize admin_right token get
    Declare Property token (ByRef s As String)  '' 'Public' to authorize admin_right token set
End Type

Property admin_right.token () As String  '' Public property admin_right token get
  Return Base.token                      '' 'Base.' to access to the base type property shadowed by this property name
End Property

Property admin_right.token (ByRef s As String)  '' Public property admin_right token set
  Base.token = s                                '' 'Base.' to access to the base type property shadowed by this property name
End Property

Dim As admin_right ar       '' Create an admin_right type object 'ar'
ar.token = "fxm123456789"   '' admin_right set the token for user_right
Print "'" & ar.token & "'"  '' admin_right get the user_right token
Print

Dim ByRef As user_right ur = ar  '' Create a user_right type reference 'ur' to the 'ar' instance of admin_right type
Print "'" & ur.token & "'"       '' user_right get its token
'ur.token = "fxm0"               '' Error: Illegal member access, USER_RIGHT.TOKEN.property.set (user_right cannot set its token)

'Dim As user_right ur1       '' Error: The default constructor has no public access
'Dim As user_right ur2 = ar  '' Error: Constructor has no public access

Sleep

Dialect Differences

  • Available only in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Class
  • Private: (Access Control)
  • Public: (Access Control)
  • Type


Pset (Statement)
Plots a single pixel

Syntax

PSet [target ,] [STEP] (x, y) [,color]

Parameters

target
specifies buffer to draw on.
STEP
indicates that coordinates are relative
(x, y)
coordinates of the pixel.
color
the color attribute.

Description

target specifies buffer to draw on. target may be an image created with ImageCreate or Get (Graphics). If omitted, target defaults to the screen's current work page.

(x, y) are the coordinates of the pixel. STEP if present, indicates that (x, y) coordinates are relative to the graphics cursor position. If omitted, (x, y) are relative to the upper left-hand corner of target. The x and y coordinates are affected by the last call to the View (Graphics) and Window statements, and respect the current clipping region as set by the View (Graphics) statement.

color specifies the color attribute, as an 8-bit palette index in 8 bpp indexed modes, a 24-bit RGB value in 16 bpp modes (upper 8 bits of the integer unused, limited precision of R,G,B), and a 32-bit RGB or RGBA value in 32 bpp modes (upper 8 bits unused or holding Alpha). Note that it does NOT accept a 16-bit value (5 bits R + 6 bits G + 5 bits B). If omitted, color defaults to the current foreground color.

Speed note: while PSet provides valid results, it is quite slow to call repeatedly due to the overhead of additional calculations and checks. Much better performance can be achieved by using direct memory access using the results obtained from ImageInfo and ScreenInfo/ScreenPtr.

Example

' Set an appropriate screen mode - 320 x 240 x 8bpp indexed color
ScreenRes 320, 240, 8

' Plot a pixel at the coordinates 100, 100, Color 15. (white)
PSet (100, 100), 15
' Confirm the operation.
Locate 1: Print "Pixel plotted at 100, 100"
' Wait for a keypress.
Sleep

' Plot another pixel at the coordinates 150, 150, Color 4. (red)
PSet (150, 150), 4
' Confirm the operation.
Locate 1: Print "Pixel plotted at 150, 150"
' Wait for a keypress.
Sleep

' Plot a third pixel relative to the second, Color 15. (white)
' This pixel is given the coordinates 60, 60. It will be placed
' at 60, 60 plus the previous coordinates (150, 150), thus plotting at 210, 210.
PSet Step (60, 60), 15
' Confirm the operation.
Locate 1: Print "Pixel plotted at 150 + 60, 150 + 60"
' Wait for a keypress
Sleep

' Explicit end of program
End

Differences from QB

  • target is new to FreeBASIC
  • In 16 bpp and 32 bpp modes, a 32-bit value is required instead of an 8-bit palette index

See also

  • Point - read out pixels
  • PReset
  • View (Graphics)
  • Window
  • Internal pixel formats


Pset (Graphics Put)
Parameter to the Put graphics statement which selects PSet as the blitting method

Syntax

Put [ target, ] [ STEP ] ( x,y ), source [ ,( x1,y1 )-( x2,y2 ) ], PSet

Parameters

PSet
Required.

Description

The PSet method copies the source pixel values onto the destination pixels.

This is the simplest Put method. The pixels in the destination buffer are directly overwritten with the pixels in the source buffer. No additional operations are done, and there are no color values that are treated as transparent. It has the same effect as PSetting each pixel individually.

Example

'' set up a screen: 320 * 200, 16 bits per pixel
ScreenRes 320, 200, 16
Line (0, 0)-(319, 199), RGB(0, 128, 255), bf

'' set up an image with the mask color as the background.
Dim img As Any Ptr = ImageCreate( 33, 33, RGB(255, 0, 255) )
Circle img, (16, 16), 15, RGB(255, 255, 0),     ,     , 1, f
Circle img, (10, 10), 3,  RGB(  0,   0, 0),     ,     , 2, f
Circle img, (23, 10), 3,  RGB(  0,   0, 0),     ,     , 2, f
Circle img, (16, 18), 10, RGB(  0,   0, 0), 3.14, 6.28

Dim As Integer x = 160 - 16, y = 100 - 16

'' Put the image with PSET
Put (x, y), img, PSet

'' free the image memory
ImageDestroy img

'' wait for a keypress
Sleep

Differences from QB

  • None

See also

  • PSet
  • Put (Graphics)


Public
Specifies a procedure having external linkage.

Syntax

Public Sub procedure_name [cdecl|stdcall|pascal] [Overload] [Alias "external_name"] [([parameter_list])] [Constructor [priority]] [Static] [Export]
..procedure body..
End Sub

Public Function procedure_name [cdecl|stdcall|pascal] [Overload] [Alias "external_name"] [([parameter_list])] [ ByRef ] As return_type [Static] [Export]
..procedure body..
End Function

Description

In procedure definitions (forbidden at declaration line level), Public specifies that a procedure has external linkage, meaning its name is visible to external modules. If Public or Private is not specified, a procedure is defined as if Public was specified.

Example

Private Sub i_am_private
End Sub

Public Sub i_am_public
End Sub

Differences from QB

  • New to FreeBASIC

See also

  • Public: (Access Control)
  • Private
  • Option Private
  • Sub
  • Function


Public: (Access Control)
Specifies public member access control in a Type or Class

Syntax

Type typename
Public:
member declarations
End Type

Parameters

typename
name of the Type or Class
member declarations
declarations for fields, functions, or enumerations

Description

Public: indicates that member declarations following it have public access.
Public members are accessible with any usage of the Type or Class.

member declarations following Public: are public until a different access control specifier is given, like Private: or Protected:

Members in a Type declaration are Public: by default if no member access control specifier is given.

Example

Type testing
  Private:
    nome As String
  Public:
    number As Integer
  Declare Sub setNome( ByRef newnome As String )
End Type

Sub testing.setnome( ByRef newnome As String )
  this.nome = newnome
End Sub

Dim As testing myVariable

'' We can access these members anywhere since
'' they're public
myVariable.number = 69 ''
myVariable.setNome( "FreeBASIC" )

Dialect Differences

  • Available only in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Class
  • Private: (Access Control)
  • Protected: (Access Control)
  • Public
  • Type


Put (Graphics)
Copies an image on to another image or screen

Syntax

Put [target, ] [ [STEP](x, y), source [, (x1, y1)-[STEP](x2, y2) ] [, method [, ( alphaval|value|blender [, param]) ] ]

Parameters

target
is the address of the buffer where the image is to be drawn. If it's omitted, the image gets blitted to screen. See below.
[STEP](x, y)
specify offsets from the upper-left corner of the destination buffer, or screen, that the image gets drawn to. STEP indicates that (x, y) offsets are relative to the current graphics cursor position.
source
is the address of the buffer of the image to be drawn. See below.
(x1, y1)-[STEP](x2, y2)
specify a rectangular area in the source buffer to draw. If omitted, the entire buffer is drawn. STEP indicates that x2 and y2 are relative to x1 and y1, respectively.
method
specifies the method used to draw the image to the destination buffer, and can be any one of the following (the default method is XOR):

Background-independent methods
PSet : Source pixel values are copied without modification.
PRESET : Source pixel values are 1's-complement negated before being copied.
Trans : Source pixel values are copied without modification. Does not draw source pixels of mask color. See below.
Background-dependent methods
And (Graphics Put) : Destination pixels are bitwise Anded with source pixels. See below.
Or : Destination pixels are bitwise Ored with source pixels. See below.
Xor : Destination pixels are bitwise Xored with source pixels. See below.
Alpha : Source is blended with a transparency factor specified either in the value parameter, or in the image's individual pixels. See below.
Add: Source is multiplied by a value and added with saturation to the destination. See below.
Custom : Uses a user-defined function to perform blending the source with the destination. See below.

value
is a 0..255 value specifying the transparency value for an ADD or ALPHA method blit.
blender
specifies the address of a user-defined function to be called in a CUSTOM method blit. See below.
param
specifies a parameter to pass to the custom blender.

Description

The Put statement can be used to draw an image onto another image or screen. The x and y coordinates are affected by the last call to the View and Window statements, and plotted image respects the current clipping region set by last call to the View statement. The source image can overflow the destination buffer or screen (then the copied image is clipped accordingly).

Valid Image Buffers
The source and target image buffers must be valid image buffers. Valid image buffers are created using the Get or ImageCreate statements. Valid image buffers can be specified in a Put statement using an array name with optional index (but never with empty parentheses), or a pointer with optional index.

Drawing methods
Depending on the method used, the existing pixel values in the destination buffer are used to calculate the pixel values that are actually drawn. The PSET, PRESET and TRANS methods do not use the destination buffer for calculating final pixel values, while the AND, OR, XOR, ALPHA and ADD methods do. Images that are drawn with these latter methods will look differently depending on the content of the destination buffer.

Different pixel formats
The pixel format of an image buffer must be compatible with the current graphics mode color depth; that is, if you acquire an image using Get and you later change screen mode via the Screen (Graphics) statement, the image data may not be valid in the new graphics mode, and you may not be able to draw it on the screen. You should note however that you will always be able to draw image buffers onto other image buffers via Put as long as these buffers were created with the same depth.

The AND, OR and XOR methods produce different results depending on the current color depth, as pixels are stored in different formats; see Internal pixel formats for details.

Mask Color
The TRANS, ALPHA and ADD methods do not draw pixels in the source image that use the mask color. The mask color depends on target (being it an image buffer or the screen) depth: in depths up to 8 bpp (paletted modes) it is equal to color index 0, while in hi/truecolor depths (16 and 32 bpp) it is equal to magenta, which is RGB(255, 0, 255). Note that in 32 bpp modes the alpha value of a color does not affect the identification of the transparent color; only the lower 24 bits are used to identify it. See Internal pixel formats for details.

Alpha drawing
The ALPHA method can be used in two modes. If the value parameter is specified, this is used to specify the level of transparency for the whole image to be drawn; a value of 0 will draw a completely transparent image, whereas a value of 255 will draw a completely solid one. This mode works only when drawing onto hi/truecolor targets (16 and 32 bpp).
If the value parameter is omitted, the ALPHA method will take the alpha level value on a per-pixel basis, allowing to draw images with an alpha channel (certain parts of the image can be made more or less transparent than others). This mode works only with 32 bpp image buffers, as this is the only color depth that allows for an embedded alpha value in each pixel.

Dealing with the alpha channel
Normally Put only allows to draw image buffers onto targets with the same depth, but there is an exception. When drawing an 8 bpp image buffer onto a 32 bpp target and the ALPHA method is used, the 8 bpp source image is drawn into the alpha channel of the 32 bpp target. This allows to easily set the whole alpha channel of an image without having to deal with low level access of its pixel data.

Custom Blend Function
The CUSTOM method uses a user-defined function to calculate the final pixel values to be drawn to the destination buffer. This function will be called once for every pixel of the source image, and will receive the source and destination pixel values, and a data pointer passed by the Put function. The pixel value returned will be the value used to draw to the destination buffer. The function has the form:

Declare Function identifier ( ByVal source_pixel As ULong, ByVal destination_pixel As ULong, ByVal parameter As Any Ptr ) As ULong
identifier is the name of the function. Can be anything.
source_pixel is the current pixel value of the source image.
destination_pixel is the current pixel value of the destination image.
parameter is the parameter that is passed by the Put command. It should be a data Pointer. If omitted, its value will be zero.

Example

The following program gives a simple example of how to Put an image to the screen, including setting up an image buffer, and freeing its memory after.
'' set up the screen and fill the background with a color
ScreenRes 320, 200, 32
Paint (0, 0), RGB(64, 128, 255)

'' set up an image and draw something in it
Dim img As Any Ptr = ImageCreate( 32, 32, RGB(255, 0, 255) )
Circle img, (16, 16), 15, RGB(255, 255, 0),     ,     , 1, f
Circle img, (10, 10), 3,  RGB(  0,   0, 0),     ,     , 2, f
Circle img, (23, 10), 3,  RGB(  0,   0, 0),     ,     , 2, f
Circle img, (16, 18), 10, RGB(  0,   0, 0), 3.14, 6.28

'' PUT the image in the center of the screen
Put (160 - 16, 100 - 16), img, Trans

'' free the image memory
ImageDestroy img

'' wait for a keypress
Sleep

The following example shows how to allocate memory for an image, draw that image using various methods, including a custom blender, and free the memory for the image:
Declare Function checkered_blend( ByVal src As ULong, ByVal dest As ULong, ByVal param As Any Ptr ) As ULong

   Screen 14, 32                                   '' set 320*240*32 gfx mode

   Dim As Any Ptr sprite
   Dim As Integer counter = 0

   sprite = ImageCreate( 32, 32 )                  '' allocate memory for 32x32 sprite

   Line sprite, ( 0, 0 )-( 31, 31 ), RGBA(255, 0, 0, 64), bf  '' draw a sprite ...
   Line sprite, ( 4, 4 )-( 27, 27 ), RGBA(255, 0, 0, 192), bf
   Line sprite, ( 0, 0 )-( 31, 31 ), RGB(0, 255, 0), b
   Line sprite, ( 8, 8 )-( 23, 23 ), RGBA(255, 0, 255, 64), bf
   Line sprite, ( 1, 1 )-( 30, 30 ), RGBA(0, 0, 255, 192)
   Line sprite, ( 30, 1 )-( 1, 30 ), RGBA(0, 0, 255, 192)

   Cls
   Dim As Integer i : For i = 0 To 63              '' draw the background
      Line( i,0 )-( i,240 ), RGB( i * 4, i * 4, i * 4 )
   Next i

   '' demonstrate all drawing methods ...
   Put( 8,14 ), sprite, PSet
   Put Step( 16,20 ), sprite, PReset
   Put Step( -16,20 ), sprite, And
   Put Step( 16,20 ), sprite, Or
   Put Step( -16,20 ), sprite, Xor
   Put Step( 16,20 ), sprite, Trans
   Put Step( -16,20 ), sprite, Alpha, 96
   Put Step( 16,20 ), sprite, Alpha
   Put Step( -16,20 ), sprite, Add, 192
   Put Step( 16,20 ), sprite, Custom, @checkered_blend, @counter

   '' print a description near each demo
   Draw String (100, 26), "<- pset"
   Draw String Step (0, 20), "<- preset"
   Draw String Step (0, 20), "<- and"
   Draw String Step (0, 20), "<- or"
   Draw String Step (0, 20), "<- xor"
   Draw String Step (0, 20), "<- trans"
   Draw String Step (0, 20), "<- alpha (uniform)"
   Draw String Step (0, 20), "<- alpha (per pixel)"
   Draw String Step (0, 20), "<- add"
   Draw String Step (0, 20), "<- custom"

   ImageDestroy( sprite )                          '' free allocated memory for sprite
   Sleep : End 0

'' custom blender function: chequered put
Function checkered_blend( ByVal src As ULong, ByVal dest As ULong, ByVal param As Any Ptr ) As ULong
   Dim As Integer Ptr counter
   Dim As ULong pixel

   counter = Cast(Integer Ptr, param)
   pixel = IIf(((*counter And 4) Shr 2) Xor ((*counter And 128) Shr 7), src, dest)
   *counter += 1
   Return pixel
End Function

Differences from QB

  • target is new to FreeBASIC
  • The TRANS, ALPHA, ADD and CUSTOM methods are new to FreeBASIC
  • FB uses a different image format internally, which is unsupported by QB
  • QB throws a run-time error instead of clipping out-of-bounds images
  • In QB, only arrays can be specified as source images

See also

  • Custom (Graphics Put)
  • Put (File I/O)
  • Get (Graphics)
  • ImageCreate
  • Alpha
  • Internal pixel formats


Put (File I/O)
Writes data from a buffer to a file

Syntax

Put #filenum As Long, [position As LongInt], data As Any [, amount As UInteger]
Put #filenum As Long, [position As LongInt], data As String
Put #filenum As Long, [position As LongInt], data() As Any

Usage

Put #filenum, position, data [, amount]
varres = Put (#filenum, position, data [, amount])

Parameters

filenum
The value passed to Open when the file was opened.
position
Is the position where Put must start in the file. If the file was opened For Random, the position is in records, else it is given in bytes. If omitted, writing starts at the present file pointer position. The position is 1-based: i.e. the first record or byte of a file is at position 1.
If position is omitted or zero (0), file writing will start from the current file position.
data
Is the buffer where data is written from. It can be a numeric variable, a string, an array or a user-defined type (including referenced by This). The operation will try to transfer to disk the complete variable, unless amount is given. For a user-defined type instance, the data impacted is only the non-static data members.
When putting arrays, data should be followed by an empty pair of brackets: '()'. Put will write all of the data in the array. amount is not allowed.
When putting Strings, the number of bytes written is the same as the number of bytes in the string data. amount is not allowed.
Note: If you want to write values from a buffer, you should NOT pass a pointer to the buffer; instead you should pass the first variable in the buffer (this can be done by dereferencing the pointer with Operator * (Value Of)). If you pass a pointer directly, then Put will put the memory from the pointer variable, not the memory it points to.
amount
Makes Put write to file amount consecutive variables to the file - i.e. it writes ( amount * SizeOf(data) ) bytes of data, starting at data's location in memory, into the file. If amount is omitted it defaults to 1, meaning that Put just writes a single variable.

Return Value

Put() returns a 32 bit Long: 0 on success; nonzero on error. "disk full" is considered as an error, and results in return code 3. An "exact" amount of data written before is not available, and wouldn't be really useful anyway.

Description

Writes binary data from a buffer variable to a file opened in Binary or Random mode.

Put can be used as a function, and will return 0 on success or an error code on failure.

For files opened in Random mode, the size in bytes of the data to write must match the specified record size.

Note:
- If a real [w/z]string variable is passed to Put, the amount parameter should be forbidden as it is when passing a string. Do not use. Otherwise, it is dangerously used to multiply the string length to be written to the file, but possibly by overflowing outside the provided [w/z]string buffer.
- If a dereferenced [w/z]string pointer is passed to Put, the amount parameter is not taken into account as it is when passing a dereferenced numeric pointer. Do not use. But instead of respecting the amount parameter, the pointed buffer is written to the file up to the zero element (terminal element which is excluded).
- For finer granularity, any [w/z]string variable can be safely passed to Put as numeric buffer by providing the first numeric element (an indexed [w/z]string variable, or a dereferenced [w/z]string pointer then indexed) and the number of numeric elements to be processed.

Note:
- Using Put # is naturally dedicated to Binary/Random Access file mode.
- It is also allowed in Ouput/Append Access file mode, but this was never well tested and results may vary.

Example

' Create variables for the file number, and the number to put
Dim As Long f
Dim As Long value

' Find the first free file number
f = FreeFile()

' Open the file "file.ext" for binary usage, using the file number "f"
Open "file.ext" For Binary As #f

  value= 10

  ' Write the bytes of the integer 'value' into the file, using file number "f"
  ' starting at the beginning of the file (position 1)
  Put #f, 1, value

' Close the file
Close #f

' Create an integer array
Dim buffer(1 To 10) As Integer
For i As Integer = 1 To 10
    buffer(i) = i
Next

' Find the first free file file number
Dim f As Long
f = FreeFile()

' Open the file "file.ext" for binary usage, using the file number "f"
Open "file.ext" For Binary As #f
' Write the array into the file, using file number "f"
' starting at the beginning of the file (position 1)
Put #f, 1, buffer()

' Close the file
Close #f

Dim As Byte Ptr lpBuffer
Dim As Long hFile
Dim As Integer Counter
Dim As UInteger Size

Size = 256

lpBuffer = Allocate(Size)
For Counter = 0 To Size-1
  lpBuffer[Counter] = (Counter And &HFF)
Next

' Get free file file number
hFile = FreeFile()

' Open the file "test.bin" in binary writing mode
Open "test.bin" For Binary Access Write As #hFile

  ' Write 256 bytes from the memory pointed to by lpBuffer
  Put #hFile, , lpBuffer[0], Size

' Close the file
Close #hFile

' Free the allocated memory
Deallocate lpBuffer

' 'THIS' can be used as argument for writing/filling all non-static data of an UDT instance to/from a file

Type UDT
    Dim As String * 32 s
    Dim As Double d
    Declare Sub Save(ByRef filename As String)
    Declare Sub Load(ByRef filename As String)
End Type

Sub UDT.Save(ByRef filename As String)
    Dim As Integer f
    f = FreeFile()
    Open filename For Binary As #f
    Put #f, , This  '' writes all non-static data of the UDT instance to the file
    Close #f
End Sub

Sub UDT.Load(ByRef filename As String)
    Dim As Integer f
    f = FreeFile()
    Open filename For Binary As #f
    Get #f, , This  '' fills all non-static data of the UDT instance from the file
    Close #f
End Sub

Dim As UDT u1
u1.s = "PI number"
u1.d = 3.14159
u1.Save("file.ext")

Dim As UDT u2
u2.Load("file.ext")
Print u2.s
Print u2.d

Differences from QB

  • Put can write full arrays as in VB or, alternatively, write a multiple of the data size from buffer's memory location.
  • Put can be used as a function in FB, to find the success/error code returned without having to use error handling procedures.

See also

  • Put (Graphics) different usage of same keyword
  • Get (File I/O)
  • Open
  • Close
  • Random
  • Binary
  • FreeFile


Random
Specifies file or device to be opened for random access mode

Syntax

Open filename for Random [Access access_type] [Lock lock_type] as [#]filenum [Len = record_length]

Parameters

filename
file name to open
access_type
indicates whether the file may be read from, written to or both
lock_type
locking to be used while the file is open
filenum
unused file number to associate with the open file
record_length
the size of the record used for the file

Description

Opens a file or device for reading and/or writing binary data in the given file filenum, with records of size record_length.
If the file does not exist, a new file will be created, otherwise any data existing in the file is preserved by Open. The file pointer is initialized by Open at the start of the file, at record number 1. File operations move the file position in steps of record_length bytes.
This file mode uses an user-defined Type buffer variable to read/write full records in a file. The buffer variable uses to include several fields.
The data is saved in binary mode, in the same internal format FreeBASIC uses, by means of Get # and Put #.

filename must be string expression resulting in a legal file name in the target OS, without wildcards. The file will be sought for in the present directory, unless a path is given.
Access_type - By default Random mode allows to both read and write the file, unless an Access type is specified, it must be one of:
    • Read - the file is opened for input only
    • Write - the file is opened for output only
    • Read Write - the file is opened for input and output (the default)

Lock_type indicates the way the file is locked for other processes (users or threads), it is one of:
    • Shared - The file can be freely accessed by other processes
    • Lock Read - The file can't be opened simultaneously for reading
    • Lock Write - The file can't be opened simultaneously for writing
    • Lock Read Write - The file cannot be opened simultaneously by other processes.
If no lock type is stated, the file will be Shared for other threads of the program and Lock Read Write for other programs.
Lock and Unlock can be used to restrict temporally access to parts of a file.

filenum is a valid FreeBASIC file number (in the range 1..255) not being used for any other file presently open. This number identifies the file for the rest of file operations. A free file number can be found using the FreeFile function.

record_length is the amount of bytes the file pointer will move for each individual Get and Put, it must match the size of the buffer variable used when Getting and Putting data. If omitted, it defaults to 128.

Example

'' This example generates a test file and then lets you view random records
'' that are read live from the file.

Type Entry
    slen As Byte
    sdata As String * 10
End Type

Dim u As Entry
Dim s As String

Open "testfile" For Random As #1 Len = SizeOf(Entry)

'' Write out 9 records with predefined data
For i As Integer = 1 To 9
    Read s
    u = Type( Len(s), s )
    Put #1, i, u
Next

Data ".,-?!'@:", "abc",      "def"
Data "ghi",      "jkl",      "mno"
Data "pqrs",     "tuv",      "wxyz"

'' Let the user view records by specifying their index number
Do
    Dim i As Integer
    Input "Record number: ", i
    If i < 1 Or i > 9 Then Exit Do

    Get #1, i, u
    Print i & ": " & Left( u.sdata, u.slen )
    Print
Loop

Close #1

Type ScoreEntry Field = 1
    As String * 20 Name
    As Single score
End Type

Dim As ScoreEntry entry

'' Generate a fake boring highscore file
Open "scores.dat" For Random Access Write As #1 Len = SizeOf(entry)
For i As Integer = 1 To 10
    entry.name = "Player " & i
    entry.score = i
    Put #1, i, entry
Next
Close #1

'' Read out and display the entries
Open "scores.dat" For Random Access Read As #1 Len = SizeOf(entry)
For i As Integer = 1 To 10
    Get #1, i, entry
    Print i & ":", entry.name, Str(entry.score), entry.score
Next
Close #1

Differences from QB

  • Care must be taken with dynamic or fixed length strings inside user defined types (UDT), see the warning at Type.
  • The keyword Field can only be used with Type to specify the packing of the UDT.

See also

  • Open
  • Binary
  • Get #
  • Put #


Randomize
Seeds the random number generator

Syntax

Declare Sub Randomize ( ByVal seed As Double = -1.0, ByVal algorithm As Long = 0 )

Usage

Randomize [ seed ][, algorithm ]

Parameters

seed
A Double seed value for the random number generator, but the fractional part is clipped for all algorithms except algorithm #4 (see below). If omitted, a value based on Timer will be used instead.
algorithm
An integer value to select the algorithm (see the standard header "fbprng.bi" for available algorithms). If omitted, the default algorithm for the current language dialect is used.

Description

Sets the random seed that helps Rnd generate random numbers, and selects the algorithm to use.
The constants for algorithm are defined in fbprng.bi. In the -lang fb dialect, these constants are part of the FB Namespace.
Valid values for algorithm are:
FB_RND_AUTO (0) - Default for current language dialect. This is algorithm FB_RND_MTWIST (3) in the -lang fb dialect, FB_RND_QB (4) in the -lang qb dialect and FB_RND_CRT (1) in the -lang fblite dialect.
FB_RND_CRT (1) - Uses the C runtime library's rand() function. This will give different results depending on the platform.
FB_RND_FAST (2) - Uses a fast implementation. This should be stable across all platforms, and provides 32-bit granularity, reasonable degree of randomness.
FB_RND_MTWIST (3) - Uses the Mersenne Twister. This should be stable across all platforms, provides 32-bit granularity, and gives a high degree of randomness.
FB_RND_QB (4) - Uses a function that is designed to give the same random number sequences as QBASIC. This should be stable across all platforms, and provides 24-bit precision, with a low degree of randomness.
FB_RND_REAL (5) - Available on Win32 and Linux, using system features (Win32 Crypto API, Linux /dev/urandom) to provide cryptographically random numbers. If those system APIs are unavailable, algorithm FB_RND_MTWIST (3) will be used instead.

For any given seed, each algorithm will produce a specific, deterministic sequence of numbers for that seed. If you want each call to Randomize to produce a different sequence of numbers, a seed that is not quite predictable should be used - for example, the value returned from Timer. Omitting the seed parameter will use a value based on this.

Note: for all algorithms except algorithm #4, because the fractional part of the seed is clipped, the using the Timer value directly as a parameter will produce the same seed if used more than once in the same second. However, it is generally not worth calling Randomize twice with unpredictable seeds anyway, because the second sequence will be no more random than the first, or even possibly worse by inducing sequence overlapping. In most cases, the Mersenne twister should provide a sufficiently random sequence of numbers, without requiring reseeding between Rnd calls.

When you call Randomize with the QB compatible algorithm, part of the old seed is retained. This means that if you call Randomize several times with the same seed, you will not get the same sequence each time. To get a specific sequence in QB compatible mode, set the seed by calling Rnd with a negative parameter.

Note:
Randomize is thread-safe (by using an internal mutex), but not thread specific.
Unfinished structures for other random number generators are also available in the standard header "fbprng.bi".

Example

'' Seed the RNG to the method using C's rand()
Randomize , 1

'' Print a sequence of random numbers
For i As Integer = 1 To 10
    Print Rnd
Next

Version

  • Before fbc 1.10.0 (for fbc 1.08.0 and fbc 1.09.0), "fbprng.bi" was named "fbmath.bi".
  • Before fbc 1.08.0:
The standard "fbmath.bi" header for available algorithms did not exist.
Randomize was not thread-safe.

Dialect Differences

The default algorithm used depends on the current dialect in use:
    • With the -lang fb dialect, a 32 bit Mersenne Twister function with a granularity of 32 bits is used.
    • With the -lang qb dialect, a function giving the same output as Rnd in QB is used. The granularity is 24 bits.
    • With the -lang deprecated and -lang fblite dialects, the function in the C runtime available in the system is used. The function has a granularity of 15 bits in Win32, and 32 bits in Linux and DOS.

Differences from QB

  • The algorithm parameter is new to FreeBASIC.
  • QBASIC only had one algorithm (replicated in FB in algorithm number 4, and set as the default in the -lang qb dialect).

See also

  • Rnd
  • Language dialects


Read
Reads values stored with the Data statement.

Syntax

Read variable_list

Description

Reads data stored in the application with the Data command.

The elements of the variable_list must be of basic types, numeric, strings or elements of arrays and user defined types.

All the Data statements in the program behave as a single list, after the last element of one Data statement is read, the first element of the following Data statement will be read.
The program should not attempt to Read after the last Data element. The results are (in all dialects) undefined, and the program may crash (Page Fault).

Data constants can only be of simple types (numeric or string). A string read into a numeric variable will be evaluated by the Val function.

The "Restore label" statement makes the first Data item after label the next item to be read, allowing the user to choose specific sections of data to be read.

Example

' Create an array of 5 integers and a string to hold the data.
Dim As Integer h(4)
Dim As String hs
Dim As Integer readindex

' Set up to loop 5 times (for 5 numbers... check the data)
For readindex = 0 To 4

  ' Read in an integer.
  Read h(readindex)

  ' Display it.
  Print "Number" ; readindex ; " = " ; h(readindex)

Next readindex

' Spacer.
Print

' Read in a string.
Read hs

' Print it.
Print  "String = " + hs

' Await a keypress.
Sleep

' Exit program.
End

' Block of data.
Data 3, 234, 4354, 23433, 87643, "Bye!"

Dialect Differences

  • None in syntax and usage of Read
  • See the Data page for more information on differences in storing the data

Differences from QB

  • None in syntax and usage of Read
  • See the Data page for more information on differences in storing the data

See also

  • Data
  • Restore


Read (File Access)
File access specifier

Syntax

Open filename As String For Binary Access Read As #filenum As Integer

Description

Specifier for the Access clause in the Open statement. Read specifies that the file is accessible for input.

Example

See example at Access

Differences from QB

  • None known.

See also

  • Access
  • Open


Read Write (File Access)
File access specifier

Syntax

Open filename As String For Binary Access Read Write As #filenum As Integer

Description

Specifier for the Access clause in the Open statement. Read Write specifies that the file is accessible for both input and output.

Example

See example at Access

Differences from QB

  • None known.

See also

  • Access
  • Open


Reallocate
Reallocates storage for an existing reserved block of memory

Syntax

Declare Function Reallocate cdecl ( ByVal pointer As Any Ptr, ByVal count As UInteger ) As Any Ptr

Usage

result = Reallocate( pointer, count )

Parameters

pointer
The address of allocated memory to be reallocated.
count
The number of bytes, in total, to be reallocated.

Return Value

The address of the reallocated memory. A null (0) pointer is returned if reallocation was unsuccessful, and the original memory pointed to by pointer remains unchanged.

Description

Reallocates memory previously allocated with Allocate or CAllocate. The contents of the buffer are preserved, although if count is less than the original size of the memory block, the buffer will be truncated. If the size is increased, the added memory range is not initialized to anything.

The reallocation first attempts to resize the memory already allocated without moving its starting address in order to avoid recopying the data to be kept (returned pointer value unchanged). If this is not possible, a new separate memory area is allocated with recopy of the data to be kept before deallocating the old memory area (returned pointer value modified).

When using Reallocate, the result pointer must be saved to prevent a potential memory leak, because the original pointer may no longer be valid after reallocation. The value of the new pointer should be checked - if it is 0, the reallocation has failed - the original pointer remains valid, and the amount of memory allocated to it has not changed.

Reallocated memory must be freed with Deallocate when no longer needed.

If pointer is null (0), then ReAllocate behaves identically to Allocate. If pointer is valid and count is null (0), then ReAllocate behaves similar to Deallocate and a null (0) pointer is returned.

If the memory has previously been deallocated by a call to Deallocate or ReAllocate, the behavior is undefined.

When manually allocating memory for String descriptors (or Udts that contain one), if count is larger than the original size of the memory block, the new extra memory range must be explicitly cleared to zeroes before the first string use (for example, using Clear). Otherwise accessing the string will cause undefined results (trying to write or read at a random place in memory, or trying to deallocate a random pointer).

Note:
Reallocate cannot be used inside a member procedure if pointer points to the instance itself of the object, because that could cause horrible crashes:
      • If pointer is modified by Reallocate (object data moved into memory), the passed This reference becomes inconsistent, in the same way as a dangling pointer.
      • In that case, all subsequent accesses to any non-static member field from this member procedure will be erroneous, except if the passed This reference would be readjusted (by means of @This = new pointer value) immediately after executing Reallocate in the body of this member procedure.

Example

Dim a As Integer Ptr, b As Integer Ptr, i As Integer

a = Allocate( 5 * SizeOf(Integer) )   ' Allocate memory for 5 integers

If a = 0 Then Print "Error Allocating a": End

For i = 0 To 4
  a[i] = (i + 1) * 2   ' Assign integers to the buffer
Next i

b = Reallocate( a, 10 * SizeOf(Integer) )   ' Reallocate memory for 5 additional integers

If b <> 0 Then

    a = b   ' Discard the old pointer and use the new one

    For i = 5 To 9
      a[i] = (i + 1) * 2   ' Assign more integers to the buffer
    Next i

    For i = 0 To 9   ' Print the integers
      Print i, a[i]
    Next i
    Print

Else '' Reallocate failed, memory unchanged

    Print "Error Reallocating a"

    For i = 0 To 4   ' Print the integers
      Print i, a[i]
    Next i
    Print

End If

Deallocate a   ' Clean up

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Reallocate.

Differences from QB

  • New to FreeBASIC

See also

  • Allocate
  • CAllocate
  • Deallocate


ReDim
Defines or resizes a variable-length array

Syntax

Declaring a Dynamic Array:
ReDim [ Shared ] symbolname([subscript [, ...]]) As datatype [, ...]
ReDim [ Shared ] As datatype symbolname([subscript [, ...]]) [, ...]
Resizing a Dynamic Array:
ReDim [ Preserve ] symbolname([subscript [, ...]]) [, ...]
or:
ReDim [ Preserve ] [ ( ] expression [ ) ] ([subscript [, ...]]) [, ...]

Parameters

Shared
Specifies shared (file-scope) access to the array throughout the module.
Preserve
When used with an existing array, the contents of the array will be preserved during the resize. Note that in some cases Preserve will not preserve data at its original index, see below.
symbolname
A new or existing array identifier.
expression or (expression)
An expression referring to an existing array. This can be used to resize arrays which are members of user-defined types. In some cases, it is necessary to specify parentheses around the expression (especially if the array expression itself contains parentheses) - see the examples below.
subscript: [ lowerbound To ] upperbound
The lower and upper bound range for a dimension of the array. Lower bound defaults to zero (0), or the default Base, if not specified.
datatype
The type of elements contained in the array.

Description

ReDim can be used to define new variable-length arrays, or resize existing variable-length arrays while keeping the same number of dimensions. ReDim always produces variable-length arrays, so, unlike Dim, variable-length arrays can be defined with constant subscripts.

When defining a new variable-length array, its elements are default constructed. For simple data types like Integer or Double, the elements are initialized to zero (0). For user-defined types with a default constructor, that will be called.

When used with in a user-defined type, ReDim creates variable-length arrays while being able to pre-size them with constant subscripts.

NOTES:
    • ReDim Preserve may not work as expected in all cases:
      • Preserve's current behavior is to keep the original data contiguous in memory, and only expand or truncate the size of the memory (if resizing is not possible, the whole original data block is first shifted to another memory location).
      • Its behavior (with a single dimension) is well-defined only when the upper bound is changed. If the lower bound is changed, the current result is that the data is in effect shifted to start at the new lower bound.
      • With multiple dimensions, only the upper bound of only the first dimension may be safely increased. If the first dimension is reduced, the existing mappable data may be lost. If lower-order dimensions are resized at all, the effects can be hard to predict (because multidimensional arrays are stored in row-major order : values differing only in the last index are contiguous).
    • ReDim cannot be used on fixed-size arrays - i.e. arrays with constant bounds made with Dim:
      • This includes the fixed-size arrays contained in UDTs (user-defined Types).
      • This also includes fixed-length arrays passed as parameters in a procedure: FreeBASIC cannot prevent you trying this at compile-time, but generates an error at run-time.
    • ReDim cannot be used inside a member procedure if the array contains as element the instance itself of the object, because that could cause horrible crashes:
      • If the array data are moved into memory by ReDim, the passed This reference becomes inconsistent, in the same way as a dangling pointer.
      • In that case, all subsequent accesses to any non-static member field from this member procedure will be erroneous, except if the passed This reference would be readjusted (by means of @This = @array(...)) immediately after executing ReDim in the body of this member procedure.

Example

'' Define a variable-length array with 5 elements
ReDim array(0 To 4) As Integer

For index As Integer = LBound(array) To UBound(array)
    array(index) = index
Next

'' Resize a variable-length array with 10 elements
'' (the lower bound should be kept the same)
ReDim Preserve array(0 To 9)

Print "index", "value"
For index As Integer = LBound(array) To UBound(array)
    Print index, array(index)
Next

This program will produce the following output:

index         value
 0             0
 1             1
 2             2
 3             3
 4             4
 5             0
 6             0
 7             0
 8             0
 9             0

'' Define a variable-length array
Dim array() As Integer

'' ReDim array to have 3*4 elements
ReDim array(1 To 3, 1 To 4)

Dim As Integer n = 1, i, j

Print "3 * 4:"
Print
For i = LBound(array, 1) To UBound(array, 1)
    For j = LBound(array, 2) To UBound(array, 2)
        array(i, j) = n
        Print Using "##  "; array(i, j);
        n += 1
    Next
    Print
Next
Print

'' ReDim Preserve array to have 4*4 elements, preserving the contents
'' (only the first upper bound should be changed)
ReDim Preserve array(1 To 4, 1 To 4)

Print "4 * 4:"
Print
For i = LBound(array, 1) To UBound(array, 1)
    For j = LBound(array, 2) To UBound(array, 2)
        Print Using "##  "; array(i, j);
    Next
    Print
Next
Print

'' ReDim Preserve array to have 2*4 elements, preserving but trancating the contents
'' (only the first upper bound should be changed)
ReDim Preserve array(1 To 2, 1 To 4)

Print "2 * 4:"
Print
For i = LBound(array, 1) To UBound(array, 1)
    For j = LBound(array, 2) To UBound(array, 2)
        Print Using "##  "; array(i, j);
    Next
    Print
Next
Print

This program will produce the following output:

3 * 4:

 1   2   3   4
 5   6   7   8
 9  10  11  12

4 * 4:

 1   2   3   4
 5   6   7   8
 9  10  11  12
 0   0   0   0

2 * 4:

 1   2   3   4
 5   6   7   8

'' Define a variable-length array as UDT field
Type UDT
   Dim As Integer array(Any)
End Type

Dim As UDT u(0 To 3)

'' For use of Redim with a complex array expression
'' (especially if the array expression itself contains parentheses),
'' the array expression must be enclosed in parentheses
'' in order to solve the parsing ambiguity:
''    Redim u(0).array(0 To 9)
''    induces error 4: Duplicated definition, u in 'Redim u(0).array(0 To 9)'
ReDim (u(0).array)(0 To 9)

Differences from QB

  • Preserve was in Visual Basic, but not in QBASIC.
  • Multi-dimensional arrays in FreeBASIC are in row-major order, rather than column-major order.

See also

  • Common
  • Dim
  • Erase
  • Extern
  • LBound
  • Preserve
  • Shared
  • Static
  • UBound
  • Var


Rem
Indicates comments in the source code.

Syntax

Rem comment

' Comment

/' Multi-line
comment '/

Description

A source code line beginning with Rem indicates that the line is a comment and will not be compiled.

The single quote character (') may also be used to indicate a comment and may appear after other keywords on a source line.

Multi-line comments are marked with the tokens /' and '/. All text between the two markers is considered comment text and is not compiled.

Example

/' this is a multi line
comment as a header of
this example '/

Rem This Is a Single Line comment

' this is a single line comment

? "Hello" : Rem comment following a statement

Dim a As Integer ' comment following a statement

? "FreeBASIC" : ' also acceptable

Dim b As /' can comment in here also '/    Integer

#if 0
    This way of commenting Out code was
    required before version 0.16
#endif

Differences from QB

  • Rem may also appear after other keywords on a source line in FreeBASIC
  • Multiline comments are new to FreeBASIC

See also

  • #if


Reset
Closes all open files, or resets standard I/O handles.

Syntax

Declare Sub Reset ( )
Declare Sub Reset ( ByVal streamno As Long )

Usage

Reset
or
Reset( streamno )

Parameters

streamno
The stream number to reset, 0 for stdin or 1 for stdout.

Description

Reset, when called with no arguments, closes all disk files.

Reset, when called with the streamno argument, will reset the redirected or piped streams associated with stdin (0), or stdout (1).

Runtime errors:
Reset(streamno) can set one of the following runtime errors:

(1) Illegal function call
    • streamno was neither 0 nor 1

(3) File I/O error
    • Resetting of stdin or stdout failed

Example

Open "test.txt" For Output As #1
Print #1, "testing 123"
Reset

Dim x As String

'' Read from STDIN from piped input
Open Cons For Input As #1
While EOF(1) = 0
  Input #1, x
  Print """"; x; """"
Wend
Close #1

'' Reset to read from the keyboard
Reset(0)

Print "Enter some text:"
Input x

'' Read from STDIN (now from keyboard)
Open Cons For Input As #1
While EOF(1) = 0
  Input #1, x
  Print """"; x; """"
Wend
Close #1

Note: Under Windows, to specify to the program that data entry is completed (transfer EOF), you can press CTRL+Z then press ENTER.

Differences from QB

  • None for Reset().
  • The Reset(streamno) usage is new to FreeBASIC.

See also

  • Close
  • Open
  • Open Cons
  • IsRedirected


Restore
Changes the next read location for values stored with the Data statement.

Syntax

Restore [label]

Description

Sets the next-data-to-read pointer to the first element of the first Data statement after the label. The label must be contained in the same module as the currently-executing code. Restore alters the normal top to bottom order in which Data are Read. It allows re-reading some Data or using multiple sets of Data in a single module.
If label is omitted, the next-data-to-read pointer is set to the first Data statement found in the module.

Example

' Create an 2 arrays of integers and a 2 strings to hold the data.
Dim h(4) As Integer
Dim h2(4) As Integer
Dim hs As String
Dim hs2 As String
Dim read_data1 As Integer
Dim read_data2 As Integer

' Set the data read to the label 'dat2:'
Restore dat2

' Set up to loop 5 times (for 5 numbers... check the data)
For read_data1 = 0 To 4

  ' Read in an integer.
  Read h(read_data1)

  ' Display it.
  Print "Bloc 1, number"; read_data1;" = "; h(read_data1)

Next

' Spacer.
Print

' Read in a string.
Read hs

' Print it.
Print  "Bloc 1 string = " + hs

' Spacers.
Print
Print

' Set the data read to the label 'dat1:'
Restore dat1

' Set up to loop 5 times (for 5 numbers... check the data)
For read_data2 = 0 To 4

  ' Read in an integer.
  Read h2(read_data2)

  ' Display it.
  Print "Bloc 2, number"; read_data2;" = "; h2(read_data2)

Next

' Spacer.
Print

' Read in a string.
Read hs2

' Print it.
Print  "Bloc 2 string = " + hs2

' Await a keypress.
Sleep

' Exit program.
End

' First block of data.
dat1:
Data 3, 234, 4354, 23433, 87643, "Bye!"

' Second block of data.
dat2:
Data 546, 7894, 4589, 64657, 34554, "Hi!"

Differences from QB

  • None

See also

  • Data
  • Read
  • Labels


Resume
Error handling statement to resume execution after a jump to an error handler

Syntax

Resume

Description

Resume is used in the traditional QB error handling mechanism within an error handler (called by On Error) to return execution to the line that caused the error. Usually this is used after the error has been handled gracefully in order to try the previously erroneous operation again with corrected data.

Resume resets the Err value to 0

Example

'' Compile with -lang fblite or qb

#lang "fblite"

Dim As Single i, j

On Error Goto ErrHandler

i = 0
j = 1 / i ' this line causes a divide-by-zero error on the first try; execution jumps to ErrHandler label

Print j ' after the value of i is corrected, prints 0.5

End ' end the program so that execution does not fall through to the error handler again

ErrHandler:

i = 2
Resume ' execution jumps back to 'j = 1 / i' line, which does not cause an error this time

Dialect Differences

  • RESUME is not supported in the -lang fb dialect. Statements can be used in its function form to return an error code
If Open( "text" For Input As #1 ) <> 0 Then
  Print "Unable to open file"
End If

Differences from QB

  • Does not accept line numbers or labels
  • Must compile with -ex or -exx option

See also

  • Err
  • Resume Next
  • Error Handling


Return (From Procedure)
Control flow statement to return from a procedure or GoSub.

Syntax

Return expression

Description

Return is used to return from a procedure.

Because Return could mean return-from-gosub or return-from-procedure, Option Gosub and Option Nogosub can be used to enable and disable GoSub support. When GoSub support is disabled, Return is then recognized as return-from-procedure. When GoSub support is enabled, Return is then recognized as return-from-gosub.

Return (from procedure) is used inside a procedure to exit the procedure possibly with a return value:
    • A Sub cannot specify a return return value. Return is roughly equivalent to the Exit Sub idiom.
    • In a Function, Return must specify its return value. Return expression is roughly equivalent to the Function = expression : Exit Function idiom.
Warning: Whatever the output branch used, the return value must be always defined, otherwise an unexpected behavior may occur.

Example

'' Return from function

Type rational              '' simple rational number type
    numerator As Integer
    denominator As Integer
End Type

'' multiplies two rational types
Function rational_multiply( r1 As rational, r2 As rational ) As rational

    Dim r As rational
    '' multiply the divisors ...
    r.numerator   = r1.numerator   * r2.numerator
    r.denominator = r1.denominator * r2.denominator

    '' ... and return the result
    Return r

End Function

Dim As rational r1 = ( 6, 105 )   '' define some rationals r1 and r2
Dim As rational r2 = ( 70, 4 )
Dim As rational r3

r3 = rational_multiply( r1, r2 )  '' multiply and store the result in r3

'' display the expression
Print r1.numerator & "/" & r1.denominator; " * ";
Print r2.numerator & "/" & r2.denominator; " = ";
Print r3.numerator & "/" & r3.denominator

Dialect Differences

  • In the -lang fb dialect Return always means return-from-procedure.
  • In the -lang qb dialect, Return means return-from-gosub by default unless changed with Option Nogosub, in which case the compiler will recognize Return as return-from-procedure.
  • In the -lang fblite dialect, Return means return-from-procedure by default unless changed with Option Gosub, in which case the compiler will recognize Return as return-from-gosub.

Differences from QB

  • None when using the -lang qb dialect.

See also

  • Sub
  • Function
  • GoSub
  • Option Gosub
  • Option Nogosub
  • Labels
  • Return (From Gosub)


Return (From Gosub)
Control flow statement to return from a procedure or GoSub.

Syntax

Return [ label ]

Description

Return is used to return from a gosub GoSub.

Because Return could mean return-from-gosub or return-from-procedure, Option Gosub and Option Nogosub can be used to enable and disable GoSub support. When GoSub support is disabled, Return is then recognized as return-from-procedure. When GoSub support is enabled, Return is then recognized as return-from-gosub.

Return (from gosub) is used to return control back to the statement immediately following a previous GoSub call. When used in combination with GoSub, no return value can be specified. If the optional label is specified, execution continues at the specified label. If no GoSub was made, a runtime error is generated, and execution continues immediately after Return.

A GoSub should always have a matching Return statement. However, if Return (from gosub) is used where no GoSub was made, a run-time error is generated.

Example

'' GOSUB & RETURN example, compile with "-lang qb" or use "$lang" as below

'$lang: "qb"

Print "Let's Gosub!"
GoSub MyGosub
Print "Back from Gosub!"
Sleep
End

MyGosub:
Print "In Gosub!"
Return

Dialect Differences

  • In the -lang fb dialect Return always means return-from-procedure.
  • In the -lang qb dialect, Return means return-from-gosub by default unless changed with Option Nogosub, in which case the compiler will recognize Return as return-from-procedure.
  • In the -lang fblite dialect, Return means return-from-procedure by default unless changed with Option Gosub, in which case the compiler will recognize Return as return-from-gosub.

Differences from QB

  • None when using the -lang qb dialect.

See also

  • Sub
  • Function
  • GoSub
  • Option Gosub
  • Option Nogosub
  • Labels
  • Return (From Procedure)


RGB
Computes a valid color value for hi/truecolor modes

Syntax

#define RGB(r,g,b) CULng((CUByte(r) Shl 16) Or (CUByte(g) Shl 8) Or CUByte(b) Or (&hFF000000ul))

Usage

result = RGB(red, green, blue)

Parameters

red
red color component value
green
green color component value
blue
blue color component value

Return Value

The combined color.

Description

red, green and blue are components ranging 0-255.

The RGB function can be used to compute a valid color value for use while in hi/truecolor modes. It returns an unsigned long, in the format &hAARRGGBB, where RR, GG and BB equal the values passed to this function, in hexadecimal format. AA is the implicit alpha value and is automatically set to &hFF (opaque).
It is possible to retrieve the red, green, blue and alpha values from a color value, by using a combination of And and Shr. The second example below shows how to #define and use macros to do this.

Note for Windows API programmers: The macro named RGB in the Windows references has been renamed BGR in the FB headers for Windows to avoid collisions.

Example

See Put (Graphics) example in addition.

ScreenRes 640,480,32  '32 bit color
Line(0,0)-(319,479), RGB(255,0,0) 'draws a bright red box on the left side of the window
Line(639,0)-(320,479), RGB(0,0,255) 'draws a bright blue box on the right side of the window

Sleep 'wait before exiting

'' setting and retrieving Red, Green, Blue and Alpha values

#define RGBA_R( c ) ( CULng( c ) Shr 16 And 255 )
#define RGBA_G( c ) ( CULng( c ) Shr  8 And 255 )
#define RGBA_B( c ) ( CULng( c )        And 255 )
#define RGBA_A( c ) ( CULng( c ) Shr 24         )

Dim As UByte r, g, b, a

Dim As ULong col = RGB(128, 192, 64)

Print Using "Color: _&H\      \"; Hex(col, 8)

r = RGBA_R( col )
g = RGBA_G( col )
b = RGBA_B( col )
a = RGBA_A( col )

Print
Print Using "Red:         _&H\\ = ###"; Hex(r, 2); r
Print Using "Green:       _&H\\ = ###"; Hex(g, 2); g
Print Using "Blue:        _&H\\ = ###"; Hex(b, 2); b
Print Using "Alpha:       _&H\\ = ###"; Hex(a, 2); a

Version

  • Before fbc 1.08.0:
Syntax:
#define RGB(r,g,b) ((CULng(r) Shl 16) Or (CULng(g) Shl 8) Or CULng(b) Or &hFF000000)
RGB function returned an unsigned integer.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Rgb.

Differences from QB

  • New to FreeBASIC

See also

  • RGBA
  • Color
  • #define


RGBA
Computes a valid color value including alpha (transparency) for hi/truecolor modes

Syntax

#define RGBA(r,g,b,a) CULng((CUByte(r) Shl 16) Or (CUByte(g) Shl 8) Or CUByte(b) Or (CUByte(a) Shl 24))

Usage

result = RGBA(red, green, blue, alpha)

Parameters

red
red color component value
green
green color component value
blue
blue color component value
alpha
alpha component value

Return Value

the combined color

Description

red, green, blue and alpha are components ranging 0-255.

The RGBA function can be used to compute a valid color value including an alpha channel for use while in hi/truecolor modes. It returns an unsigned long, in the format &hAARRGGBB, where RR, GG, BB, AA equal the values passed to this function, in hexadecimal format.
It is possible to retrieve the red, green, blue and alpha values from a color value, by using a combination of And and Shr. The second example below shows how to #define and use macros to do this.

Example

'open a graphics screen (320 * 240, 32-bit)
ScreenRes 320, 240, 32

Dim As Any Ptr img
Dim As Integer x, y

'make an image that varies in transparency and color
img = ImageCreate(64, 64)
For x = 0 To 63
  For y = 0 To 63
    PSet img, (x, y), RGBA(x * 4, 0, y * 4, (x + y) * 2)
  Next y
Next x
Circle img, (31, 31), 25,      RGBA(0, 127, 192, 192), ,,, F 'semi-transparent blue circle
Line   img, (26, 20)-(38, 44), RGBA(255, 255, 255, 0),    BF 'transparent white rectangle

'draw a background (diagonal white lines)
For x = -240 To 319 Step 10
  Line (x, 0)-Step(240, 240), RGB(255, 255, 255)
Next

Line (10,  10)-(310,  37), RGB(127, 0, 0), BF 'red box for text
Line (10, 146)-(310, 229), RGB(0, 127, 0), BF 'green box for Putting onto

'draw the image and some text with PSET
Draw String(64, 20), "PSet"
Put(48,  48), img, PSet
Put(48, 156), img, PSet

'draw the image and some text with ALPHA
Draw String (220, 20), "Alpha"
Put(208,  48), img, Alpha
Put(208, 156), img, Alpha

'Free the image memory
ImageDestroy img

'Keep the window open until the user presses a key
Sleep

'' setting and retrieving Red, Green, Blue and Alpha values

#define RGBA_R( c ) ( CULng( c ) Shr 16 And 255 )
#define RGBA_G( c ) ( CULng( c ) Shr  8 And 255 )
#define RGBA_B( c ) ( CULng( c )        And 255 )
#define RGBA_A( c ) ( CULng( c ) Shr 24         )

Dim As UByte r, g, b, a

Dim As ULong col = RGBA(255, 192, 64, 128)

Print Using "Color: _&H\      \"; Hex(col, 8)

r = RGBA_R( col )
g = RGBA_G( col )
b = RGBA_B( col )
a = RGBA_A( col )

Print
Print Using "Red:         _&H\\ = ###"; Hex(r, 2); r
Print Using "Green:       _&H\\ = ###"; Hex(g, 2); g
Print Using "Blue:        _&H\\ = ###"; Hex(b, 2); b
Print Using "Alpha:       _&H\\ = ###"; Hex(a, 2); a

Version

  • Before fbc 1.08.0:
Syntax:
#define RGBA(r,g,b,a) ((CULng(r) Shl 16) Or (CULng(g) Shl 8) Or CULng(b) Or (CULng(a) Shl 24))
RGBA function returned an unsigned integer.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Rgba.

Differences from QB

  • New to FreeBASIC

See also

  • RGB
  • Color
  • #define


Right
Returns the rightmost substring of a string

Syntax

Declare Function Right ( ByRef str As Const String, ByVal n As Integer ) As String
Declare Function Right ( ByRef str As Const WString, ByVal n As Integer ) As WString

Usage

result = Right[$]( str, n )

Parameters

str
The source string.
n
The substring length, in characters.

Return Value

Returns the rightmost substring from str.

Description

Returns the rightmost n characters starting from the right (end) of str. If str is empty, then the null string ("") is returned. If n <= 0 then the null string ("") is returned. If n > len(str) then the entire source string is returned.

Example

Dim text As String = "hello world"
Print Right(text, 5)

will produce the output:

world
An Unicode example:
Platform Differences

  • DOS does not support the wide-character string version of Right.

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • QB does not support Unicode.

See also

  • Left
  • Mid (Function)

dim text as wstring*20
text = "Привет, мир!"
print right(text, 5) 'displays " мир!"


RmDir
Removes a folder/directory from the file system

Syntax

Declare Function RmDir ( ByRef folder As Const String ) As Long

Usage

result = RmDir( folder )

Parameters

folder
The folder/directory to be removed.

Return Value

Returns zero (0) on success, and negative one (-1) on failure.

Description

Removes a folder from the file system. The function will fail if the folder is not empty.

Example

Dim pathname As String = "foo\bar\baz"
Dim result As Integer = RmDir( pathname )

If 0 <> result Then Print "error: unable to remove folder " & pathname & " in the current path."

Platform Differences

  • Linux requires the folder case matches the real name of the file. Windows and DOS are case insensitive.
  • Path separators in Linux are forward slashes / . Windows uses backward slashes \ but it allows for forward slashes . DOS uses backward \ slashes.

Differences from QB

  • None

See also

  • Shell
  • CurDir
  • ChDir
  • MkDir
  • Kill


Rnd
Returns a random Double precision number in the range [0, 1)

Syntax

Declare Function Rnd ( ByVal seed As Single = 1.0 ) As Double

Usage

result = Rnd( seed )

Parameters

seed
Optional Single argument. If seed has a value of zero (0.0), the last random number generated is repeate. For any other number a new random number is returned. With the QB-compatible algorithm, a negative number fully reseeds the generator. The default for no argument is to return a new random number.

Return Value

Returns the random number generated.

Description

Returns a number of type Double in the range [0, 1) (i.e. 0 <= Rnd < 1), based on a random seed (see Randomize).

Rnd can use a variety of different algorithms - see Randomize for details of the default and selectable algorithms.

Rnd will return the same sequence of numbers every time a program is run. This sequence can be changed by reseeding the generator.

Note:
Rnd is thread-safe (by using an internal mutex), but not thread specific. Used in the context of a multi-threaded program, only the execution speed is significantly slowed down compared to a single thread call context.
Unfinished structures for other random number generators are also available in the standard header "fbprng.bi".

Example

'' Function to a random number in the range [first, last), or {first <= x < last}.
Function rnd_range (first As Double, last As Double) As Double
    Function = Rnd * (last - first) + first
End Function

'' seed the random number generator, so the sequence is not the same each time
Randomize

'' prints a random number in the range [0, 1), or {0 <= x < 1}.
Print Rnd

'' prints a random number in the range [0, 10), or  {0 <= x < 10}.
Print Rnd * 10

'' prints a random integral number in the range  [1, 10], or {1 <= n <= 10}.
''     (because: 0 <= Rnd * 10 < 10)
Print Int(Rnd * 10) + 1

'' prints a random integral number in the range [69, 420], or {69 <= n <= 420}.
''     (because: 69 <= rnd_range(69, 421) < 421)
Print Int(rnd_range(69, 421))

Version

  • Before fbc 1.10.0 (for fbc 1.08.0 and fbc 1.09.0), "fbprng.bi" was named "fbmath.bi".
  • Before fbc 1.08.0:
Rnd was not thread-safe (many values temporarily returned in duplicate for a same thread).
The standard "fbmath.bi" header for available algorithms did not exist.

Dialect Differences

The default algorithm used depends on the current dialect in use:
    • With the -lang fb dialect, a 32 bit Mersenne Twister function with a granularity of 32 bits is used.
    • With the -lang qb dialect, a function giving the same output as Rnd in QB is used. The granularity is 24 bits.
    • With the -lang deprecated and -lang fblite dialects, the function in the C runtime available in the system is used. The function available in Win32 has a granularity of 15 bits, and 32 bits in Linux and DOS.

Differences from QB

  • None, if compiled in the -lang qb dialect. Other dialects can also use the same seeding and generating algorithms by calling Randomize with the appropriate parameter.
  • For the non-QB-compatible algorithms, if the optional argument is less than 0, it has the same meaning as passing an argument of 1.

See also

  • Randomize
  • Timer
  • Int


RSet
Right justifies a string in a string buffer

Syntax

Declare Sub RSet ( ByRef dst As String, ByRef src As Const String )
Declare Sub RSet ( ByVal dst As WString Ptr, ByVal src As Const WString Ptr )

Usage

RSet dst, src

Parameters

dst
A String or WString buffer to copy the text into.
src
The source String or WString to be right justified.

Description

RSet right justifies text into the string buffer dst, filling the right part of the string with src and the left part with spaces. The string buffer size is not modified.

If text is too long for the string buffer size, RSet truncates characters from the right.

Example

Dim buffer As String
buffer = Space(10)
RSet buffer, "91.5"
Print "-[" & buffer & "]-"

The example above outputs:
-[      91.5]-
Differences from QB

  • In QBasic the syntax was RSet dst = src. That syntax is also supported by FB.

See also

  • LSet
  • Space
  • Put (File I/O)
  • MKD
  • MKI
  • MKL
  • MKS


RTrim
Removes surrounding substrings or characters on the right side of a string

Syntax

Declare Function RTrim ( ByRef str As Const String, [ Any ] ByRef trimset As Const String = " " ) As String
Declare Function RTrim ( ByRef str As Const WString, [ Any ] ByRef trimset As Const WString = WStr(" ") ) As WString

Usage

result = RTrim[$]( str [, [ Any ] trimset ] )

Parameters

str
The source string.
trimset
The substring to trim.

Return Value

Returns the trimmed string.

Description

This procedure trims surrounding characters from the right (end) of a source string:
    • Substrings matching trimset will be trimmed if specified, otherwise spaces (ASCII code 32) are trimmed.
    • If the Any keyword is used, any character matching a character in trimset will be trimmed.
All comparisons are case-sensitive.

Example

Dim s1 As String = "Article 101  "
Print "'" + RTrim(s1) + "'"
Print "'" + RTrim(s1, " 01") + "'"
Print "'" + RTrim(s1, Any " 10") + "'"

Dim s2 As String = "Test Pattern aaBBaaBaa"
Print "'" + RTrim(s2, "Baa") + "'"
Print "'" + RTrim(s2, Any "Ba") + "'"

will produce the output:

'Article 101'
'Article 101  '
'Article'
'Test Pattern aaB'
'Test Pattern '

Platform Differences

  • DOS version/target of FreeBASIC does not support the wide-character version of RTrim.

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • QB does not support specifying a trimset string or the ANY clause.

See also

  • LTrim
  • Trim


Run
Transfers execution to an external program

Syntax

Declare Function Run ( ByRef program As Const String, ByRef arguments As Const String = "" ) As Long

Usage

result = Run( program [, arguments ] )

Parameters

program
The file name (including file path) of the program (executable) to transfer control to.
arguments
The command-line arguments to be passed to the program.

Return Value

Returns negative one (-1) if the program could not be executed.

Description

Transfers control over to an external program. When the program exits, execution will return to the system.

Example

'' Attempt to transfer control to "program.exe" in the current directory.
Dim result As Integer = Run("program.exe")

'' at this point, "program.exe" has failed to execute, and
'' result will be set to -1.

Platform Differences

  • Linux requires the program case matches the real name of the file. Windows and DOS are case insensitive. The program being run may be case sensitive for its command line parameters.
  • Path separators in Linux are forward slashes ("/"). Windows uses backward slashes ("\") although some versions of Windows allow forward slashes. DOS uses backward slashes.

Differences from QB

  • Run needs the full executable name, including extension (.exe) on platforms that have one (Win32, DOS).
  • Returning an error code is new to FreeBASIC.

See also

  • Exec transfer temporarily, with arguments
  • Chain transfer temporarily, without arguments
  • Command pick arguments


SAdd
Returns a pointer to a string variable's data

Syntax

Declare Function SAdd ( ByRef str As String ) As ZString Ptr
Declare Function SAdd ( ByRef str As WString ) As WString Ptr
Declare Function SAdd ( ByRef str As ZString ) As ZString Ptr

Usage

result = SAdd( str )

Parameters

str
the string expression or variable to get the address of

Return Value

A pointer to the data associated with str.

Description

Returns the memory offset of the string data in the string variable.

Example

Dim s As String

Print SAdd(s)
s = "hello"
Print SAdd(s)
s = "abcdefg, 1234567, 54321"
Print SAdd(s)

Differences from QB

  • QB returned an integer instead of a pointer.

See also

  • StrPtr
  • VarPtr
  • ProcPtr


Scope...End Scope
Statement to begin a new scope block

Syntax

Scope
[statements]
End Scope

Description

The Scope block allows variables to be (re)defined and used locally in a program.

When a variable is (re)defined with Dim within a scope structure, this local working variable can be used from its (re)definition until the end of the scope. During this time, any variables outside the scope that have the same name will be ignored, and will not be accessible by that name. Any statements in the Scope block before the variable is redefined will use the variable as defined outside the Scope.

The local variables are reserved on stack at granularity level of each procedure (including the main part of the program), not at granularity level of each individual scope block. So a same memory space can be used by local variables belonging to different scope blocks.

To access duplicated symbols defined as global outside the scope block, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

Scope..End Scope is not permitted when compiling with in the -lang qb dialect.

Example

Dim As Integer x = 5, y = 2
Print "x ="; x; ", "; "y ="; y
Scope
    Dim x As Integer = 3
    Print "x ="; x; ", "; "y ="; y
    Scope
        Dim y As Integer = 4
        Print "x ="; x; ", "; "y ="; y
    End Scope
End Scope
Print "x ="; x; ", "; "y ="; y

Dialect Differences

  • Explicit Scope..End Scope blocks are available only in the -lang fb and -lang deprecated dialects.
  • Explicit Scope..End Scope blocks are not available in the -lang fblite and -lang qb dialects.

Differences from QB

  • New to FreeBASIC

See also

  • Dim
  • ReDim
  • Static
  • Var
  • Byref (Variables)


Screen (Graphics)
Initializes a graphics mode using QB-like mode numbers

Syntax

-lang fb|fblite dialects:
Screen mode [, [ depth ] [, [ num_pages ] [, [ flags ] [, [ refresh_rate ]]]]]
Screen , [ active_page ] [, [ visible_page ]]
-lang qb dialect:
Screen [ mode ] [, [ colormode ] [, [ active_page ] [, [ visible_page ]]]]

Parameters

mode
is a QB style graphics screen mode number (see below). If mode is 0, then any currently set graphics mode is closed, and all functions resume their normal console-mode functionality. See below for available modes.
depth
is the color depth in bits per pixel. This only has an effect for modes 14 and higher. Values of 8, 16 and 32 are allowed. 15 and 24 are also allowed as aliases for 16 and 32, respectively. If omitted, it defaults to 8.
num_pages
is the number of video pages you want, see below. If omitted, it defaults to 1.
flags
Are used to select several things as graphics driver priority, fullscreen mode. There are constants predefined in the fbgfx.bi file ready to use. See the page ScreenRes for available flags.
refresh_rate
requests a refresh rate. If it is not available in the present card or the parameter is omitted, FreeBASIC chooses the rate automatically.
active_page
Used to set the active page, where printing/drawing commands take effect
visible_page
Used to set the visible page, which is shown to the user
colormode
Unused - allowed for compatibility with the QB syntax

Description

Screen tells the compiler to link the GfxLib and initializes a QB-only, QB-on-GUI or OpenGL graphics mode, depending on the flags setting.

In QB-only modes a dumb window or fullscreen resolution is set, one or more buffers in standard memory are created, console commands are redirected to their graphic versions, a default palette is set and an automatic screen refresh thread is started. QB-like graphics and console statements can be used.

In QB-on-GUI modes one or more buffers in standard memory are created, console commands are redirected to their graphic versions and a default palette is set. QB-like graphics and console statements can be used. It is up to the user to create a window and to refresh it with the contents of the graphics buffers.

In OpenGL modes a dumb window or fullscreen resolution is set, one or more buffers in standard memory are created, and the OS's OpenGL library is initialized. From here only OpenGL commands can be used; QB-like and console commands are forbidden. This allows to initialize OpenGL in a portable way; you can then also use ScreenControl to properly customize the GL pixel format to be used before Screen is called or to retrieve the list of supported OpenGL extensions after a mode has been set, and ScreenGLProc to obtain extension function pointers.

Any buffer that is created in standard memory uses one of three supported internal pixel formats, depending on the desired color depth; see Internal pixel formats for details.

If Screen fails to set the required mode, an "Illegal function call" error is issued and the screen pointer is set to 0. Thus Screen failures can be detected using standard On Error processing or retrieving the screen pointer with ScreenPtr.

Before setting a fullscreen mode the program should check if that mode is available in the graphics card using ScreenList.

mode details
Available modes list:
QB compatibility modes:
Mode nr | Resolution | Emulation | Text | char size | colors on screen
1 | 320x200 | CGA | 40X25 | 8x8 | 16 background, 1 of four sets foreground
2 | 640x200 | CGA | 80x25 | 8x8 | 16 colors to 2 attributes
7 | 320x200 | EGA | 40x25 | 8x8 | 16 colors to 16 attributes
8 | 640x200 | EGA | 80x25 | 8x8 | 16 colors to 16 attributes
9 | 640x350 | EGA | 80x25 0r 80x43 | 8x14 or 8x8 | 16 colors to 16 attributes
11 | 640x480 | VGA | 80x30 or 80x60 | 8x16 or 8x8 | 256K colors to 2 attributes
12 | 640x480 | VGA | 80x30 or 80x60 | 8x16 or 8x8 | 256K colors to 16 attributes
13 | 320x200 | MCGA | 40X25 | 8X8 | 256K colors to 256 attributes
(the mode number '0' is dedicated to text mode)

New FreeBASIC modes:
Mode nr | Resolution | Emulation | Text | char size | colors on screen
14 | 320x240 |  | 40x30 | 8x8 | 256K colors to 256 attributes or direct color
15 | 400x300 |  | 50x37 | 8x8 | 256K colors to 256 attributes or direct color
16 | 512x384 |  | 64x24 or 64x48 | 8x16 or 8x8 | 256K colors to 256 attributes or direct color
17 | 640x400 |  | 80x25 or 80x50 | 8x16 or 8x8 | 256K colors to 256 attributes or direct color
18 | 640x480 |  | 80x30 or 80x60 | 8x16 or 8x8 | 256K colors to 256 attributes or direct color
19 | 800x600 |  | 100x37 or 100x75 | 8x16 or 8x8 | 256K colors to 256 attributes or direct color
20 | 1024x768 |  | 128x48 or 128x96 | 8x16 or 8x8 | 256K colors to 256 attributes or direct color
21 | 1280x1024 |  | 160x64 or 160x128 | 8x16 or 8x8 | 256K colors to 256 attributes or direct color

depth details
For modes 14 and up, the depth parameter changes the color depth to the specified new one; if depth is not specified, these modes run in 8bpp. For modes 13 and below, depth has no effect.

num_pages details
You can request any number of pages for any video mode; if you omit the parameter, only the visible page (number 0) will be available. A page is either the visible screen or an offscreen buffer, you can show a page while working on another one; see the ScreenSet statement for details. All pages are created in standard memory, the video card memory is never used for video buffering.

flags details:
(documented at the page ScreenRes)

Other details
While in windowed mode, clicking on the window close button will add a keypress of (Chr(255) & "k") to the Inkey buffer. Clicking on the Maximize window button will switch to fullscreen mode if possible. A successful Screen call sets currently visible and working pages both to page number 0, resets the palette to the specified mode one (see Default palettes), resets the clipping region to the size of the screen, disables custom coordinates mappings, moves the graphics cursor to the center of the screen, moves the text cursor to the top-left corner of the screen (but never visible on any graphics screen), and sets foreground and background colors to bright white and black respectively.

Note on using Screen 0 (text mode only)
The default BASIC screen mode (Screen 0) is text based.
Screen 0 closes any graphics window, but also clears the console window if it exists.
Screen 0, , , GFX_SCREEN_EXIT (with GFX_SCREEN_EXIT=&h80000000) also closes any graphics window, but does not clear the console window if it exists (previous text is preserved).

Example

' Sets screen mode 13 (320*200, 8bpp)
Screen 13
Print "Screen mode 13 set"

Sleep

#include "fbgfx.bi"
#if __FB_LANG__ = "fb"
Using FB '' Screen mode flags are in the FB namespace in lang FB
#endif

' Sets screen mode 18 (640*480) with 32bpp color depth and 4 pages, in windowed mode; switching disabled
Screen 18, 32, 4, (GFX_WINDOWED Or GFX_NO_SWITCH)

' Check to make sure Screen was opened successfully
If ScreenPtr = 0 Then
    Print "Error setting video mode!"
    End
End If

Print "Successfully set video mode"
Sleep

Platform Differences

  • In DOS, Windowing and OpenGL related switches are not available, and other issues, see GfxLib overview

Dialect Differences

  • In the -lang fb and -lang fblite dialects, the usage is:
Screen mode [, [depth] [, [num_pages] [, [flags] [, [refresh_rate]]]]]
or:
Screen , [active_page] [, [visible_page]]]

  • In the -lang qb dialect, the usage is:
Screen [mode] [, [colormode] [, [active_page] [, [visible_page]]]]

Differences from QB

  • None in the -lang qb dialect.
  • In QB the syntax was Screen mode,colormode,active_page,visible_page. Of those parameters FreeBASIC supports only mode and redefines the rest. The use of Screen , , apage,vpage to swap screen pages is only available in the -lang qb dialect.
  • ScreenSet should be used in the -lang fb and -lang fblite dialects.

See also

  • Screen (Console)
  • ScreenRes More flexible alternative to Screen
  • ScreenList Check display modes available for FB GfxLib to use
  • ScreenControl Select driver and more
  • ScreenLock
  • ScreenUnlock
  • ScreenPtr Semi-low level access
  • ScreenSet
  • ScreenCopy
  • ScreenInfo
  • ScreenGLProc
  • Internal pixel formats


Screen (Console)
Gets the character or color attribute at a given location

Syntax

Declare Function Screen ( ByVal row As Long, ByVal column As Long, ByVal colorflag As Long = 0 ) As ULong

Usage

result = Screen( row, column [, colorflag ] )

Parameters

row
1-based offset from the top left corner of the console.
column
1-based offset from the top left corner of the console.
colorflag
If equal to 0, the ASCII code is returned, otherwise the color attribute is returned. If omitted, it defaults to 0.

Return Value

The ASCII or color attribute of the character.

Description

Screen returns the character or the color attribute found at a given position of a console output. It works in console mode and in graphics mode.

The format of the color attribute depends on the current color depth:

If the color type is a palette type with up to 4 bits per pixel (such as the Win32 console), then the color attribute is an 8-bit value, where the higher four bits hold the cell background color and the lower four bits hold the foreground (character) color.

If the color type is an 8-bit palette, then the color attribute is a 16-bit value, where the high byte holds the background color and the low byte holds the foreground color.

If the color type is full color, then the color attribute is a 32-bit integer, holding a single color value. If colorflag is equal to 1, then the foreground color is returned; if colorflag is equal to 2, then the background color is returned.

The color values for the standard 16 color palette are:

Value | Color | Value | Color
0 | Black | 8 | Gray
1 | Blue | 9 | Bright Blue
2 | Green | 10 | Bright Green
3 | Cyan | 11 | Bright Cyan
4 | Red | 12 | Bright Red
5 | Magenta | 13 | Pink
6 | Brown | 14 | Yellow
7 | White | 15 | Bright White

Example

Dim character_ascii_value As ULong
Dim attribute As ULong
Dim background As ULong
Dim cell_color As ULong
Dim row As Long, col As Long

character_ascii_value = Screen( row, col )
attribute = Screen( row, col, 1 )
background = attribute Shr 4
cell_color = attribute And &hf

'' open a graphics screen with 4 bits per pixel
'' (alternatively, omit this line to use the console)
ScreenRes 320, 200, 4

'' print a character
Color 7, 1
Print "A"

Dim As ULong char, col, fg, bg

'' get the ASCII value of the character we've just printed
char = Screen(1, 1, 0)

''get the color attributes
col = Screen(1, 1, 1)
fg = col And &HF
bg = (col Shr 4) And &HF

Print Using "ASCII value: ### (""!"")"; char; Chr(char)
Print Using "Foreground color: ##"; fg
Print Using "Background color: ##"; bg
Sleep

'' open a graphics screen with 8 bits per pixel
ScreenRes 320, 200, 8

'' print a character
Color 30, 16
Print "Z"

Dim As ULong char, col, fg, bg

'' get the ASCII value of the character we've just printed
char = Screen(1, 1, 0)

''get the color attributes
col = Screen(1, 1, 1)
fg = col And &HFF
bg = (col Shr 8) And &HFF

Print Using "ASCII value: ### (""!"")"; char; Chr(char)
Print Using "Foreground color: ###"; fg
Print Using "Background color: ###"; bg
Sleep

'' open a full-color graphics screen
ScreenRes 320, 200, 32

'' print a character
Color RGB(255, 255, 0), RGB(0, 0, 255) 'yellow on blue
Print "M"

Dim As ULong char, fg, bg

'' get the ASCII value of the character we've just printed
char = Screen(1, 1, 0)

''get the color attributes
fg = Screen(1, 1, 1)
bg = Screen(1, 1, 2)

Print Using "ASCII value: ### (""!"")"; char; Chr(char)
Print Using "Foreground color: &"; Hex(fg, 8)
Print Using "Background color: &"; Hex(bg, 8)
Sleep

Platform Differences

  • On the Linux version, the value returned can differ from the character shown on the console. For example, unprintable control codes - such as the LF character (10) that implicitly occurs after the end of Printed text - may be picked up instead of the untouched character in its place.

Differences from QB

  • In QB Screen triggered an error if the coordinates were out of screen.

See also

  • Screen (Graphics)
  • Color


ScreenCopy
Copies the contents of a graphical page into another graphical page

Syntax

Declare Function ScreenCopy ( ByVal from_page As Long = -1, ByVal to_page As Long = -1 ) As Long

Usage

ScreenCopy [ from_page ] [, to_page ]

Parameters

from_page
page to copy from
to_page
page to copy to

Return Value

Returns zero (0) if successful, or a non-zero error code to indicate a failure.

Description

from_page is the page to copy from. If this argument is omitted, the current work page is assumed. to_page is the page to copy to. If this argument is omitted, the currently visible page is assumed. Page numbers range from 0 to num_pages - 1, where num_pages is the number of pages specified when setting the graphics mode with ScreenRes or Screen (Graphics).

You can use this function to add a double buffer to your graphics. Any graphics screen mode with multiple pages supports this function.

ScreenCopy is inactive if the destination page is locked.

There are two other functions similar to this: Flip and PCopy. Flip is designed to work in OpenGL modes, while PCopy supports console pages on some platforms. Both do the same thing as ScreenCopy in normal graphics modes.

The error code returned by ScreenCopy can be checked using Err in the next line. The function version of ScreenCopy returns directly the error code as a 32 bit Long.

Example

See also ScreenSet example.

'' 320x200x8, with 3 pages
Screen 13,,3

'' image for working page #1 (visible page #0)
ScreenSet 1, 0
Cls
Circle( 160, 100 ), 90, 1 ,,,, f
Circle( 160, 100 ), 90, 15
Print "Press 2 to copy page #2 to visible page"
Print "Press escape to exit"

'' image for working page #2 (visible page #0)
ScreenSet 2, 0
Cls
Line( 50, 50 )-( 270, 150 ), 2, bf
Line( 50, 50 )-( 270, 150 ), 15, b
Print "Press 1 to copy page #1 to visible page"
Print "Press escape to exit"

'' page #0 is the working page (visible page #0)
ScreenSet 0, 0
Cls
Print "Press 1 to copy page #1 to visible page"
Print "Press 2 to copy page #2 to visible page"
Print "Press escape to exit"

Dim k As String

Do
  k = Inkey
  Select Case k
  Case Chr(27)
    Exit Do
  Case "1"
    ScreenCopy 1, 0
  Case "2"
    ScreenCopy 2, 0
  End Select

  Sleep 25
Loop

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screencopy.

Differences from QB

  • New to FreeBASIC. It is a graphics-only version of PCopy - which works in both text and graphics modes.

See also

  • PCopy
  • Screen (Graphics)
  • ScreenRes
  • ScreenSet


ScreenControl
Sets or gets internal graphics library settings

Syntax

Declare Sub ScreenControl ( ByVal what As Const Long, ByRef param1 As Long = &h80000000, ByRef param2 As Long = &h80000000, ByRef param3 As Long = &h80000000, ByRef param4 As Long = &h80000000 )
Declare Sub ScreenControl ( ByVal what As Const Long, ByRef param1 As LongInt, ByRef param2 As LongInt = &h80000000, ByRef param3 As LongInt = &h80000000, ByRef param4 As LongInt = &h80000000 )
Declare Sub ScreenControl ( ByVal what As Const Long, ByRef param As String)

Usage

in the LONG (or INTEGER<32>) version of the sub:
ScreenControl( what [, [ param1 ] [, [ param2 ] [, [ param3 ] [, param4 ]]]] )
or,
ScreenControl( what , param )
in the LONGINT (or INTEGER<64>) version of the sub:
ScreenControl( what , param1 [, [ param2 ] [, [ param3 ] [, param4 ]]] )
or,
ScreenControl( what , param )

Parameters

what
specifies the function to perform
param1
first integer parameter, contains value to be set on entry or value got on exit
param2
second integer parameter, contains value to be set on entry or value got on exit
param3
third integer parameter, contains value to be set on entry or value got on exit
param4
fourth integer parameter, contains value to be set on entry or value got on exit
param
string parameter, contains text to be set on entry or text got on exit

Description

This function can be used to set or get internal GfxLib states. The what parameter specifies which operation to perform. On operations that set states, the param* parameters must contain the values to be set. On operations that get states, param* will hold the values returned by GfxLib when the function returns.
The meaning of the param* parameters depend on the what parameter, whose possible values are defined as constants in fbgfx.bi. In lang fb, they are set to be stored in the FB Namespace.
Below is a list of the supported what constants - and their values as defined at time of writing - along with the parameters associated with them.

Supported operations
Note:
(*) denotes operations that are allowed while a graphics mode has not yet been set via Screen (Graphics) or ScreenRes,
(**) denotes the operations that must be performed before the graphics mode is set via Screen (Graphics) or ScreenRes,
for all other operations, return values are zero (0) or the empty string ("") and the operation has no effect if a graphics mode is not available at call time.

Get operations
  • GET_WINDOW_POS Returns the current window position, in desktop coordinates.
[OUT] param1 x
[OUT] param2 y
  • (*) GET_WINDOW_TITLE Returns the title of the program window.
[OUT] param title
  • GET_WINDOW_HANDLE Returns a handle to the program window.
[OUT] param1 handle; this is a HWND in Windows, a "Window" XID in X11
[OUT] param2 display ptr in X11
  • (*) GET_DESKTOP_SIZE Returns the desktop size, in pixels.
[OUT] param1 width
[OUT] param2 height
  • GET_SCREEN_SIZE Returns the current screen size in pixels.
[OUT] param1 width
[OUT] param2 height
  • GET_SCREEN_DEPTH Returns current graphics mode screen depth.
[OUT] param1 bits per pixel
  • GET_SCREEN_BPP Returns current graphics mode BPP.
[OUT] param1 bytes per pixel
  • GET_SCREEN_PITCH Returns the current graphics mode framebuffer pitch, in bytes.
[OUT] param1 pitch
  • GET_SCREEN_REFRESH Returns the current graphics mode refresh rate, in hertz.
[OUT] param1 rate
  • GET_DRIVER_NAME Returns the current graphics mode driver name.
[OUT] param name
  • GET_TRANSPARENT_COLOR Returns the transparent color value for the current graphics mode depth.
[OUT] param1 value
  • GET_VIEWPORT Returns the current viewport as set by the View (Graphics) statement, in screen coordinates.
[OUT] param1 x1
[OUT] param2 y1
[OUT] param3 x2
[OUT] param4 y2
  • GET_PEN_POS Returns the last graphical pen position, in screen coordinates. This position is used in graphics functions supporting relative coordinates using the Step keyword.
[OUT] param1 x
[OUT] param2 y
  • GET_COLOR Returns the current graphics mode color.
[OUT] param1 foreground
[OUT] param2 background
  • GET_ALPHA_PRIMITIVES Returns if primitives drawing support for alpha channel is enabled.
[OUT] param1 TRUE (-1) if alpha primitives is enabled, FALSE (0) otherwise
  • GET_GL_EXTENSIONS Returns a string holding all supported GL extensions, or the empty string if not in OpenGL mode.
[OUT] param supported GL extensions
  • GET_HIGH_PRIORITY Returns if GFX_HIGH_PRIORITY was specified in the flags passed to Screen (Graphics) or ScreenRes.
[OUT] param1 higher priority graphics processing enabled
  • GET_SCANLINE_SIZE Returns how currently many scan lines each row of fbgfx video memory should occupy on the screen
[OUT] param1 scan line size multiplier
  • (*) GET_X86_MMX_ENABLED Returns if MMX specific blitters are enabled on X86. fbgfx will select MMX blitters if MMX is enabled and available.
[OUT] param1 TRUE (-1) if MMX is enabled, FALSE (0) otherwise
  • GET_GL_COLOR_BITS Returns the current number of bits dedicated to the OpenGL color buffer
[OUT] param1 bits
  • GET_GL_COLOR_RED_BITS Returns the current number of bits dedicated to the red component of the OpenGL color buffer
[OUT] param1 bits
  • GET_GL_COLOR_GREEN_BITS Returns the current number of bits dedicated to the green component of the OpenGL color buffer
[OUT] param1 bits
  • GET_GL_COLOR_BLUE_BITS Returns the current number of bits dedicated to the blue component of the OpenGL color buffer
[OUT] param1 bits
  • GET_GL_COLOR_ALPHA_BITS Returns the current number of bits dedicated to the alpha component of the OpenGL color buffer
[OUT] param1 bits
  • GET_GL_DEPTH_BITS Returns the current number of bits dedicated to the OpenGL depth buffer
[OUT] param1 bits
  • GET_GL_STENCIL_BITS Returns the current number of bits dedicated to the OpenGL stencil buffer
[OUT] param1 bits
  • GET_GL_ACCUM_BITS Returns the current number of bits dedicated to the OpenGL accumulation buffer
[OUT] param1 bits
  • GET_GL_ACCUM_RED_BITS Returns the current number of bits dedicated to the red component of the OpenGL accumulation buffer
[OUT] param1 bits
  • GET_GL_ACCUM_GREEN_BITS Returns the current number of bits dedicated to the green component of the OpenGL accumulation buffer
[OUT] param1 bits
  • GET_GL_ACCUM_BLUE_BITS Returns the current number of bits dedicated to the blue component of the OpenGL accumulation buffer
[OUT] param1 bits
  • GET_GL_ACCUM_ALPHA_BITS Returns the current number of bits dedicated to the alpha component of the OpenGL accumulation buffer
[OUT] param1 bits
  • GET_GL_NUM_SAMPLES Returns the current number of samples to be used for OpenGL multisampling
[OUT] param1 samples
  • GET_GL_2D_MODE Returns the current OpenGL 2D render
[OUT] param1:
OGL_2D_NONE No rendering
OGL_2D_MANUAL_SYNC Manual rendering (when Flip is called)
OGL_2D_AUTO_SYNC Automatic rendering
  • GET_GL_SCALE Returns the current zoom factor on OpenGL 2D render (only allowed if OpenGL 2D render mode has yet been activated via SET_GL_2D_MODE)
[OUT] param1 zoom factor (0: no rendering)

Set operations
  • SET_WINDOW_POS Sets the current program window position, in desktop coordinates.
[IN] param1 x
[IN] param2 y
  • (*) SET_WINDOW_TITLE Sets the current program window title. This is equivalent to calling WindowTitle( param ).
[IN] param title
  • SET_PEN_POS Sets the current graphical pen position, in screen coordinates. This position is used in graphics functions supporting relative coordinates using the Step keyword.
[IN] param1 x
[IN] param2 y
  • (*) SET_DRIVER_NAME Sets the name of the internal graphics driver to be used in subsequent calls to Screen (Graphics) or ScreenRes.
[IN] param driver name
  • SET_ALPHA_PRIMITIVES Sets if primitives drawing should honor alpha channel.
[IN] param1 enabled
  • (*) SET_GL_COLOR_BITS Sets the number of bits dedicated to the OpenGL color buffer
[IN] param1 bits
  • (*) SET_GL_COLOR_RED_BITS Sets the number of bits dedicated to the red component of the OpenGL color buffer
[IN] param1 bits
  • (*) SET_GL_COLOR_GREEN_BITS Sets the number of bits dedicated to the green component of the OpenGL color buffer
[IN] param1 bits
  • (*) SET_GL_COLOR_BLUE_BITS Sets the number of bits dedicated to the blue component of the OpenGL color buffer
[IN] param1 bits
  • (*) SET_GL_COLOR_ALPHA_BITS Sets the number of bits dedicated to the alpha component of the OpenGL color buffer
[IN] param1 bits
  • (*) SET_GL_DEPTH_BITS Sets the number of bits dedicated to the OpenGL depth buffer
[IN] param1 bits
  • (*) SET_GL_STENCIL_BITS Sets the number of bits dedicated to the OpenGL stencil buffer
[IN] param1 bits
  • (*) SET_GL_ACCUM_BITS Sets the number of bits dedicated to the OpenGL accumulation buffer
[IN] param1 bits
  • (*) SET_GL_ACCUM_RED_BITS Sets the number of bits dedicated to the red component of the OpenGL accumulation buffer
[IN] param1 bits
  • (*) SET_GL_ACCUM_GREEN_BITS Sets the number of bits dedicated to the green component of the OpenGL accumulation buffer
[IN] param1 bits
  • (*) SET_GL_ACCUM_BLUE_BITS Sets the number of bits dedicated to the blue component of the OpenGL accumulation buffer
[IN] param1 bits
  • (*) SET_GL_ACCUM_ALPHA_BITS Sets the number of bits dedicated to the alpha component of the OpenGL accumulation buffer
[IN] param1 bits
  • (*) SET_GL_NUM_SAMPLES Sets the number of samples to be used for OpenGL multisampling
[IN] param1 samples
  • (*) SET_X86_MMX_ENABLED Sets if MMX blitters should be used on X86
[IN] param1 enabled
  • (**) SET_GL_2D_MODE Sets OpenGL 2D render
[IN] param1:
OGL_2D_NONE No rendering
OGL_2D_MANUAL_SYNC Manual rendering (when Flip is called)
OGL_2D_AUTO_SYNC Automatic rendering
  • (**) SET_GL_SCALE Apply a zoom factor on OpenGL 2D render (only allowed if OpenGL 2D render mode has yet been activated via SET_GL_2D_MODE)
[IN] param1 zoom factor (0: no rendering)

Other operations
  • POLL_EVENTS Cause the library to poll all events, ie to check the system event queue, specifically used for retrieving keyboard and mouse events. This is most useful for OpenGL code where Flip is not used, as normally Flip will cause these events to be polled.

Example

'' include fbgfx.bi for some useful definitions
#include "fbgfx.bi"

'' use FB namespace for easy access to types/constants
Using FB

Dim e As Event
Dim As Long x0, y0, x, y
Dim As Integer shakes = 0
Dim As Any Ptr img

ScreenRes 320, 200, 32
Print "Click to shake window"

'' find window coordinates
ScreenControl GET_WINDOW_POS, x0, y0

Do

    If (shakes > 0) Then

        '' do a shake of the window

        If (shakes > 1) Then

            '' move window to a random position near its original coordinates
            x = x0 + Int(32 * (Rnd() - 0.5))
            y = y0 + Int(32 * (Rnd() - 0.5))
            ScreenControl SET_WINDOW_POS, x, y

        Else

            '' move window back to its original coordinates
            ScreenControl SET_WINDOW_POS, x0, y0

        End If

        shakes -= 1

    End If

    If (ScreenEvent(@e)) Then
        Select Case e.type

        '' user pressed the mouse button
        Case EVENT_MOUSE_BUTTON_PRESS

            If (shakes = 0) Then
                '' set to do 20 shakes
                shakes = 20

                '' find current window coordinates to shake around
                ScreenControl GET_WINDOW_POS, x0, y0
            End If

        '' user closed the window or pressed a key
        Case EVENT_WINDOW_CLOSE, EVENT_KEY_PRESS
            '' exit to end of program
            Exit Do

        End Select
    End If

    '' free up CPU for other programs
    Sleep 5

Loop

'' include fbgfx.bi for some useful definitions
#include "fbgfx.bi"

Dim As String driver

#ifdef __FB_WIN32__
'' set graphics driver to GDI (Windows only), before calling ScreenRes
ScreenControl FB.SET_DRIVER_NAME, "GDI"
#endif

ScreenRes 640, 480

'' fetch graphics driver name and display it to user
ScreenControl FB.GET_DRIVER_NAME, driver
Print "Graphics driver name: " & driver

'' wait for a keypress before closing the window
Sleep

Version

  • Since fbc 1.20.0: GET_X86_MMX_ENABLED, SET_X86_MMX_ENABLED
  • Since fbc 1.10.0: 16 getters from GET_SCANLINE_SIZE to GET_GL_SCALE added.
  • Before fbc 1.08.0:
Syntax:
Declare Sub ScreenControl ( ByVal what As Long, ByRef param1 As Integer = 0, ByRef param2 As Integer = 0, ByRef param3 As Integer = 0, ByRef param4 As Integer = 0 )
Declare Sub ScreenControl ( ByVal what As Long, ByRef param As String = "" )
Usage:
ScreenControl( what [, [ param1 ] [, [ param2 ] [, [ param3 ] [, [ param4 ]]]]] )
or,
ScreenControl( what [, param ] )

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screencontrol.

Differences from QB

  • New to FreeBASIC

See also

  • Screen (Graphics)
  • ScreenEvent
  • ScreenInfo
  • WindowTitle
  • View (Graphics)


ScreenEvent
Queries for and retrieves system events.

Syntax

Declare Function ScreenEvent ( ByVal event As Any Ptr = 0 ) As Long

Usage

result = ScreenEvent( [ event ] )

Parameters

event
Specifies the buffer where the function should store the event data.

Return Value

Returns -1 if there are pending events to be retrieved, 0 otherwise.

Description

This function returns the latest available system event from the internal GfxLib events queue. By "event" we mean any mouse or keyboard activity, for example.

The event data (if available) will be copied into the buffer pointed that should be declared as an Event.
On the Event page, see the list of event types and how to use their associated fields (see also the example below).

Querying for events
The function returns -1 if there are pending events to be retrieved, 0 otherwise. If the event parameter is set to 0 (the default if omitted) ScreenEvent will not be able to copy the event data and it will not dequeue it from the internal events queue. Calling the function this way can be useful to check if there are pending events without actually fetching them.

Note
If you receive a KEY_PRESS, KEY_RELEASE or KEY_REPEAT event, it does not clear the keyboard buffer. If you need the buffer to be clear after you receive the event, you will need to clear it manually. See Inkey.

Example

'' include fbgfx.bi for some useful definitions
#include "fbgfx.bi"
#if __FB_LANG__ = "fb"
Using fb '' constants and structures are stored in the FB namespace in lang fb
#endif

Dim e As Event

ScreenRes 640, 480
Do
    If (ScreenEvent(@e)) Then
        Select Case e.type
        Case EVENT_KEY_PRESS
            If (e.scancode = SC_ESCAPE) Then
                End
            End If
            If (e.ascii > 0) Then
                Print "'" & e.ascii & "'";
            Else
                Print "unknown key";
            End If
            Print " was pressed (scancode " & e.scancode & ")"
        Case EVENT_KEY_RELEASE
            If (e.ascii > 0) Then
                Print "'" & e.ascii & "'";
            Else
                Print "unknown key";
            End If
            Print " was released (scancode " & e.scancode & ")"
        Case EVENT_KEY_REPEAT
            If (e.ascii > 0) Then
                Print "'" & e.ascii & "'";
            Else
                Print "unknown key";
            End If
            Print " is being repeated (scancode " & e.scancode & ")"
        Case EVENT_MOUSE_MOVE
            Print "mouse moved to " & e.x & "," & e.y & " (delta " & e.dx & "," & e.dy & ")"
        Case EVENT_MOUSE_BUTTON_PRESS
            If (e.button = BUTTON_LEFT) Then
                Print "left";
            ElseIf (e.button = BUTTON_RIGHT) Then
                Print "right";
            Else
                Print "middle";
            End If
            Print " button pressed"
        Case EVENT_MOUSE_BUTTON_RELEASE
            If (e.button = BUTTON_LEFT) Then
                Print "left";
            ElseIf (e.button = BUTTON_RIGHT) Then
                Print "right";
            Else
                Print "middle";
            End If
            Print " button released"
        Case EVENT_MOUSE_DOUBLE_CLICK
            If (e.button = BUTTON_LEFT) Then
                Print "left";
            ElseIf (e.button = BUTTON_RIGHT) Then
                Print "right";
            Else
                Print "middle";
            End If
            Print " button double clicked"
        Case EVENT_MOUSE_WHEEL
            Print "mouse wheel moved to position " & e.z
        Case EVENT_MOUSE_ENTER
            Print "mouse moved into program window"
        Case EVENT_MOUSE_EXIT
            Print "mouse moved out of program window"
        Case EVENT_WINDOW_GOT_FOCUS
            Print "program window got focus"
        Case EVENT_WINDOW_LOST_FOCUS
            Print "program window lost focus"
        Case EVENT_WINDOW_CLOSE
            End
        Case EVENT_MOUSE_HWHEEL
            Print "horizontal mouse wheel moved to position " & e.w
        End Select
    End If

    Sleep 1
Loop

Platform Differences

  • ScreenEvent does not return window related events in the DOS version, but does return input events.

Dialect Differences

  • Not available in the -lang qb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Event
  • Screen (Graphics)
  • Inkey
  • MultiKey
  • GetMouse
  • Event Handling
  • Keyboard scancodes
  • Keyboard Input


ScreenGLProc
Gets the address of an OpenGL procedure

Syntax

Declare Function ScreenGLProc ( ByRef procname As Const String ) As Any Ptr

Parameters

procname
name of the procedure to retrieve the address of

Description

This function can be used to get the address of any OpenGL procedure, to be used to retrieve the pointers to new functions associated with OpenGL extensions. If given procedure named procname cannot be found, ScreenGLProc will return NULL (0).

Example

'' include fbgfx.bi for some useful definitions
#include "fbgfx.bi"

Dim SwapInterval As Function(ByVal interval As Integer) As Integer
Dim extensions As String

'' Setup OpenGL and retrieve supported extensions
ScreenRes 640, 480, 32,, FB.GFX_OPENGL
ScreenControl FB.GET_GL_EXTENSIONS, extensions

If (InStr(extensions, "WGL_EXT_swap_control") <> 0) Then
    '' extension supported, retrieve proc address
    SwapInterval = ScreenGLProc("wglSwapIntervalEXT")
    If (SwapInterval <> 0) Then
        '' Ok, we got it. Set OpenGL to wait for vertical sync on buffer swaps
        SwapInterval(1)
    End If
End If

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screenglproc.

Platform Differences

  • Not available for DOS target.

Differences from QB

  • New to FreeBASIC

See also

  • Screen (Graphics)
  • ScreenControl


ScreenInfo
Retrieves information about current video mode or the desktop.

Syntax

Declare Sub ScreenInfo ( ByRef w As Long = 0, ByRef h As Long = 0, ByRef depth As Long = 0, ByRef bpp As Long = 0, ByRef pitch As Long = 0, ByRef rate As Long = 0, ByRef driver As String = "" )
Declare Sub ScreenInfo ( ByRef w As LongInt, ByRef h As LongInt, ByRef depth As LongInt = 0, ByRef bpp As LongInt = 0, ByRef pitch As LongInt = 0, ByRef rate As LongInt = 0, ByRef driver As String = "" )

Usage

in the LONG (or INTEGER<32>) version of the sub:
ScreenInfo [ w ] [, [ h ] [, [ depth ] [, [ bpp ] [, [ pitch ] [, [ rate ] [, driver ]]]]]]
in the LONGINT (or INTEGER<64>) version of the sub:
ScreenInfo w , h [, [ depth ] [, [ bpp ] [, [ pitch ] [, [ rate ] [, driver ]]]]]

Parameters

w
Width.
h
Height.
depth
Color depth in bits.
bpp
Bytes per pixel.
pitch
Bytes per scan line.
rate
Refresh rate.
driver
Driver name.

Description

This function can be useful to get current mode informations like graphics driver name, color depth, screen size and more.

If ScreenInfo is called when no graphics mode is set, it returns the information about the desktop.

Here's a description of available fields:

w | Width of the screen in pixels
h | Height of the screen in pixels
depth | Current pixel format bits per pixel: this can be 1, 2, 4, 8, 16, or 32
bpp | Bytes per pixel
pitch | Size of a framebuffer row in bytes
rate | Current refresh rate, or 0 if unknown
driver | Name of current graphics driver in use, like DirectX (Direct2D added on new systems) or X11

Example

Dim w As Long, h As Long
Dim depth As Long
Dim driver_name As String

Screen 15, 32
' Obtain info about current mode
ScreenInfo w, h, depth,,,,driver_name
Print Str(w) + "x" + Str(h) + "x" + Str(depth);
Print " using " + driver_name + " driver"
Sleep
' Quit graphics mode and obtain info about desktop
Screen 0
ScreenInfo w, h, depth
Print "Desktop running at " + Str(w) + "x" + Str(h) + "x" + Str(depth);

Version

  • Before fbc 1.08.0:
Syntax:
Declare Sub ScreenInfo ( ByRef w As Integer = 0, ByRef h As Integer = 0, ByRef depth As Integer = 0, ByRef bpp As Integer = 0, ByRef pitch As Integer = 0, ByRef rate As Integer = 0, ByRef driver As String = "" )
Usage:
ScreenInfo [ w ] [, [ h ] [, [ depth ] [ , [ bpp ] [ , [ pitch ] [ , [ rate ] [, driver ]]]]]]

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screeninfo.

Differences from QB

  • New to FreeBASIC

See also

  • Screen (Graphics)


ScreenList
Finds available fullscreen video modes

Syntax

Declare Function ScreenList ( ByVal depth As Long = 0 ) As Long

Usage

result = ScreenList( [ depth ] )

Parameters

depth
the color depth for which the list of modes is requested (supported depths are 8, 15, 16, 24 and 32)

Return Value

returns 0, when there are no more resolutions to read.

Description

It works like the Dir function: the first call to the function requires the depth parameter to be specified, it returns the lowest supported resolution for the requested depth. Further calls to ScreenList without arguments returns the next resolutions. When no more resolutions are available, ScreenList returns 0.

The result of ScreenList is encoded as a 32 bit value, with the screen width as the High Word and the height as the Low Word.

Resolutions are returned from lowest to highest supported ones.

It is safe to call this function before any graphics mode has been set.

Example

Dim As Long mode
Dim As UInteger w, h

Print "Resolutions supported at 8 bits per pixel:"

mode = ScreenList(8)
While (mode <> 0)
    w = HiWord(mode)
    h = LoWord(mode)
    Print w & "x" & h
    mode = ScreenList()
Wend

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screenlist.

Differences from QB

  • New to FreeBASIC

See also

  • Screen (Graphics)
  • ScreenRes


ScreenLock
Locks the working page's frame buffer

Syntax

Declare Sub ScreenLock ( )

Usage

ScreenLock

Description

All of FreeBASIC's Graphics Library functions draw to a frame buffer and an automatic routine copies the frame buffer to the actual screen memory at each draw. If the user program does a lot of drawing, the automatic refreshes may take a significant amount of time.

The ScreenLock function locks the automatic refresh, so several drawing operations may be done before the screen refresh is performed, thus increasing the speed of execution, and preventing the user from seeing partial results.

Frame buffer memory may be freely accessed by using pointers (see ScreenPtr) ONLY while the screen is locked. Primitive graphics statements (Line, PSet, Draw String, ...) may be used at any time.

The screen refresh remains locked until the use of ScreenUnlock statement, which resumes it.

Calls to ScreenLock must be paired with a matching call to ScreenUnlock. The graphics driver keeps track of how many times ScreenLock has been called using a counter. Only the first call to ScreenLock actually performs a locking operation. Subsequent calls to ScreenLock only increment the counter. Conversely, ScreenUnlock only decrements the lock counter until it reaches zero at which time the actual unlock operation will be performed. Using Screen (Graphics) or ScreenRes will release all locks and set the lock counter back to zero before changing screen modes.

It is strongly recommended that the lock on a page be held for as short a time as possible. Only screen drawing should occur while the screen is locked, input/output and waiting must be avoided. In Win32 and Linux the screen is locked by stopping the thread that processes also the OS' events. If the screen is kept locked for a long time the event queue could overflow and make the system unstable. When the induced lock time becomes too long, use preferably the method of double buffering (with ScreenCopy).

The automatic refresh takes place only in the visible page of the frame buffer. ScreenLock has no effect when drawing to pages other than the visible one.

Example

'' Draws a circle on-screen at the mouse cursor
Dim As Long mx, my
Dim As String key

ScreenRes 640, 480, 32

Do

  'process
  GetMouse(mx, my)
  key = Inkey()

  'draw
  ScreenLock()
  Cls()
  Circle (mx, my), 8, RGB(255, 255, 255)
  ScreenUnlock()

  'free up CPU time
  Sleep(18, 1)

Loop Until key = Chr(27) Or key = Chr(255, 107)

Platform Differences

  • In DOS, the mouse arrow does not react to mouse movements while the screen is locked

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screenlock.

Differences from QB

  • New to FreeBASIC

See also

  • Screen (Graphics) - Setting mode
  • ScreenRes - Setting mode
  • ScreenUnlock
  • ScreenPtr


ScreenPtr
Returns a pointer to the current work page's frame buffer

Syntax

Declare Function ScreenPtr ( ) As Any Ptr

Usage

result = ScreenPtr

Return Value

a pointer to the current work page frame buffer memory, or NULL (0) if no graphics mode is set.

Description

ScreenPtr provides a way to directly read/write the working page's frame buffer. ScreenLock should be used before any read or writes are attempted. The pointer returned is valid up until any subsequent call to Screen (Graphics) or ScreenRes, which invalidates it.

ScreenPtr can also be used to test if a call to Screen (Graphics) or ScreenRes was successful, indicated by a non-NULL (<> 0) return value.

In order to access a pixel in the screen buffer, you will need to know the screen's bytes per pixel and pitch (bytes per row), and also the width and height to avoid going out of bounds. This information can be found out using ScreenInfo.
Each row in the frame buffer is pitch bytes long. The frame buffer consists of height rows, stored in order of their position on the screen, running from top to bottom, left to right.

Because of the design of FreeBASIC graphics library, ScreenPtr (if non-NULL) will always point to the backbuffer, and never to actual video RAM.

Example

Const SCREEN_WIDTH = 640, SCREEN_HEIGHT = 480
Dim As Long w, h, bypp, pitch

'' Make 8-bit screen.
ScreenRes SCREEN_WIDTH, SCREEN_HEIGHT, 8

'' Get screen info (w and h should match the constants above, bypp should be 1)
ScreenInfo w, h, , bypp, pitch

'' Get the address of the frame buffer. An Any Ptr
'' is used here to allow simple pointer arithmetic
Dim buffer As Any Ptr = ScreenPtr()
If (buffer = 0) Then
    Print "Error: graphics screen not initialized."
    Sleep
    End -1
End If

'' Lock the screen to allow direct frame buffer access
ScreenLock()

    '' Find the address of the pixel in the centre of the screen
    '' It's an 8-bit pixel, so use a UByte Ptr.
    Dim As Integer x = w \ 2, y = h \ 2
    Dim As UByte Ptr pixel = buffer + (y * pitch) + (x * bypp)

    '' Set the center pixel color to 10 (light green).
    *pixel = 10

'' Unlock the screen.
ScreenUnlock()

'' Wait for the user to press a key before closing the program
Sleep

Const SCREEN_WIDTH = 256, SCREEN_HEIGHT = 256
Dim As Long w, h, bypp, pitch

'' Make 32-bit screen.
ScreenRes SCREEN_WIDTH, SCREEN_HEIGHT, 32

'' Get screen info (w and h should match the constants above, bypp should be 4)
ScreenInfo w, h, , bypp, pitch

'' Get the address of the frame buffer. An Any Ptr
'' is used here to allow simple pointer arithmetic
Dim buffer As Any Ptr = ScreenPtr()
If (buffer = 0) Then
    Print "Error: graphics screen not initialized."
    Sleep
    End -1
End If

'' Lock the screen to allow direct frame buffer access
ScreenLock()

    '' Set row address to the start of the buffer
    Dim As Any Ptr row = buffer

    '' Iterate over all the pixels in the screen:

    For y As Integer = 0 To h - 1

        '' Set pixel address to the start of the row
        '' It's a 32-bit pixel, so use a ULong Ptr
        Dim As ULong Ptr pixel = row

        For x As Integer = 0 To w - 1

            '' Set the pixel value
            *pixel = RGB(x, x Xor y, y)

            '' Get the next pixel address
            '' (ULong Ptr will increment by 4 bytes)
            pixel += 1

        Next x

        '' Go to the next row
        row += pitch

    Next y

'' Unlock the screen.
ScreenUnlock()

'' Wait for the user to press a key before closing the program
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screenptr.

Differences from QB

  • New to FreeBASIC

See also

  • Screen (Graphics)
  • ScreenRes
  • ScreenInfo
  • ScreenLock
  • ScreenUnlock


ScreenRes
Initializes a graphics mode by specifying horizontal and vertical resolution

Syntax

Declare Function ScreenRes ( ByVal width As Long, ByVal height As Long, ByVal depth As Long = 8, ByVal num_pages As Long = 1, ByVal flags As Long = 0, ByVal refresh_rate As Long = 0 ) As Long

Usage

ScreenRes width, height [, [depth] [, [num_pages] [, [flags] [, refresh_rate ]]]]
result = ScreenRes( width, height [, [depth] [, [num_pages] [, [flags] [, refresh_rate ]]]] )

Parameters

width, height
The display width and height, respectively. For fullscreen mode, the user should check the availability of the resolution using ScreenList.
depth
The color depth in bits per pixel. Valid color depths are: 1, 2, 4, 8, 16 and 32. Values of 15 and 24 are also allowed as aliases for 16 and 32, respectively. If omitted, the default is 8 bits per pixel. 8 bits and below will give a palette image. The default palette will be the first 2 ^ depth colors of the 256-color palette used in Screen (Graphics) 13.
num_pages
The number of video pages to create, defaults to 1. (see Screen (Graphics))
flags
Used to set various properties of the screen, including fullscreen mode and graphics driver priority. (see the standard header "fbgfx.bi" for available flags)
refresh_rate
The desired refresh rate of the screen, only has an effect for fullscreen modes, and some systems and drivers only. Defaults to an appropriate value, invalid refresh rates will be ignored.

Return Value

Returns zero (0) if successful, or a non-zero error code to indicate a failure. (throws a runtime error) (???)

Description

ScreenRes tells the compiler to link the GfxLib and initializes a QB-only, QB-on-GUI or OpenGL graphics mode, depending on the flags setting.

ScreenRes clears the created window or the full screen. In non-fullscreen modes, the resolution does not have to match any resolution of the graphics card. Resolutions like 555x111 are possible, GfxLib will create a window of such size. See the page GfxLib overview for DOS issues.

The font size in ScreenRes modes is set to 8x8 by default. This can be changed by setting the number of text rows/columns, using the Width function.

In QB-only modes a dumb window or fullscreen resolution is set, one or more buffers in standard memory are created, console commands are redirected to their graphic versions, a default palette is set and an automatic screen refresh thread is started. QB-like graphics and console statements can be used.

In QB-on-GUI modes one or more buffers in standard memory are created, console commands are redirected to their graphic versions and a default palette is set. QB-like graphics and console statements can be used. It is up to the user to create a window and to refresh it with the contents of the graphics buffers.

In OpenGL modes a dumb window or fullscreen resolution is set, one or more buffers in standard memory are created, and the system's OpenGL library is initialized. From here only OpenGL commands can be used to write to the graphics buffer. QB-like and console commands are forbidden. This mode allows to initialize OpenGL in a portable way.

The error code returned by ScreenRes can be checked using Err in the next line. The function version of ScreenRes returns directly the error code as a 32 bit Long.

flags details:

If flags are omitted, FreeBASIC uses QB-compatible graphics in windowed (except in DOS) mode. These constants are defined in fbgfx.bi. In the -lang fb dialect, these constants are part of the FB Namespace. Their values can be combined to form a mask using Operator Or. Note that most of the flags are not supported in DOS.

Available flags:

graphic mode flags
GFX_NULL: Starts a QB-on-GUI graphics mode. It creates a graphics buffer but not a window. User must implement the window, the events manager and refresh the screen as needed. This mode allows to mix FreeBASIC drawing functions with API-driven windows. Alternatively, it allows to process graphics (for example files) without making it visible on the screen, even in a purely console application. This flag overrides all other mode flags. See an Example of GFX_NULL in Windows.
GFX_OPENGL: Initializes OpenGL to draw in a dumb window. FreeBASIC graphic functions can't be used. The screen is not automatically updated, Flip must be used. This option provides a portable way to initialize the OpenGL Library.
If none of the above options is specified, FreeBASIC enters the QB-only graphics mode: it creates a buffer and a dumb window and sets a thread that automatically updates the screen and manages keyboard and mouse. The FreeBASIC drawing functions can be used.

window mode flags
Window mode flags are meaningless if GFX_NULL mode is used
GFX_WINDOWED: If windowed mode is supported, FreeBASIC opens a window of the requested size in the present desktop
GFX_FULLSCREEN: The graphics card switch mode is switched to the requested mode and color depth and OS fullscreen mode is used. If the mode is not available in the present card FreeBASIC switches to windowed mode.
If GFX_FULLSCREEN is not specified, the behavior for GFX_WINDOWED is assumed.
GFX_NO_SWITCH: Prevents the user from changing to fullscreen or to windowed mode by pressing Alt-Enter.
GFX_NO_FRAME: Creates a window without a border.
GFX_SHAPED_WINDOW: Creates transparent regions wherever RGBA(255, 0, 255, 0) is drawn on the screen.
GFX_ALWAYS_ON_TOP: Creates a window that stays always on top.

option flags
Flags working in any mode, they activate special behaviors
GFX_ALPHA_PRIMITIVES: Tells the graphics library to enable alpha channel support for all drawing primitives. This means the alpha specified in a color value (via either the RGBA macro or direct color in the form &hAARRGGBB) will always be used by all primitives.
GFX_HIGH_PRIORITY: Tells the graphics library to enable a higher priority for graphics processing. Only has an effect on gdi and DirectX drivers on Win32 platform and Direct2D added on new platforms.
GFX_NO_X86_MMX: Tells the graphics library not to use MMX blitters on X86 even if MMX cpu instructions are available.

OpenGL Buffer flags
These flags work only in OpenGL graphics mode, must be combined with GFX_OPENGL
GFX_STENCIL_BUFFER: Forces OpenGL to use Stencil buffer
GFX_ACCUMULATION_BUFFER: Forces OpenGL to use Accumulation buffer
GFX_MULTISAMPLE: Requests fullscreen anti-aliasing through the ARB_multisample extension

Depending on whether the GFX_FULLSCREEN parameter is present or not, Screen will try to set the specified video mode in fullscreen or windowed mode, respectively. If fullscreen mode is set and the system cannot set specified mode in fullscreen, it will try in windowed mode. If windowed mode is set and the system fails to open a window for specified mode, it will try fullscreen. If everything fails, Screen will have no effect and execution will resume from the statement following the Screen call. You should take care of checking if a graphics mode has been set or not, and behave accordingly; a way to check if Screen is successful is to test the return value of the ScreenPtr function; see its page for details.

Note:
Screen 0 closes any graphics window, but also clears the console window if it exists.
Screen 0, , , GFX_SCREEN_EXIT (with GFX_SCREEN_EXIT=&h80000000) also closes any graphics window, but does not clear the console window if it exists (previous text is preserved).

Graphics mode console
Console commands (Locate, Print), input can be used both with standard QB Screen modes and with the extended ones too, provided the standard color depth is not modified by using the second argument of Screen. Where the table says more than one text resolution is available for the text mode, the required text resolution can be requested by using Width. Any characters Printed will erase the background around them; it does not use a transparent background.

Example

' Set the screen mode to 320*200, with 8 bits per pixel
ScreenRes 320, 200, 8

' Draw color bands in a diagonal pattern over the whole screen
For y As Long = 0 To 200-1
    For x As Long = 0 To 320-1
        PSet (x,y),(x + y) And 255
    Next x
Next y

' Display the text "Hello World!!" over the lines we've drawn, in the top-left hand corner
Print "Hello world!!"

' Keep the window open until the user presses a key
Sleep

Version

Since 1.20.0: GFX_NO_X86_MMX

Platform Differences

  • In DOS, Windowing and OpenGL related switches are not available, and other issues, see GfxLib overview

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screenres.

Differences from QB

  • New to FreeBASIC

See also

  • Screen (Graphics) The QB-like way to set graphics mode
  • ScreenList Check display modes available for FB GfxLib to use
  • ScreenControl Select driver and more
  • ScreenLock
  • ScreenUnlock
  • ScreenPtr Semi-low level access
  • ScreenSet
  • ScreenCopy
  • ScreenInfo
  • ScreenGLProc
  • Internal pixel formats
  • FaqPggfxlib2


ScreenSet
Sets current work and visible pages

Syntax

Declare Sub ScreenSet ( ByVal work_page As Long = -1, ByVal visible_page As Long = -1 )

Usage

ScreenSet [ work_page ] [, visible_page ]

Parameters

work_page
index to working page
visible_page
index to visible page

Description

ScreenSet allows to set the current working page and the current visible page. Page numbers range from 0 to num_pages - 1, where num_pages is the number of pages specified when setting the graphics mode with ScreenRes or Screen (Graphics). You can use this function to achieve page-flipping or double-buffering.

If you provide visible_page but omit work_page, only the visible page is changed. If you provide work_page but omit visible_page, only the work page is changed. If you omit both arguments, both work page and visible page are reset to page 0.

ScreenSet provides one method of writing to the screen without instantly displaying changes to the user. See also ScreenLock / ScreenUnlock for an alternative method of doing this.

Note: The current cursor position is not handled independently for each video page. Therefore, when another working page is selected, the starting cursor position corresponds to the last cursor position on the previous working page (same behavior for the text cursor and the graphics cursor).

Example

' Open graphics screen (320*200, 8bpp) with 2 pages
ScreenRes 320, 200, 8, 2

' Work on page 1 while displaying page 0
ScreenSet 1, 0

Dim As Integer x = -40

Do
    '' Clear the screen, draw a box, update x
    Cls
    Line (x, 80)-Step(39, 39), 4, BF
    x += 1: If (x > 319) Then x = -40

    ' Wait for vertical sync: only used to control refresh rate, can be put anywhere in the Do loop
    ScreenSync

    ' Copy work page to visible page
    ScreenCopy

Loop While Inkey = ""

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screenset.

Differences from QB

  • New to FreeBASIC

See also

  • Screen (Graphics)
  • ScreenRes
  • ScreenCopy
  • ScreenLock
  • ScreenUnlock


ScreenSync
Synchronizes display updates with hardware

Syntax

Declare Function ScreenSync ( ) As Long

Usage

result = ScreenSync

Return Value

Zero if successful, or non-zero if a graphics mode was not previously set.

Description

This GfxLib statement stops the execution of the program until the graphics card signals it has ended tracing a frame and is going to start the new one.

If the program uses this small interval of time between frames to redraw the image, the flickering is greatly reduced. In that use, ScreenSync is a reminiscence of QB where there was only that equivalent method (Wait &H3DA, 8) to improve the flickering. It is an empirical method because it only allows to synchronize the beginning of the drawing with the fixed dead time between two frames. To be used occasionally to avoid flickering when only very short time of drawing.

Except the purpose to reduce the flickering, ScreenSync can be also used simply as a method of synchronization of graphic drawing with the screen frame tracing (similarly to statement Sleep).

The error code returned by ScreenSync can be checked using Err in the next line. The function version of ScreenSync returns directly the error code as a 32 bit Long.

The use of the QB-compatible form Wait &H3DA, 8 is deprecated.

Example

'main loop
Do

  ' do user input
  ' calculate_a_frame

  ScreenSync

  ' draw_ a_ frame

Loop Until Inkey <> ""

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screensync.

Differences from QB

  • New to FreeBASIC.
  • QBasic used Wait &H3DA, 8 for this purpose.

See also

  • Wait


ScreenUnlock
Unlocks work page's framebuffer

Syntax

Declare Sub ScreenUnlock ( ByVal startline As Long = -1, ByVal endline As Long = -1 )

Usage

ScreenUnlock [ start_line ] [, end_line ]

Parameters

startline
optional argument specifying first screen line to be updated. If omitted, top screen line is assumed.
endline
optional argument specifying last screen line to be updated. If omitted, bottom screen line is assumed.

Description

ScreenUnlock unlocks the current work page assuming it was previously locked by calling ScreenLock and lets the system restart updating the screen regularly. When called with start_line and end_line , only the screen area between those lines is assumed to have changed, and will be updated.

An internal counter exists that remembers the screen lock state, thus ScreenUnlock has an effect only on a screen that is locked. A screen that has not been locked with ScreenLock cannot get unlocked, however ScreenUnlock still will force an update of given area or full screen.

Calls to ScreenUnlock must be paired with matching calls to ScreenLock. Only the first call to ScreenLock actually performs a locking operation. Subsequent calls to ScreenLock only increment the lock counter. Conversely, ScreenUnlock only decrements the lock counter until it reaches zero at which time the actual unlock operation will be performed. Using Screen (Graphics) or ScreenRes will release all locks and set the lock counter back to zero before changing screen modes.

All graphic statements automatically lock the screen before the function call, and unlock the screen afterwards, so you do not need to do this explicitly using ScreenLock and ScreenUnlock. You only need to lock the screen when you wish to access the screen (framebuffer) directly using ScreenPtr or when you wish to group several graphic statements together so their effects appear simultaneously on screen, thus avoiding potential screen flicker during screen updates.

Warning (Win32, Linux) : The screen is locked by stopping the thread that processes also the OS' events. This means the screen should be locked only for the short time required to redraw it, and no user input will be received while the screen is locked. When the induced lock time becomes too long, use preferably the method of double buffering (with ScreenCopy).

Example

See ScreenPtr example.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Screenunlock.

Differences from QB

  • New to FreeBASIC

See also

  • Screen (Graphics)
  • ScreenLock
  • ScreenPtr


Second
Gets the seconds from a Date Serial

Syntax

Declare Function Second ( ByVal date_serial As Double ) As Long

Usage

#include "vbcompat.bi"
result = Second( date_serial )

Parameters

date_serial
the date serial

Return Value

Returns the seconds from a variable containing a date in Date Serial format.

Description

The compiler will not recognize this function unless vbcompat.bi is included.

Example

#include "vbcompat.bi"

Dim ds As Double = DateSerial(2005, 11, 28) + TimeSerial(7, 30, 50)

Print Format(ds, "yyyy/mm/dd hh:mm:ss "); Second(ds)

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials


Seek (Statement)
Sets the position of the next read/write operation on a file

Syntax

Seek [#]filenum, position

Parameters

filenum
file number of an opened a file
position
the new position for i/o operations

Description

Sets the position at which the next read or write operation on a file will occur.

The position is given in records if the file was opened in Random access mode, in bytes in any other case. The position is 1 based -- the first record of a file is at position 1.

The Seek function is used to get the position of the next read or write operation.

Example

' e.g. if you want to skip to the 100th byte in the file for reading/writing:

Dim f As Long

f = FreeFile
Open "file.ext" For Binary As #f

Seek f, 100

Close #f

Differences from QB

  • None

See also

  • Seek (Function)
  • Open


Seek (Function)
Gets the position of the next read/write operation for a file or device

Syntax

Declare Function Seek ( ByVal filenum As Long ) As LongInt

Parameters

filenum
file number of an open file

Return Value

The file position where the next read or write operation will take place.

Description

The position is given in records if the file was opened in Random access mode, in bytes in any other case. The file position returned is 1-based, so the first record of a file is 1.

The Seek statement is used to set the position of the next read or write operation.

Example

Dim f As Long, position As LongInt

f = FreeFile
Open "file.ext" For Binary As #f

position = Seek(f)

Close #f

Differences from QB

  • None

See also

  • Seek (Statement)
  • LOC
  • Open


Select Case
Conditional statement block

Syntax

Select Case expression
[ Case expressionlist]
[statements]
[ Case Else ]
[statements]
End Select
or
Select Case As Const integer_expression
[ Case constant | enumeration ]
[ statements ]
[ Case Else ]
[ statements ]
End Select

Description

Select Case executes specific code depending on the value of an expression. The expression is evaluated once, and compared against each Case, in order, until a matching expression is found. The code inside the matching Case branch is executed, and the program skips down to the end of the Select Case block. Case Else matches any case not already matched, so if there is a Case Else, at least one Case is guaranteed to be executed. If no Cases match, the whole Select Case block will be skipped.
End Select is used to close the Select Case...End Select block.

Exit Select can be used to escape the Select Case block, preventing further code in the Case block from being executed.

Note for C users: In FreeBASIC, Select Case works like a switch block where all cases have a break at the end. As there is no fall-through, multiple options must be put in an expression list in a single Case.

Besides integer types, floating point and string expressions are also supported with the first syntax.

Syntax of an expression list:
{ expression | expression To expression | Is relational operator expression }[, ...]

  • expr: evaluates expr, and compares for equality with the original expression. If they are equal, then a match has been found. This could be considered as a shorthand for "Is = expr" (see below).
  • expr1 To expr2: evaluates expr1 and checks to see if it is less than or equal to the original expression. If so, it evaluates expr2, and checks to see if it is greater than or equal to the original expression. If so, then a match has been found.
  • Is relational_operator expr: evaluates expr, and compares the original operation against it, using the supplied relational_operator (=, >, <, <>, <=, >=). If the comparison is true, then a match has been found.

Multiple checks can be made in each Case, by separating them by a comma (,). Once a match is found, the program finishes its checks, and goes on to execute the code statements for that Case block. No further expressions are evaluated or checked.

example of expression lists:
Case 1 | constant
Case 5.4 To 10.1 | range
Case Is > 3 | bigger than-smaller than
Case 1, 3, 5, 7 to 9 | match against a set of values
Case x | value of a variable

If As Const is used, only integer constants (all numeric constants excluding the two floating-point constants: single and double) can be evaluated and the expression list supports simple constants and enumerations only. "To" ranges are supported, but "Is" relational operators are not.

With As Const, a jump table is created to contain the full range of integer Cases handled. This allows Select Case As Const to be faster than Select Case.
However, the size of the range of values is limited, and after converting the values to the uinteger type, the largest value in the range may be no higher than the smallest value + 8191 (current implementation).

Note: No statement can be placed between the Select Case statement and the first Case statement.

Example

Dim choice As Integer

Input "Choose a number between 1 and 10: "; choice

Select Case As Const choice
Case 1
    Print "number is 1"
Case 2
    Print "number is 2"
Case 3, 4
    Print "number is 3 or 4"
Case 5 To 10
    Print "number is in the range of 5 to 10"
Case Else
    Print "number is outside the 1-10 range"
End Select

'' SELECT CASE vs. SELECT CASE AS CONST speed test

Const N = 50000000

Dim As Integer dummy = 0
Dim As Double t = Timer()

For i As Integer = 1 To N
    Select Case i
    Case 1, 3, 5, 7, 9
        dummy += 1
    Case 2, 4, 6, 8, 10
        dummy += 1
    Case 11 To 20
        dummy += 1
    Case 21 To 30
        dummy += 1
    Case 31
        dummy += 1
    Case 32
        dummy += 1
    Case 33
        dummy += 1
    Case Is >= 34
        dummy += 1
    Case Else
        Print "can't happen"
    End Select
Next

Print Using "SELECT CASE: ##.### seconds"; Timer() - t
t = Timer()

For i As Integer = 1 To N
    Select Case As Const i
    Case 1, 3, 5, 7, 9
        dummy += 1
    Case 2, 4, 6, 8, 10
        dummy += 1
    Case 11 To 20
        dummy += 1
    Case 21 To 30
        dummy += 1
    Case 31
        dummy += 1
    Case 32
        dummy += 1
    Case 33
        dummy += 1
    Case Else
        If( i >= 34 ) Then
            dummy += 1
        Else
            Print "can't happen"
        End If
    End Select
Next

Print Using "SELECT CASE AS CONST: ##.### seconds"; Timer() - t
Sleep

Differences from QB

  • Select Case As Const did not exist in QB.
  • in an "expr1 TO expr2" case, QB would always evaluate both expressions, even if expr1 was higher than the original expression.
  • In the -lang qb and -lang fblite dialects, variables declared inside a Select..End Select block have a function-wide scope as in QB.
  • In the -lang fb and -lang deprecated dialects, variables declared inside a Select..End Select block are visible only inside the block, and can't be accessed outside it. To access duplicated symbols defined as global outside this block, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

See also

  • If...Then


SetDate
Sets the current system date

Syntax

Declare Function SetDate ( ByRef newdate As Const String ) As Long

Usage

result = SetDate( newdate )

Parameters

newdate
the new date to set

Return Value

Returns zero on success or non-zero on failure on all ports except DOS.

Description

To set the date you just format newdate and send to SetDate in a valid format following one of the following: "mm-dd-yy", "mm-dd-yyyy", "mm/dd/yy", or "mm/dd/yyyy" (mm is the month, dd is the day, yy or yyyy is the year). Two-digit year numbers are based on the year 1900.

The error code returned by SetDate can be checked using Err in the next line. The function version of SetDate returns directly the error code as a 32 bit Long.

Example

Dim m As String, d As String, y As String
m = "03" 'march
d = "13" 'the 13th
y = "1994" 'good ol' days
SetDate m + "/" + d + "/" + y

Platform Differences

  • On Windows the privilege SE_SYSTEMTIME_NAME is required, which typically means that the calling process has to be run with administrator privileges.
  • On Linux the capability CAP_SYS_TIME is required, which typically means that the calling process has to run as root/superuser.

Differences from QB

  • The DATE statement was used in QB and the syntax was "DATE = string"

See also

  • Date
  • SetTime


SetMouse
Sets the position and visibility of the mouse cursor

Syntax

Declare Function SetMouse ( ByVal x As Long = -1, ByVal y As Long = -1, ByVal visibility As Long = -1, ByVal clip As Long = -1 ) As Long

Usage

result = SetMouse([ x ] [, [ y ] [, [ visibility ] [, [ clip ]]]])

Parameters

(For each parameter, -1 is a special value indicating "no changes.")
x
optional - set x coordinate
y
optional - set y coordinate
visibility
optional - set visibility: 1 indicates visible, 0 indicates hidden
clip
optional - set clipping: 1 indicates mouse is clipped to graphics window, 0 indicates no clipping

Return Value

Zero (0) on success, non-zero to indicate failure.

Description

SetMouse will set the (x, y) coordinates of the mouse pointer, as well as setting its visibility. The mouse position is set using the x and y parameters. The mouse will be visible if visibility is set to 1, and invisible if visibility is set to 0. SetMouse is intended for graphics modes initiated using the Screen (Graphics) statement only.

The error code returned by SetMouse can be checked using Err in the next line. The function version of SetMouse returns directly the error code as a 32 bit Long.

Example

Dim As Long x, y, buttons

' create a screen 640*480
ScreenRes 640, 480
Print "Click the mouse button to center the mouse"

Do
    ' get mouse x, y and button state (wait until mouse is onscreen)
    Do: Sleep 1: Loop While GetMouse( x, y , , buttons) <> 0

    If buttons And 1 Then
        ' on left mouse click, center mouse
        SetMouse 320, 240
    End If

    ' run loop until a key is pressed or the window is closed
Loop While Inkey = ""

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Setmouse.

Differences from QB

  • New to FreeBASIC

See also

  • GetMouse
  • Screen (Graphics)
  • MultiKey
  • GetKey


SetTime
Sets the current system time

Syntax

Declare Function SetTime ( ByRef newtime As Const String ) As Long

Usage

result = SetTime( newtime )

Parameters

newtime
the new time to set

Return Value

Returns zero on success or non-zero on failure on all ports except DOS.

Description

To set the time, format the date and send to Settime in one of the following formats: "hh:mm:ss", "hh:mm", or "hh" (hh is the hour, mm is the minute, and ss is the second).

The error code returned by SetTime can be checked using Err in the next line. The function version of SetTime returns directly the error code as a 32 bit Long.

Example

SetTime "1:20:30"

Platform Differences

  • On Windows the privilege SE_SYSTEMTIME_NAME is required, which typically means that the calling process has to be run with administrator privileges.
  • On Linux the capability CAP_SYS_TIME is required, which typically means that the calling process has to be run as root/superuser.

Differences from QB

  • The Time statement was used QB and the syntax was TIME = newtime.

See also

  • Time
  • SetDate


Sgn
Returns the sign part of a number

Syntax

Declare Function Sgn ( ByVal number As numtype ) As numtype

Usage

result = Sgn( number )

Parameters

number
the number to find the sign of
numtype
a numeric type

Return Value

Returns the sign part of number.
  • If number is greater than zero, then Sgn returns 1.
  • If number is equal to zero, then Sgn returns 0.
  • If number is less than zero, then Sgn returns -1.

Description

The required number argument can be any valid numeric expression.
Unsigned numbers of size greater than or equal to SizeOf(Any Ptr) will be treated as if they were signed, i.e. if the highest bit is set the number will be treated as negative, and -1 will be returned.

Note:
The return type of Sgn depends on the type of the passed argument (variable or constant, its data type), and of the used backend (gas, gas64, gcc 32-bit, gcc 64-bit):
- Sgn return type for a variable passed as argument:
Argument | gas (32-bit) | gas64 (64-bit) | gcc 32-bit (*) | gcc 64-bit (*)
[U]BYTE variable | INTEGER | INTEGER | LONG | LONG
[U]SHORT variable | INTEGER | INTEGER | LONG | LONG
[U]LONG variable | INTEGER | INTEGER | LONG | LONG
[U]INTEGER variable | INTEGER | INTEGER | LONG | LONG
[U]LONGINT variable | LONGINT | INTEGER | LONG | LONG
SINGLE variable | SINGLE | LONG | LONG | LONG
DOUBLE variable | DOUBLE | LONG | LONG | LONG

- Sgn return type for a constant passed as argument:
Argument | gas (32-bit) | gas64 (64-bit) | gcc 32-bit (*) | gcc 64-bit (*)
[U]BYTE constant | INTEGER | INTEGER | INTEGER | INTEGER
[U]SHORT constant | INTEGER | INTEGER | INTEGER | INTEGER
[U]LONG constant | INTEGER | INTEGER | INTEGER | INTEGER
[U]INTEGER constant | INTEGER | INTEGER | INTEGER | INTEGER
[U]LONGINT constant | LONGINT | INTEGER | LONGINT | INTEGER
SINGLE constant | SINGLE | SINGLE | SINGLE | SINGLE
DOUBLE constant | DOUBLE | DOUBLE | DOUBLE | DOUBLE

(*): applicable also for clang and llvm

The Sgn unary Operator can be overloaded with user defined types.

Example

Dim N As Integer = 0

Print Sgn ( -1.87 )
Print Sgn ( 0 )
Print Sgn ( 42.658 )
Print Sgn ( N )

The output would look like:
-1
0
1
0
Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Abs
  • Operator


SFX
Loads and controls short reusable sound effects through sfxlib.

Syntax

Sfx Load id, filename
Sfx Play id
Sfx Play channel, id[, pitch]
Sfx Loop id
Sfx Loop channel, id[, pitch]
Sfx Stop [id]
Sfx Stop Channel, channel
Sfx Pause [id]
Sfx Pause Channel, channel
Sfx Resume [id]
Sfx Resume Channel, channel
status = Sfx Status()
status = Sfx Status(id)
status = Sfx Status(Channel, channel)

Description

SFX is for short reusable samples, usually game and user-interface sounds. The usual pattern is to load a sample once with Sfx Load, then play it whenever the program needs that sound.

The id is a number chosen by the program. After loading id 1 with a click sound, Sfx Play 1 plays that click. The channel forms let a program decide where the sound is mixed, and the optional pitch value changes playback speed for simple variation.

Sfx Loop repeats a sample until it is stopped. That is useful for engine hum, wind, alarms, or any sound that should continue while something is happening. Stop, Pause, and Resume can affect all effects, one loaded id, or one channel depending on the form used.

Sfx Status lets a program ask whether any effect is active, whether a specific id is active, or whether a specific channel is active.

Example

sfx load 1, "click.wav"
sfx play 1

sfx load 2, "engine.wav"
sfx loop 0, 2, 1.0
sleep 1000, 1
sfx stop channel, 0

Notes

  • Use MUSIC for one longer background track.
  • Use generated commands such as NOISE or SOUND for effects that do not need a sample file.

See also

  • MUSIC
  • VOLUME
  • Sound Library Reference


Shared
Variable declaration modifier specifying visibility throughout a module

Syntax

Dim Shared ...
ReDim Shared ...
Common Shared ...
Static Shared ...
[Static] Var Shared ...

Description

Shared makes module-level variables visible inside Subs and Functions.
If Shared is not used on a module-level variable's declaration, the variable is only visible to the module-level code in that file (furthermore, only a variable declared with Dim without Shared modifier, and not inside a Namespace block, is stored on the stack).

NOTES (for Shared variables excluding Common variables):
    • Generally a Shared variable may only be initialized with a constant value (its starting value is set at the start of the program in the .data section before any code is run, and so it cannot depend on any variables or functions in it).
    • A first exception is a Shared variable of var-len string type, that never can be initialized, even with a constant string (because of its structure with a descriptor in the .data section, but to point to a dynamic memory block).
    • A second exception is a Shared variable of user-defined type having a constructor even implicit, that can be initialized with a non-constant value (because it's the constructor code, called when the program starts, which writes the "initial" values into the .data section).

To access from a local scope block to duplicated symbols of Shared variables defined in the global namespace, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

Example

'' Compile with -lang qb or fblite

'$lang: "qb"

Declare Sub MySub
Dim Shared x As Integer
Dim y As Integer

x = 10
y = 5

MySub

Sub MySub
    Print "x is "; x 'this will report 10 as it is shared
    Print "y is "; y 'this will not report 5 because it is not shared
End Sub

Differences from QB

  • The Shared statement inside scope blocks -- functions, subs, if/thens, and loops -- is not supported. Use Dim|Redim|Common|Static Shared in the main program instead. Or if you're inside a scope block and Redimming a variable or array previously set up with Shared, just do a Redim without Shared; it will work fine and won't ruin anything.

See also

  • Common
  • Dim
  • Erase
  • Extern
  • LBound
  • ReDim
  • Preserve
  • Static
  • UBound
  • Var
  • Byref (Variables)


Shell
Sends a command to the system command interpreter

Syntax

Declare Function Shell ( ByRef command As Const String ) As Long

Usage

result = Shell( command )

Parameters

command
A string specifying the command to send to the command interpreter.

Return Value

If the command could not be executed, -1 is returned. Otherwise, the command is executed and its exit code is returned.

Description

Program execution will be suspended until the command interpreter exits.

Example

'e.g. for windows:
Shell "dir c:*.*"

'e.g. for linux:
Shell "ls"

Platform Differences

  • Linux requires the command case matches the real name of the command. Windows and DOS are case insensitive. The program being shelled may be case sensitive for its command line parameters.
  • Path separators in Linux are forward slashes / . Windows uses backward slashes \ but it allows for forward slashes. DOS uses backward \ slashes.
  • If an empty command string is passed, DOS will open an interactive command prompt. On Windows, an error may be returned.

Differences from QB

  • QB allowed SHELL on its own without a "command" argument which caused a default command shell to be started. Execution in the main program would suspend until exit from the command shell. The behaviour in FB is platform-dependent.

See also

  • Exec
  • Run


Operator Shl (Shift Left)
Shifts the bits of a numeric expression to the left

Syntax

Declare Operator Shl ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator Shl ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As UInteger
Declare Operator Shl ( ByRef lhs As LongInt, ByRef rhs As LongInt ) As LongInt
Declare Operator Shl ( ByRef lhs As ULongInt, ByRef rhs As ULongInt ) As ULongInt

Usage

result = lhs Shl rhs

Parameters

lhs
The left-hand side expression.
rhs
The right-hand side shift expression.

Return Value

Returns the result of lhs being shifted left rhs number of times.

Description

Operator Shl (Shift left) shifts all of the bits in the left-hand side expression (lhs) left a number of times specified by the right-hand side expression (rhs). Numerically, the result is the same as "CInt( lhs * 2 ^ rhs )". For example, "&b0101 Shl 1" returns the binary number &b01010, and "5 Shl 1" returns 10.

Neither of the operands are modified in any way.

If the result is too large to fit inside the result's data type, the leftmost bits are discarded ("shifted out").
The results of this operation are undefined for values of rhs less than zero, or greater than or equal to the number of bits in the result's data type.

This operator can be overloaded for user-defined types.

Example

'Double a number
For i As Integer = 0 To 10

    Print 5 Shl i, Bin(5 Shl i, 16)

Next i

Output:
 5            0000000000000101
 10           0000000000001010
 20           0000000000010100
 40           0000000000101000
 80           0000000001010000
 160          0000000010100000
 320          0000000101000000
 640          0000001010000000
 1280         0000010100000000
 2560         0000101000000000
 5120         0001010000000000

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Shl.

Differences from QB

  • New to FreeBASIC

See also

  • Operator Shl= (Shift Left And Assign)
  • Operator Shr (Shift Right)
  • Bin
  • Mathematical Functions


Operator Shl= (Shift Left And Assign)
Shifts left and assigns a value to a variable

Syntax

Declare Operator Shl= ( ByRef lhs As Integer, ByRef rhs As Integer )
Declare Operator Shl= ( ByRef lhs As UInteger, ByRef rhs As UInteger )
Declare Operator Shl= ( ByRef lhs As LongInt, ByRef rhs As LongInt )
Declare Operator Shl= ( ByRef lhs As ULongInt, ByRef rhs As ULongInt )

Usage

lhs shl= rhs

Parameters

lhs
The variable to assign to.
rhs
The value to shift lhs left by.

Description

This operator shifts the bits in its left-hand side (lhs) parameter a number of times specified by its right-hand side (rhs) parameter, and assigns the result to lhs. It is functionally equivalent to:
lhs = lhs Shl rhs

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol 'Shl=>' can be also used.

Example

Dim i As Integer
i = &b00000011   '' = 3
i Shl= 3         '' = i*2^3
'' Result: 11000          24            24
Print Bin(i), i, 3*2^3
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Shl=.

Differences from QB

  • New to FreeBASIC

See also

  • Operator Shl (Shift Left)
  • Operator Shr= (Shift Right And Assign)
  • Mathematical Functions


Short
Standard data type: 16 bit signed

Syntax

Dim variable As Short

Description

16-bit signed whole-number data type. Can hold values from -32768 to 32767.

Example

  Dim x As Short = CShort(&H8000)
  Dim y As Short = CShort(&H7FFF)
  Print "Short Range = "; x; " to "; y

Output:
Short Range = -32768 to  32767

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Short.

Differences from QB

  • The name "short" is new to FreeBASIC, however they are the same as integers in QB

See also

  • UShort
  • CShort
  • Table with variable types overview, limits and suffixes


Operator Shr (Shift Right)
Shifts the bits of a numeric expression to the right

Syntax

Declare Operator Shr ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator Shr ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As UInteger
Declare Operator Shr ( ByRef lhs As LongInt, ByRef rhs As LongInt ) As LongInt
Declare Operator Shr ( ByRef lhs As ULongInt, ByRef rhs As ULongInt ) As ULongInt

Usage

result = lhs Shr rhs

Parameters

lhs
The left-hand side expression.
rhs
The right-hand side shift expression.

Return Value

Returns the result of lhs being shifted right rhs number of times.

Description

Operator Shr (Shift right) shifts all of the bits in the left-hand side expression (lhs) right a number of times specified by the right-hand side expression (rhs). Numerically, the result is the same as "Int(lhs / 2 ^ rhs)". For example, "&b0101 Shr 1" returns the binary number &b010, and "5 Shr 1" returns 2.

If the left-hand side expression is signed and negative, the sign bit is copied in the newly created bits on the left after the shift. For example, "-5 Shr 2" returns -2.

Neither of the operands are modified in any way.

The results of this operation are undefined for values of rhs less than zero, or greater than or equal to the number of bits in the result's data type.

This operator can be overloaded for user-defined types.

Example

'Halve a number
For i As Integer = 0 To 10

    Print 1000 Shr i, Bin(1000 Shr i, 16)

Next i

Output:
 1000         0000001111101000
 500          0000000111110100
 250          0000000011111010
 125          0000000001111101
 62           0000000000111110
 31           0000000000011111
 15           0000000000001111
 7            0000000000000111
 3            0000000000000011
 1            0000000000000001
 0            0000000000000000

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Shr.

Differences from QB

  • New to FreeBASIC

See also

  • Operator Shr= (Shift Right And Assign)
  • Operator Shl (Shift Left)
  • Bin
  • Mathematical Functions


Operator Shr= (Shift Right And Assign)
Shifts right and assigns a value to a variable

Syntax

Declare Operator Shr= ( ByRef lhs As Integer, ByRef rhs As Integer )
Declare Operator Shr= ( ByRef lhs As UInteger, ByRef rhs As UInteger )
Declare Operator Shr= ( ByRef lhs As LongInt, ByRef rhs As LongInt )
Declare Operator Shr= ( ByRef lhs As ULongInt, ByRef rhs As ULongInt )

Usage

lhs shr= rhs

Parameters

lhs
The variable to assign to.
rhs
The value to shift lhs right by.

Description

This operator shifts the bits in its left-hand side (lhs) parameter a number of times specified by its right-hand side (rhs) parameter, and assigns the result to lhs. It is functionally equivalent to:
lhs = lhs Shr rhs

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol 'Shr=>' can be also used.

Example

Dim i As Integer
i = &b00011000   '' = 24
i Shr= 3         '' = i\2^3
'' Result: 11          3            3
Print Bin(i), i, 24\2^3
Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Shr=.

Differences from QB

  • New to FreeBASIC

See also

  • Operator Shr (Shift Right)
  • Operator Shl= (Shift Left And Assign)
  • Mathematical Functions


Sin
Returns the sine of an angle

Syntax

Declare Function Sin ( ByVal angle As Double ) As Double

Usage

result = Sin( angle )

Parameters

angle
the angle (in radians)

Return Value

Returns the sine of the argument angle as a Double within the range of -1.0 to 1.0.

Description

The argument angle is measured in radians (not degrees).

The value returned by this function is undefined for values of angle with an absolute value of 2 ^ 63 or greater.

Sin can be overloaded as operator to accept user-defined types.

Example

Const PI As Double = 3.1415926535897932
Dim a As Double
Dim r As Double
Input "Please enter an angle in degrees: ", a
r = a * PI / 180    'Convert the degrees to Radians
Print ""
Print "The sine of a" ; a; " degree angle is"; Sin ( r )
Sleep

The output would look like:
Please enter an angle in degrees: 30
The sine of a 30 degree angle Is 0.5

Differences from QB

  • None

See also

  • Asin
  • Cos
  • Tan
  • A Brief Introduction To Trigonometry


Single
Standard data type: 32 bit floating point

Syntax

Dim variable As Single

Description

Single is a 32-bit, floating point data type used to store decimal numbers. They can hold positive values in the range 1.401298e-45 to 3.402823e+38, or negative values in the range -1.401298e-45 to -3.402823e+38, or zero (0). They contain at most 24 bits of precision, or about 6 decimal digits.

They are similar to Double data types, but less precise.

Example

'Example of using a single variable.

Dim a As Single
a = 1.9857665
Print a

Sleep

Differences from QB

  • None

See also

  • Double More precise float type
  • CSng
  • Table with variable types overview, limits and suffixes


SizeOf
Returns the size of a variable or type in bytes.

Syntax

SizeOf ( variable | DataType )

Description

The SizeOf operator returns an Integer value: the number of bytes taken up by a variable or DataType (including the data fields of a UDT).

When used with fixed-length strings (including fixed-length ZStrings and WStrings) it will return the number of bytes they use, and when used with variable-length strings, it will return the size of the string descriptor.
(it is equal to Len only when used with fixed-length strings of STRING*N type and this since fbc version 1.20.0)

If there is both a user defined type and a variable visible with the same name in the current scope, the user defined type takes precedence over the variable. To ensure that the SizeOf takes the variable instead of the user defined type, wrap the argument to SizeOf with parentheses to force it to be seen as an expression. For example Sizeof((variable)).

Note: When used with array names, SizeOf returns the size of the array dadatype, but not the total size of the array. This differs from its behavior in C, where arrays could only be a fixed size, and sizeof() would return the number of it used.
For clarity, it is recommended that you avoid this potential confusion, and use SizeOf on the array datatype doing SizeOf(Typeof(array)), rather than the array name only doing SizeOf(array).

Remark: When used with a dereferenced z/wstring pointer, SizeOf always returns the number of bytes taken up by one z/wstring character.

Example

Print SizeOf(Byte) ' returns 1

Type bar
    a As Integer
    b As Double
End Type
Dim foo As bar
Print SizeOf(foo)

Version

  • Before fbc 1.08.0:
SizeOf was not returning the size of the data fields of a UDT.
When a variable from a given namespace was accessed with the namespace's name prefix, the argument to SizeOf had to be wrapped with parentheses to force it to be seen as an expression. For example Sizeof((namespace_name.variable)).

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Sizeof.

Differences from QB

  • New to FreeBASIC

See also

  • Len


Sleep
Waits until a specified time has elapsed, or a key is pressed.

Syntax

Declare Sub Sleep ( ByVal amount As Long = -1 )
Declare Function Sleep ( ByVal amount As Long , ByVal keyflag As Long ) As Long

Usage

Sleep [ amount [, keyflag ]]
result = Sleep ( amount, keyflag )

Parameters

amount
Optional number of milliseconds to wait (default is to wait for a key press).
keyflag
Optional flag; give it a value of 0 for a normal sleep, or 1 to specify that the wait cannot be interrupted by a key press.

Return Value

Returns 1 if keyflag was not a valid value (i.e. something other than 0 or 1) to indicate failure, or 0 otherwise.

Description

Sleep will wait until amount milliseconds (can be seconds in -lang qb, see below) given elapsed (if any value was passed) or until the user presses a key. If amount is below 100 ms then Sleep will always wait the full requested amount (key presses are ignored).

Include the second parameter, 1, for a "deep" sleep, which cannot be interrupted by pressing a key.

The accuracy of Sleep is variable depending on the OS cycle time:
Windows NT/2K/XP: 15 ms, 9x/Me: 50 ms, Linux: 10ms, DOS: 55 ms.
And using for the amount parameter values lower than these accuracy values does not allow Sleep to produce the corresponding wait values (always higher wait, and of the order of these values) except for the amount value '0'.

Call Sleep with 25ms or less to release time-slice when waiting for user input or looping inside a thread. This will prevent the program from unnecessarily hogging the CPU.

Sleep does not clear the keyboard input buffer and any keys pressed during a call to Sleep are retained and can be later read by Inkey or GetKey or Input.
When Sleep has no parameters (waiting for a key pressed only), GetKey keyword can be used instead of Sleep.
For the general form of Sleep (with parameters), if the user want to clear the keyboard input buffer from any eventual keys pressed during the Sleep execution, he can use after the Sleep instruction line something like the following method:
While Inkey <> "": Wend  '' loop until the keyboard input buffer is empty

Example

Print "press a key"
Sleep
GetKey  '' clear the keyboard input buffer, and even in that code case, the 'Sleep' keyword can be outright omitted
Print "waiting half second"
Sleep 500

Dim As String s

Print "wait 3 seconds or press a key"
Sleep 3000
Print "outputed by timeout or key pressed"
While Inkey <> ""  '' loop until the keyboard input buffer is empty
Wend

Input "enter a string"; s
Print "string entered: " & "'" & s & "'"

Sleep

Dialect Differences

  • In the -lang fb and -lang fblite dialects, the amount value is in milliseconds.
  • In the -lang qb dialect, the amount value is in seconds as in QB. If the second parameter keyflag is given, or the keyword is written as __Sleep the value is expected to be in milliseconds.

Differences from QB

  • None in the -lang qb dialect.
  • In QB, the delay was given in whole seconds only and did not support the keyflag parameter.

See also

  • Timer
  • Inkey


SOUND
Plays a generated tone through sfxlib.

Syntax

Sound frequency, duration
Sound channel, frequency, duration, volume

Description

SOUND is the easiest way to make the computer produce a pitched tone. The frequency tells sfxlib how high the tone is. A value of 440 produces the concert A above middle C; lower numbers sound lower and higher numbers sound higher.

The duration tells sfxlib how long to play the tone. Fractional values are useful for short game sounds and musical notes, for example 0.25 for roughly a quarter of a second. The classic two-argument BASIC form is kept for simple programs and compatibility-minded code.

The four-argument form is for programs that need more control. The first argument selects the sfxlib channel, so different sounds can be organized separately. The last argument sets the loudness for that tone.

Example

sound 440, 0.25
sound 1, 660, 0.20, 0.5
sleep 300, 1

Notes

  • Use NOTE when you want to name musical notes instead of calculating frequencies.
  • Use TONE when you always want to be explicit about the channel.

See also

  • Sound Library Reference
  • Generated Sound Commands


Space
Creates a string of a given length filled with spaces (" ")

Syntax

Declare Function Space( ByVal count As Integer ) As String

Usage

result = Space[$]( count )

Parameters

count
An integer type specifying the length of the string to be created.

Return Value

The created string. An empty string will be returned if count <= 0.

Description

Space creates a string with the specified number of spaces.

Example

Dim a As String
a = "x" + Space(3) + "x"
Print a ' prints: x   x

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • None

See also

  • WSpace
  • Spc
  • String (Function)


Spc
Output function to skip spaces when writing to screen or file

Syntax

Spc( columns )

Usage

Print Spc( spaces ) [(, | ;)] ...

Parameters

spaces
number of spaces to skip

Description

Spc skips over the given number of spaces when Printing to screen or to a file. The character cells skipped over are left unchanged.

Example

Print "foo"; Spc(5); "bar"
Print "hello"; Spc(4); "world"

'' Uses Spc to justify text instead of Tab

Dim As String A1, B1, A2, B2

A1 = "Jane"
B1 = "Doe"
A2 = "Bob"
B2 = "Smith"

Print "FIRST NAME"; Spc(35 - 10); "LAST NAME"
Print "----------"; Spc(35 - 10); "----------"
Print A1; Spc(35 - Len(A1)); B1
Print A2; Spc(35 - Len(A2)); B2

The output would look like:
FIRST NAME                         LAST NAME
----------                         ----------
Jane                               Doe
Bob                                Smith

Differences from QB

  • In QBASIC, spaces were printed in the gap, while in FreeBASIC, the characters are just skipped over and left untouched. The Space function can still be used to achieve this effect.

See also

  • Tab
  • Space
  • Print
  • ?


Sqr
Returns a square root of a number

Syntax

Declare Function Sqr ( ByVal number As Double ) As Double

Usage

result = Sqr( number )

Parameters

number
the number (greater than or equal to zero)

Return Value

Returns the square root of the argument number.

If number equals zero, Sqr returns zero (0.0).

If number is less than zero, Sqr returns a special value representing "not defined", printing like "NaN" or "IND", exact text is platform dependent.

Description

This is the same as raising the argument number to the one-half power: y = x ^ (1/2) . The required number argument can be any valid numeric expression greater than or equal zero.

If a LongInt or ULongInt is passed to Sqr, it may be converted to Double precision first. For numbers over 2^52, this will cause a very small loss of precision. Without making any assumptions about the rounding method, the maximum error due to this will be Sqr(2^64) - Sqr(2^64-2^12), which is about 4.8e-7. However this may cause erroneous results if the floor or ceiling of this value is taken, and the result of this may be out by 1, particularly for square numbers and numbers that are close by.

Sqr can be overloaded as operator to accept user-defined types.

Example

'' Example of Sqr function: Pythagorean theorem
Dim As Single a, b

Print "Pythagorean theorem, right-angled triangle"
Print
Input "Please enter one leg side length: ", a
Input "Please enter the other leg side length: ", b
Print
Print "The hypotenuse has a length of: " & Sqr( a * a + b * b )

The output would look like:
Pythagorean theorem, right-angled triangle

Please enter one leg side length: 1.5
Please enter the other leg side length: 2

The hypotenuse has a length of: 2.5

Differences from QB

  • None

See also

  • Operator ^ (Exponentiate)
  • Arithmetic Operators


Static
Defines variables, objects and arrays having static storage

Syntax

Static symbol1 [ (array-dimensions) ] As DataType [ = expression] [, symbol2 [ (array-dimensions) ] As DataType [ = expression], ...]
or
Static As DataType symbol1 [ (array-dimensions) ] [ = expression] [, symbol2 [ (array-dimensions) ] [ = expression], ...]
or
Static Var symbol1 = expression [, symbol2 = expression, ...]

or

Sub|Function procedurename ( parameters ) [[ ByRef ] As DataType] Static
...
End Sub|Function

Parameters

symbol
variable or array symbol name.
array-dimensions
lower-bound To upper-bound [, ...]
or
Any [, Any...]
or empty.
expression
An constant expression, or an array of constant expressions

Description

Specifies static storage for variables, objects and arrays; they are allocated at program startup and deallocated upon exit. Objects are constructed once when they are defined, and destructed upon program exit.

When declaring static arrays, only numeric literals, Constants or Enumerations may be used as subscript range values. Static variable-length arrays must be declared empty (no subscript range list) and resized using ReDim before used.

In both iterative and recursive blocks, like looping control flow statements or procedures, static variables, objects and arrays local to the block are guaranteed to occupy the same storage across all instantiations of the block. For example, procedures that call themselves - either directly or indirectly - share the same instances of their local static variables.

A static variable may only be initialised with a constant value: its starting value is set at the start of the program before any code is run, and so it cannot depend on any variables or functions in it.

When used at procedure definition level (forbidden at declaration line level), Static specifies static storage for all local variables, objects and arrays, except temporary types and internal variables (objects not explicitly declared).

At module-level variable declaration only, the modifier Shared may be used with the keyword Static to make module-level static variables visible inside procedures.

When used with in a user-defined type, Static creates Static Member Procedures Or Variables.

Example

Sub f
    '' times called is initially 0
    Static timesCalled As Integer = 0
    timesCalled += 1
    Print "Number of times called: " & timesCalled
End Sub

'' the static variable in f() retains its value between
'' multiple procedure calls.
f()
f()

Will output:

Number of times called: 1
Number of times called: 2

Dialect Differences

  • Variables cannot be initialised in the -lang qb dialect.

Differences from QB

  • QuickBASIC allows variables and arrays to be declared using the Static keyword within procedures and DEF FN routines only.
  • Static forces local visibility of variables and arrays in QuickBASIC DEF FN routines. FreeBASIC supports neither DEF FN routines nor this usage of Static.

See also

  • Static (Member)
  • Dim, ReDim
  • Shared
  • Byref (Variables)
  • Sub (Module), Function (Module)
  • Sub (Member), Function (Member)
  • Option Static
  • Storage Classes
  • Pointers to Procedures


Static (Member)
Declare a static member procedure or variable

Syntax

Type typename
Static variablename As DataType [, ...]
Declare Static Sub|Function procedurename ...
...
End Type

Dim typename.variablename As DataType [= initializer] [, ...]

[Static] Sub|Function typename.procedurename ...
...
End Sub|Function

Description

  • Static member procedures
Static methods do not have an implicit This instance argument passed to them. This allows them to be used like normal non-member procedures (for example with callback procedure pointers). An advantage of Static methods are that they are encapsulated in the typename namespace, and therefore have the ability to access the Private or Protected members or methods of instances of typename.

Static methods can be called directly anywhere in code, like normal non-member procedures, or on objects of type typename, similar to non-static methods, however either way there is no implicit or explicit This (or explicit Base (Member Access)) access possible from within a static method.

For member procedures with a Static declaration, Static may also be specified on the corresponding procedure bodies, for improved code readability.

  • Static member variables
Static member variables are created and initialized only once independently of any object construction, in contrast to non-static ("instance") member variables which are created again and again for each separate object. Static members are always Shared, even if Shared was not specified in the declaration. Thus, Static member variables are similar to global variables, except that they are declared in a Type namespace.

Each Static member variable declared in a Type must be explicitly allocated somewhere outside the type by using a Dim statement. The declaration inside the Type is the prototype that is visible to every module seeing the Type declaration. The definition outside the Type allocates and optionally initializes the Static member variable. There can only be one definition per Static member variable: it can only be allocated in a single module, not in multiple ones. This is the same as for Extern variables.

A Static member variable is subject to member access control except for its definition outside the Type. If a private Static member variable is to be explicitly initialized outside the Type's member procedures, an initializer must be provided with the definition.

Example

'' Example showing how the actual procedure invoked by a member can be set at runtime.
'' using static member procedures.
Type _Object

  Enum handlertype
    ht_default
    ht_A
    ht_B
  End Enum

  Declare Constructor( ByVal ht As handlertype = ht_default)

  Declare Sub handler()

Private:
  Declare Static Sub handler_default( ByRef obj As _Object )
  Declare Static Sub handler_A( ByRef obj As _Object )
  Declare Static Sub handler_B( ByRef obj As _Object )
  handler_func As Sub( ByRef obj As _Object )

End Type

Constructor _Object( ByVal ht As handlertype )
  Select Case ht
  Case ht_A
    handler_func = @_Object.handler_A
  Case ht_B
    handler_func = @_Object.handler_B
  Case Else
    handler_func = @_Object.handler_default
  End Select
End Constructor

Sub _Object.handler()
  handler_func(This)
End Sub

Sub _Object.handler_default( ByRef obj As _Object )
  Print "Handling using default method"
End Sub

Sub _Object.handler_A( ByRef obj As _Object )
  Print "Handling using method A"
End Sub

Sub _Object.handler_B( ByRef obj As _Object )
  Print "Handling using method B"
End Sub

Dim objects(1 To 4) As _Object => _
  { _
    _Object.handlertype.ht_B, _
    _Object.handlertype.ht_default, _
    _Object.handlertype.ht_A _
  }
  '' 4th array item will be _Object.handlertype.ht_default

For i As Integer = 1 To 4
  Print i,
  objects(i).handler()
Next i

'' Assign an unique ID to every instance of a Type (ID incremented in order of creation)

Type UDT
  Public:
    Declare Property getID () As Integer
    Declare Constructor ()
  Private:
    Dim As Integer ID
    Static As Integer countID
End Type
Dim As Integer UDT.countID = 0

Property UDT.getID () As Integer
  Property = This.ID
End Property

Constructor UDT ()
  This.ID = UDT.countID
  UDT.countID += 1
End Constructor

Dim As UDT uFirst
Dim As UDT uSecond
Dim As UDT uThird

Print uFirst.getID
Print uSecond.getID
Print uThird.getID

Differences from QB

  • New to FreeBASIC

See also

  • Class
  • Declare
  • Type
  • Static


stdcall
Specifies a stdcall-style calling convention in a procedure declaration

Syntax

Sub name stdcall [Overload] [Alias "alias"] ( parameters )
Function name stdcall [Overload] [Alias "alias"] ( parameters ) [ ByRef ] As return_type

Description

In procedure declarations, stdcall specifies that a procedure will use the stdcall calling convention. In the stdcall calling convention, any parameters are to be passed (pushed onto the stack) in the reverse order in which they are listed, that is, from right to left. The procedures need not preserve the EAX, ECX or EDX registers, and must clean up the stack (pop any parameters) before it returns.

stdcall is not allowed to be used with variadic procedure declarations (those with the last parameter listed as "...").

stdcall is the default calling convention on Windows, unless another calling convention is explicitly specified or implied by one of the Extern Blocks. stdcall is also the standard (or most common) calling convention used in BASIC languages, and the Windows API.

If a procedure definition has a declaration (with calling convention explicit or by default) and the definition does not explicitly specify a calling convention, then the calling convention is implied by the declaration.

Example

Declare Function Example stdcall (param1 As Integer, param2 As Integer) As Integer
Declare Function Example2 cdecl (param1 As Integer, param2 As Integer) As Integer

Function Example stdcall (param1 As Integer, param2 As Integer) As Integer
    ' This is an STDCALL function, the first parameter on the stack is param2, since it was pushed last.
    Print param1, param2
    Return param1 Mod param2
End Function

Function Example2 cdecl (param1 As Integer, param2 As Integer) As Integer
    ' This is a CDECL function, the first parameter on the stack is param1, since it was pushed last.
    Print param1, param2
    Return param1 Mod param2
End Function

Platform Differences

  • On Windows systems, stdcall procedures have an "@N" decoration added to their internal/external name, where N is the size of the parameter list, in bytes.

Differences from QB

  • New to FreeBASIC

See also

  • pascal, cdecl
  • Declare
  • Sub, Function


Step
Statement modifier.

Syntax

For iterator = initial_value To end_value Step increment

Line [ buffer, ] Step ( x1, y1 ) - Step ( x2, y2 ) [, [ color ][, [ B|BF ][, style ] ] ]

Circle [ target, ] Step ( x, y ), radius [, [ color ][, [ start ][, [ end ][, [ aspect ][, F] ] ] ] ]

Paint [ target, ] STEP ( x, y ) [, [ paint ][, [ border_color ] ] ]

Description

In a For statement, Step specifies the increment of the loop iterator with each loop.

In a Line, Circle or Paint statement, Step indicates that the following coordinate has values relative to the graphics cursor.

Example

Dim i As Integer
For I=10 To 1 Step -1
Next

Line -Step(10,10),13

See also

  • For...Next
  • Line
  • Circle
  • Paint


Stick
Reads axis position from attached gaming devices

Syntax

Declare Function Stick ( ByVal axis As Long ) As Long

Usage

result = Stick( axis )

Parameters

axis
the axis number to query for position

Return Value

Returns a number between 1 and 200 for specified axis, otherwise zero (0), if the device is not attached.

Description

Stick will retrieve the axis position for the first and second axes on the first and second gaming devices. axis must be a number between 0 and 3 having the following meaning:

Axis | Returns
0 | X position of gaming device A
1 | Y position of gaming device A when STICK(0) was called
2 | X position of gaming device B when STICK(0) was called
3 | Y position of gaming device B when STICK(0) was called

Stick(0) must first be called to obtain the positions for the other axes.

Example

'' Compile with -lang qb

'$lang: "qb"

Screen 12

Do
    Locate 1, 1
    Print "Joystick A-X position : "; Stick(0); "   "
    Print "Joystick A-Y position : "; Stick(1); "   "
    Print "Joystick B-X position : "; Stick(2); "   "
    Print "Joystick B-Y position : "; Stick(3); "   "
    Print
    Print "Button A1 was pressed : "; Strig(0); "  "
    Print "Button A1 is pressed  : "; Strig(1); "  "
    Print "Button B1 was pressed : "; Strig(2); "  "
    Print "Button B1 is pressed  : "; Strig(3); "  "
    Print "Button A2 was pressed : "; Strig(4); "  "
    Print "Button A2 is pressed  : "; Strig(5); "  "
    Print "Button B2 was pressed : "; Strig(6); "  "
    Print "Button B2 is pressed  : "; Strig(7); "  "
    Print
    Print "Press ESC to Quit"

    If Inkey$ = Chr$(27) Then
        Exit Do
    End If

    Sleep 1

Loop

Dialect Differences

  • Only available in the -lang qb dialect.

Differences from QB

  • None

See also

  • GetJoystick
  • Strig


Stop
Halts program execution, and waits for a key press before ending the program.

Syntax

Declare Sub Stop ( ByVal retval As Long = 0 )

Usage

Stop

Parameters

retval
Error code returned to system.

Description

Halts the execution of the program and stands by. It's provided as a help to debugging, as it preserves the memory and doesn't close files. For normal program termination the End (Statement) keyword should be used. An optional return value, an integer, can be specified to return an error code to the system. If no return value is given, a value of 0 is automatically returned.

Note: STOP is not implemented properly yet; currently it is the same as System.

Example

Print "this text is shown"
Sleep
Stop
Print "this text will never be shown"

Differences from QB

  • None

See also

  • End (Statement)


Str
Returns a string representation of a number, boolean or Unicode character string

Syntax

Declare Function Str ( ByVal n As Byte ) As String
Declare Function Str ( ByVal n As UByte ) As String
Declare Function Str ( ByVal n As Short ) As String
Declare Function Str ( ByVal n As UShort ) As String
Declare Function Str ( ByVal n As Long ) As String
Declare Function Str ( ByVal n As ULong ) As String
Declare Function Str ( ByVal n As LongInt ) As String
Declare Function Str ( ByVal n As ULongInt ) As String
Declare Function Str ( ByVal n As Single ) As String
Declare Function Str ( ByVal n As Double ) As String
Declare Function Str ( ByVal b As Boolean ) As String
Declare Function Str ( ByRef str As Const String ) As String
Declare Function Str ( ByVal str As Const WString ) As String

Usage

result = Str[$]( number )
or
result = Str( string )

Parameters

number
Numeric expression to convert to a string.
string
String expression to convert to a string.

Description

Str converts numeric variables to their string representation. Used this way it is the String equivalent to WStr applied to numeric variables, and the opposite of the Val function, which converts a string into a number.

Str converts boolean variables to their string representation "false" / "true".

Str also converts Unicode character strings to ASCII character strings. Used this way it does the opposite of WStr. If an ASCII character string is given, that string is returned unmodified.

Example

Dim a As Integer
Dim b As String
a = 8421
b = Str(a)
Print a, b

Dialect Differences

  • In the -lang qb dialect, Str will left pad a positive number with a space.
  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Platform Differences

  • DOS version/target of FreeBASIC does not support the wide-character string version of Str.

Differences from QB

  • QB does not support the wide-character string version of Str.

See also

  • Val
  • CBool
  • Chr
  • Asc


Strig
Reads button state from attached gaming devices

Syntax

Declare Function Strig ( ByVal button As Long ) As Long

Usage

result = Strig( button )

Parameters

button
the button to query for state

Return Value

Returns -1 (pressed) or 0 (not-pressed) to indicate the state of the button requested.

Description

Strig will retrieve the button state for the first and second buttons on the first and second gaming devices. button must be a number between 0 and 7 and has the following meaning:

Button | State to return
0 | First button on gaming device A pressed since STICK(0) was called
1 | First button on gaming device A is pressed
2 | First button on gaming device B pressed since STICK(0) was called
3 | First button on gaming device B is pressed
4 | Second button on gaming device A pressed since STICK(0) was called
5 | Second button on gaming device A is pressed
6 | Second button on gaming device B pressed since STICK(0) was called
7 | Second button on gaming device B is pressed

Calling Stick(0) will reset the state returned where button is equal to 0, 2, 4, or 6.

Example

'' Compile with -lang qb

'$lang: "qb"

Screen 12

Do
    Locate 1, 1
    Print "Joystick A-X position : "; Stick(0); "   "
    Print "Joystick A-Y position : "; Stick(1); "   "
    Print "Joystick B-X position : "; Stick(2); "   "
    Print "Joystick B-Y position : "; Stick(3); "   "
    Print
    Print "Button A1 was pressed : "; Strig(0); "  "
    Print "Button A1 is pressed  : "; Strig(1); "  "
    Print "Button B1 was pressed : "; Strig(2); "  "
    Print "Button B1 is pressed  : "; Strig(3); "  "
    Print "Button A2 was pressed : "; Strig(4); "  "
    Print "Button A2 is pressed  : "; Strig(5); "  "
    Print "Button B2 was pressed : "; Strig(6); "  "
    Print "Button B2 is pressed  : "; Strig(7); "  "
    Print
    Print "Press ESC to Quit"

    If Inkey$ = Chr$(27) Then
        Exit Do
    End If

    Sleep 1

Loop

Dialect Differences

  • Only available in the -lang qb dialect.

Differences from QB

  • None

See also

  • GetJoystick
  • Stick


String (Function)
Creates and fills a string of a certain length with a certain character

Syntax

Declare Function String ( ByVal count As Integer, ByVal ch_code As Long ) As String
Declare Function String ( ByVal count As Integer, ByRef ch As Const String ) As String

Usage

result = String[$]( count, ch_code )
or
result = String[$]( count, ch )

Parameters

count
An integer specifying the length of the string to be created.
ch_code
A long specifying the ASCII character code to be used to fill the string.
ch
A string whose first character is to be used to fill the string.

Return Value

The created string. An empty string will be returned if either ch is an empty string, or count <= 0.

Description

The String function returns a string with a length of count, filled with a given ch_code or ch.

Example

Print String( 4, 69 )         '' prints "EEEE"
Print String( 5, "Indeed" )   '' prints "IIIII"
End 0

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • None

See also

  • String (data type)
  • Space


String
Standard data type: 8 bit character string

Syntax

Dim variable As String [ * size]

Description

A String is an array of characters.

A String declared without the size parameter is dynamically resized depending on the length of the string. The length can range from 0 bytes to 2 gigabytes. A descriptor contains a pointer to the actual string, the length of the string, and the amount of space allocated for it. VarPtr will return a pointer to the descriptor, while StrPtr will point to the actual string.

Because of the hidden descriptor with a String, manual allocation of space, for example using the memory allocation function CAllocate (preferentially), for a String is not encouraged. The common way to ensure a certain amount of space is reserved for a String, to prevent unnecessary allocations inside a loop for instance, is to use the Space or String functions.

Nevertheless if necessary, dynamic allocation may be carefully used by means of the memory allocation functions Allocate, CAllocate, Reallocate (see precautions for use) and string pointer (which is a pointer to a string descriptor, not string data). When memory is allocated to hold string descriptors, the string must always be destroyed (setting to "") before deallocate each string descriptor (allowing to deallocate the memory taken up by the string data), otherwise, it is not possible to deallocate it later, and it may induce memory leak in the program continuation.

A simpler and safer method for dynamic allocation /deallocation is to use the advanced New / Delete keyword (which itself also calls the constructor / destructor of the string object).

Despite the use of the descriptor, an implicit NULL character (Chr(0)) is added to the end of the string, to allow passing them to functions in external libraries without making slow copies. FreeBASIC's internal functions will ignore this character, and not treat it as part of the string.

A String declared with a fixed size (numeric constant, or expression that can be evaluated at compile time) is a QB-style fixed length string, with the exception that unused characters are set to 0 before fbc version 1.20.0, or set to space since fbc version 1.20.0, regardless of what "-lang" compiler option is used. It has no descriptor and it is not resized to fit its contents. As in QB, if data overflows the size of the string, it is truncated on the right side.
Only before fbc version 1.20.0, fixed length strings were also terminated with a NULL character, and so they used size + 1 bytes of space. This NULL terminator is removed since fbc version 1.20.0, to prevent the redundant character complicating data layout in user-defined Types.
Note: Before fbc version 1.20.0, it was not recommended to use explicit NULL character (Chr(0)) in a string expression involving a fixed length string variable because this could lead to different unexpected results depending on usage context (initialization, assignment, concatenation, ...).
Note: Since fbc version 1.20.0, dereferencing a pointer to a fixed length string can lead to an extension of the string by some pollution, or even to an outright crash (because there is no longer NULL terminal character).

String variable names need not end in a dollar sign $ as in other dialects of BASIC. In lang fb variable suffixes, including the dollar sign, are disallowed entirely.

Example

'' Variable length
Dim a As String

a = "Hello"
Print a

a += ", world!"
Print a

Dim As String b = "Welcome to FreeBASIC"
Print b + "! " + a

'' QB-like $ suffixes
#lang "qb"

'' DIM based on $ suffix
Dim a$
a$ = "Hello"

'' Implicit declaration based on $ suffix
b$ = ", world!"

Print a$ + b$

'' Variable-length strings as buffers

'' Reserving space for a string,
'' using Space() to produce lots of space characters (ASCII 32)
Dim As String mybigstring = Space(1024)
Print "buffer address: &h" & Hex( StrPtr( mybigstring ), 8 ) & ", length: " & Len( mybigstring )

'' Explicitly destroying a string
mybigstring = ""
Print "buffer address: &h" & Hex( StrPtr( mybigstring ), 8 ) & ", length: " & Len( mybigstring )

'' Variable-length string as Const parameter

'' Const qualifier preventing string from being modified
Sub silly_print( ByRef printme As Const String )
    Print ".o0( " & printme & " )0o."
    'next line will cause error if uncommented
    'printme = "silly printed"
End Sub

Dim As String status = "OK"

silly_print( "Hello FreeBASIC!" )
silly_print( "Status: " + status )

Version

  • Before fbc 1.20.0, fixed length strings were terminated with a NULL character, and so they used size + 1 bytes of space.

Differences from QB

  • In QB the strings were limited to 32767 characters.
  • In QB, the unused characters of a fixed-length string were initialized with 32 (space, or " ", in ASCII).
  • In QB static or fixed-size strings were often used in records to represent a number of bytes of data; for example, a string of 1 length to represent 1 byte in a UDT read from a file. This was not possible in FreeBASIC before fbc version 1.20.0 since strings always had an NULL character following (when converting before fbc version 1.20.0 QBasic code that reads UDTs from files, make sure all instances of "As String * n" are replaced with "As uByte (0 to n - 1)" or your files will be incompatible).

See also

  • String (Function)
  • Space
  • ZString
  • WString
  • Str
  • StrPtr
  • VarPtr
  • Standard Data Type Limits
  • Operator [] (String Index)
  • String Operators


Operator Strptr (String Pointer)
Returns the address of a string's character data.

Syntax

Declare Operator StrPtr ( ByRef lhs As String ) As ZString Ptr
Declare Operator StrPtr ( ByRef lhs As WString ) As WString Ptr

Usage

result = StrPtr ( lhs )

Parameters

lhs
A string.

Return Value

Returns a ZString/WString Ptr to a string/wstring's character data (null value in case of empty string).

Description

This operator returns a ZString/WString Ptr that points to the beginning of a string/wstring's character data. Operator Strptr is the proper method for acquiring the address of a string's character data.
In case of empty String (only for variable length strings), Operator Strptr returns a null pointer.

The related Operator Varptr (Variable Pointer) and Operator @ (Address Of), when used with a String, return the address of the internal string descriptor.
When a variable length string is modified, the address of its descriptor remains always the same, but the the string's character data address (returned by Operator Strptr) may change (like any allocated memory that must be reallocated).
When a fixed length string is modified, the string's character data address (returned by Operator Strptr) is unchanged.

Note: For a variable length string, the operator returns a ZString Const Ptr (because returning by reference the string's characters pointer set in the string descriptor, this one is to be considered as read only). If the keyword Var is used to declare/initialize a user pointer from Operator Strptr, this user pointer is also defined as read only (it can not be modified further).

Warning: Since fbc version 1.20.0, dereferencing a zstring pointer to a fixed-length string of STRING*N type (which no longer has an additional terminal null character as before) can lead to an extension of the string by some pollution, or even an outright crash.

Example

'' This example uses Strptr to demonstrate using pointers with strings
Dim myString As String
Dim toMyStringDesc As Any Ptr
Dim toMyString As ZString Ptr

'' Note that using standard VARPTR notation will return a pointer to the
'' descriptor, not the string data itself
myString = "Improper method for Strings"
toMyStringDesc = @myString
Print myString
Print Hex( toMyStringDesc )
Print

'' However, using Strptr returns the proper pointer
myString = "Hello World Examples Are Silly"
toMyString = StrPtr(myString)
Print myString
Print *toMyString
Print

'' And the pointer acts like pointers to other types
myString = "MyString has now changed"
Print myString
Print *toMyString
Print

Differences from QB

  • New to FreeBASIC, but does exactly the same thing as SAdd

See also

  • SAdd
  • VarPtr
  • ProcPtr
  • Pointers


Sub
Defines a procedure

Syntax

[Public|Private] Sub identifier [cdecl|pascal|stdcall] [Overload] [Alias external_identifier] [( [parameter_list] )] [Static] [Export]
statements
...
[Return]
...
End Sub

[Public] Sub identifier [cdecl|pascal|stdcall] [Overload] [Alias external_identifier] [()] [Constructor|Destructor] [Static]
statements
...
[Return]
...
End Sub

Parameters

identifier: the name of the subroutine
external_identifier: externally visible (to the linker) name enclosed in quotes
parameter_list: parameter[, parameter[, ...]]
parameter: [ByRef|ByVal] identifier [As type] [= default_value]
identifier: the name of the variable referenced in the subroutine. If the argument is an array then the identifier must be followed by an empty parenthesis.
type: the type of variable
default_value: the value of the argument if none is specified in the call (default_value can be a literal, or a constant, or a shared variable)
statements: one or more statements that make up the subroutine body

Description

A subroutine is a block of code which may be called at any time from a program. This code may need to be executed multiple times, and subroutines provide an invaluable means to simplify code by replacing these blocks of code with a single subroutine call. A subroutine also serves to allow a user to extend the FreeBASIC language to provide custom commands. Many of the functions built into FreeBASIC are merely subroutines part of a "runtime library" linked to by default.

The Sub keyword marks the beginning of a subroutine, and its end is marked by End Sub. The "name" parameter is the name by which this subroutine is called. For instance, if the declaration is "Sub...End Sub", the user can execute the code in between "Sub foo" and "End Sub" by using "foo" as a statement. This code is executed separate from the code which calls the subroutine, so any variable names, unless they are shared, are not available to the subroutine. Values can, however, be passed using parameters.

Parameters are the arguments passed to any statement. For instance, if a user executes a statement as "Print 4", the value "4" is passed to the function "Print". Parameters that need to be passed to a subroutine are supplied by one or more parameter arguments in the "Sub" keyword. Creating a subroutine with "Sub mysub(foo, bar)...End Sub", allows the code in between "Sub" and "End Sub" to refer to the first passed argument as "foo" and the second passed argument as "bar". If a parameter is given a default value, that parameter is optional.
Array parameters are specified by following an identifier with an empty parenthesis. Note that array parameters are always ByRef and the ByRef keyword is neither required nor allowed for array parameters. When calling a subroutine with an array argument the parenthesis must be supplied there too.

In the default dialect -lang fb, parameters must also have a supplied type, in the form "parameter as type". Type suffixes are not allowed.

In the -lang qb and -lang fblite dialects only, it will be given a default type if the type is not explicitly given either by name or by type suffix. The default type is Single in the -lang qb dialect and Integer in the -lang fblite dialect.

A subroutine can also specify how parameters are passed, either as "ByRef" or "ByVal", as shown in the syntax definition. If a parameter is "ByRef", the parameter name literally becomes a reference to the original variable passed to the subroutine. Any changes made to that variable will be reflected outside of the subroutine. If a parameter is passed "ByVal", however, the value of any passed variable is copied into a new variable, and any changes made to it will not affect the original.

The Static specifier indicates that the values of all local variables defined in the sub should be preserved between calls. To specify individual local variables as static see the Static keyword.
To access duplicated symbols defined as global outside the subroutine body, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

Sub is the same as Function, except it does not allow a value to be returned.

When calling a subroutine, parentheses after the subroutine name (surrounding the argument list if any) are optional.

The second syntax defines either a constructor or destructor using the Constructor and Destructor keywords, respectively. Constructor subroutines are executed before the first line of code in the module, while destructors execute on module exit. Note the public access specifier and empty parameter list for both constructors and destructors.

Warning for 64-bit compiler only: See the Identifier Rules page for the choice of user procedure identifier names (and specially the 'Platform Differences' paragraph).

Example

'' Example of writing colored text using a sub:

Sub PrintColoredText( ByVal colour As Integer, ByRef text As String )
   Color colour
   Print text
End Sub

   PrintColoredText( 1, "blue" )        '' a few colors
   PrintColoredText( 2, "green" )
   PrintColoredText( 4, "red" )
   Print

   Dim i As Integer
   For i = 0 To 15                        '' all 16 colors
      PrintColoredText( i, ("color " & i) )
   Next i

' The following demonstrates optional parameters.

Sub TestSub(P As String = "Default")
    Print P
End Sub

TestSub "Testing:"
TestSub

Dialect Differences

  • The -lang qb and -lang fblite dialects keep the QB convention: parameters are ByRef by default.
  • In the -lang fb dialect, numeric parameters are passed ByVal by default. Strings and UDTs are passed ByRef by default.

Differences from QB

  • Public and Private access specifiers are new to FreeBASIC.
  • Constructor subroutines are new to FreeBASIC.

See also

  • Declare
  • Function
  • Exit
  • Public
  • Private
  • Static


Sub (Member)
Declares or defines a member procedure

Syntax

{ Type | Class | Union } typename
Declare [ Static | Const ] Sub fieldname [calling convention specifier] [ Alias external_name ] ( [ parameters ] ) [ Static ]
End { Type | Class | Union }

Sub typename.fieldname ( [ parameters ] ) [ Export ]
statements
End Sub

Parameters

typename
name of the Type, Class, or Union
fieldname
name of the procedure
external_name
name of field as seen when externally linked
parameters
the parameters to be passed to the procedure
calling convention specifier
can be one of: cdecl, stdcall or pascal

Description

Sub members are accessed with Operator . (Member Access) or Operator -> (Pointer To Member Access) to call a member procedure and may optionally accept parameters either ByVal or ByRef. typename be overloaded without explicit use of the Overload keyword.

typename is the name of the type for which the Sub method is declared and defined. Name resolution for typename follows the same rules as procedures when used in a Namespace.

A hidden This parameter having the same type as typename is passed to non-static member procedures. This is used to access the fields of the Type, Class, or Union.
To access duplicated symbols defined as global outside the Type, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

A Static (Member) may be declared using the Static specifier. A Const (Member) may be declared using the Const specifier.

Example

Type Statistics
  count As Single
  sum As Single
  Declare Sub AddValue( ByVal x As Single )
  Declare Sub ShowResults( )
End Type

Sub Statistics.AddValue( ByVal x As Single )
  count += 1
  sum += x
End Sub

Sub Statistics.ShowResults( )
  Print "Number of Values = "; count
  Print "Average          = ";
  If( count > 0 ) Then
    Print sum / count
  Else
    Print "N/A"
  End If
End Sub

Dim stats As Statistics

stats.AddValue 17.5
stats.AddValue 20.1
stats.AddValue 22.3
stats.AddValue 16.9

stats.ShowResults

Output:
Number of Values =  4
Average          =  19.2

Dialect Differences

  • Only available in the -lang fb dialect.

See also

  • Class
  • Function (Member)
  • Sub
  • Type


Sub Pointer
Data type that stores a pointer to a Sub procedure

Syntax

Dim variable As Sub [cdecl|pascal|stdcall] [( [parameter_list] )] [= initializer]

Parameters

parameter_list: parameter[, parameter[, ...]]
parameter: [ByRef|ByVal] identifier [As type] [= default_value]
identifier: the name of the variable referenced in the subroutine
type: the type of variable
default_value: the value of the argument if none is specified in the call
intializer: address of a subroutine to set as the intial value

Description

A Sub pointer is a procedure pointer that stores the memory location of compiled code. If no intializer is given the default initial value is zero (0).

The memory address for the Sub procedure can be assigned to the variable by taking the address of a subroutine with ProcPtr or Operator @ (Address Of).

The procedure must match the same Sub declaration as the declared Sub pointer.

To call the subroutine assigned, use the variable name as if it were a normal declared Sub, always with parentheses around the parameter list even empty (without parentheses, only the pointer value, ie the address of the subroutine, would be accessed).

One of the primary uses for Sub pointers is to create callback procedures:
- A callback Sub is a Sub that is passed through an argument (a Sub pointer) to another procedure which is expected to call back (execute) the "argument" at a convenient time.
- If the callback Sub is completely executed before the invocation returns to the caller code, then the callback process is said to be "synchronous".
- If the invocation immediately returns to the caller code, and the callback Sub and the caller's next code are running in parallel, then the callback process is said to be "asynchronous".

Example

    Sub Hello()
        Print "Hello"
    End Sub

    Sub Goodbye()
        Print "Goodbye"
    End Sub

    Dim x As Sub() = ProcPtr( Hello )

    x()

    x = @Goodbye  '' or procptr(Goodbye)

    x()

Sub s0 ()
  Print "'s0 ()'"
End Sub

Sub s1 (ByVal I As Integer)
  Print "'s1 (Byval As Integer)'", I
End Sub

Sub s2 (ByRef S As String, ByVal D As Double)
  Print "'s2 (Byref As String, Byval As Double)'", S, D
End Sub

Dim s0_ptr As Sub () = @s0
Dim s1_ptr As Sub (ByVal I As Integer) = @s1
Dim s2_ptr As Sub (ByRef S As String, ByVal D As Double) = @s2

s0_ptr()
s1_ptr(3)
s2_ptr("PI", 3.14)

' Example of advanced callback Sub mechanism (asynchronous) to implement a key pressed event:
' (the user callback Sub address can be modified while the event thread is running)
'   - An asynchronous thread tests the keyboard in a loop, and calls a user callback Sub each time a key is pressed.
'   - An UDT groups the common variables used (callback Sub pointer, character of key pressed, thread end flag),
'       and the static thread Sub plus the thread handle.
'   - An UDT instance pointer is passed to the thread, which then transmits it to the callback Sub each time.
'   - The callback Sub prints the character of the key pressed character,
'       but if the key pressed is <escape> it orders the thread to finish.
'   - As the user callback pointer is a member field of the UDT, it can be modified while the thread is running.

'' UDT for thread environment
  Type threadUDT
    Dim As Sub (ByVal As ThreadUDT Ptr) callback             '' callback Sub pointer
    Dim As Integer threadEnd                                 '' thread end flag
    Dim As String s                                          '' character of the key pressed
    Declare Static Sub threadInkey (ByVal p As Any Ptr)      '' static thread Sub
    Dim As Any Ptr threadHandle                              '' handle to the thread
  End Type

'' thread Sub definition
  Sub threadUDT.threadInkey (ByVal p As Any Ptr)
    Dim As threadUDT Ptr pt = p                              '' convert the any ptr to a threadUDT pointer
    Do
      pt->s = Inkey
      If pt->s <> "" AndAlso pt->callback > 0 Then           '' test condition key pressed & callback Sub defined
        pt->callback(p)
      End If
      Sleep 50, 1
    Loop Until pt->threadEnd                                 '' test condition to finish thread
  End Sub

'' user callback Sub definition
  Sub printInkey (ByVal pt As threadUDT Ptr)
    If Asc(pt->s) = 27 Then                                  '' test condition key pressed = <escape>
      pt->threadEnd = -1                                     '' order thread to finish
      Print
    Else
      Print pt->s;
    End If
  End Sub

'' user main code
  Dim As ThreadUDT t                                         '' create an instance of threadUDT
  t.threadHandle = ThreadCreate(@threadUDT.threadInkey, @t)  '' launch the thread, passing the instance address
  t.callback = @printInkey                                   '' initialize the callback Sub pointer
  ThreadWait(t.threadHandle)                                 '' wait for the thread finish

Differences from QB

  • New to FreeBASIC

See also

  • Sub
  • ProcPtr
  • Operator @ (Address Of)


Swap
Exchanges the values of two variables

Syntax

Declare Sub Swap ( ByRef a As Any, ByRef b As Any )

Usage

Swap a, b

Parameters

a
A variable to swap.
b
A variable to swap.

Description

Swaps the value of two variables, including UDT instances (swaps all data members).

Since fbc version 1.20.0, because a fixed-length string fields of STRING*N type no longer has a terminal null character, Swap will pad values with spaces where one of the arguments is of the STRING*N type.

Note: When the data are referenced by a pointer, alone or within a descriptive structure (a UDT, for example), Swap only exchanges the values of the pointers or the contents of the descriptive structures without accessing data themselves.
For var-len strings, Swap only exchanges the descriptors of the strings rather than reallocate memory for exchange all strings data characters.
For UDTs, Swap simply exchanges the contents of the structures, without any operators or methods being called.

Example

' using swap to order 2 numbers:
Dim a As Integer, b As Integer

Input "input a number: "; a
Input "input another number: "; b
If a > b Then Swap a, b
Print "the numbers, in ascending order are:"
Print a, b

Differences from QB

  • None

See also

  • Operator = (Assignment)


System
Closes all open files and ends the program

Syntax

Declare Sub System ( ByVal retval As Long = 0 )

Usage

System( [ retval ] )

Parameters

retval
Error code returned to system.

Description

Closes all open files, exits the program, and returns to the operating system. An optional return value, an integer, can be specified to return an error code to the system. If no return value is given, a value of 0 is automatically returned. This is the same as End (Statement) and is here for compatibility between older BASIC dialects. It is recommended to use End (Statement) instead.

Usage of this statement does not cleanly close scope. Local variables will not have their destructors called automatically, because FreeBASIC does not do stack unwinding. Only the destructors of global variables will be called in this case.

For this reason, it is discouraged to use System simply to mark the end of a program; the program will come to an end automatically, and in a cleaner fashion, when the last line of module-level code has executed.

Example

Print "this text is shown"
System
Print "this text will never be shown"

Differences from QB

  • None

See also

  • End (Statement)


Tab
Sets the column when writing to screen or file

Syntax

Tab( col_num )

Usage

Print Tab( column ) [(, | ;)] ...

Parameters

column
1-based column number to move to

Description

Tab will move the cursor to given column number when Printing to screen or to a file. Character cells skipped over between the old and new cursor positions are left unchanged.
If the current column is greater than column, then Tab will move the cursor to the requested column number on the next line. If the current column is equal to column, then the cursor will not move anywhere.

Example

'' Using Print with Tab to justify text in a table

Dim As String A1, B1, A2, B2

A1 = "Jane"
B1 = "Doe"
A2 = "Bob"
B2 = "Smith"

Print "FIRST NAME"; Tab(35); "LAST NAME"
Print "----------"; Tab(35); "----------"
Print A1; Tab(35); B1
Print A2; Tab(35); B2

The output would look like:
FIRST NAME                         LAST NAME
----------                         ----------
Jane                               Doe
Bob                                Smith

Differences from QB

  • In QBASIC, spaces were printed in the gap, while in FreeBASIC, the characters are just skipped over and left untouched.

See also

  • Spc
  • Locate
  • Pos
  • Print
  • ?


Tan
Returns the tangent of an angle

Syntax

Declare Function Tan ( ByVal angle As Double ) As Double

Usage

result = Tan( angle )

Parameters

angle
the angle (in radians)

Return Value

Returns the tangent of the argument angle as a Double within the range of -infinity to infinity.

Description

The argument angle is measured in radians (not degrees).

The value returned by this function is undefined for values of angle with an absolute value of 2 ^ 63 or greater.

Tan can be overloaded as operator to accept user-defined types.

Example

Const PI As Double = 3.1415926535897932
Dim a As Double
Dim r As Double
Input "Please enter an angle in degrees: ", a
r = a * PI / 180    'Convert the degrees to Radians
Print ""
Print "The tangent of a" ; a; " degree angle is"; Tan ( r )
Sleep

The output would look like:
Please enter an angle in degrees: 75
The tangent of a 75 degree angle Is 3.732050807568878

Differences from QB

  • None

See also

  • Atn
  • Atan2
  • Sin
  • Cos
  • A Brief Introduction To Trigonometry


Tcp Accept
Accepts a connection from an Open Tcp Server file number.

Syntax

Declare Function Tcp Accept ( [#]serverfilenum As Long ) As Long

Usage

clientfilenum = Tcp Accept( [#]serverfilenum )

Parameters

serverfilenum
A file number previously opened with Open Tcp Server.

Return Value

Returns a new client file number on success, or zero (0) on failure.

Description

Tcp Accept waits for the next incoming connection on a TCP server file number. It chooses a free file number for the accepted client and returns it to the program. The returned file number should be closed with Close when the connection is no longer needed.

The server file number remains open and may be passed to Tcp Accept again to accept another connection.

Example

See examples/network/opentcp/http-server.bas and examples/network/opentcp/two-connections-selftest.bas.

Differences from QB

  • New to FreeBASIC.

See also

  • Open Tcp Server
  • Open Tcp
  • Eoc
  • FreeFile


TEMPO
Sets or returns the PLAY-string tempo.

Syntax

Tempo bpm
bpm = Tempo()

Description

TEMPO controls the speed used by PLAY strings and other note-oriented generated sound. The value is beats per minute. A larger number plays the same music faster; a smaller number plays it slower.

Setting TEMPO changes the default used by later commands. Calling TEMPO as a function returns the current default, which is useful when a routine wants to save the old tempo, change it temporarily, and then restore it.

TEMPO does not make sound by itself. It changes how future note durations are interpreted.

Example

tempo 140
play "o4 cdef"
print "current tempo="; tempo()

Notes

  • Tempo affects generated-note timing, not the speed of loaded SFX samples.

See also

  • Sound Library Reference
  • Generated Sound Commands


Then
Control flow statement for conditional branching.

Syntax

If expression Then [statement(s)] [Else [statement(s)]] [End If]
or
If expression Then : [statement(s)] [Else [statement(s)]] : End If
or
If expression Then
[statement(s)]
[ ElseIf expression Then ]
[statement(s)]
[ Else ]
[statement(s)]
End If

Remark: EndIf (without blank) is also supported like in QB for backward compatibility.

Example

See example at If...Then.

Differences from QB

  • None

See also

  • If...Then


This
Hidden instance parameter passed to non-static member functions in a Type or Class

Syntax

This.fieldname
or
With This
.fieldname
End With

Description

This is a reference to an instance of a Type or Class that is passed (through a hidden Byref Parameter) to all non-static member functions of that type or class. Non-static member functions are procedures declared inside the body of a Type or Class and include Sub, Function, Constructor, Destructor, assignment or Cast Operator, and Property procedures.

The This additional parameter also has the same data type as the Type or Class in which the procedure is declared. Therefore using it corresponds to referring to the instance declared name on which the non-static member function is called.

The This parameter can be used just like any other variable, ie., pass it to procedures taking an object of the same type, call other member procedures and access member data using Operator . (Member Access), etc.

Most of the time, using This explicitly for member access is unnecessary; member procedures can refer to other members of the instance which they are passed directly by name, without having to qualify it with This and Operator . (Member Access). The only times when you need to qualify member names with This is when the member is shadowed, for example by duplicating its name for a local variable or parameter. In these situations, qualifying the member name is the only way to refer to these masked member names.

Example

Type sometype
    Declare Sub MyCall()
    value As Integer
End Type

Dim example As sometype

'' Set element test to 0
example.value = 0
Print example.value

example.MyCall()

'' Output should now be 10
Print example.value

End 0

Sub sometype.MyCall()
    This.value = 10
End Sub

Differences from QB

  • New to FreeBASIC

See also

  • Base (Member Access)
  • Class
  • Type


__Thiscall
Specifies the Thiscall calling convention in a procedure declaration

Syntax

declare Sub name __Thiscall [Overload] [Alias "alias"] ( parameters )
declare Function name __Thiscall [Overload] [Alias "alias"] ( parameters ) [ ByRef ] As return_type

Sub name [__Thiscall] [Overload] [Alias "alias"] ( parameters )
Function name [__Thiscall] [Overload] [Alias "alias"] ( parameters ) [ ByRef ] As return_type

Description

Thiscall is a calling convention for x86 targets where the first integral argument is passed in the ECX register instead of on the stack. All other arguments are passed right to left and callee cleans up the stack (like Stdcall).

On win32 x86, mingw+gcc will use Thiscall as the default calling convention for non-static member procedures in classes passing the hidden This parameter by ECX register instead of pushing to the stack.

Default calling convention on win32 x86 inside an extern "c++" block for non-static member procedures is Thiscall.

Thiscall can be explicitly specified for normal procedures and static member procedures to override the default calling convention.
Other calling conventions (Cdecl/Stdcall/etc) can be explicitly specified to override the default Thiscall calling convention on non-static member procedures.

Thiscall can be specified at both the declaration and the definition.
If a procedure definition has a declaration (with calling convention explicit or by default) and the definition does not explicitly specify a calling convention, then the calling convention is implied by the declaration.

Example

- If inside extern "c++" and also on win32 / x86, and it's a non-static member procedure, and no other calling convention is given, the default is __thiscall:
Extern "c++"
    Type T Extends Object
        Declare Constructor() '' __thiscall is default
    End Type

    Constructor T()  '' __thiscall is default
    End Constructor
End Extern

- If definition is outside the extern "c++" block, then __thiscall is optional on the definition (if the default for the non-static member procedure was __thiscall):
Extern "c++"
    Type T Extends Object
        Declare Constructor() '' __thiscall is default
    End Type

End Extern

Constructor T() '' __thiscall is implied by declaration
End Constructor

Version

  • Since fbc 1.10.0:
- Thiscall calling convention is used as default for non-static member procedures under extern "c++" / win32 / x86.
- Thiscall can also be explicitly specified for normal procedures and static member procedures to override the default calling convention.
  • Since fbc 1.08.0

Differences from QB

  • New to FreeBASIC

See also

  • cdecl, stdcall, Fastcall
  • Declare
  • Sub, Function


ThreadCall
Starts a user-defined procedure with parameters in a separate execution thread

Threadcall uses LibFFI internally: people who write programs using this functionality should be careful to follow LibFFI's license, which can be found at http://github.com/atgreen/libffi/blob/master/LICENSE.

Syntax

Function ThreadCall subname([paramlist]) As Any Ptr

Usage

threadid = ThreadCall subname([paramlist])

Parameters

subname
The name of a subroutine
paramlist
A list of parameters to pass to the subroutine, as with a normal sub call.

Return Value

Threadcall returns an Any Ptr handle to the thread created, or the null pointer (0) on failure.

Description

Like ThreadCreate, Threadcall creates a thread which runs at the same time as the code calling it. By placing "Threadcall" before almost any normal call to sub, the sub is called inside of a new thread and returns a pointer to that thread.

Using Threadcall is simpler method of creating threads, and allows data to be passed to the thread without global variables or pointers which are not type safe. However, ThreadCreate is more efficient and should be used for programs creating a large number of threads.

While most subroutines are supported, the following types of subroutines may not be called:
    • Subroutines using Variable Arguments.
    • Subroutines with unions which are passed as arguments.
    • Subroutines with user types containing unions, arrays, strings, or bitfields which are passed as arguments.

When using Threadcall, parenthesis around the parameter list are required unless the subroutine has no parameters.

Warning:
- Presently when Threadcall involves to pass parameters to the thread, there is no guarantee that the corresponding data are still maintained after the end of the Threadcall statement and this until the thread is launched.
- That can cause bad behavior:
- see example below where the not displayed parameter id seems to be the consequence of a prematurely destroyed string argument (visible for a fbc version >= 1.00),
- replace id As String with id As Zstring in the parameters declaration seems to workaround the problem when passing this parameter.
- Therefore it is more advisable for the moment to use ThreadCreate (100% safe) instead.

Example

'' Threading using "ThreadCall"

Sub thread( id As String, tlock As Any Ptr, count As Integer )
    For i As Integer = 1 To count
        MutexLock tlock
        Print "thread " & id;
        Locate , 20
        Print i & "/" & count
        MutexUnlock tlock
    Next
End Sub

Dim tlock As Any Ptr = MutexCreate()
Dim a As Any Ptr = ThreadCall thread("A", tlock, 6)
Dim b As Any Ptr = ThreadCall thread("B", tlock, 4)
ThreadWait a
ThreadWait b
MutexDestroy tlock
Print "All done (and without Dim Shared!)"

Dialect Differences

  • Threading is not allowed in the -lang qb dialect.

Platform Differences

  • Threadcall is not available with the DOS version / target of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.
  • In Linux the threads are always started in the order they are created, this can't be assumed in Win32. It's an OS, not a FreeBASIC issue.
  • In Linux, the stdcall and pascal calling conventions are not supported
  • In Windows, the pascal calling convention is not supported.

Differences from QB

  • New to FreeBASIC

See also

  • ThreadCreate
  • ThreadWait
  • MutexCreate
  • MutexLock
  • MutexUnlock
  • MutexDestroy


ThreadCreate
Starts a user-defined procedure in a separate execution thread

Syntax

Declare Function ThreadCreate _
( _
ByVal procptr As Sub ( ByVal userdata As Any Ptr ), _
ByVal param As Any Ptr = 0, _
ByVal stack_size As Integer = 0 _
) As Any Ptr

Usage

result = ThreadCreate ( procptr [, [ param ] [, stack_size ] ] )

Parameters

procptr
A pointer to the Sub intended to work as a thread (see Operator Procptr (Procedure Pointer And Vtable Index) to get a pointer to a sub). The sub must have the following signature (same parameters, same calling convention) to be compatible to procptr:
Declare Sub myThread ( ByVal userdata As Any Ptr )
userdata
The Any Ptr parameter of the Sub intended to work as a thread. FreeBASIC expects this parameter to be present, it must not be omitted!
param
Any Ptr argument that will be passed to the thread Sub pointed to by procptr through its userdata parameter. For example, this can be a pointer to a structure or an array containing various information for the thread sub to work with. If param is not given, 0 (zero) will be passed to the thread sub's userdata parameter instead.
stack_size
Optional number of bytes to reserve for this thread's stack.

Return Value

ThreadCreate returns an Any Ptr handle to the thread created, or a null pointer (0) on failure.

Description

The sub pointed to by procptr is started as a thread. It will be passed the content of param, or 0 (zero) if not specified, in its userdata parameter.

The sub that was started as a thread will execute in parallel with the main part of the program. The OS achieves this by assigning it to a different processor if it exists, or by alternating between execution threads on a single processor.
There is no guarantee about the order in which different threads execute, and no assumptions can be made about the order in which multiple created threads actually start executing.
The time between ThreadCreate and the actual start of execution of the thread is variable and can be longer or shorter depending on the context (so some statements following the ThreadCreate statement can be executed before the actual launch of the thread).
In the fastest launch cases, the thread body may start executing even before ThreadCreate returns.

Each running thread can be identified by its handle which is unique among all running threads. See ThreadSelf.

Before closing, programs should wait for the termination of all launched threads by using ThreadWait. Alternatively, if it's not necessary to safely wait for a thread to finish execution, ThreadDetach can be used. However, if a program exits while some threads are still active, those threads will be aborted by the system. For every thread created, programs should call either ThreadWait or ThreadDetach to ensure that the system resources associated with the thread handles are released. Otherwise, there may be memory or system resource leaks.

Due to the nature of threads, no assumptions about execution order can be made. In order to exchange data between multiple threads, including a thread and the main part of the program, mutexes must be used. These mutual exclusion locks can be "owned" by a single thread while doing critical work, causing other threads to wait for their turn. See MutexCreate, MutexLock, MutexUnlock, MutexDestroy.

stack_size can be used to change the thread's stack size from the system's default. This can be useful when the program requires a big stack, for example due to lots of procedure recursion or when allocating huge strings/arrays on the stack. On some systems (Linux), the stack automatically grows beyond stack_size if more space is needed; on others (Win32), this is the fixed maximum allowed. Behavior is undefined when more stack is used than the reserved size on systems where stacks are not able to grow.

The intrinsic macro __FB_MT__ is only automatically set from the point of usage of ThreadCreate onward.

Note:
- The userdata parameter can be unused in the body of the myThread sub, but declaring it as an Any Ptr parameter is always mandatory in the header. In this case, the corresponding param parameter can then be omitted when calling ThreadCreate, or else a needless argument can still be passed ('0' is commonly used because this value is directly compatible with any pointer). See the 2nd and 3rd example.
- In the case where data must be passed to myThread, the Any Ptr param can be used to reference them, usually requiring a type conversion (implicit or explicit) into Any Ptr before passing it to ThreadCreate, and a reverse type conversion from Any Ptr in the body of myThread before using it. See the 1st example.

Example

'' Threading synchronization using Mutexes
'' If you comment out the lines containing "MutexLock" and "MutexUnlock",
'' the threads will not be in sync and some of the data may be printed
'' out of place.

Const MAX_THREADS = 10

Dim Shared As Any Ptr ttylock

'' Teletype unfurls some text across the screen at a given location
Sub teletype( ByRef text As String, ByVal x As Long, ByVal y As Long )
    ''
    '' This MutexLock makes simultaneously running threads wait for each
    '' other, so only one at a time can continue and print output.
    '' Otherwise, their Locates would interfere, since there is only one
    '' cursor.
    ''
    '' It's impossible to predict the order in which threads will arrive
    '' here and which one will be the first to acquire the lock thus
    '' causing the rest to wait.
    ''
    MutexLock ttylock

    For i As Integer = 0 To (Len(text) - 1)
        Locate x, y + i
        Print Chr(text[i])
        Sleep 25, 1
    Next

    '' MutexUnlock releases the lock and lets other threads acquire it.
    MutexUnlock ttylock
End Sub

Sub thread( ByVal userdata As Any Ptr )
    Dim As Integer id = CInt(userdata)
    teletype "Thread (" & id & ").........", 1 + id, 1
End Sub

    '' Create a mutex to syncronize the threads
    ttylock = MutexCreate()

    '' Create child threads
    Dim As Any Ptr handles(0 To MAX_THREADS-1)
    For i As Integer = 0 To MAX_THREADS-1
        handles(i) = ThreadCreate(@thread, CPtr(Any Ptr, i))
        If handles(i) = 0 Then
            Print "Error creating thread:"; i
            Exit For
        End If
    Next

    '' This is the main thread. Now wait until all child threads have finished.
    For i As Integer = 0 To MAX_THREADS-1
        If handles(i) <> 0 Then
            ThreadWait(handles(i))
        End If
    Next

    '' Clean up when finished
    MutexDestroy(ttylock)

Sub print_dots(ByRef char As String)
    For i As Integer = 0 To 29
        Print char;
        Sleep CInt(Rnd() * 100), 1
    Next
End Sub

Sub mythread(param As Any Ptr)
    '' Work (other thread)
    print_dots("*")
End Sub

    Randomize(Timer())

    Print " main thread: ."
    Print "other thread: *"

    '' Launch another thread
    Dim As Any Ptr thread = ThreadCreate(@mythread, 0)

    '' Work (main thread)
    print_dots(".")

    '' Wait until other thread has finished, if needed
    ThreadWait(thread)
    Print
    Sleep

'' Threaded consumer/producer example using mutexes

Dim Shared As Any Ptr produced, consumed

Sub consumer( ByVal param As Any Ptr )
    For i As Integer = 0 To 9
        MutexLock produced
        Print ", consumer gets:", i
        Sleep 500, 1
        MutexUnlock consumed
    Next
End Sub

Sub producer( ByVal param As Any Ptr )
    For i As Integer = 0 To 9
        Print "Producer puts:", i;
        Sleep 500, 1
        MutexUnlock produced
        MutexLock consumed
    Next i
End Sub

    Dim As Any Ptr consumer_id, producer_id

    produced = MutexCreate
    consumed = MutexCreate
    If( ( produced = 0 ) Or ( consumed = 0 ) ) Then
        Print "Error creating mutexes! Exiting..."
        End 1
    End If

    MutexLock produced
    MutexLock consumed
    consumer_id = ThreadCreate(@consumer)
    producer_id = ThreadCreate(@producer)
    If( ( producer_id = 0 ) Or ( consumer_id = 0 ) ) Then
        Print "Error creating threads! Exiting..."
        End 1
    End If

    ThreadWait consumer_id
    ThreadWait producer_id

    MutexDestroy consumed
    MutexDestroy produced

    Sleep

Dialect Differences

  • Threading is not allowed in the -lang qb dialect.

Platform Differences

  • Threadcreate is not available with the DOS version / target of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.
  • In Linux the threads are always started in the order they are created, this can't be assumed in Win32. It's an OS, not a FreeBASIC issue.

Differences from QB

  • New to FreeBASIC

See also

  • ThreadSelf
  • ThreadWait
  • ThreadDetach
  • MutexCreate
  • MutexLock
  • MutexUnlock
  • MutexDestroy
  • Operator Procptr (Procedure Pointer And Vtable Index)


ThreadDetach
Releases a thread handle without waiting for the thread to finish

Syntax

Declare Sub ThreadDetach ( ByVal id As Any Ptr )

Usage

#include "fbthread.bi"
ThreadDetach( id )

Parameters

id
Any Ptr handle of a thread created by ThreadCreate or ThreadCall

Description

ThreadDetach releases resources associated with the thread handle returned by ThreadCreate or ThreadCall. The thread handle will be destroyed by ThreadDetach and cannot be used anymore.
Unlike ThreadWait, ThreadDetach does not wait for the thread to finish and thread execution continues independently. Any allocated resources will be freed once the thread exits.

In order to avoid memory leaks, the safe way to end a thread is to always signal to it that it must end, and then call ThreadWait on that thread except if ThreadDetach has previously been called.

Note: As ThreadDetach destroys the thread handle, ThreadWait can no longer check for the thread ending, and even the use of ThreadWait becomes unpredictable (may crash the program). The use between ThreadWait and ThreadDetach must be exclusive.
But mutexes and conditional variables can also be used with detached threads.

Example

#include "fbthread.bi"

Sub mythread( ByVal param As Any Ptr )
    Print "hi!"
End Sub

Dim As Any Ptr thread = ThreadCreate( @mythread )
ThreadDetach( thread )
'' or
ThreadDetach( ThreadCreate( @mythread ) )

Sleep

Dialect Differences

  • Threading is not allowed in the -lang qb dialect.

Platform Differences

  • ThreadDetach is not available with the DOS version of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.

Differences from QB

  • New to FreeBASIC

See also

  • ThreadWait
  • ThreadCreate


ThreadSelf
Return the thread handle of the current thread.

Syntax

Declare Function ThreadSelf ( ) As Any Ptr

Usage

#include "fbthread.bi"
result = ThreadSelf

Return Value

ThreadSelf returns an Any Ptr handle of the current thread.

Description

ThreadSelf is used to get the handle of the current thread.

This function can uniquely identify the existing threads:
- If there are multiple threads, and one thread is completed, then that handle can be reused.
- So for all the only threads still running, the handles are unique.

When a new thread is created, a handle to the thread is returned by the creation function.
When the thread runs code, ThreadSelf allows to return the handle of the thread (the implicit main thread also has its own unique handle).

ThreadSelf may be used to code some sort of TLS (Thread Local Storage) from the unique handle of each thread (including the implicit main thread).
Therefore, a same global variable name may be defined, but with a stored value specific to the thread that accesses it.
This allows generic procedures to be coded, but with parameters depending on the thread which executes them (see 3rd example below).

Example

#include "fbthread.bi"

Dim As Any Ptr phandle(1 To 10)

Sub myThread (ByVal p As Any Ptr)
    Print "Thread handle: " & ThreadSelf()
End Sub

For I As Integer = 1 To 10
    phandle(I) = ThreadCreate(@myThread)
Next I

For I As Integer = 1 To 10
    ThreadWait(phandle(I))
Next I

Sleep

Checking for equality between the thread handle returned by ThreadCreate at thread creation and the one returned by ThreadSelf from thread running:
#include "fbthread.bi"

Dim As Any Ptr phandle(1 To 10)
Dim Shared As Any Ptr pmutex

Sub myThread (ByVal p As Any Ptr)
    MutexLock(pmutex)  ' to ensure that ThreadCreate line is completed before accessing the handle value
    Dim As Any Ptr phandle1 = *CPtr(Any Ptr Ptr, p)
    MutexUnlock(pmutex)
    Dim As Any Ptr phandle2 = ThreadSelf()
    Print Left("   ThreadCreate: " & phandle1 & Space(18), 36) _
          & "   ThreadSelf: " & phandle2  ' single print with concatenated string avoids using a mutex
    Sleep 100, 1
End Sub

Print "Handles returned from:"
pmutex = MutexCreate()
For I As Integer = 1 To 10
    MutexLock(pmutex)  ' to ensure that ThreadCreate line is completed before thread accesses the handle value
    phandle(I) = ThreadCreate(@myThread, @phandle(I))
    MutexUnlock(pmutex)
Next I

For I As Integer = 1 To 10
    ThreadWait(phandle(I))
Next I
MutexDestroy(pmutex)

Sleep

Example of a sort of TLS (Thread Local Storage):
(see the end of the "Description" paragraph)
#include Once "fbthread.bi"

Function TLSindex() As Integer  ' returning a unique thread index (incremented with each new thread)
    Static As Any Ptr TLSind()
    Dim As Integer index = -1
    For I As Integer = LBound(TLSind) To UBound(TLSind)
        If TLSind(I) = ThreadSelf() Then
            index = I
            Exit For
        End If
    Next I
    If index = -1 Then
        index = UBound(TLSind) + 1
        ReDim Preserve TLSind(index)
        TLSind(index) = ThreadSelf()
    End If
    Return index
End Function

Function TLSinteger() ByRef As Integer  ' emulation of global integer with value depending on thread using it
    Static As Integer TLSint()
    Dim As Integer index = TLSindex()
    If index > UBound(TLSint) Then
        ReDim Preserve TLSint(index)
    End If
    Return TLSint(index)
End Function

'------------------------------------------------------------------------------

Type threadData
    Dim As Any Ptr handle
    Dim As String prefix
    Dim As String suffix
    Dim As Double tempo
End Type

Function counter() As Integer  ' definition of a generic counter with counting depending on thread calling it
    TLSinteger() += 1
    Return TLSinteger()
End Function

Sub Thread(ByVal p As Any Ptr)
    Dim As threadData Ptr ptd = p
    Dim As UInteger c
    Do
        c = counter()
        Print ptd->prefix & c & ptd->suffix & " ";  ' single print with concatenated string avoids using a mutex
        Sleep ptd->tempo, 1
    Loop Until c = 12
End Sub

'------------------------------------------------------------------------------

Print "|x| : counting from thread a"
Print "(x) : counting from thread b"
Print "[x] : counting from thread c"
Print

Dim As threadData mtlsa
mtlsa.prefix = "|"
mtlsa.suffix = "|"
mtlsa.tempo = 250
mtlsa.handle = ThreadCreate(@Thread, @mtlsa)

Dim As threadData mtlsb
mtlsb.prefix = "("
mtlsb.suffix = ")"
mtlsb.tempo = 150
mtlsb.handle = ThreadCreate(@Thread, @mtlsb)

Dim As threadData mtlsc
mtlsc.prefix = "["
mtlsc.suffix = "]"
mtlsc.tempo = 100
mtlsc.handle = ThreadCreate(@Thread, @mtlsc)

ThreadWait(mtlsa.handle)
ThreadWait(mtlsb.handle)
ThreadWait(mtlsc.handle)

Print
Print
Print "end of threads"

Sleep

Version

  • Since fbc 1.08.0

Dialect Differences

  • Threading is not allowed in the -lang qb dialect.

Platform Differences

  • ThreadSelf is not available with the DOS version of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.

Differences from QB

  • New to FreeBASIC

See also

  • ThreadCreate


ThreadWait
Waits for a thread to finish execution and releases the thread handle

Syntax

Declare Sub ThreadWait ( ByVal id As Any Ptr )

Usage

ThreadWait( id )

Parameters

id
Any Ptr handle of a thread created by ThreadCreate or ThreadCall

Description

ThreadWait waits for a thread created by ThreadCreate or ThreadCall to finish execution, and then releases the resources associated with the thread handle. ThreadWait does not return until the thread designated by id ends.
During the wait, no CPU time is consumed by the caller.

In order to release a thread handle without waiting for the thread to finish, use ThreadDetach.

ThreadWait does not force the thread to end; if a thread requires a signal to force its end, a mechanism such as shared variables and mutexes must be used.

In order to avoid memory leaks, the safe way to end a thread is to always signal to it that it must end, and then call ThreadWait on that thread except if ThreadDetach has previously been called.

The intrinsic macro __FB_MT__ is only automatically set from the point of usage of ThreadWait onward.

Example

See the ThreadCreate examples.

Dialect Differences

  • Threading is not allowed in the -lang qb dialect.

Platform Differences

  • ThreadWait is not available with the DOS version of FreeBASIC, because multithreading is not supported by DOS kernel nor the used extender.

Differences from QB

  • New to FreeBASIC

See also

  • ThreadCreate
  • ThreadDetach


Time
Returns the current system time as a string

Syntax

Declare Function Time ( ) As String

Usage

result = Time[$]

Return Value

Returns the current system time.

Description

Returns the current system time in the format hh:mm:ss.

Example

Print "the current time is: "; Time

Differences from QB

  • The QB TIME statement (to set the system time) is now called SetTime.
  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

See also

  • Date
  • Timer


Timer
Returns the amount of time that has passed since a static reference point.

Syntax

Declare Function Timer ( ) As Double

Usage

result = Timer

Return Value

Returns a Double precision result with the time, in seconds, since a static reference point.

Description

The Timer function is useful for finding out how long a section of code takes to run, or for control the timing of your code. To find out how much time has passed between two points in your program, you can record the value of Timer at the start and end points, and then subtract the start value from the end value.

On some platforms, the value of Timer resets to zero at midnight (see below), so if the start and end time are on either side of the reset point, the difference will be negative. This could cause unexpected behavior in some programs. In those cases, adding 86400 (the number of seconds in 24 hours) to the difference should return the correct result. If the time taken is longer than a day, then it will be also be necessary to check the number of days that have elapsed.

The value returned by Timer is NOT affected by the automatic changing of the system clock, in Spring and Autumn, for DST (Daylight Savings Time).

Example

'' Example of using TIMER function
'' Note: see text about correct waiting strategies
Dim Start As Double
Print "Wait 2.5 seconds."
Start = Timer
Do
    Sleep 1, 1
Loop Until (Timer - Start) > 2.5
Print "Done."

Platform Differences

  • On Win32 and Linux, if the program must wait for periods of 0.1 seconds or more, Sleep should be used, this allows other programs to run during the waiting period. For shorter delays, a loop using TIMER can be more precise.
  • The reference point chosen varies, depending on the platform. On Windows, the time is measured relative to the point the computer was booted up. On DOS, the time is measured relative to Jan 1 1970.

Note for DOS users: today, the number of seconds since 1970 is in excess of 10^9, and is therefore unsuitable for storing in Single-precision variables, also it shouldn't be multiplied (to get 1/10 seconds or so) and stored in 32-bit integer variables then

  • The precision of TIMER varies, depending on the computer used. If the processor has a precision timer (as the Performance Counter Pentium processors from Intel have) and the OS uses it, the precision is linked to the processor clock and microseconds can be expected. With older processors (386, 486), and always in DOS, the resolution is 1/18 second.

  • Usage of TIMER can cause disk accesses in DOS, see forum for analysis and solutions

Differences from QB

  • In QB, TIMER returned the number of seconds from last midnight, and its accuracy was 1/18 secs

See also

  • Time
  • Sleep


TimeSerial
Gets a Date Serial for the specified hours, minutes, and seconds

Syntax

Declare Function TimeSerial ( ByVal hour As Long, ByVal minute As Long, ByVal second As Long ) As Double

Usage

#include "vbcompat.bi"
result = TimeSerial( hours, minutes, seconds )

Parameters

hour
number of hours, in the range 0-23
minute
number of minutes
second
number of seconds

Return Value

Returns a date serial containing the time formed by the values in the hours, minutes and seconds parameters.The date serial returned has no integer part.

Description

hours must be specified in the range 0-23

The compiler will not recognize this function unless vbcompat.bi or datetime.bi is included.

Example

#include "vbcompat.bi"

Dim ds As Double = DateSerial(2005, 11, 28) + TimeSerial(7, 30, 50)

Print Format(ds, "yyyy/mm/dd hh:mm:ss")

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials
  • DateSerial
  • TimeValue
  • DateValue


TimeValue
Gets a Date Serial from a time string

Syntax

Declare Function TimeValue ( ByRef timestring As String ) As Double

Usage

#include "vbcompat.bi"
result = TimeValue( timestring )

Parameters

timestring
the string to convert

Return Value

Returns a Date Serial from a time string.

Description

The time string must be in the format "23:59:59" or "11:59:59PM"

The compiler will not recognize this function unless vbcompat.bi or datetime.bi is included.

Example

#include "vbcompat.bi"

Dim ds As Double = TimeValue("07:12:28AM")

Print Format(ds, "hh:mm:ss")

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials
  • DateSerial
  • TimeValue
  • DateValue


To
Statement modifier to specify a range.

Syntax

For iterator intial_value To ending_value
statement(s).
Next [ iterator ]
or
Select Case case_comparison_value
Case lower_bound To upper_bound
statement(s).
End Select
or
Dim variable_identifier( lower_bound To upper_bound ) As type_specifier

Description

The To keyword is used to define a certain numerical range. This keyword is valid only if used with For ... Next, Case and Dim statements.

In the first syntax, the To keyword defines the initial and ending values of the iterator in a For statement.

In the second syntax, the To keyword defines lower and upper bounds for Case comparisons.

In the third syntax, the To keyword defines the array bounds in a Dim statement

For more information, see For...Next, Dim and Select Case.

Example

'' this program uses bound variables along with the TO keyword to create an array, store random
'' temperatures inside the array, and to determine output based upon the value of the temperatures
Randomize Timer

'' define minimum and maximum number of temperatures we will create
Const minimum_temp_count As Integer = 1
Const maximum_temp_count As Integer = 10

'' define the range of temperatures zones in which bacteria breed rapidly (in degrees)
Const min_low_danger As Integer = 40
Const max_low_danger As Integer = 69
Const min_medium_danger As Integer = 70
Const max_medium_danger As Integer = 99
Const min_high_danger As Integer = 100
Const max_high_danger As Integer = 130

'' define array to hold temperatures using our min/max temp count bounds
Dim As Integer array( minimum_temp_count To maximum_temp_count )

'' declare a for loop that iterates from minimum to maximum temp count
Dim As Integer it
For it = minimum_temp_count To maximum_temp_count

   array( it ) = Int( Rnd( 1 ) * 200 ) + 1

   '' display a message based on temperature using our min/max danger zone bounds
   Select Case array( it )
      Case min_low_danger To max_low_danger
         Color 11
         Print "Temperature" ; it ; " is in the low danger zone at" ; array( it ) ; " degrees!"
      Case min_medium_danger To max_medium_danger
         Color 14
         Print "Temperature" ; it ; " is in the medium danger zone at" ; array( it ) ; " degrees!"
      Case min_high_danger To max_high_danger
         Color 12
         Print "Temperature" ; it ; " is in the high danger zone at" ; array( it ) ; " degrees!"
      Case Else
         Color 3
         Print "Temperature" ; it ; " is safe at" ; array( it ) ; " degrees."
   End Select

Next it

Sleep

Differences from QB

  • none

See also

  • For...Next
  • Dim
  • Select Case


TONE
Plays a generated tone on a selected channel.

Syntax

Tone channel, frequency, duration

Description

TONE is the channel-first form of generated tone playback. It does the same basic job as SOUND, but it makes the channel choice part of the command every time.

Channels are useful in games and music because they let you separate different sound jobs. For example, channel 0 might be a melody, channel 1 might be effects, and channel 2 might be a warning beep. Other commands such as PAN and VOLUME can then target those channels.

The frequency controls pitch and the duration controls how long the tone lasts.

Example

tone 0, 440, 0.20
tone 1, 660, 0.20
sleep 300, 1

Notes

  • Channels are zero-based.
  • Use CHANNEL to set the default channel for generated commands that do not take a channel argument.

See also

  • Sound Library Reference
  • Generated Sound Commands


Trans
Parameter to the Put graphics statement which selects transparent background as the blitting method

Syntax

Put [ target, ] [ STEP ] ( x,y ), source [ ,( x1,y1 )-( x2,y2 ) ], Trans

Parameters

Trans
Required.

Description

Trans selects transparent background as the method for blitting an image buffer. This is similar to the PSET method, but pixels containing the mask color are skipped.
For 8-bit color images, the mask color is palette index 0. For 16/32-bit color images, the mask color is Magenta, which is RGB(255, 0, 255). The alpha value is ignored when checking for the mask color in 32-bit images.

Note: for 32-bit images, the alpha value of pixels may be changed to 0. This is for efficiency reasons. To preserve the alpha values, a custom blender may be used, as in the second example below.

Example

'' set up a screen: 320 * 200, 16 bits per pixel
ScreenRes 320, 200, 16

'' set up an image with the mask color as the background.
Dim img As Any Ptr = ImageCreate( 32, 32, RGB(255, 0, 255) )
Circle img, (16, 16), 15, RGB(255, 255, 0),     ,     , 1, f
Circle img, (10, 10), 3,  RGB(  0,   0, 0),     ,     , 2, f
Circle img, (23, 10), 3,  RGB(  0,   0, 0),     ,     , 2, f
Circle img, (16, 18), 10, RGB(  0,   0, 0), 3.14, 6.28

'' Put the image with PSET (gives the exact contents of the image buffer)
Draw String (110, 50 - 4), "Image put with PSET"
Put (60 - 16, 50 - 16), img, PSet

'' Put the image with TRANS
Draw String (110, 150 - 4), "Image put with TRANS"
Put (60 - 16, 150 - 16), img, Trans

'' free the image memory
ImageDestroy img

'' wait for a keypress
Sleep

Function trans32 ( ByVal source_pixel As ULong, ByVal destination_pixel As ULong, ByVal parameter As Any Ptr ) As ULong
    '' returns the source pixel
    '' unless it is &hff00ff (magenta), then return the destination pixel
    If (source_pixel And &hffffff) <> &hff00ff Then
        Return source_pixel
    Else
        Return destination_pixel
    End If
End Function

'' set up a screen: 320 * 200, 16 bits per pixel
ScreenRes 320, 200, 32

'' set up an image with the mask color as the background.
Dim img As Any Ptr = ImageCreate( 32, 32, RGB(255, 0, 255) )
Circle img, (16, 16), 15, RGB(255, 255, 0),     ,     , 1, f
Circle img, (10, 10), 3,  RGB(  0,   0, 0),     ,     , 2, f
Circle img, (23, 10), 3,  RGB(  0,   0, 0),     ,     , 2, f
Circle img, (16, 18), 10, RGB(  0,   0, 0), 3.14, 6.28

'' Put the image with PSET (gives the exact contents of the image buffer)
Draw String (110, 50 - 4), "Image put with PSET"
Put (60 - 16, 50 - 16), img, PSet

'' Put the image with TRANS
Draw String (110, 100 - 4), "Image put with TRANS"
Put (60 - 16, 100 - 16), img, Trans

'' Put the image with TRANS
Draw String (110, 150 - 4), "Image put with trans32"
Put (60 - 16, 150 - 16), img, Custom, @trans32

'' free the image memory
ImageDestroy img

'' wait for a keypress
Sleep

Differences from QB

  • New to FreeBASIC

See also

  • Put (Graphics)
  • Custom


Trim
Removes surrounding substrings or characters on the left and right side of a string

Syntax

Declare Function Trim ( ByRef str As Const String, [ Any ] ByRef trimset As Const String = " " ) As String
Declare Function Trim ( ByRef str As Const WString, [ Any ] ByRef trimset As Const WString = WStr(" ") ) As WString

Usage

result = Trim[$]( str [, [ Any ] trimset ] )

Parameters

str
The source string.
trimset
The substring to trim.

Return Value

Returns the trimmed string.

Description

This procedure trims surrounding characters from the left (beginning) and right (end) of a source string:
    • Substrings matching trimset will be trimmed if specified, otherwise spaces (ASCII code 32) are trimmed.
    • If the Any keyword is used, any character matching a character in trimset will be trimmed.
All comparisons are case-sensitive.

Example

Dim s1 As String = " ... Stuck in the middle ... "
Print "'" + Trim(s1) + "'"
Print "'" + Trim(s1, Any " .") + "'"

Dim s2 As String = "BaaBaaaaB With You aaBBaaBaa"
Print "'" + Trim(s2, "Baa") + "'"
Print "'" + Trim(s2, Any "Ba") + "'"

will produce the output:

'... Stuck in the middle ...'
'Stuck in the middle'
'aaB With You aaB'
' With You '

Platform Differences

  • DOS version/target of FreeBASIC does not support the wide-character version of Trim.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Trim.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • New to FreeBASIC

See also

  • LTrim
  • RTrim


True
Intrinsic constant set by the compiler

Syntax

Const True As Boolean

Description

Gives the True Boolean value where used.

Example

Dim b As Boolean = True
If b Then
    Print "b is True"
Else
    Print "b is False"
End If

b is True

Version

  • Since fbc 1.04.0

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __True.

Differences from QB

  • New to FreeBASIC

See also

  • False
  • Boolean


Type (Alias)
Declares an alternative name for a type

Syntax

Type typename As symbol

Parameters

typename
new alternative name.
symbol
symbol or data type declaration to associate with typename.

Description

symbol may refer to any declared data type including a built-in data type, Sub or Function pointer, Type declaration, Union declaration, or Enum declaration.

A Type Alias can be used to allow forward declarations of parameters in procedure declarations, but only used with pointers (whatever their passing mode), or otherwise with parameters (excluding arrays) but passed or returned only by reference. Generally the bodies of such procedures need to be implemented further in the code once the actual types are well defined (for example because of the passing of such a reference; or when such a pointer passed is then dereferenced in any form).
A Type Alias can also be used to allow forward declarations of data fields in User Defined Types, but only used with pointers.

A type alias must be used to allow declaration of pointer to a function pointer, otherwise ptr applies on return type and not on function.

A Type Alias declaration can also be nested inside a UDT structure declaration for a Type or a Union.

Example

Type ParentFwd As Parent
Type Child
    Name As ZString * 32
    ParentRef As ParentFwd Ptr
    ''...
End Type

Type Parent
    Name As ZString * 32
    ChildList(0 To 9) As Child
    ''...
End Type

Dim p As Parent
p.Name = "Foo"
With p.ChildList(0)
    .Name = "Jr."
    .ParentRef = @p
    '' ...
End With

With p.ChildList(0)
    Print .Name; " is child of "; .parentRef->Name
End With

Function triple (ByVal i As Integer) As Integer
    Return 3 * i
End Function

Type As Function (ByVal As Integer) As Integer function_alias

'Dim As Function (Byval As Integer) As Integer f  ''this syntax works but is less readable than the next code line
Dim As function_alias f
f = @triple
Print f(123)

'Dim As Function (Byval As Integer) As Integer Ptr pf  ''this syntax does not work because Ptr applies on Integer and not on function
Dim As function_alias Ptr pf                           ''this syntax works
pf = @f
Print (*pf)(123)  ''the dereferenced pointer to procedure pointer must be enclosed in parentheses

Version

  • Since fbc 1.10.0: Nesting of a Type Alias declaration inside a structure declaring a UDT (Type or Union) is allowed.

Differences from QB

  • New to FreeBASIC

See also

  • Type...End Type
  • Type (Temporary)


Type (Temporary)
Creates a temporary copy of a user defined type

Syntax

result = Type( initializers, ... )
or
result = Type<typename>( initializers, ... )

Parameters

initializers
Initial values for the type (or only the firsts)
typename
The name of the Type or Union

Return Value

A temporary copy of the type.

Description

Used to create a temporary type. If typename is not explicitly given, it will be inferred from its usage if possible. Usage of the temporary copy may include assigning it to a variable, passing it as a parameter to a procedure, or returning it as a value from a procedure.

For a type without own or inherited constructor (excluding also any type that is directly or indirectly derived from Object), the temporary type syntax is allowed if all type data-fields (including those inherited) are numeric primitives only and without any default initializers.
If at same time the type is without destructor, the compiler does a direct assignment instead of using a temporary copy.

The Constructor for the type, if there is one with parameters matching with the initializers provided, will be called when the temporary copy is created, and the Destructor for the type, if there is one, will be called immediately after its use. But when there is a matching constructor, the temporary type expression may be simply replaced by typename( initializers, ... ).
If there is a constructor at least but none which matches with the initializers, the temporary type syntax is obviously disallowed.

It can create not only a temporary copy of an user defined type, but also a temporary copy of predefined data-type as a variable-length string or any numeric data-type (all standard data-types excluding fixed-length strings).

It can also be used as an even shorter shortcut than With (see below) if you are changing all the data-fields (or the n firsts only).

A temporary object is destroyed at the end of execution of the statement (where it's defined), but its corresponding allocated memory is not released and remains available (unused) until going out the scope where statement is.
In the particular case where a temporary type is used in a With expression, the destruction of the temporary type is deferred to the end of the With scope.

Note: Static qualifier used at procedure definition level does not apply to temporary types.

Example

Type Example
    As Integer field1
    As Integer field2
End Type

Dim myexample As Example

'' Filling the type by setting each field
myexample.field1 = 1
myexample.field2 = 2

'' Filling the type by setting each field using WITH
With myexample
    .field1 = 1
    .field2 = 2
End With

'' Fill the variable's fields with a  temporary type
myexample = Type( 1, 2 )

'' Passing a user-defined types to a procedure using a temporary type
'' where the type can be inferred.

Type S
  As Single x, y
End Type

Sub test ( v As S )
  Print "S", v.x, v.y
End Sub

test( Type( 1, 2 ) )

'' Passing a user-defined type to a procedure using temporary types
'' where the type is ambiguous and the name of the type must be specified.

Type S
  As Single x, y
End Type

Type T
  As Integer x, y
End Type

Union U
  As Integer x, y
End Union

'' Overloaded procedure test()
Sub test Overload ( v As S )
  Print "S", v.x, v.y
End Sub

Sub test ( v As T )
  Print "T", v.x, v.y
End Sub

Sub test ( v As U )
  Print "U", v.x, v.y
End Sub

'' Won't work: ambiguous
'' test( type( 1, 2 ) )

'' Specify name of type instead
test( Type<S>( 1, 2 ) )
test( Type<T>( 1, 2 ) )
test( Type<U>( 1 ) )

Differences from QB

  • New to FreeBASIC

See also

  • Type...End Type
  • Type (Alias)


Type (Udt)
Declares a user-defined type.

Syntax

Type typename
fieldname1 As DataType
fieldname2 As DataType
As DataType fieldname3, fieldname4
...
End Type

Type typename [Alias "alternatename"] [Extends base_typename] [Field = alignment]
[Private:|Public:|Protected:]

Declare Sub|Function|Constructor|Destructor|Property|Operator ...
Static variablename As DataType
ReDim arrayname(array dimensions) As DataType

fieldname As DataType [= initializer]
fieldname(array dimensions) As DataType [= initializer]
fieldname(Any [, Any...]) As DataType
fieldname : bits As DataType [= initializer]

As DataType fieldname [= initializer], ...
As DataType fieldname(array dimensions) [= initializer], ...
As DataType fieldname(Any [, Any...])
As DataType fieldname : bits [= initializer], ...

Union
fieldname As DataType
Type
fieldname As DataType
...
End Type
...
End Union

Type typename2 ...
...
End Type

...
End Type

Description

Type is used to declare custom data types containing one or more data fields, including integer types, floating point types, fixed-size or variable-length (dynamic) arrays, fixed-size or variable-length strings, bitfields, or other user-defined types.

Types support various functionality related to object-oriented programming:
    • Inheritance through the use of the Extends keyword.
    • Member procedures such as Subs or Functions, including Abstract or Virtual ones.
    • Member procedures with special semantic meaning such as Constructors or a Destructor.
    • Static member variables.
    • Member visibility specifiers: Public:, Private:, Protected:.

A Type can also contain nested Types or Unions, of different kinds:
    • Nested Anonymous Type/Union:
- Nested Anonymous Type/Union allows data members to be grouped as desired.
- Anonymous Type or Anonymous Union can be nested on condition of alternating their nesting.
- Nested Anonymous Type/Union can not have procedure members or static data members (same restriction than for a local scope named Type/Union).
    • Nested Named Type/Union:
- Nested Named Type/Union allows declaration of an inner structure inside a (named) Type/Union namespace and according to the access right of the place
- Mostly everything it can be done in a Named Type/Union can also be done in a Nested Named Type/Union.
- When there are circular dependencies between Types/Unions, Nested Named Type/Union usage can avoid using type aliases and forward referencing.
    • Nested Type-Def:
- Nested Type-Def allows declaration of an inner Type (Alias) inside a Type/Union and according to the access right of the place.
The main structure (Type/Union) must be always named, the other (nested) structures can be anonymous or named.

Alias "alternatename" specifies that if typename must be encoded (mangled) in to a public symbol (as in an object module or library), then specifically use alternate name instead of the usual encoding (mangling) of typename.

Memory layout
Types lay out their fields consecutively in memory, following the native alignment and padding rules (described on the Field page). Special care must be taken when using Types for file I/O or interacting with other programs or programming languages, in case the alignment and padding rules are different. The optional Field = number specifier can be used to change the behavior on the FreeBASIC side.

Variable-length data
In FreeBASIC, Type data structures must ultimately be fixed-size, such that the compiler knows how much memory to allocate for objects of that Type. Nevertheless, Types may contain variable-length (dynamic) string or array data members. However, the string's/array's data will not be embedded in the Type directly. Instead, the Type will only contain a String/array descriptor structure, which FreeBASIC uses behind the scenes to manage the variable-length string/array data. For sizing the structure of the array descriptor in the Type, a variable-length (dynamic) array data member must be always declared by using Any(S) in place of the array bounds, in order to fix the amount of dimensions based on the number of Anys specified. A variable-length (dynamic) array data member can also be pre-sized in its declaration by using syntax with ReDim.
Variable-length array fields are considered as pseudo-objects when they are declared in a Type, just like variable-length strings (the implicit copy constructor and the implicit let operator themselves support [re]sizing and copying such arrays, or their erasing).

Because of that, saving such a Type into a file will write out the descriptor, not the actual string/array data. In order to embed strings/arrays into Types directly, fixed-length strings/arrays must be used.

Similarly, when maintaining dynamic data manually through the use of pointers within a Type, it does usually not make sense to save the Type to a file, because the address stored in the pointer field will be written to file, not the actual memory it points to. Addresses are meaningful to a specific process only though, and cannot be shared that way.

Special note on fixed-length strings
Since fbc version 1.20.0, fixed-length string fields of String * N type no longer have extra null terminator at their end, making them compatible with QB strings inside Types, because they actually use just N bytes.
Before fbc version 1.20.0, fixed-length string fields of String * N type had an extra null terminator at their end, for compatibility with C strings, making them incompatible with QB strings inside Types, because they actually used up N+1 bytes, instead of just N bytes. A possible work-around was to declare the field As String * (N-1), though this will not work in future releases if the null terminator is removed. Another alternative was to use a Byte or UByte array with the proper size.

Note on bitfields ( fieldname : bits )
Bitfields can only be declared inside a type or a union, and allow to specify some very small objects of a given number of bits in length. Each field is accessed and manipulated as if it were an ordinary member of the structure.
Only integer data-types (up to 32-bit for 32-bit development or 64-bit for 64-bit development) are valid. The sizes of the declared data-types, large enough to contain the bit patterns, affect how the bitfields are placed in memory.
Bitfield members in a type are packed together, unless the next member is a non-bitfield (nested union is considered a non-bitfield).
A bitfield does not have any address (one cannot get a pointer to it and its offset inside the structure).

Example

This is an example of a QB-style type, not including procedure definitions
Type clr
    red As UByte
    green As UByte
    blue As UByte
End Type

Dim c As clr
c.red = 255
c.green = 128
c.blue = 64

/'
Alternatively (See 'Type (Temporary)' page) we could use
Dim c As clr
c = Type <clr> (255, 128, 64)
or
c = Type(255, 128, 64)  '' implicit typename
'/

And this is an example of a type working as an object:
'' Example showing the problems with fixed length string fields in UDTs and fbc version < 1.20.0
'' Suppose we have read a GIF header from a file
''                        signature         width        height
Dim As ZString*(10+1) z => "GIF89a" + MKShort(10) + MKShort(11)

Print "Using fixed-length string"

Type hdr1 Field = 1
   #if __FB_VERSION__ < "1.20.0"
      As String*(6-1) sig /' We have to dimension the string with 1 char
         '  less to avoid misalignments '/
   #else
      As String*(6) sig
   #endif
   As UShort wid, hei
End Type

Dim As hdr1 Ptr h1 = CPtr(hdr1 Ptr, @z)
Print h1->sig, h1->wid, h1->hei '' Prints GIF89 (misses a char!)  10  11

'' We can do comparisons only with the 5 visible chars and creating a temporary string with LEFT

If Left(h1->sig, 5) = "GIF89" Then Print "ok" Else Print "error"

'' Using a ubyte array, we need an auxiliary function to convert it to a string
Function ub2str( ub() As UByte ) As String
    Dim As String res = Space(UBound(ub) - LBound(ub) + 1)
    For i As Integer = LBound(ub) To UBound(ub)
        res[i - LBound(ub)] = ub(i)
    Next
    Function = res
End Function

Print
Print "Using an array of ubytes"

Type hdr2 Field = 1
   sig(0 To 6-1) As UByte '' Dimension 6
   As UShort wid, hei
End Type

Dim As hdr2 Ptr h2 = CPtr(hdr2 Ptr, @z)
'' Viewing and comparing is correct but a conversion to string is required

Print ub2str(h2->sig()), h2->wid, h2->hei '' Prints GIF89a  10  11 (ok)
If ub2str(h2->sig()) = "GIF89a" Then Print "ok" Else Print "error" '' Prints ok

This is an example of conversion from an Ubyte to a digit string in base 8 (octal string), by using bitfields in a local UDT (conversion equivalent to 'Oct(x, 3)'):
Function UbyteToOctalString (ByVal b As UByte) As String

    Union UbyteOctal
        number As UByte
        Type
            d0 : 3 As UByte
            d1 : 3 As UByte
            d2 : 2 As UByte
        End Type
    End Union

    Dim uo As UbyteOctal
    uo.number = b
    Return uo.d2 & uo.d1 & uo.d0

End Function

For I As Integer = 0 To 255
    Print Using "###: "; I;
''    Print Oct(I, 3),
    Print UbyteToOctalString(I),  '' this line is thus equivalent to the previous one
Next I
Print

Sleep

This is an example with Nested Named Type:
Type Parent
    Private:
        Dim As String nameParent
        Declare Constructor()
        Declare Constructor(ByRef As Parent)
        Type Child
            Dim As String nameChild
            Dim As Parent Ptr ptrParent
            Declare Sub kinship()
        End Type
        Dim As Child listChild(Any)
    Public:
        Declare Constructor(ByRef _nameParent As String)
        Declare Sub addChild(ByRef _nameChild As String)
        Declare Sub kinship()
End Type

Constructor Parent(ByRef _nameParent As String)
    This.nameParent = _nameParent
End Constructor

Sub Parent.addChild(ByRef _nameChild As String)
    ReDim Preserve This.listChild(UBound(This.listChild) + 1)
    This.listChild(UBound(This.listChild)).nameChild = _nameChild
    This.listChild(UBound(This.listChild)).ptrParent = @This
End Sub

Sub Parent.Child.kinship()
    Print "'" & This.nameChild & "'" & " is child of " & "'" & This.ptrParent->nameParent & "'"
End Sub

Sub Parent.kinship()
    For i As Integer = 0 To UBound(This.listChild)
        This.listChild(i).kinship()
    Next i
End Sub

Dim As Parent p = Parent("Kennedy")
p.addChild("John Jr.")
p.addChild("Caroline")
p.addChild("Patrick")
p.kinship()

Sleep

Version

  • Since fbc 1.10.0: Nested Named Type/Union capability added.

Platform Differences

  • The default Field alignment parameter is 4 bytes for DOS and Linux targets.
  • The default Field alignment parameter is 8 bytes for Windows targets (this difference with regard to 4 bytes applies only to Longint and Double members).

Dialect Differences

  • Object-related features such as functions declared inside Type blocks are supported only with the -lang fb dialect since version 0.17b
  • In the -lang fb and -lang fblite dialects, the default Field alignment parameter depends on the target platform.
  • With the -lang qb dialect the fields are aligned to byte boundaries by default, unless otherwise specified.
  • To force byte alignment use FIELD=1.

Differences from QB

  • Since fbc version 1.20.0, fixed-length strings of STRING*N type have no extra, redundant character on the end, which means they take same number of bytes than they do in QB.
  • Before fbc version 1.20.0, all fixed-length strings have an extra, redundant character on the end, which means they take up one more byte than they do in QB. For this reason, UDTs that use them are not compatible with QB when used for file I/O.

See also

  • Type (Alias)
  • Type (Temporary)
  • Union
  • Enum
  • TypeOf
  • OffsetOf
  • Alias (Name)
  • Field
  • Extends
  • Extends Zstring
  • Extends Wstring
  • With
  • Standard Data Type Limits
  • Coercion and Conversion


TypeOf
Returns the type of a variable.

Syntax

TypeOf ( variable | datatype )

Parameters

variable
A variable of any type.
datatype
A DataType.

Description

TypeOf is a compiler intrinsic that replaces itself with the type of the variable passed to it. It can either be used in a variable declaration (Example 1) or it can be used in the preprocessor for comparison, printing. (Example 2)

TypeOf also supports passing any intrinsic data type, or user-defined type (and its data fields), not only variables defined as those types. Also supported are expressions, the type is inferred from the expression (much like Var).

If there is both a user defined type and a variable visible with the same name in the current scope, the user defined type takes precedence over the variable. To ensure that the TypeOf takes the variable instead of the user defined type, wrap the argument to TypeOf with parentheses to force it to be seen as an expression. For example Typeof((variable)).

Example

Example 1:
Dim As Integer foo
Dim As TypeOf(67.2) bar '' '67.2' is a literal double
Dim As TypeOf( foo + bar ) teh_double '' double + integer results in double
Print SizeOf(teh_double)

Example 2:
Dim As String foo
#print TypeOf(foo)
#if TypeOf(foo) = TypeOf(Integer)
  #print "Never happened!"
#endif

#if TypeOf(foo) = TypeOf(String)
  #print "It's a String!"
#endif

Version

  • Before fbc 1.10.0, TypeOf was not returning the type of the non-static member procedures of a UDT.
  • Before fbc 1.08.0:
TypeOf was not returning the type of the data fields of a UDT.
When a variable from a given namespace was accessed with the namespace's name prefix, the argument to TypeOf had to be wrapped with parentheses to force it to be seen as an expression. For example Typeof((namespace_name.variable)).

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Typeof.

Differences from QB

  • New to FreeBASIC

See also

  • SizeOf
  • Var
  • Type (Alias)
  • Type...End Type


UBound
Returns the upper bound of an array's dimension

Syntax

Declare Function UBound ( array() As Any, ByVal dimension As Integer = 1 ) As Integer

Usage

result = UBound( array [, dimension ] )

Parameters

array
an array of any type
dimension
the dimension to get upper bound of

Return Value

Returns the upper bound of an array's dimension.

Description

UBound returns the largest value that can be used as an index into a particular dimension of an array.

Array dimensions are numbered from one (1) to n, where n is the total number of dimensions. If dimension is not specified, UBound will return the upper bound of the first dimension.

If dimension is zero (0), UBound returns n, the number of dimensions in the array. For any other dimension values outside of the valid range 1..n, the result is -1. This can be used to detect the number of dimensions of variable-length arrays, and in combination with the result of Lbound() for such cases, whether a given dimension exists, or whether the array is empty (zero dimensions). See the LBound page for more information.

Example

Dim array(-10 To 10, 5 To 15, 1 To 2) As Integer

Print UBound(array) 'returns 10
Print UBound(array, 2) 'returns 15
Print UBound(array, 3) 'returns 2

'' determining the size of an array
Dim As Short array(0 To 9)
Dim As Integer arraylen, arraysize

arraylen = UBound(array) - LBound(array) + 1
arraysize = arraylen * SizeOf( Short )

Print "Number of elements in array:", arraylen    '10
Print "Number of bytes used in array:", arraysize '10 * 2 = 20

'' determining the size of a multi-dimensional array
Dim As Long array4D(1 To 2, 1 To 3, 1 To 4, 1 To 5)
Dim As Integer arraylen, arraysize

arraylen = (UBound(array4D, 4) - LBound(array4D, 4) + 1) _
         * (UBound(array4D, 3) - LBound(array4D, 3) + 1) _
         * (UBound(array4D, 2) - LBound(array4D, 2) + 1) _
         * (UBound(array4D, 1) - LBound(array4D, 1) + 1)

arraysize = arraylen * SizeOf( Long )

Print "Number of elements in array:", arraylen    '2 * 3 * 4 * 5 = 120
Print "Number of bytes used in array:", arraysize '120 * 4 = 480

'' determining whether an array is empty
Dim array() As Integer

Print "lbound: "; LBound( array ), "ubound: "; UBound( array )  ''  0 and -1

If LBound( array ) > UBound( array ) Then
    Print "array is empty"
Else
    Print "array is not empty"
End If

Sub printArrayDimensions( array() As Integer )
    Print "dimensions: " & UBound( array, 0 )

    '' For each dimension...
    For d As Integer = LBound( array, 0 ) To UBound( array, 0 )
        Print "dimension " & d & ": " & LBound( array, d ) & " to " & UBound( array, d )
    Next
End Sub

Dim array() As Integer
printArrayDimensions( array() )

Print "---"

ReDim array(10 To 11, 20 To 22)
printArrayDimensions( array() )

See also

  • LBound
  • Static
  • Dim
  • ReDim
  • SizeOf


UByte
Standard data type: 8 bit unsigned.
Equivalent to Unsigned Byte.

Syntax

Dim variable As UByte

Description

8-bit unsigned whole-number data type. Can hold a value in the range of 0 to 255.

Example

Dim ubytevar As UByte
ubytevar = 200
Print "ubytevar= ", ubytevar

Example

  Dim x As UByte = 0
  Dim y As UByte = &HFF
  Print "UByte Range = "; x; " to "; y

Output:
UByte Range = 0 to 255

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Ubyte.

Differences from QB

  • New to FreeBASIC

See also

  • Byte
  • CUByte
  • Table with variable types overview, limits and suffixes


UCase
Returns an upper case copy of a string

Syntax

Declare Function UCase ( ByRef str As Const String, ByVal mode As Long = 0 ) As String
Declare Function UCase ( ByRef str As Const WString, ByVal mode As Long = 0 ) As WString

Usage

result = UCase[$]( str [ , mode ] )

Parameters

str
String to convert to uppercase.
mode
The conversion mode: 0 = current locale, 1 = ASCII only

Return Value

Uppercase copy of str.

Description

Returns a copy of str with all of the letters converted to upper case.

If str is empty, the null string ("") is returned.

Example

Print UCase("AbCdEfG")

will produce the output:

ABCDEFG

Platform Differences

  • The wide-character string version of UCase is not supported for DOS target.

Dialect Differences

  • The string type suffix "$" is required in the -lang qb dialect.
  • The string type suffix "$" is optional in the -lang fblite dialect.
  • The string type suffix "$" is ignored in the -lang fb dialect, warn only with the -w suffix compile option (or -w pedantic compile option).

Differences from QB

  • QB does not support Unicode.

See also

  • LCase


UInteger
Standard data type: 32-bit or 64-bit unsigned, same size as SizeOf(Any Ptr).
Equivalent to Unsigned Integer.

Syntax

Dim variable As UInteger
Dim variable As UInteger<bits>

Parameters

bits
A numeric constant expression indicating the size in bits of unsigned integer desired. The values allowed are 8, 16, 32 or 64.

Description

32-bit or 64-bit unsigned whole-number data type, depending on the platform.

If an explicit bit size is given, a data type is provided that can hold values from 0 up to (1ULL Shl (bits)) - 1. The selected data type is UByte for UInteger<8>, UShort for UInteger<16>, ULong for UInteger<32> and ULongInt for UInteger<64>.

Example

#ifdef __FB_64BIT__
    Dim x As UInteger = 0
    Dim y As UInteger = &HFFFFFFFFFFFFFFFF
    Print "UInteger Range = "; x; " to "; y
#else
    Dim x As UInteger = 0
    Dim y As UInteger = &HFFFFFFFF
    Print "UInteger Range = "; x; " to "; y
#endif

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Uinteger.

Differences from QB

  • New to FreeBASIC

See also

  • Integer
  • Unsigned
  • CUInt
  • Table with variable types overview, limits and suffixes


ULong
Standard data type: 32-bit unsigned integer.
Equivalent to Unsigned Long.

Syntax

Dim variable As ULong

Description

32-bit unsigned whole-number data type. Can hold values from 0 to 4294967295. Corresponds to an unsigned DWORD.

Example

  Dim x As ULong = 0
  Dim y As ULong = &HFFFFFFFF
  Print "ULong Range = "; x; " to "; y

Output:
ULong Range = 0 to 4294967295

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Ulong.

Differences from QB

  • New to FreeBASIC

See also

  • Long
  • UInteger
  • ULongInt
  • Table with variable types overview, limits and suffixes


ULongInt
Standard data type: 64 bit unsigned.
Equivalent to Unsigned LongInt.

Syntax

Dim variable As ULongInt

Description

A 64-bit unsigned whole-number data type. Can hold values from 0 to 18 446 744 073 709 551 615. Corresponds to an unsigned QWORD.

Example

Dim x As ULongInt = 0
Dim y As ULongInt = &HFFFFFFFFFFFFFFFFull
Print "ULongInt Range = "; x; " to "; y

Output:
ULongInt Range = 0 to 18446744073709551615

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Ulongint.

Differences from QB

  • New to FreeBASIC

See also

  • LongInt
  • CULngInt
  • Table with variable types overview, limits and suffixes


Union
Declares a union user defined type.

Syntax

Union typename
fieldname as datatype
Declare member function declaration ...
...
End Union

Parameters

typename
Name of the Union
fieldname
Name of a data field member
member function declaration
Any of the supported member functions

Description

The data elements of a Union occupy a common space in memory (same memory address for all data elements of the Union). A data element of a Union can be a simple data field or an anonymous Type block of data fields.
The size of the Union is the size of the largest data element.
Since they occupy a common memory space, only one data element can usually be used at a given time (when a given data element is written, the other data elements sharing the common union space may be overwritten or mutilated).

Like Type, Union can use the optional Field = number specifier for field alignment, and supports also inheritance through the use of the Extends keyword.
Unlike Type, Union can not contain variable-length strings and arrays, and more generally can not have object fields (or bases) with constructors or destructors. Therefore, Union does not support to inherit from the Object built-in type.

A Union supports member procedures including Constructor, Destructor, Function, Operator, Property and Sub. All members of a Union are public and access modifiers are not supported.

Initializer elements for UNION's should pair with only the first member of the UNION and skip the rest.

A Union can also contain nested Types or Unions, of different kinds:
    • Nested Anonymous Type/Union:
- Nested Anonymous Type/Union allows data members to be grouped as desired.
- Anonymous Type or Anonymous Union can be nested on condition of alternating their nesting.
- Nested Anonymous Type/Union can not have procedure members or static data members (same restriction than for a local scope named Type/Union).
    • Nested Named Type/Union:
- Nested Named Type/Union allows declaration of an inner structure inside a (named) Type/Union namespace and according to the access right of the place
- Mostly everything it can be done in a Named Type/Union can also be done in a Nested Named Type/Union.
- When there are circular dependencies between Types/Unions, Nested Named Type/Union usage can avoid using type aliases and forward referencing.
    • Nested Type-Def:
- Nested Type-Def allows declaration of an inner Type (Alias) inside a Type/Union and according to the access right of the place.
The main structure (Type/Union) must be always named, the other (nested) structures can be anonymous or named.

A Union can be passed in a similar way of a User Defined Type to overloaded operator procedures.

Note: When Union extends a base, it can be confusing because no new fields are added to the base, but instead the base is added to the derived Union. This means that data elements in the derived Union can share the same memory space like the base (here it doesn't matter whether the base is a Union or not). Of course it can be dangerous, but that is always the problem with Unions.
If only the base is a Union, then it won't be affected by data elements from the derived UDT.
Since Union is not allowed to have complex data elements (i.e. UDTs with constructor/destructor, or dynamic strings), a derived Union can not be allowed to have (contain) a complex base.

Note: Since fbc version 1.20.0, unions containing STRING*N fields will initialize to spaces if the first field is a STRING*N type, otherwise will initialize to zero (as for a STRING*N field before fbc version 1.20.0).

Example

' Example 0: Little-endianness
' For larger integer values (as the following Ulong data type),
'   bytes are arranged in memory in 'little-endian' byte order
'   (the least significant byte gets stored first).

Union UDU
   ul As ULong      ' 32-bit data type
   Type
      ub0 As UByte  ' 8-bit data type
      ub1 As UByte  ' 8-bit data type
      ub2 As UByte  ' 8-bit data type
      ub3 As UByte  ' 8-bit data type
   End Type
End Union

Dim As UDU u
u.ul = &h12345678
Print Hex(u.ul)                                       ' Result: 12345678
Print Hex(u.ub3), Hex(u.ub2), Hex(u.ub1), Hex(u.ub0)  ' Result: 12   34   56   78

Sleep

' Example 1: Only one union member can be relevantly accessed at a time
Union member
  username As String * 32
  posts As ULong
End Union

Dim As member userX
userX.username = "Samantha"
userX.posts = 1234

Print userX.username  ' value of username corrupted because final value assigned to posts occupies same memory location
'                     ' (and this is reason that value of posts is displayed well)
Print userX.posts
Print

Dim As member userY
userY.posts = 4321
userY.username = "Alexander"

Print userY.username
Print userY.posts  ' value of posts corrupted because final value assigned to username occupies same memory location
'                  ' (and this is reason that value of username is displayed well)
Print

Sleep

' Example 2: Alternative to RGBA keyword and allowing to retrieve elementary colors values
Union BGRA_UNION
   colour As ULong
   Type
      blue  As UByte
      green As UByte
      red   As UByte
      Alpha As UByte
   End Type
End Union

Dim ubgra As BGRA_UNION

' Setting the individual color values...
ubgra.red = &h33
ubgra.green = &hcc
ubgra.blue = &h66
' We can get a ULONG value
Print Hex(ubgra.colour)  ' Result: 33CC66
Print

' Setting a ULONG value...
ubgra.colour = &h228844
' We can get the individual color values
Print Hex(ubgra.red)    ' Result: 22
Print Hex(ubgra.green)  ' Result: 88
Print Hex(ubgra.blue)   ' Result: 44
Print

Sleep

' Example 3.
' Define a simple union.
Union AUnion
    a As UByte
    b As UInteger
End Union
' Define a composite type with an unnamed union.
Type CompType
    s As String * 20
    ui As UByte 'Flag to tell us what to use in union.
    Union
        au As UByte
        bu As UInteger
    End Union
End Type

' Flags to let us know what to use in union,
' because it's relevant to only use a single element of a union at a given time.
Const IsInteger = 1
Const IsUByte = 2

Dim MyUnion As AUnion
Dim MyComposite As CompType

' Only one field within the union is set, without choice criterion.
MyUnion.a = 128

MyComposite.s = "Type + Union"
MyComposite.ui = IsInteger ' Tells us this is an integer union.
MyComposite.bu = 1500      ' Field set according to the above flag.

Print "Simple Union: ";MyUnion.a

Print MyComposite.s & ": ";
If MyComposite.ui = IsInteger Then
    Print MyComposite.bu
ElseIf MyComposite.ui = IsUByte Then
    Print MyComposite.au
Else
    Print "Unknown Type."
End If
Print

Sleep

' Example 4: Using Nested Named Union
Type T
    Union U
        a As Short
        Type
            b1 As Byte
            b2 As Byte
        End Type
        Declare Sub proc(ByVal _b1 As Byte, ByVal _b2 As Byte)
    End Union
    m As U
    Declare Sub proc()
End Type

Sub T.U.proc(ByVal _b1 As Byte, ByVal _b2 As Byte)
    This.b1 = _b1
    This.b2 = _b2
End Sub

Sub T.proc()
    Print This.m.b1, This.m.b2, This.m.a
End Sub

Dim x As T
x.m.proc(1, 2)
x.proc()

Sleep

Version

  • Since fbc 1.10.0: Nested Named Type/Union capability added.

Dialect Differences

  • Object-related features as functions defined inside the Union block are supported only in the -lang fb dialect.
  • Not available in the -lang qb dialect unless referenced with the alias __Union.

Differences from QB

  • New to FreeBASIC

See also

  • Type
  • Coercion and Conversion


Unlock
Removes a previous access restriction (lock) on a file

Syntax

Unlock #filenum, record
Unlock #filenum, start To end

Parameters

filenum
The file number used to Open the file.
record
The record (Random files) to unlock.
start
The first byte position (Binary files) in a range to unlock.
end
The last byte position (Binary files) in a range to unlock.

Description

Unlock removes the temporary access restriction set by Lock.

It is strongly recommended to use the same arguments used in the previous Lock.

Note: This command does not always work, neither as documented nor as expected. It appears to be broken at the moment.

Example

For an example see Lock.

Differences from QB

  • Currently, FB cannot implicitly unlock the entire file
  • In Random mode, FB cannot unlock a range of records

See also

  • Lock
  • Open
  • ScreenUnlock


Unsigned
Integer data type modifier

Syntax

Dim variable As Unsigned {integer-based data type}

Description

Forces an integer-based data type to be unsigned (cannot contain negative numbers, but has its maximum value doubled).

Example

'e.g. notice what is displayed:

Dim x As Unsigned Integer
x = -1
Print x

'output is 4294967295

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Unsigned.

Differences from QB

  • New to FreeBASIC

See also

  • UInteger


Until
Conditional clause used in Do..Loop statements.

Syntax

Do Until condition
or
Loop Until condition

Description

Until is used with the Do...Loop structure.

Example

Dim a As Integer

a = 1
Do
    Print "hello"
a = a + 1
Loop Until a > 10

'This will continue to print "hello" on the screen until the condition (a > 10) is met.

Differences from QB

  • None

See also

  • Do...Loop


UShort
Standard data type: 16 bit unsigned.
Equivalent to Unsigned Short.

Syntax

Dim variable As UShort

Description

16-bit unsigned whole-number data type. Can hold values from 0 to 65535.

Example

  Dim x As UShort = 0
  Dim y As UShort = &HFFFF
  Print "UShort Range = "; x; " to "; y

Output:
UShort Range = 0 to 65535

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Ushort.

Differences from QB

  • New to FreeBASIC

See also

  • Short
  • CUShort
  • Table with variable types overview, limits and suffixes


Using (Namespaces)
Brings namespace symbols into the current scope

Syntax

Using identifier [, identifier [, ...] ]

Parameters

identifier: The name of the Namespace that you want to use.

Description

The Using command allows all symbols from a given namespace to be accessed without the namespace's name prefix. Unlike C++ but like C#, the Namespace keyword is not needed after Using, because individual symbols cannot be inherited from a namespace.

Using is allowed in namespaces and procedures only (not in type or union or enum declarations).

Inheriting a whole namespace can save typing, but sometimes some meaning of the code can be lost, and conflicts with other symbols could be created.

Example

Namespace Sample
    Type T
        x As Integer
    End Type
End Namespace

'' Just using the name T would not find the symbol,
'' because it is inside a namespace.
Dim SomeVariable As Sample.T

'' Now the whole namespace has been inherited into
'' the global namespace.
Using Sample

'' This statement is valid now, since T exists
'' without the "Sample." prefix.
Dim OtherVariable As T

Version

  • Before fbc 1.09.0, if there is duplicated symbol in the global namespace (unnamed namespace), access to local symbol is captured by duplicated global symbol (in that case, full prefixing is required to access local symbol).

Differences from QB

  • QB had the Using keyword, but for other purposes. Namespaces did not exist in QB.

See also

  • Print Using
  • ? Using
  • Palette Using
  • Namespace


va_arg
Returns the current argument from a variable argument list.

Syntax

variable = va_arg ( argument_list, datatype )

Description

The va_arg macro allows the use of a variable number of arguments within a function:
    • va_arg returns the current argument in the list, argument_list, with an expected data type of datatype.
    • Before first va_arg use, argument_list must be initialized with the command va_first.
    • Unlike the C macro with the same name, va_arg does not automatically increment argument_list to the next argument within the list. Instead va_next must be used to find the next argument in adjusting argument_list.

Not supported when using -gen gcc. Use Cva_List variadic argument list type for cross platform compatibility.

Example

See the Va_First() examples.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __va_arg.

Differences from QB

  • New to FreeBASIC

See also

  • ... (Ellipsis)
  • va_first
  • va_next


va_first
Returns a pointer to the first argument in a variable argument list

Syntax

pointer_variable = va_first()

Description

The va_first function provides an untyped pointer value that points to the first variable argument passed to a function.

Not supported when using -gen gcc. Use Cva_List variadic argument list type for cross platform compatibility.

Example

Function average cdecl(count As Integer, ... ) As Double
    Dim arg As Any Ptr
    Dim sum As Double = 0
    Dim i As Integer

    arg = va_first()

    For i = 1 To count
        sum += va_arg(arg, Double)
        arg = va_next(arg, Double)
    Next

    Return sum / count
End Function

Print average(4, 3.4,5.0,3.2,4.1)  '' all passed variable arguments must be of type double
Print average(2, 65.2,454.65481)   '' all passed variable arguments must be of type double
Sleep

The output would look like:
3.925
259.927405

'' Example of a simple custom printf
Sub myprintf cdecl(ByRef formatstring As String, ...)
    '' Get the pointer to the first var-arg
    Dim As Any Ptr arg = va_first()

    '' For each char in format string...
    Dim As UByte Ptr p = StrPtr(formatstring)
    Dim As Integer todo = Len(formatstring)
    While (todo > 0)
        Dim As Integer char = *p
        p += 1
        todo -= 1

        '' Is it a format char?
        If (char = Asc("%")) Then
            If (todo = 0) Then
                '' % at the end
                Print "%";
                Exit While
            End If

            '' The next char should tell the type
            char = *p
            p += 1
            todo -= 1

            '' Print var-arg, depending on the type
            Select Case char
            '' integer?
            Case Asc("i")
                Print Str(va_arg(arg, Integer));
                '' Note, different from C: va_next() must be
                '' used as va_arg() won't update the pointer.
                arg = va_next(arg, Integer)

            '' long integer? (64-bit)
            Case Asc("l")
                Print Str(va_arg(arg, LongInt));
                arg = va_next(arg, LongInt)

            '' single or double?
            '' Note: because the C ABI, all singles passed on
            '' var-args are converted to doubles.
            Case Asc( "f" ), Asc( "d" )
                Print Str(va_arg(arg, Double));
                arg = va_next(arg, Double)

            '' string?
            Case Asc("s")
                '' Strings are passed byval, so the length is unknown
                Print *va_arg(arg, ZString Ptr);
                arg = va_next(arg, ZString Ptr)

            End Select

        '' Ordinary char, just print as-is
        Else
            Print Chr( char );
        End If
    Wend
End Sub

    Dim As String s = "bar"

    myprintf(!"integer=%i, longint=%l single=%f, double=%d, string=%s, string=%s\n", _
             1, 1ll Shl 32, 2.2, 3.3, "foo", s)

    Sleep

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Va_first.

Differences from QB

  • New to FreeBASIC

See also

  • ... (Ellipsis)
  • va_arg
  • va_next


va_next
Returns a pointer to the next argument in a variable argument list

Syntax

Argument_Pointer = va_next ( Argument_List, datatype )

Description

The va_next macro provides a datatype pointer value that points to the next argument within the list Argument_List, datatype being the type of the current argument being stepped over.

Not supported when using -gen gcc. Use Cva_List variadic argument list type for cross platform compatibility.

Example

See the Va_First() examples.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Va_next.

Differences from QB

  • New to FreeBASIC

See also

  • ... (Ellipsis)
  • va_arg
  • va_first


Val
Converts a string to a floating point number

Syntax

Declare Function Val ( ByRef str As Const String ) As Double
Declare Function Val ( ByRef str As Const WString ) As Double

Usage

result = Val( strnum )

Parameters

strnum
the string containing a number to convert

Return Value

Returns a converted Double precision number

If the first character of the string is invalid, Val will return 0.

Description

Val("10") will return 10.0, and Val("10.10") will return 10.1. The function parses the string from the left, skipping any white space, and returns the longest number it can read, stopping at the first non-suitable character it finds. Scientific notation is recognized, with "D" or "E" used to specify the exponent.

Val can be used to convert integer numbers in binary / octal / hexadecimal format, if they have the relevant identifier ("&B" / "&O" / "&H") prefixed, for example: Val("&HFF") returns 255.

Note:
If you want to get an integer value from a string, consider using ValInt or ValLng instead. They are faster, since they don't use floating-point numbers, and only ValLng provides full 64-bit precision for LongInt types.

If you want to convert a number into string format, use the Str function.

Example

Dim a As String, b As Double
a = "2.1E+30xa211"
b = Val(a)
Print a, b

2.1E+30xa211   2.1e+030

Differences from QB

  • None

See also

  • CDbl
  • ValInt
  • ValUInt
  • ValLng
  • ValULng
  • Str
  • Chr
  • Asc


ValLng
Converts a string to a 64bit integer

Syntax

Declare Function ValLng ( ByRef strnum As Const String ) As LongInt
Declare Function ValLng ( ByRef strnum As Const WString ) As LongInt

Usage

result = ValLng ( strnum )

Parameters

strnum
the string to convert

Return Value

Returns a LongInt of the converted string

If the first character of the string is invalid, ValLng will return 0.

Description

For example, ValLng("10") will return 10, and ValLng("10.60") will return 10 as well. The function parses the string from the left, skipping any white space, and returns the longest number it can read, stopping at the first non-suitable character it finds. Any non-numeric characters, including decimal points and exponent specifiers, are considered non-suitable, for example, ValLng("23.1E+6") will just return 23.

ValLng can be used to convert integer numbers in Binary / Octal / Hexadecimal format, if they have the relevant identifier ("&B" / "&O" / "&H") prefixed, for example: ValLng("&HFF") returns 255.

If you want to convert a number into string format, use the Str function.

Example

Dim a As String, b As LongInt
a = "20xa211"
b = ValLng(a)
Print a, b

20xa211   20

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Vallng.

Differences from QB

  • New to FreeBASIC

See also

  • CLngInt
  • Val
  • ValInt
  • ValULng
  • Str
  • Chr
  • Asc


ValInt
Converts a string to a 32bit integer

Syntax

Declare Function ValInt ( ByRef strnum As Const String ) As Long
Declare Function ValInt ( ByRef strnum As Const WString ) As Long

Usage

result = ValInt ( strnum )

Parameters

strnum
the string to convert

Return Value

Returns a Long value of the converted string

If the first character of the string is invalid, ValInt will return 0.

Description

For example, ValInt("10") will return 10, and ValInt("10.60") will return 10 as well. The function parses the string from the left, skipping any white space, and returns the longest number it can read, stopping at the first non-suitable character it finds. Any non-numeric characters, including decimal points and exponent specifiers, are considered non-suitable, for example, ValInt("23.1E+6") will just return 23.

ValInt can be used to convert integer numbers in Binary / Octal / Hexadecimal format, if they have the relevant identifier ("&B" / "&O" / "&H") prefixed, for example: ValInt("&HFF") returns 255.

If you want to convert a number into string format, use the Str function.

Example

Dim a As String, b As Long
a = "20xa211"
b = ValInt(a)
Print a, b

20xa211   20
Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Valint.

Differences from QB

  • New to FreeBASIC

See also

  • CLng
  • Val
  • ValUInt
  • ValLng
  • Str
  • Chr
  • Asc


ValUInt
Converts a string to an unsigned 32bit integer

Syntax

Declare Function ValUInt ( ByRef strnum As Const String ) As ULong
Declare Function ValUInt ( ByRef strnum As Const WString ) As ULong

Usage

result = ValUInt ( strnum )

Parameters

strnum
the string to convert

Return Value

Returns a ULong value of the converted string

If the first character of the string is invalid, ValUInt will return 0.

Description

For example, ValUInt("10") will return 10, and ValUInt("10.60") will return 10 as well. The function parses the string from the left, skipping any white space, and returns the longest number it can read, stopping at the first non-suitable character it finds. Any non-numeric characters, including decimal points and exponent specifiers, are considered non-suitable, for example, ValUInt("23.1E+6") will just return 23.

ValUInt can be used to convert integer numbers in Binary / Octal / Hexadecimal format, if they have the relevant identifier ("&B" / "&O" / "&H") prefixed, for example: ValUInt("&HFF") returns 255.

If you want to convert a number into string format, use the Str function.

Example

Dim a As String, b As ULong
a = "20xa211"
b = ValUInt(a)
Print a, b

20xa211   20
Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Valuint.

Differences from QB

  • New to FreeBASIC

See also

  • Val
  • ValInt
  • ValULng
  • CULng
  • Str
  • Chr
  • Asc


ValULng
Converts a string to a unsigned 64bit integer

Syntax

Declare Function ValULng ( ByRef strnum As Const String ) As ULongInt
Declare Function ValULng ( ByRef strnum As Const WString ) As ULongInt

Usage

result = ValULng ( strnum )

Parameters

strnum
the string to convert

Return Value

Returns a ULongInt of the converted string

If the first character of the string is invalid, ValULng will return 0.

Description

For example, ValULng("10") will return 10, and ValULng("10.60") will return 10 as well. The function parses the string from the left, skipping any white space, and returns the longest number it can read, stopping at the first non-suitable character it finds. Any non-numeric characters, including decimal points and exponent specifiers, are considered non-suitable, for example, ValULng("23.1E+6") will just return 23.

ValULng can be used to convert integer numbers in Binary / Octal / Hexadecimal format, if they have the relevant identifier ("&B" / "&O" / "&H") prefixed, for example: ValULng("&HFF") returns 255.

If you want to convert a number into string format, use the Str function.

Example

Dim a As String, b As ULongInt
a = "20xa211"
b = ValULng(a)
Print a, b

20xa211   20

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Valulng.

Differences from QB

  • New to FreeBASIC

See also

  • CULngInt
  • Val
  • ValUInt
  • ValLng
  • Str
  • Chr
  • Asc


Var
Declares a variable whose type is implied from the initializer expression

Syntax

[Static] Var [Shared] symbolname = expression[, symbolname = expression]

Description

Var declares a variable whose type is implied from the initializer expression. It is illegal to specify an explicit type in a Var declaration. The initializer expression can be either a constant or any variable of any type.

Note: WString is not supported with Var, due to the fact that there is no var-len WString type. This isn't likely to change, due to the complexities involved with handling Unicode.

Since the type of the variable is inferred from what you assign into it, it's helpful to know how literals work. Any literal number without a decimal point defaults to Integer. A literal number with a decimal point defaults to Double. See Literals for further information.

All ZString expressions, including string literals and dereferenced ZString Ptrs, will be given the String variable type.

Explicit suffixes may be used on literal variables, to change/clarify their type. See Literals and Variable Types for some more information about suffixes that can be used on literals.

Note: Suffixes must appear on the initializer, not on the variable. Trying to use Var with a variable that has a suffix will throw a compile error.

Example

Var a  = Cast(Byte, 0)
Var b  = Cast(Short, 0)
Var c  = Cast(Integer, 0)
Var d  = Cast(LongInt, 0)
Var au = Cast(UByte, 0)
Var bu = Cast(UShort, 0)
Var cu = Cast(UInteger, 0)
Var du = Cast(ULongInt, 0)
Var e  = Cast(Single, 0.0)
Var f  = Cast(Double, 0.0)
Var g  = @c      '' integer ptr
Var h  = @a      '' byte ptr
Var s2 = "hello" '' var-len string

Var ii = 6728   '' implicit integer
Var id = 6728.0 '' implicit double

Print "Byte: ";Len(a)
Print "Short: ";Len(b)
Print "Integer: ";Len(c)
Print "Longint: ";Len(d)
Print "UByte: ";Len(au)
Print "UShort: ";Len(bu)
Print "UInteger: ";Len(cu)
Print "ULongint: ";Len(du)
Print "Single: ";Len(e)
Print "Double: ";Len(f)
Print "Integer Pointer: ";Len(g)
Print "Byte Pointer: ";Len(h)
Print "Variable String: ";Len(s2)
Print
Print "Integer: ";Len(ii)
Print "Double: ";Len(id)

Sleep

Differences from QB

  • New to FreeBASIC

Dialect Differences

  • Only valid in the -lang fb dialect.

See also

  • Common
  • Dim
  • Erase
  • Extern
  • LBound
  • ReDim
  • Preserve
  • Shared
  • Static
  • Byref (Variables)
  • UBound
  • Pointers to Procedures


Operator Varptr (Variable Pointer)
Returns the address of a variable or object

Syntax

Declare Operator VarPtr ( ByRef lhs As T ) As T Ptr

Syntax

result = VarPtr ( lhs )

Parameters

lhs
A variable or object.
T
Any data type.

Return Value

Returns the address of a variable or object.

Description

This operator returns the address of its operand.

When the operand is of type String, the address of the internal string descriptor is returned. Use Operator Strptr (String Pointer) to retrieve the address of the string data.

The operand cannot be an array, but may be an array element. For example, "VarPtr(myarray(0))" returns the address of "myarray(0)".

Operator @ (Address Of), when used with variables or objects, has identical behavior.

Example

Dim a As Integer, addr As Integer
a = 10

'' place the address of a in addr
addr = CInt( VarPtr(a) )

'' change all 4 bytes (size of INTEGER) of a
Poke Integer, addr, -1000
Print a

'' place the address of a in addr (same as above)
addr = CInt( @a )

'' print the least or most significant byte, depending on the CPU endianess
Print Peek( addr )

Differences from QB

  • None

See also

  • Pointers
  • Peek
  • Poke


View Print
Sets the printable text area of the screen

Syntax

View Print [ firstrow To lastrow ]

Parameters

firstrow
first row of print area
lastrow
last row of print area

Description

Sets the boundaries of the text printing area on the screen (in console mode or in graphics mode) to the lines starting at first up to and including last (the number of the available text lines on the screen depends on the screen mode). Text lines are counted starting with 1. The text cursor is moved to the beginning of the first line specified.
If the row numbers are omitted, the entire screen is used as the text printing area.

Example

Cls
View Print 5 To 6
Color , 1
'' clear only View Print area
Cls

View Print can be used in graphics mode to avoid the text output overwriting graphics:
Screen 12
Dim As Integer R,Y,x,y1
Dim As Single y2
View Print 20 To 27
Line (0,0)-(639,300),1,BF
Line (100,50)-(540,200),0,BF
Do
 r = (r + 1) And 15
 For y = 1 To 99
   y1 = ((1190 \ y + r) And 15)
   y2 = 6 / y
   For x = 100 To 540
    PSet (x, y + 100), CInt((319 - x) * y2) And 15 Xor y1
  Next x,y
 If r=0 Then Color Int(Rnd*16): Print "blah"
Loop Until Len(Inkey)

Differences from QB

  • None.

See also

  • Cls
  • Print
  • ?
  • Color


View (Graphics)
Sets new physical coordinate mapping and clipping region for graphics keywords

Syntax

View
View ( x1, y1 )-( x2, y2 ) [ [, fill_color ] [, border_color ] ]
View Screen ( x1, y1 )-( x2, y2 ) [ [, fill_color ] [, border_color ] ]

Parameters

x1 As Integer, y1 As Integer
The horizontal and vertical offsets, in pixels, of one corner of the viewport relative to the top-left corner of the screen.
x2 As Integer, y2 As Integer
The horizontal and vertical offsets, in pixels, of the opposite corner of the viewport relative to the top-left corner of the screen.
fill_color As ULong
The color to fill the new viewport.
border_color As ULong
The color of the border to draw around the new viewport.

Description

The viewport, or clipping region, is a rectangular area of the graphics screen, outside of which no graphics drawing will be done. That is, only graphics drawing done within this area will be shown. A graphics screen must be created with Screen (Graphics) or ScreenRes before calling View or View Screen.

The first statement (View) sets the viewport to encompass the entire screen, which is the default viewport for a new graphics screen.

The second and third statements (View parameters and View Screen parameters) both allow a new viewport to be defined. The indicated effects for each parameter only occur if that parameter is specified:
    • The corners of the viewport are specified by the x1, y1, x2 and y2 parameters.
    • fill_color and border_color are both in the format accepted by Color.
    • The second statement (View parameters) modifies the coordinate mapping of the graphics screen such that coordinates specified for graphics drawing statements and procedures are relative to the top-left corner of the viewport.
    • The third statement (View Screen parameters) modifies the coordinate mapping of the graphics screen such that coordinates specified for graphics drawing statements and procedures are relative to the top-left corner of the screen.
    • In both cases no new scale factor is applied (see Window for that).

Example

Screen 12
Dim ip As Any Ptr
Dim As Integer x, y

'simple sprite
ip = ImageCreate(64,64)
For y = 0 To 63
  For x = 0 To 63
    PSet ip, (x, y), (x\4) Xor (y\4)
  Next x
Next y

'viewport with blue border
Line (215,135)-(425,345), 1, bf
View (220,140)-(420,340)

'move sprite around the viewport
Do

  x = 100*Sin(Timer*2.0)+50
  y = 100*Sin(Timer*2.7)+50

  ScreenSync
  ScreenLock

  'clear viewport and put image
  Cls 1
  Put (x, y), ip, PSet

  ScreenUnlock

Loop While Inkey = ""

ImageDestroy(ip)

Differences from QB

  • QBASIC preserves the WINDOW coordinate mapping after subsequent calls to VIEW.
  • FreeBASIC's current behavior is to preserve the WINDOW coordinates after calls to VIEW, or when working on images, meaning that the coordinate mapping may undergo scaling/translations if the viewport changes. (If a WINDOW hasn't been set, there is no coordinate mapping, and so it doesn't change after calls to VIEW.) The behavior may change in future, but consistent behavior can be assured over inconstent viewport coordinates by re-calling WINDOW whenever you change the VIEW.

See also

  • View Print
  • Screen (Graphics)
  • Window
  • PMap


Virtual
Declare virtual methods

Syntax

Type typename Extends base_typename
Declare Virtual Sub|Function|Property|Operator|Destructor ...
End Type

Description

Virtual methods are methods that can be overridden by data types derived from the type they were declared in, allowing for dynamic polymorphism. In contrast to Abstract methods, virtual methods must have an implementation, which is used when the virtual is not overridden.

A derived type can override virtual methods declared in its base type by declaring a non-static method with the same identifier and signature, meaning same number and type of parameters (invariant parameters), same calling convention, and if any, same return type (or a covariant return type for return by reference or by pointer):
    • if that differs only in parameter passing mode or calling convention or return type, then an overriding error is returned at compile time,
    • otherwise, only shadowing is allowed for any other signature difference, corresponding to case where both methods would be overloadable (if within the same type).
The property of being a virtual method is not implicitly inherited by the overriding method in the derived type. If this overriding method must be overridden in turn in a lower level derived type, it must also be declared as virtual.
On the other hand, since a derived static method can never override a base virtual/abstract method, it can therefore shadow any base method (including virtual/abstract) with same identifier and regardless of the signature.

When calling virtual methods, the compiler may need to do a vtable lookup in order to find out which method must be called for a given object. This requires an extra hidden vtable pointer field to be added at the top of each type with virtual methods. This hidden vptr is provided by the built-in Object type. Because of that, virtual methods can only be declared in a type that directly or indirectly Extends Object.

Dynamic polymorphism by using override procedures:
    • Normally only a typename procedure (or upper in hierarchy) is accessible through a base-typename reference/pointer even if this one refers to an object derived from typename.
    • But when the procedure is virtual, this tells the running program to resolve the override procedure the most derived relating to the real object type by vtable lookup (dynamic binding at runtime), rather than procedure normally accessible from the raw base-type of the reference/pointer (static binding at compile time).

Constructors cannot be virtual because they create objects, while virtual methods require an already-existing object with a specific type. The type of the constructor to call is determined at compile-time from the code.
In addition, when calling a virtual method inside a constructor, only the version of the method corresponding to an object of type of this constructor is used. That is because the vptr has not yet been set up by the derived type constructor, but only by the local type constructor.

Destructors often must be virtual when deleting an object manipulated through a pointer to its base type, so that the destruction starts at the most derived type and works its way down to the base type. To do this, it may be necessary to add virtual destructors with an empty body anywhere an explicit destruction was not yet required, in order to supersede each non-virtual implicit destructor built by the compiler.
On the other hand, when calling a virtual (or abstract) method inside a destructor (virtual or not), only the version of the method corresponding to an object of type of this destructor is used because the vptr is reset at the top of the destructor according to its own type's vtable. This avoids to access child methods and so to refer to child members previously destroyed by the child destructor execution.

For member methods with Virtual in their declaration, Virtual can also be specified on the corresponding method bodies, for improved code readability.

Caution: In a multi-level inheritance, a same named method (same identifier and signature) can be declared Abstract, Virtual or normal (without specifier) at each inheritance hierarchy level. When there is mixing of specifiers, the usual order is abstract -> virtual -> normal, from top to bottom of the inheritance hierarchy.
The access control (Public/Protected/Private) of an overriding method is not taken into account by the internal polymorphism process, but only for the initial call at compile-time.
Base (Member Access).method() calls always the base's own method, never the overriding method.

Note: Virtual is also used as qualifier for ProcPtr to request it to return the index in the vtable for a virtual/abstract member procedure or member operator.
ProcPtr ( identifier, Virtual [ Any|user_proctype ] )

Example

'' Example with overriding subroutines

Type Hello Extends Object
    Declare Virtual Sub hi( )
End Type

Type HelloEnglish Extends Hello
    Declare Sub hi( )            '' overriding subroutine
End Type

Type HelloFrench Extends Hello
    Declare Sub hi( )            '' overriding subroutine
End Type

Type HelloGerman Extends Hello
    Declare Sub hi( )            '' overriding subroutine
End Type

Sub Hello.hi( )
    Print "hi!"
End Sub

Sub HelloEnglish.hi( )           '' overriding subroutine
    Print "hello!"
End Sub

Sub HelloFrench.hi( )            '' overriding subroutine
    Print "Salut!"
End Sub

Sub HelloGerman.hi( )            '' overriding subroutine
    Print "Hallo!"
End Sub

Randomize( Timer( ) )

Dim As Hello Ptr h

For i As Integer = 0 To 9
    Select Case( Int( Rnd( ) * 4 ) + 1 )
    Case 1
        h = New HelloEnglish
    Case 2
        h = New HelloFrench
    Case 3
        h = New HelloGerman
    Case Else
        h = New Hello
    End Select

    h->hi( )
    Delete h
Next

Sleep

'' Example with overriding destructor and
''              overriding function with covariant return

Type myBase Extends Object
  Declare Virtual Function clone () As myBase Ptr
  Declare Virtual Sub Destroy ()
End Type

Function myBase.clone () As myBase Ptr
  Dim As myBase Ptr pp = New myBase(This)
  Print "myBase.clone() As myBase Ptr", pp
  Function = pp
End Function

Sub myBase.Destroy ()
  Print "myBase.Destroy()", , @This
  Delete @This
End Sub

Type myDerived Extends myBase
  Declare Function clone () As myDerived Ptr     '' overriding member function with covariant return
  Declare Sub Destroy ()                         '' overriding member subroutine
End Type

Function myDerived.clone () As myDerived Ptr     '' overriding member function with covariant return
  Dim As myDerived Ptr pc = New myDerived(This)
  Print "myDerived.clone() As myDerived Ptr", pc
  Function = pc
End Function

Sub myDerived.Destroy ()                         '' overriding member subroutine
  Print "myDerived.Destroy()", , @This
  Delete @This
End Sub

Dim As myDerived c

Dim As myBase Ptr ppc = @c
Dim As myDerived Ptr pcc = @c

Dim As myBase Ptr ppc1 = ppc->clone()            '' using base pointers and polymorphism
Dim As myDerived Ptr pcc1 = pcc->clone()         '' using derived pointers and covariance of return value
Print
ppc1->Destroy()                                  '' using base pointer and polymorphism
pcc1->Destroy()                                  '' using derived pointer

Sleep

Dialect Differences

  • Only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Type
  • Object
  • Extends
  • Extends Zstring
  • Extends Wstring
  • Abstract
  • Override


VOICE
Sets or returns the default instrument.

Syntax

Voice instrument
instrument = Voice()

Description

VOICE selects the default instrument used by generated notes and PLAY strings. In sfxlib, an instrument is a combination of a waveform and an envelope, which together decide the general tone color and how the sound starts and fades.

Changing VOICE is like switching from one simple synth patch to another. A sine-like instrument can sound soft and clean, while a square or saw instrument can sound more electronic or buzzy.

Calling VOICE as a function returns the current default instrument id.

Example

wave 1, 0
envelope 1, 0.01, 0.10, 0.60, 0.20
instrument 1, 1, 1
voice 1
play "o4 c e g"

Notes

  • Define custom instruments with WAVE, ENVELOPE, and INSTRUMENT.

See also

  • Sound Library Reference
  • Generated Sound Commands


VOL
Sets the master generated-sound volume.

Syntax

Vol level

Description

VOL is the short BASIC-style way to change overall generated-sound loudness. It affects the master level used by sfxlib output rather than one individual channel.

Use VOL when a program only needs to set the volume and does not need to read it back. It is good for simple examples, quick sound tests, and programs written in a compact older BASIC style.

For new code that needs more control, VOLUME is usually clearer because it can set the master level, set a channel level, and return the current level.

Example

vol 8
sound 440, 0.25
sleep 300, 1

Notes

  • Use VOLUME for channel-specific volume or when a function return value is needed.

See also

  • Sound Library Reference
  • Generated Sound Commands


VOLUME
Sets or returns master or channel volume.

Syntax

Volume level
level = Volume()
Volume channel, level
level = Volume(channel)

Description

VOLUME controls how loud sfxlib output is. The one-argument statement changes the master volume, which affects the overall mix. The two-argument statement changes the volume for one channel, which is useful for balancing music, effects, and generated voices separately.

The function forms return the current master or channel volume. That lets a program save a value, fade it over time, or restore it later.

Volume changes affect sounds mixed through sfxlib. They do not change the operating system speaker volume.

Example

volume 0.5
volume 1, 0.25
print "master="; volume()
print "channel 1="; volume(1)

Notes

  • Use PAN or BALANCE for left/right placement instead of loudness.

See also

  • Sound Library Reference
  • Generated Sound Commands


Wait
Reads from a hardware port with a mask.

Syntax

Declare Function Wait ( ByVal port As UShort, ByVal and_mask As Long, ByVal xor_mask As Long = 0 ) As Long

Usage

Wait port, and_value [, xor_value]

Parameters

port
Port to read.
and_mask
Mask value to And the port value with.
xor_mask
Mask value to Xor the port value with.

Return Value

0 if successful, -1 on failure.

Description

Wait keeps reading port until the reading ANDed with and_mask and optionally XORed with xor_mask gives a non-zero result.

Example

Wait &h3da, &h8 'Old Qbasic way of waiting for the monitor's vsync
ScreenSync 'FreeBASIC way of accomplishing the same thing

Platform Differences

  • In the Windows and Linux versions three port numbers (&H3C7, &H3C8, &H3C9) are hooked by the graphics library when a graphics mode is in use to emulate VGA palette handling as in QB. This use is deprecated; use Palette to retrieve and set palette colors.

  • Using true port access in the Windows version requires the program to install a device driver for the present session. For that reason, Windows executables using hardware port access should be run with administrator permits each time the computer is restarted. Further runs don't require admin rights as they just use the already installed driver. The driver is only 3K in size and is embedded in the executable.

See also

  • Inp
  • Out


WAVE
Defines a generated waveform slot.

Syntax

Wave id, waveform

Description

WAVE defines the raw shape used by a generated instrument. It does not play sound by itself. Instead, it stores a waveform in a numbered slot so INSTRUMENT can use it later.

The waveform shape changes the tone color. Sine is smooth and plain, square is buzzy and retro, triangle is softer than square, saw is bright and sharp, and noise is useful for percussion and effects.

The currently supported waveform values are 0 for sine, 1 for square, 2 for triangle, 3 for saw, and 4 for noise.

Example

wave 1, 0
envelope 1, 0.01, 0.10, 0.50, 0.20
instrument 1, 1, 1
voice 1

Notes

  • WAVE defines part of an instrument; use SOUND, NOTE, PLAY, or TONE to actually hear something.

See also

  • Sound Library Reference
  • Generated Sound Commands


WBin
Returns the binary WString (Unicode) representation of a number

Syntax

Declare Function WBin ( ByVal number As UByte ) As WString
Declare Function WBin ( ByVal number As UShort ) As WString
Declare Function WBin ( ByVal number As ULong ) As WString
Declare Function WBin ( ByVal number As ULongInt ) As WString
Declare Function WBin ( ByVal number As Const Any Ptr ) As WString

Declare Function WBin ( ByVal number As UByte, ByVal digits As Long ) As WString
Declare Function WBin ( ByVal number As UShort, ByVal digits As Long ) As WString
Declare Function WBin ( ByVal number As ULong, ByVal digits As Long ) As WString
Declare Function WBin ( ByVal number As ULongInt, ByVal digits As Long ) As WString
Declare Function WBin ( ByVal number As Const Any Ptr, ByVal digits As Long ) As WString

Usage

result = WBin( number [, digits] )

Parameters

number
A whole number or expression evaluating to a whole number.
digits
Optional number of digits to return.

Return Value

Returns a binary WString representation of number, truncated or padded with zeros ("0") to fit the number of digits, if specified.

Description

Returns a WString (Unicode) representing the binary value of the integer number. Binary digits range from 0 to 1.

If you specify digits > 0, the result wstring will be exactly that length. It will be truncated or padded with zeros on the left, if necessary.

The length of the returned string will not be longer than the maximum number of digits required for the type of expression (32 for a Long, 64 for floating point or LongInt)

Example

Print WBin(54321)
Print WBin(54321, 5)
Print WBin(54321, 20)

will produce the output:

1101010000110001
10001
00001101010000110001
Platform Differences

  • Unicode strings are not supported in the DOS port of FreeBASIC.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Wbin.

Differences from QB

  • New to FreeBASIC

See also

  • Bin
  • WHex
  • WOct


WChr
Returns a wide-character string containing one or more Unicode characters

Syntax

Declare Function Wchr ( ByVal ch As Integer [, ... ] ) As WString

Usage

result = WChr( ch0 [, ch1 ... chN ] )

Parameters

ch
The Unicode integer value of a character (up to 32 characters).

Return Value

Returns a wide-character string.

Description

WChr returns a wide-character string containing the character(s) represented by the Unicode values passed to it.

When WChr is used with numerical constants or literals, the result is evaluated at compile-time, so it can be used in variable initializers.

Not all Unicode characters can be displayed on any machine, the characters available depend on the font presently in use in the console. Graphics modes can't display Unicode characters, as the GfxLib built-in font is not Unicode.

Example

Print "The character represented by the UNICODE code of 934 is: "; WChr(934)
Print "Multiple UNICODE characters: "; WChr(933, 934, 935)

will produce the output:
Platform Differences

  • DOS does not support WChr.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Wchr.

Differences from QB

  • New to FreeBASIC

See also

  • Chr
  • WStr

The character represented by the UNICODE code of 934 is: Φ
Multiple UNICODE characters: ΥΦΧ


Weekday
Gets the number of day of the week from a Date Serial

Syntax

Declare Function Weekday ( ByVal serial As Double , ByVal firstdayofweek As Long = fbusesystem ) As Long

Usage

#include "vbcompat.bi"
result = Weekday( date_serial [, firstdayofweek ] )

Parameters

date_serial
the date
firstdayofweek
the first day of the week

Return Value

Returns the week day number from a variable containing a date in Date Serial format.

Description

The week day values must be in the range 1-7, its meaning depends on the firstdayofweek parameter

firstdayofweek is optional.

value | first day of week | constant
omitted | sunday |
0 | local settings | fbUseSystem
1 | sunday | fbSunday
2 | monday | fbMonday
3 | tuesday | fbTuesday
4 | wednesday | fbWednesday
5 | thursday | fbThursday
6 | friday | fbFriday
7 | saturday | fbSaturday

The compiler will not recognize this function unless vbcompat.bi is included.

Example

#include "vbcompat.bi"

Dim a As Double = DateSerial (2005, 11, 28) + TimeSerial(7, 30, 50)

Print Format(a, "yyyy/mm/dd hh:mm:ss "); Weekday(a)

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials


WeekdayName
Gets the name of a week day from its integral representation

Syntax

Declare Function WeekdayName ( ByVal weekday As Long, ByVal abbreviate As Long = 0, ByVal firstdayofweek As Long = fbUseSystem ) As String

Usage

#include "vbcompat.bi"
result = WeekdayName( weekday [, abbreviate [, firstdayofweek ] ] )

Parameters

weekday
the number of the day of the week
abbreviate
flag to indicate that name should be abbreviated
firstdayofweek
first day of the week

Return Value

Returns the local operating system language day of week name from the weekday value 1 to 7.

Description

How weekday is interpreted depends on the firstdayofweek parameter.

If abbreviate is true, a 3 letter abbreviation is returned, if false or omitted, the whole name is returned.

firstdayofweek is an optional parameter specified as follows:

value | first day of week | constant
omitted | sunday |
0 | local settings | fbUseSystem
1 | sunday | fbSunday
2 | monday | fbMonday
3 | tuesday | fbTuesday
4 | wednesday | fbWednesday
5 | thursday | fbThursday
6 | friday | fbFriday
7 | saturday | fbSaturday

The compiler will not recognize this function unless vbcompat.bi or datetime.bi is included.

Example

#include "vbcompat.bi"

Dim a As Double = DateSerial(2005, 11, 28) + TimeSerial(7, 30, 50)

Print Format(a, "yyyy/mm/dd hh:mm:ss "); WeekdayName(Weekday(a))

Differences from QB

  • Did not exist in QB. This function appeared in Visual Basic.

See also

  • Date Serials


Wend
Control flow statement.

Syntax

While [condition]
[statement block]
Wend

Description

Wend specifies the end of a While...Wend loop block.

Example

See example at While...Wend.

Differences from QB

  • None

See also

  • While...Wend


While
Control flow statement.

Syntax

Do While condition
[statement block]
Loop
or
Do
[statement block]
Loop While condition
or
While [condition]
[statement block]
Wend

Description

While specifies that a loop block will continue if the condition following it evaluates as true. This condition is checked during each loop iteration.

Example

Dim a As Integer

a = 0
Do While a < 10
    Print "hello"
a = a + 1
Loop

'This will continue to print "hello" on the screen while the condition (a < 10) is met.

Differences from QB

  • None

See also

  • Do...Loop
  • While...Wend


While...Wend
Control flow statement for looping

Syntax

While [condition]
[statement block]
Wend

Description

The While statement will cause the following set of statements in the statement block to execute repeatedly if and while the expression condition evaluates to true.

If condition evaluates to false when the While statement is first executed, then the statement block is skipped and execution resumes immediately following the enclosing Wend statement.

If an Exit While statement is encountered inside the statement block, the loop is terminated, and execution resumes immediately following the enclosing Wend statement. If a Continue While statement is encountered, the rest of the statement block is skipped and execution resumes at the While statement.

Like all control flow statements, the While statement can be nested, that is, it can be used in a statement block of another While statement.

note: the While keyword is also used in the Do...Loop statement to indicate the type of comparison. Used in this way, the Do statement becomes functionally equivalent to the While statement, so do not confuse their enclosing keywords Loop and Wend, respectively.

Example

In this example, a While loop is used to reverse a string by iterating through it backward. The loop stops if index is less than 0 (0 being the first index in the string).
Dim As String sentence                          '' string to reverse
sentence = "The quick brown fox jumps over the lazy dog."

Dim As String ecnetnes
Dim As Integer index
index = Len( sentence ) - 1                     '' point to last character
While( index >= 0 )                             '' stop after first character
  ecnetnes += Chr( sentence[index] )           '' append character to new string
  index -= 1
Wend

Print "original: """ ; sentence ; """"
Print "reversed: """ ; ecnetnes ; """"

End 0

Dialect Differences

  • In the -lang qb and -lang fblite dialects, variables declared inside a While..Wend loop have a function-wide scope as in QB
  • In the -lang fb and -lang deprecated dialects, variables declared inside a While..Wend block are visible only inside the block, and can't be accessed outside it. To access duplicated symbols defined as global outside this block, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

Differences from QB

  • None

See also

  • Exit
  • Continue
  • Do...Loop


WHex
Returns the hexadecimal WString (Unicode) representation of a number

Syntax

Declare Function WHex ( ByVal number As UByte ) As WString
Declare Function WHex ( ByVal number As UShort ) As WString
Declare Function WHex ( ByVal number As ULong ) As WString
Declare Function WHex ( ByVal number As ULongInt ) As WString
Declare Function WHex ( ByVal number As Const Any Ptr ) As WString

Declare Function WHex ( ByVal number As UByte, ByVal digits As Long ) As WString
Declare Function WHex ( ByVal number As UShort, ByVal digits As Long ) As WString
Declare Function WHex ( ByVal number As ULong, ByVal digits As Long ) As WString
Declare Function WHex ( ByVal number As ULongInt, ByVal digits As Long ) As WString
Declare Function WHex ( ByVal number As Const Any Ptr, ByVal digits As Long ) As WString

Usage

result = WHex( number [, digits ] )

Parameters

number
A whole number or expression evaluating to a whole number.
digits
Optional number of digits to return.

Return Value

Returns a hexadecimal WString representation of number, truncated or padded with zeros ("0") to fit the number of digits, if specified.

Description

Hexadecimal digits range from 0-9, or A-F.

If you specify digits > 0, the resulting WString will be exactly that length. It will be truncated or padded with zeros on the left, if necessary.

The length of the wstring will not go longer than the maximum number of digits required for the type of expression (8 for a Long, 16 for floating point or LongInt)

Example

Print Hex(54321)
Print Hex(54321, 2)
Print Hex(54321, 5)

will produce the output:
D431
31
0D431

Platform Differences

  • Unicode strings are not supported in the DOS port of FreeBASIC.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Whex.

Differences from QB

  • New to FreeBASIC.

See also

  • Hex
  • WBin
  • WOct


Width
Sets or gets the number of rows and columns of the display

Syntax

Width [columns] [, rows]
Width LPrint columns
Width { #filenum | devicename }, columns
result = Width( )

Parameters

columns
columns (in characters) for output
rows
rows (in characters) for output
filenum
file number to apply to
devicename
device name to apply to

Return Value

Returns a 32 bit Long where the High Word is the number of rows and the Low Word is the number of columns currently set.

Description

Sets the maximum number of columns of characters of an output device (console, printer or text file). If text sent to the device reaches the width an automatic carriage return is generated.

Using Width as a function returns the current console width in the low word and the current height in the high word.

If a device is not given then Width takes effect on the active console/graphics screen, and a second argument specifying maximum number of rows is allowed.

In graphics modes Width is used to indirectly select the font size by setting one of the character height * width pairs allowed (See Screen (Graphics)). If rows / cols is an invalid combination, no changes are made to the screen display.
Valid font heights are 8 pixels, 14 pixels and 16 pixels. The fonts all have a fixed width of 8 pixels.
(see second example)

Using the Width command in graphic mode also forces a screen clear (Cls).

Example

Dim As Long w
w = Width
Print "rows: " & HiWord(w)
Print "cols: " & LoWord(w)

''Set up a graphics screen
Const W = 320, H = 200
ScreenRes W, H

Dim As Long twid
Dim As UInteger tw, th

'' Fetch and print current text width/height:
twid = Width()
tw = LoWord(twid): th = HiWord(twid)
Print "Default for current screen (8*8)"
Print "Width:  " & tw
Print "Height: " & th
Sleep

Width W\8, H\16 '' Use 8*16 font

twid = Width()
tw = LoWord(twid): th = HiWord(twid)
Print "Set to 8*16 font"
Print "Width:  " & tw
Print "Height: " & th
Sleep

Width W\8, H\14 '' Use 8*14 font

twid = Width()
tw = LoWord(twid): th = HiWord(twid)
Print "Set to 8*14 font"
Print "Width:  " & tw
Print "Height: " & th
Sleep

Width W\8, H\8 '' Use 8*8 font

twid = Width()
tw = LoWord(twid): th = HiWord(twid)
Print "Set to 8*8 font"
Print "Width:  " & tw
Print "Height: " & th
Sleep

Platform Differences

  • In a Windows console any values > 0 can be used in windowed mode.
  • On a DOS or Windows full-screen console, the valid dimensions depend on the capabilities of the hardware.
  • Linux doesn't allow applications to change the console size.

Differences from QB

  • columns was limited to 40 or 80, while rows could be 25, 30, 43, 50 or 60, depending on the graphics hardware and screen mode being used.

See also

  • LoWord
  • HiWord
  • CsrLin
  • Pos


Window
Sets new view coordinates mapping for current viewport

Syntax

Window [ [Screen] ( x1, y1 )-( x2, y2 ) ]

Parameters

Screen
Optional argument specifying y coordinates increase from top to bottom.
( x1, y1 )-( x2, y2 )
New floating point values corresponding to the opposite corners of the current viewport. If omitted, the Window coordinate mapping is removed.

Description

Window is used to define a new coordinates system. (x1, y1) and (x2, y2) are the new coordinates to be mapped to the opposite corners of the current viewport; all future coordinates passed to graphics primitive statements will be affected by this new mapping. If Screen is omitted, the new coordinates system will be Cartesian, that is, with y coordinates increasing from bottom to top. Call Window with no argument to disable the coordinates transformation.

FreeBASIC's current behavior is to keep track of the corners of the Window, rather than a specific coordinate mapping. This means that the coordinate mapping can change after calls to View.
The Window corners are also currently taken into account when working on image buffers, so when a Window is in effect, the coordinate mapping will be different from image to image.

When there is no Window in effect, there is no coordinate mapping in effect, so the effective coordinate system is constant, independent of image buffer sizes or View coordinates (if any).

Example

'' The program shows how changing the view coordinates mapping for the current viewport changes the size of a figure drawn on the screen.
'' The effect is one of zooming in and out:
''   - As the viewport coordinates get smaller, the figure appears larger on the screen, until parts of it are finally clipped,
''        because they lie outside the window.
''   - As the viewport coordinates get larger, the figure appears smaller on the screen.

Declare Sub Zoom (ByVal X As Integer)
Dim As Integer X = 500, Xdelta = 50

Screen 12
Do
  Do While X < 525 And X > 50
    X += Xdelta                      '' Change window size.
    Zoom(X)
    If Inkey <> "" Then Exit Do, Do  '' Stop if key pressed.
    Sleep 100
  Loop
  X -= Xdelta
  Xdelta *= -1                       '' Reverse size change.
Loop

Sub Zoom (ByVal X As Integer)
  Window (-X,-X)-(X,X)               '' Define new window.
  ScreenLock
  Cls
  Circle (0,0), 60, 11, , , 0.5, F   '' Draw ellipse with x-radius 60.
  ScreenUnlock
End Sub

Screen 13

'' define clipping area
View ( 10, 10 ) - ( 310, 150 ), 1, 15

'' set view coordinates
Window ( -1, -1 ) - ( 1, 1 )

'' Draw X axis
Line (-1,0)-(1,0),7
Draw String ( 0.8, -0.1 ), "X"

'' Draw Y axis
Line (0,-1)-(0,1),7
Draw String ( 0.1, 0.8 ), "Y"

Dim As Single x, y, s

'' compute step size (corresponds to a step of 1 pixel on x coordinate)
s = 2 / PMap( 1, 0 )

'' plot the function
For x = -1 To 1 Step s
  y = x ^ 3
  PSet( x, y ), 14
Next x

'' revert to screen coordinates
Window

'' remove the clipping area
View

'' draw title
Draw String ( 120, 160 ), "Y = X ^ 3"

Sleep

Differences from QB

  • QBASIC preserves the coordinate mapping after subsequent calls to VIEW.
  • FreeBASIC's current behavior is to preserve the WINDOW coordinates after calls to VIEW, or when working on images, meaning that the coordinate mapping may undergo scaling/translations. (If a WINDOW hasn't been set, there is no coordinate mapping, and so it doesn't change after calls to VIEW.) The behavior may change in future, but consistent behavior can be be assured over inconstent viewport coordinates by re-calling WINDOW when you change the VIEW.

See also

  • Screen (Graphics)
  • View (Graphics)
  • PMap


WindowTitle
Sets the program window title

Syntax

Declare Sub WindowTitle ( ByRef title As Const String )

Usage

WindowTitle title

Parameters

title
the string to be assigned as new window title.

Description

This statement is useful to change the program window title. The new title set will become active immediately if the program already runs in windowed mode, otherwise will become the new title for any window produced by subsequent calls to the Screen (Graphics) or ScreenRes statement. If this function is not called before setting a new windowed mode via Screen (Graphics) or ScreenRes, the program window will use the executable file name (without the extension) as title by default.
This command has no effect in consoles.

Example

'Set screen mode
Screen 13

'Set the window title
WindowTitle "FreeBASIC example program"

Sleep

Platform Differences

  • Not present in DOS version / target of FreeBASIC

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Windowtitle.

Differences from QB

  • New to FreeBASIC

See also

  • ScreenControl (function SET_WINDOW_TITLE or GET_WINDOW_TITLE)
  • Screen (Graphics)
  • ScreenRes


Winput()
Reads a number of wide-characters from console or file

Syntax

Declare Function WInput( ByVal num As Integer ) As WString
Declare Function WInput( ByVal num As Integer, ByVal filenum As Long = 0 ) As WString

Usage

result = WInput( num [, [#]filenum } )

Parameters

num
Number of characters to read.
filenum
File number of bound file or device.

Return Value

Returns a WString of the characters read.

Description

Reads a number of wide-characters from the console, or a bound file/device specified by filenum.

The first version waits for and reads n wide characters from the keyboard buffer. Extended keys are not read. The characters are not echoed to the screen.

The second version waits for and reads n wide characters from a file or device. The file position is updated.

Note: FreeBASIC does not currently support reading wide-characters from the console.

Note: Using Winput() is dedicated to Input Access file mode, but it is also allowed in Binary/Random Access file mode with some restrictions on the values of num (for random mode, num must be equal to the record length set by the LEN clause (or implicit value) in the OPEN statement).

Example

Dim char As WString * 2

Dim filename As String, enc As String
Dim f As Long

Line Input "Please enter a file name: ", filename
Line Input "Please enter an encoding type (optional): ", enc
If enc = "" Then enc = "ascii"

f = FreeFile
If Open(filename For Input Encoding enc As #f) = 0 Then

    Print "Press space to read a character from the file, or escape to exit."

    Do

        Select Case Input(1)

        Case " " 'Space

            If EOF(f) Then

                Print "You have reached the end of the file."
                Exit Do

            End If

            char = WInput(1, f)
            Print char & " (char no " & Asc(char) & ")"

        Case Chr(27) 'Escape

            Exit Do

        End Select

    Loop

    Close #f

Else

    Print "There was an error opening the file."

End If

Dialect Differences

  • Not available in the -lang qb dialect.

Differences from QB

  • QB does not support Unicode

See also

  • Input()
  • Open


With
Statement block to allow implicit access to fields in a user defined type variable

Syntax

With user_defined_var
statements
End With

Description

The With...End With block allows the omission of the name of a variable of a user-defined Type when referring to its fields. The fields may then be accessed with just a single period (.) before them, e.g. if the Type contains an field element called "element", then it could be accessed within the With block as ".element".

It can be used as a shorthand to save typing and avoid cluttering the source.
With can also be used with dereferenced pointers as the second example shows, with temporary instances (with explicit or implicit constructor) as the third example, and even with temporary types (without any constructor), because destruction of such temporary instances/types is deferred to the end of the With scope.
But returns byref of functions from temporary instances/types taken as arguments are not supported (and no error message).

With blocks may be nested. In this case, only the innermost With block is active, and any outer ones are ignored until the inner one is closed again. See the third example for an illustration of this.

Internally, the address of the variable is taken at the start of the With block, and then is used to calculate any element accesses within the block.
Note that this means that Goto should not be used to jump into a With block, otherwise the address will not have been set, and the results of trying to access it will be undefined.

Note for With block used inside member procedure:
To access duplicated symbols defined as global outside the Type, add two dots as prefix: "..SomeSymbol" (inside a With...End With block).

Example

Type rect_type
    x As Single
    y As Single
End Type

Dim the_rectangle As rect_type
Dim As Integer temp, t

With the_rectangle
    temp = .x
    .x = 234 * t + 48 + .y
    .y = 321 * t + 2
End With

Type rect_type
    x As Single
    y As Single
End Type

Dim the_rectangle As rect_type Ptr

the_rectangle = CAllocate( 5 * Len( rect_type ) )

Dim As Integer loopvar, temp, t

For loopvar = 0 To 4

  With the_rectangle[loopvar]  '' dereferenced pointer

    temp = .x
    .x = 234 * t + 48 + .y
    .y = 321 * t + 2

  End With

Next

Type rect_type
    x As Single
    y As Single
    Declare Constructor()
    Declare Constructor(ByVal x0 As Single, ByVal y0 As Single)
End Type

Constructor rect_type()
End Constructor

Constructor rect_type(ByVal x0 As Single, ByVal y0 As Single)
    This.x = x0
    This.y = y0
End Constructor

Dim the_rectangle As rect_type

With rect_type(1, 2)  '' temporary instance created here held up to 'End With'
    the_rectangle.x = .x + .y  '' 1 + 2 = 3
    the_rectangle.y = .x - .y  '' 1 - 2 = -1
End With

Type rect_type
    x As Single
    y As Single
End Type

Dim As rect_type rect1, rect2

'' Nested With blocks
With rect1

    .x = 1
    .y = 2

    With rect2

        .x = 3
        .y = 4

    End With

End With

Print rect1.x, rect1.y '' 1,  2
Print rect2.x, rect2.y '' 3,  4

Version

  • Before fbc 1.10.0, temporary types (without any constructor) were not supported (because destruction of such temporary types was called immediately after the With statement).

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __With.
  • In the -lang qb and -lang fblite dialects, variables declared inside a With..End With block have a function-wide scope as in QB.
  • In the -lang fb and -lang deprecated dialects, variables declared inside a With..End With block are visible only inside the block, and can't be accessed outside it.

Differences from QB

  • New to FreeBASIC

See also

  • Type


WOct
Converts a number to a Unicode octal representation

Syntax

Declare Function WOct ( ByVal number As UByte ) As WString
Declare Function WOct ( ByVal number As UShort ) As WString
Declare Function WOct ( ByVal number As ULong ) As WString
Declare Function WOct ( ByVal number As ULongInt ) As WString
Declare Function WOct ( ByVal number As Const Any Ptr ) As WString

Declare Function WOct ( ByVal number As UByte, ByVal digits As Long ) As WString
Declare Function WOct ( ByVal number As UShort, ByVal digits As Long ) As WString
Declare Function WOct ( ByVal number As ULong, ByVal digits As Long ) As WString
Declare Function WOct ( ByVal number As ULongInt, ByVal digits As Long ) As WString
Declare Function WOct ( ByVal number As Const Any Ptr, ByVal digits As Long ) As WString

Usage

result = WOct( number [, digits ] )

Parameters

number
Number to convert to octal representation.
digits
Desired number of digits in the returned string.

Return Value

The Unicode octal representation of the number, truncated or padded with zeros ("0") to fit the number of digits, if specified.

Description

Returns the octal WString (Unicode) representation of number. Octal digits range from 0 to 7.

If you specify digits > 0, the result string will be exactly that length. It will be truncated or padded with zeros on the left, if necessary.

The length of the returned string will not be longer than the maximum number of digits required for the type of number (3 characters for Byte, 6 for Short, 11 for Long, and 22 for LongInt)

Example

Print WOct(54321)
Print WOct(54321, 4)
Print WOct(54321, 8)

will produce the output:

152061
2061
00152061

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Woct.

Platform Differences

  • Unicode strings are not supported in the DOS port of FreeBASIC.

Differences from QB

  • In QBASIC Unicode was not supported.

See also

  • WBin
  • WHex


Write
Outputs a comma-separated list of values to the screen

Syntax

Write [ expressionlist ]

Parameters

expressionlist
Comma-separated list of items to print

Description

Outputs the values in expressionlist to the screen. The values are separated with commas, and strings are enclosed in double quotes. Numeric values with an absolute value of less than one are prefixed with a zero (0) if none is given (e.g. 0.5, -0.123). Floating-point numbers with absolute values greater than or equal to 10^16, or with absolute values greater than 0 and less than 10^-5 are printed in scientific notation (e.g. 1.8e+019, 3e-005)

If no expression list is given, Write outputs a carriage return.

Example

Dim i As Integer = 10
Dim d As Double = 123.456
Dim s As String = "text"

Write 123, "text", -.45600
Write
Write i, d, s

will produce the output:

123,"text",-0.456

10,123.456,"text"

Differences from QB

  • QBASIC might print format floating-point values in slightly different ways.

See also

  • Write #
  • Print
  • ?


Write #
Outputs a comma-separated list of values to a text file or device

Syntax

Write # filenum , [ expressionlist ]

Parameters

filenum
File number of an open file or device opened for Output or Append.
expressionlist
Comma-separated list of items to print

Description

Outputs the values in expressionlist to the text file or device bound to filenum. The values are separated with commas, and strings are enclosed in double quotes. Numeric values greater than zero (0) and less than one (1) are prefixed with a zero (0) if none is given (e.g., a value of -.123 will be output as -0.123). Extra zeroes are truncated.

If no expression list is given, Write # outputs a carriage return (note that the comma after filenum is still necessary, even if no expression list is given).
The purpose of Write # is to create a file that can be read back by using Input #.

Note:
- Using Write # is naturally dedicated to Output/Append Access file mode.
- It is also allowed in Binary/Random Access file mode, but this was never well tested and results may vary.

Example

Const filename As String = "file.txt"

Dim filenum As Integer = FreeFile()
If 0 <> Open(filename, For Output, As filenum) Then
    Print "error opening " & filename & " for output."
    End -1
End If

Dim i As Integer = 10
Dim d As Double = 123.456
Dim s As String = "text"

Write #filenum, 123, "text", -.45600
Write #filenum,
Write #filenum, i, d, s

will produce the file:

123,"text",-0.456

10,123.456,"text"

Differences from QB

  • None

See also

  • Write
  • Print #
  • ? #
  • Input #


Write (File Access)
File access specifier

Syntax

Open filename As String For Binary Access Write As #filenum As Integer

Description

Specifier for the Access clause in the Open statement. Write specifies that the file is accessible for output.

Example

See example at Access

Differences from QB

  • None known.

See also

  • Access
  • Open


WSpace
Creates a WString of a given length filled with spaces (" ")

Syntax

Declare Function WSpace( ByVal count As Integer ) As WString

Usage

result = WSpace( count )

Parameters

count
An integer type specifying the length of the string to be created.

Return Value

The created WString. An empty string will be returned if count <= 0.

Description

WSpace creates a wstring (wide character string- Unicode) with the specified number of spaces.

Example

Dim a As WString * 10
a = "x" + WSpace(3) + "x"
Print a ' prints: x   x

Platform Differences

  • Unicode strings are not supported in the DOS port of FreeBASIC.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Wspace.

Differences from QB

  • New to FreeBASIC

See also

  • Space
  • WString


WStr
Returns a wide-character string representation of a number or ASCII character string

Syntax

Declare Function WStr ( ByVal n As Byte ) As WString
Declare Function WStr ( ByVal n As UByte ) As WString
Declare Function WStr ( ByVal n As Short ) As WString
Declare Function WStr ( ByVal n As UShort ) As WString
Declare Function WStr ( ByVal n As Long ) As WString
Declare Function WStr ( ByVal n As ULong ) As WString
Declare Function WStr ( ByVal n As LongInt ) As WString
Declare Function WStr ( ByVal n As ULongInt ) As WString
Declare Function WStr ( ByVal n As Single ) As WString
Declare Function WStr ( ByVal n As Double ) As WString
Declare Function WStr ( ByRef str As Const String ) As WString
Declare Function WStr ( ByVal str As Const WString Ptr ) As WString

Usage

result = WStr( number )
or
result = WStr( string )

Parameters

number
Numeric expression to convert to a wide-character string.
string
String expression to convert to a wide-character string.

Return Value

Returns the wide-character representation of the numeric or string expression.

Description

WStr converts numeric variables to their wide-character string representation. It is the wide-character equivalent to Str.

WStr also converts ASCII character strings to Unicode character strings. If a wide-character string is given, that string is returned unmodified.

Example

#if defined( __FB_WIN32__ )
#include "windows.bi"
#endif

Dim zs As ZString * 20
Dim ws As WString * 20

zs = "Hello World"
ws = WStr(zs)

#if defined( __FB_WIN32__ )

MessageBox(null, ws, WStr("Unicode 'Hello World'"), MB_OK Or MB_ICONINFORMATION)

#else

Print ws
Print WStr("Unicode 'Hello World'")

#endif

Platform Differences

  • DOS does not support WStr.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Wstr.

Differences from QB

  • New to FreeBASIC

See also

  • Str
  • WString


WString

Standard data type: wide character string

Syntax

Dim variable As WString * size
Dim variable As WString Ptr

Description

A WString is a fixed-size array of wide-character elements. It has no descriptor and does not resize automatically. If a WString value is declared with a fixed size, that size includes the terminating zero element, so the usable text capacity is size - 1 elements. When assigning to a fixed-size WString, FreeBASIC truncates the assigned text as needed so that the terminator still fits.

A WString Ptr points at a zero-terminated wide-character buffer. Pointer-based WString storage must be allocated, reallocated, and freed explicitly with Allocate, Callocate, Reallocate, and Deallocate.

The end of a WString is marked by element value 0. The FreeBASIC string handling routines append that terminator when creating or assigning WString data, and they calculate the length by scanning for the first zero element. Embedded zero elements therefore terminate the visible string for the normal WString operations.

For a WString, Len returns the number of stored wide-character elements before the first zero terminator. SizeOf returns the number of bytes reserved by the variable when that storage size is known by the compiler, such as a fixed-size WString variable. SizeOf cannot recover the original allocation size from a dereferenced pointer or from a ByRef argument.

The size and meaning of one WString element depends on the target. Do not assume that WString is always UTF-16, always UTF-32, or always a complete Unicode scalar value per element. On targets with 16-bit wide characters, values outside the Basic Multilingual Plane are represented as UTF-16 surrogate pairs where the compiler or conversion routines support them, and string indexing still sees two 16-bit elements.

SizeOf(WString) returns the number of bytes used by one WString element for the current target.

WString is useful for calling wide-character APIs and for text that should be processed with FreeBASIC wide-string routines. For portable file formats, network protocols, and stored data, prefer an explicit encoding such as UTF-8 and convert at the boundary where needed.

When processing source files, FreeBASIC can parse ASCII source with Unicode escape sequences (\u or \U), or source files saved as UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, or UTF-32BE when the file has a Byte Order Mark (BOM). For broad portability, plain ASCII or UTF-8 source without relying on target-specific wide-character width is usually the safest choice.

The FreeBASIC text file functions can read and write Unicode files in different encodings when Encoding is specified when the file is opened. The data is converted between the file encoding and the target's internal WString representation.

When allocating dynamic memory for a WString, prefer Callocate or otherwise make sure the buffer is initialized or assigned immediately. A manually allocated WString buffer without a zero terminator is not a valid zero-terminated string.

Pointer note: *pw, where pw is a WString Ptr, can be interpreted either as a WString expression or as a reference to one wide-character element depending on context. If the other operand is numeric, *pw is treated as the element pointed to by pw. Use parentheses when the intent matters. For example, (*pw)[n] indexes the pointed-to string, while pw[n] indexes the pointer as an array of WString elements.

Example

Dim As WString * 13 str1 => "hello, world"
Print str1
Print Len(str1)    'returns 12, the number of stored WString elements
Print SizeOf(str1) 'returns 13 * SizeOf(WString), the bytes reserved by str1
Dim As WString Ptr str2

str2 = Callocate( 13 * SizeOf(WString) )
If str2 = 0 Then End 1

*str2 = "hello, world"
Print *str2
Print Len(*str2)   'returns 12, the number of stored WString elements

Deallocate str2

Platform Differences

WString follows the target wide-character model used by the runtime and C/CRT declarations.

  • On DOS, WString elements are byte-sized. The DOS port does not provide a full Unicode wide-character environment, so portable Unicode code should not rely on DOS WString storage.
  • On Android in this tree, wchar_t and WString elements are byte-sized. Prefer UTF-8 String or ZString storage for portable Android text unless a specific API requires otherwise.
  • On Windows targets, including Win32, Win64, and Windows ARM64, WString elements are 16-bit UTF-16 code units. Xbox and Cygwin also use 16-bit wide-character elements in this tree. Len and indexing count code units, not user-perceived characters.
  • On most other Unix-like targets, including Linux, the BSD targets, macOS/Darwin, Solaris/illumos, Haiku, and Wii, WString elements are 32-bit signed wide-character values.
  • For C interop, include the appropriate CRT headers and use the target's wchar_t declarations instead of assuming a fixed width.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __WString.

Differences from QB

  • New to FreeBASIC.

See also

  • String (data type)
  • ZString (data type)
  • String (function)
  • WString (function)
  • WSpace
  • WStr
  • WChr
  • WBin
  • WHex
  • WOct
  • WInput()
  • Standard Data Type Limits
  • Extends WString


Wstring (Function)
Fills a WString with a certain length of a certain wide character

Syntax

Declare Function WString ( ByVal count As Integer, ByVal ch_code As Long ) As WString
Declare Function WString ( ByVal count As Integer, ByRef ch As Const WString ) As WString

Usage

result = WString( count, ch_code )
or
result = WString( count, ch )

Parameters

count
An Integer specifying the length of the string to be created.
ch_code
A Long specifying the Unicode char to be used to fill the string.
ch
A WString whose first character is to be used to fill the string.

Return Value

The created WString. An empty string will be returned if either ch is an empty string, or count <= 0.

Description

WString generates a temporary WString filled with count copies of a Unicode character. This string can be printed or assigned to a previously Dimed WString.

Example

Print WString( 4, 934 )
Print WString( 5, WStr("Indeed") )
End 0

Platform Differences

  • Unicode strings are not supported in the DOS port of FreeBASIC.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Wstring.

Differences from QB

  • QBasic does not support Unicode

See also

  • String (data type)
  • WSpace
  • WString (data type)

ΦΦΦΦ
IIIII


Operator Xor (Exclusive Disjunction)
Returns the bitwise-xor (exclusive disjunction) of two numeric values

Syntax

Declare Operator Xor ( ByRef lhs As T1, ByRef rhs As T2 ) As Ret

Usage

result = lhs Xor rhs

Parameters

lhs
The left-hand side expression.
T1
Any numeric or boolean type.
rhs
The right-hand side expression.
T2
Any numeric or boolean type.
Ret
A numeric or boolean type (varies with T1 and T2).

Return Value

Returns the bitwise-xor of the two operands.

Description

This operator returns the bitwise-exclusion of its operands, a logical operation that results in a value with bits set depending on the bits of the operands (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value).

The truth table below demonstrates all combinations of a boolean-exclusion operation:

Lhs Bit | Rhs Bit | Result
0 | 0 | 0
1 | 0 | 1
0 | 1 | 1
1 | 1 | 0

No short-circuiting is performed - both expressions are always evaluated.

The return type depends on the types of values passed. Byte, UByte and floating-point type values are first converted to Integer. If the left and right-hand side types differ only in signedness, then the return type is the same as the left-hand side type (T1), otherwise, the larger of the two types is returned. Only if the left and right-hand side types are both Boolean, the return type is also Boolean.

This operator can be overloaded for user-defined types.

Example

' Using the XOR operator on two numeric values
Dim As UByte numeric_value1, numeric_value2
numeric_value1 = 15 '00001111
numeric_value2 = 30 '00011110

'Result =  17  =     00010001
Print numeric_value1 Xor numeric_value2
Sleep

' Using the XOR operator on two conditional expressions
Dim As UByte numeric_value1, numeric_value2
numeric_value1 = 10
numeric_value2 = 15

If numeric_value1 = 10 Xor numeric_value2 = 20 Then Print "Numeric_Value1 equals 10 or Numeric_Value2 equals 20"
Sleep

' This will output "Numeric_Value1 equals 10 or Numeric_Value2 equals 20"
' because only the first condition of the IF statement is true

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Operator Truth Tables


Operator Xor= (Exclusive Disjunction And Assign)
Performs a bitwise-xor (exclusive disjunction) and assigns the result to a variable

Syntax

Declare Operator Xor= ( ByRef lhs As T1, ByRef rhs As T2 )

Usage

lhs Xor= rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric or boolean type.
rhs
The value to perform a bitwise-xor (exclusive or) with lhs.
T2
Any numeric or boolean type.

Description

This operator performs a bitwise-or and assigns the result to a variable (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value). It is functionally equivalent to:
lhs = lhs Xor rhs

Xor= compares each bit of its operands, lhs and rhs, and if both bits are the same (both 1 or both 0), then the corresponding bit in the first operand, lhs, is set to 0, otherwise it is set to 1.

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol 'Xor=>' can be also used.

Example

Dim As UByte a = &b00110011
Dim As UByte b = &b01010101
a Xor= b
'' Result    a = &b01100110
Print Bin(a)

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Xor


Xor (Graphics Put)
Parameter to the Put graphics statement which uses a bit-wise Xor as the blitting method

Syntax

Put [ target, ] [ STEP ] ( x,y ), source [ ,( x1,y1 )-( x2,y2 ) ], Xor

Parameters

Xor
Required.

Description

The Xor method combines each source pixel with the corresponding destination pixel, using the bit-wise Xor function. The result of this is output as the destination pixel.
This method works in all graphics modes. There is no mask color, although color values of 0 (RGBA(0, 0, 0, 0) in full-color modes) will have no effect, because of the behavior of Xor.

In full-color modes, each component (red, green, blue and alpha) is kept in a discrete set of bits, so the operation can be made to only affect some of the channels, by making sure the all the values of the other channels are set to 0.

Example

''open a graphics window
ScreenRes 320, 200, 16

''create a sprite containing a circle
Const As Integer r = 32
Dim c As Any Ptr = ImageCreate(r * 2 + 1, r * 2 + 1, 0)
Circle c, (r, r), r, RGBA(255, 255, 255, 0), , , 1, f

''put the three sprites, overlapping each other in the middle
Put (146 - r, 108 - r), c, Xor
Put (174 - r, 108 - r), c, Xor
Put (160 - r,  84 - r), c, Xor

''free the memory used by the sprite
ImageDestroy c

''pause the program before closing
Sleep

Differences from QB

  • None

See also

  • Xor
  • Put (Graphics)


Year
Gets the year from a Date Serial

Syntax

Declare Function Year ( ByVal date_serial As Double ) As Long

Usage

#include "vbcompat.bi"
result = Year( date_serial )

Parameters

date_serial
the date

Return Value

Returns the year from a variable containing a date in Date Serial format.

Description

The compiler will not recognize this function unless vbcompat.bi is included.

Example

#include "vbcompat.bi"

Dim a As Double = DateSerial (2005, 11, 28) + TimeSerial(7, 30, 50)

Print Format(a, "yyyy/mm/dd hh:mm:ss "); Year(a)

Differences from QB

  • Did not exist in QB. This function appeared in PDS and VBDOS

See also

  • Date Serials


ZString
Standard data type: 8 bit character string

Syntax

Dim variable As ZString * size
Dim variable As ZString Ptr

Description

A ZString is a C-style fixed-size array of chars. It has no descriptor so its length is calculated faster to pass it as an argument to functions. When the variable has a fixed size (numeric constant, or expression that can be evaluated at compile time), FreeBASIC avoids any overflow that could occur on assignment, by truncating the contents to a length of size - 1.

A ZString Ptr can point to a standard ZString, also can be used to implement an "user-managed" ZString, in this case Allocate/Reallocate/Deallocate must be used to size-resize-dispose it and is up to the user to avoid overflows .

The end of the string is marked by a null character (0 ASCII). This is automatically added by the FreeBASIC string handling functions. A null character will be appended when the string is created, and the length will be calculated by scanning the string for the first null character. A null character (e.g. Chr(0)) may never be contained in the text of a ZString or the rest of the string will be truncated.

In a ZString, Len returns the size of the contained string and SizeOf returns the space allocated to the ZString. SizeOf only works if the size is known by the compiler, i.e. a fixed-size ZString variable is passed directly, not as a dereferenced pointer or a ByRef function argument.

Any intrinsic string functions like Left will work with ZString's too, plus any string operator.

This type is provided for easy interfacing with C libraries and to also replace the fixed-length strings, that can't be managed through pointers.
Any string type argument may be directly passed to a procedure referring to a parameter declared as ZString Ptr. The compiler performs itself an automatic conversion (without warning message) between any string type argument and the ZString Ptr type parameter.

When allocating dynamic memory for a ZString, the safest is to use CAllocate (or at worst, to use Allocate followed by an immediate assignment of the string data, as in the second example), in order to avoid creating string data without any null character (the terminal character for a ZString).

Note : When any operand of a binary operator (as assignment, equal, +, *, ...) consists in dereferencing a 'Zstring Ptr' pointer ('pz'), this can give a 'Zstring' string or a 'Ubyte' variable, depending on the other operand. If the other operand is numeric, so the dereferenced 'Zstring Ptr' pointer ('*pz') will be treated as a 'Ubyte' reference to the one character pointed. If a 'Zstring' pointer indexing '[]' operator is used as dereferencing syntax ('pz[n]'), it is basically a short-cut version of the 'String' indexing '[]' operator ('(*pz)[n]').

Example

Dim As ZString * 13 str1 => "hello, world"
Print str1
Print Len(str1)     'returns 12, the size of the string it contains
Print SizeOf(str1)  'returns 13, the size of the variable

Dim As ZString Ptr str2
str2 = Allocate( 13 )
*str2 = "hello, world"
Print *str2
Print Len(*str2)     'returns 12, the size of the string it contains

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __Zstring.

Differences from QB

  • New to FreeBASIC

See also

  • String
  • WString
  • Standard Data Type Limits
  • Extends Zstring


Functional Keyword List
List of FreeBASIC keywords sorted by the function they perform.

Arrays
  • Arraylen
  • Arraysize
  • Erase
  • Fbarray (Array Descriptor Structure And Access)
  • LBound
  • ReDim
  • Preserve
  • UBound

Bit manipulation
  • Bit
  • BitReset
  • BitSet
  • HiByte
  • HiWord
  • LoByte
  • LoWord

Compiler switches
  • DefByte
  • DefDbl
  • DefInt
  • DefLng
  • DefLongInt
  • DefShort
  • DefSng
  • DefStr
  • DefUByte
  • DefUInt
  • Defulongint
  • DefUShort
  • Option Base
  • Option ByVal
  • Option Dynamic
  • Option Escape
  • Option Explicit
  • Option Gosub
  • Option Nogosub
  • Option NoKeyword
  • Option Private
  • Option Static

Console
  • ? (Shortcut For 'Print')
  • ? Using (Shortcut For 'Print Using')
  • Beep
  • Cls
  • Color
  • CsrLin
  • Locate
  • Open Cons
  • Open Err
  • Open Pipe
  • Open Scrn
  • Pos
  • Print
  • Print Using
  • Screen (Console)
  • Spc
  • Tab
  • View (Console)
  • Width
  • Write

Data types and declarations
  • Alias (Modifier)
  • As
  • Boolean
  • Byref (Variables)
  • Byte
  • Class
  • Const
  • Const (Qualifier)
  • Dim
  • Double
  • Enum
  • Extends
  • Extends Wstring
  • Extends Zstring
  • Function (Pointer)
  • Implements
  • Integer
  • Long
  • LongInt
  • Object
  • Scope
  • Shared
  • Short
  • Single
  • Static
  • String
  • Sub (Pointer)
  • Type
  • Type (Alias)
  • Type (Temporary)
  • UByte
  • UInteger
  • ULong
  • ULongInt
  • Union
  • Unsigned
  • UShort
  • Var
  • With
  • WString
  • ZString

Date and time
  • Date
  • DateAdd
  • DateDiff
  • DatePart
  • DateSerial
  • DateValue
  • Day
  • Hour
  • IsDate
  • Minute
  • Month
  • MonthName
  • Now
  • Second
  • SetDate
  • SetTime
  • Time
  • TimeSerial
  • TimeValue
  • Timer
  • Year
  • Weekday
  • WeekdayName

Debug support
  • Assert
  • AssertWarn
  • Stop

Error handling
  • Erfn
  • Erl
  • Ermn
  • Err
  • Error
  • Local
  • On Error
  • Resume
  • Resume Next

Files
  • ? # (Shortcut For 'Print #')
  • Access
  • Append
  • Binary
  • BLoad
  • BSave
  • Close
  • Encoding
  • Eoc
  • EOF
  • FreeFile
  • Get # (File I/O)
  • Input (File I/O)
  • Input #
  • Line Input #
  • LOC
  • Lock
  • LOF
  • Open
  • Open Tcp
  • Open Tcp Server
  • Output
  • Print #
  • Put # (File I/O)
  • Random
  • Read (File Access)
  • Read Write (File Access)
  • Reset
  • Seek (Statement)
  • Seek (Function)
  • Tcp Accept
  • Unlock
  • Write #
  • Write (File Access)

Graphics
  • Add (Graphics Put)
  • Alpha (Graphics Put)
  • And (Graphics Put)
  • Circle
  • Cls
  • Color
  • Custom (Graphics Put)
  • Draw
  • Draw String
  • Event (Message Data From Screenevent)
  • Flip
  • Get (Graphics)
  • ImageConvertRow
  • Getxpad
  • GetTouch
  • GetTouchCount
  • GetTouchHit
  • ImageCreate
  • ImageDestroy
  • ImageInfo
  • Line
  • Or (Graphics Put)
  • Paint
  • Palette
  • PCopy
  • PMap
  • Point
  • PointCoord
  • PReset
  • PSet
  • Pset (Graphics Put)
  • Put (Graphics)
  • RGB
  • RGBA
  • Screen (Graphics)
  • ScreenControl
  • ScreenCopy
  • ScreenEvent
  • ScreenInfo
  • ScreenGLProc
  • ScreenList
  • ScreenLock
  • ScreenPtr
  • ScreenRes
  • ScreenSet
  • ScreenSync
  • ScreenUnlock
  • Trans (Graphics Put)
  • View (Graphics)
  • Window
  • Xor (Graphics Put)

Sound
  • Balance
  • Beep
  • Capture
  • Channel
  • Device
  • Envelope
  • Generated Sound Commands
  • Instrument
  • Midi
  • Music
  • Noise
  • Note
  • Octave
  • Pan
  • Play
  • Sfx
  • Sound
  • Tempo
  • Tone
  • Voice
  • Vol
  • Volume
  • Wave

Hardware access
  • Inp
  • Out
  • Wait
  • Open Com
  • Open Lpt
  • LPos
  • LPrint

Assignment Operators
  • =[>] (Assignment)
  • &= (Concatenate And Assign)
  • += (Add And Assign)
  • -= (Subtract And Assign)
  • *= (Multiply And Assign)
  • /= (Divide And Assign)
  • \= (Integer Divide And Assign)
  • ^= (Exponentiate And Assign)
  • Mod= (Modulus And Assign)
  • And= (Conjunction And Assign)
  • Eqv= (Equivalence And Assign)
  • Imp= (Implication And Assign)
  • Or= (Inclusive Disjunction And Assign)
  • Xor= (Exclusive Disjunction And Assign)
  • Shl= (Shift Left And Assign)
  • Shr= (Shift Right And Assign)
  • Let (Assignment)
  • Let() (Assignment)

Arithmetic Operators
  • + (Add)
  • - (Subtract)
  • * (Multiply)
  • / (Divide)
  • \ (Integer Divide)
  • ^ (Exponentiate)
  • Mod (Modulus)
  • - (Negate)
  • Shl (Shift Left)
  • Shr (Shift Right)

Bitwise operators
  • And
  • Eqv
  • Imp
  • Or
  • Not
  • Xor

Short Circuit operators
  • AndAlso
  • OrElse

Math
  • Abs
  • Acos
  • Asin
  • Atan2
  • Atn
  • Cos
  • Exp
  • Fix
  • Frac
  • Int
  • Log
  • Randomize
  • Rnd
  • Sgn
  • Sin
  • Sqr
  • Tan

Memory
  • Allocate
  • CAllocate
  • Clear
  • Deallocate
  • Delete
  • Fb_Memcopy
  • fb_MemCopyClear
  • Fb_Memmove
  • Field
  • Fre
  • New
  • Peek
  • Poke
  • Reallocate

Meta Commands
  • $Dynamic
  • $Static
  • $Include
  • $Lang

Miscellaneous
  • Asm
  • Data
  • End (Block)
  • Is (Run-Time Type Information Operator)
  • Let
  • OffsetOf
  • Option()
  • To
  • Read
  • Rem
  • Restore
  • SizeOf
  • Swap
  • TypeOf
Modularizing
  • Common
  • DyLibFree
  • DyLibLoad
  • DyLibSymbol
  • Export
  • Extern
  • Extern...End Extern
  • Import
  • Namespace
  • Private
  • Public
  • Using (Namespaces)

Multithreading
  • CondBroadcast
  • CondCreate
  • CondDestroy
  • CondSignal
  • CondWait
  • MutexCreate
  • MutexDestroy
  • MutexLock
  • MutexUnlock
  • ThreadCall
  • ThreadCreate
  • ThreadDetach
  • ThreadSelf
  • ThreadWait

OS / shell
  • Chain
  • ChDir
  • Command
  • CurDir
  • Dir
  • End (Statement)
  • Environ
  • Exec
  • ExePath
  • FileAttr
  • FileCopy
  • FileDateTime
  • FileExists
  • FileFlush
  • FileSetEof
  • FileLen
  • IsRedirected
  • Kill
  • MkDir
  • Name
  • RmDir
  • Run
  • SetEnviron
  • Shell
  • System
  • WindowTitle

Pointers
  • Pointer
  • ProcPtr
  • Ptr (Shortcut For 'Pointer')
  • SAdd
  • StrPtr
  • VarPtr

Predefined symbols
  • __DATE__
  • __DATE_ISO__
  • __FB_64BIT__
  • __FB_ANDROID__
  • __FB_ARG_COUNT__
  • __FB_ARG_EXTRACT__
  • __FB_ARG_LEFTOF__
  • __Fb_Arg_Listexpand__
  • __FB_ARG_RIGHTOF__
  • __FB_ARGC__
  • __FB_ARGV__
  • __FB_ARM__
  • __FB_ASM__
  • __FB_BACKEND__
  • __FB_BIGENDIAN__
  • __FB_BUILD_DATE__
  • __FB_BUILD_DATE_ISO__
  • __FB_BUILD_FORK_ID__
  • __FB_BUILD_SHA1__
  • __FB_CYGWIN__
  • __FB_DARWIN__
  • __FB_DEBUG__
  • __FB_DOS__
  • __FB_ERR__
  • __FB_EVAL__
  • __FB_FPMODE__
  • __FB_FPU__
  • __FB_FREEBSD__
  • __FB_GCC__
  • __FB_GUI__
  • __FB_IIF__
  • __FB_JOIN__
  • __FB_JS__
  • __FB_LANG__
  • __FB_LINUX__
  • __Fb_Loongarch64__
  • __FB_MAIN__
  • __FB_MIN_VERSION__
  • __FB_MT__
  • __FB_NETBSD__
  • __FB_OPENBSD__
  • __FB_OPTIMIZE__
  • __FB_OPTION_BYVAL__
  • __FB_OPTION_DYNAMIC__
  • __FB_OPTION_ESCAPE__
  • __FB_OPTION_EXPLICIT__
  • __FB_OPTION_GOSUB__
  • __FB_OPTION_PRIVATE__
  • __FB_OPTION_PROFILE__
  • __FB_OUT_DLL__
  • __FB_OUT_EXE__
  • __FB_OUT_LIB__
  • __FB_OUT_OBJ__
  • __FB_PCOS__
  • __FB_PPC__
  • __FB_PROFILE__
  • __Fb_Query_Symbol__
  • __FB_QUOTE__
  • __Fb_Riscv64__
  • __Fb_S390X__
  • __FB_SIGNATURE__
  • __FB_SSE__
  • __FB_UNIQUEID__
  • __FB_UNIQUEID_POP__
  • __FB_UNIQUEID_PUSH__
  • __FB_UNIX__
  • __FB_UNQUOTE__
  • __FB_VECTORIZE__
  • __FB_VER_MAJOR__
  • __FB_VER_MINOR__
  • __FB_VER_PATCH__
  • __FB_VERSION__
  • __FB_WIN32__
  • __FB_XBOX__
  • __Fb_Wii__
  • __FB_X86__
  • __FILE__
  • __FILE_NQ__
  • __FUNCTION__
  • __FUNCTION_NQ__
  • __LINE__
  • __PATH__
  • __TIME__
  • False
  • True

Preprocessor
  • #assert
  • #Cmdline
  • #define
  • Fb_No_Gfxlib
  • Fb_No_Sfxlib
  • #else
  • #elseif
  • #elseifdef
  • #elseifndef
  • #endif
  • #endmacro
  • #error
  • #if
  • #ifdef
  • #ifndef
  • #inclib
  • #include
  • #libpath
  • #lang
  • #line
  • #macro
  • #pragma
  • #Pragma Reserve
  • #print
  • #undef
  • defined
  • Once

Procedures
  • Abstract (Member)
  • Alias
  • Any
  • Base (Initialization)
  • Base (Member Access)
  • Byref (Parameters)
  • Byref (Function Results)
  • ByVal
  • Call
  • cdecl
  • Const (Member)
  • Constructor
  • Constructor (Module)
  • Destructor
  • Destructor (Module)
  • Declare
  • Fastcall
  • Function
  • Function (Member)
  • Lib
  • Naked
  • Operator
  • Overload
  • Override
  • pascal
  • Private (Member)
  • Protected (Member)
  • Property
  • Public (Member)
  • Static (Member)
  • Sub
  • Sub (Member)
  • stdcall
  • This
  • Thiscall
  • Virtual (Member)

Variadic Procedures
  • ...
  • Cva_Arg
  • Cva_Copy
  • Cva_End
  • Cva_List
  • Cva_Start
  • va_arg
  • va_first
  • va_next

Program flow
  • Continue
  • Case
  • Do
  • Do...Loop
  • Else
  • ElseIf
  • End If
  • Exit
  • GoSub
  • Goto
  • If...Then
  • IIf
  • Is (Select Case)
  • For
  • For...Next
  • Loop
  • Next
  • On...Gosub
  • On...Goto
  • Return (From Procedure)
  • Return (From Gosub)
  • Select Case
  • Sleep
  • Step
  • Then
  • Until
  • Wend
  • While
  • While...Wend

String functions
  • InStr
  • InStrRev
  • LCase
  • Left
  • Len
  • LSet
  • LTrim
  • Mid (Statement)
  • Mid (Function)
  • Right
  • RSet
  • RTrim
  • Space
  • String (Function)
  • Trim
  • UCase
  • WSpace
  • Wstring (Function)

String and number conversion
  • Asc
  • Bin
  • Chr
  • CVD
  • CVI
  • CVL
  • CVLongInt
  • CVS
  • CVShort
  • Format
  • Hex
  • MKD
  • MKI
  • MKL
  • MKLongInt
  • MKS
  • MKShort
  • Oct
  • Str
  • Val
  • ValLng
  • ValInt
  • ValUInt
  • ValULng
  • WBin
  • WChr
  • WHex
  • WOct
  • WStr

Type casting/conversion
  • Cast
  • CBool
  • CByte
  • CDbl
  • CInt
  • CLng
  • CLngInt
  • CPtr
  • CShort
  • CSign
  • CSng
  • CUByte
  • CUInt
  • CULng
  • CULngInt
  • CUnsg
  • CUShort

User input
  • GetJoystick
  • GetKey
  • GetMouse
  • GetTouch
  • GetTouchCount
  • GetTouchHit
  • Inkey
  • Input()
  • Input (Statement)
  • Line Input
  • MultiKey
  • SetMouse
  • Stick
  • Strig
  • WInput


Operator =[>] (Assign)
Assigns a value to a variable

Syntax

Declare Operator Let ( ByRef lhs As T1, ByRef rhs As T2 )

Usage

lhs = rhs
or
lhs => rhs

or, in the QB dialect,

[ Let ] lhs = rhs
or
[ Let ] lhs => rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric, boolean, string or pointer type.
rhs
The value to assign to lhs.
T2
Any type convertible to T2.

Description

This operator assigns the value of its right-hand side operand (rhs) to its left-hand side operand (lhs). The right-hand side operand must be implicitly convertible to the left-hand side type (T1) (for conversion of a boolean to an integer, false or true boolean value becomes 0 or -1 integer value). For example, you cannot assign a numeric value to a string type; to do that, first convert the numeric value to a string using Str or WStr.
Assignment between arrays is not supported presently.

Avoid confusion with Operator = (Equal), which also uses the '=' symbol.
For this purpose and for solving some cases of ambiguity of the parser (see Byref (Function Results)), the alternative symbol '=>' can be used for assignments in place of '=' (same as already for the initializers).
Note: the '=>' symbol has been chosen against '<=' (already the operator 'Less Than Or Equal') and ':=' (':' used as statement separator).

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Example

Dim i As Integer
i = 420    ' <- this is the assignment operator

If  i = 69 Then   '<-this is the equivalence operator
  Print "ERROR: i should equal 420"
  End -1
End If

Print "All is good."
End 0

' compile with -lang fblite or qb

#lang "fblite"

Dim i As Integer
Let i = 300 ' <-alternate syntax

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.
  • In the -lang qb dialect, an assignment expression can be preceded by the Let keyword.

Differences from QB

  • None

See also

  • Operator = (Equal)
  • Operator Let (Assignment)
  • Swap
  • Coercion and Conversion


Operator &= (Concatenate And Assign)
Appends and assigns a string onto another string

Syntax

Declare Operator &= ( ByRef lhs As String, ByRef rhs As T2 )
Declare Operator &= ( ByRef lhs As WString, ByRef rhs As T2 )

Usage

lhs &= rhs

Parameters

lhs
The string to assign to.
rhs
The value to append to lhs.
T2
Any numeric, string or user-defined type that can be converted to a string.

Description

This operator appends one string onto another. The right-hand side expression (rhs) is converted to a string before concatenation. It is functionally equivalent to,

lhs = lhs & rhs

where the result is assigned back to the left-hand side string.

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: This operator exists in C/C++ with a different meaning - there it performs a bitwise And=.
Note: Similarly to the operator '=[>]' (assign), the alternative symbol '&=>' can be also used.

Example

Dim s As String = "Hello, "
s &= " world!"
Print s

will produce the output:

Hello, world!

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Operator & (String Concatenation With Conversion)
  • Operator += (Add And Assign)


Operator += (Add And Assign)
Adds and assigns a value to a variable

Syntax

Declare Operator += ( ByRef lhs As T1, ByRef rhs As T2 )

Declare Operator += ( ByRef lhs As T Ptr, ByRef rhs As Integer )

Declare Operator += ( ByRef lhs As String, ByRef rhs As String )
Declare Operator += ( ByRef lhs As WString, ByRef rhs As WString )

Usage

lhs += rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric type.
rhs
The value to add to lhs.
T2
Any numeric type.
T
Any data type.

Description

This operator adds and assigns a value to a variable. It is functionally equivalent to:

lhs = lhs + rhs

For numeric types, the right-hand side expression (rhs) will be converted to the left-hand side type (T1).

For string types, this operator is functionally equivalent to Operator &= (Concatenate And Assign).

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol '+=>' can be also used.

Example

Dim n As Double
n = 6
n += 1
Print n
Sleep

Output:
7

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Operator + (Add)
  • Mathematical Functions


Operator -= (Subtract And Assign)
Subtracts and assigns a value to a variable

Syntax

Declare Operator -= ( ByRef lhs As T1, ByRef rhs As T2 )
Declare Operator -= ( ByRef lhs As T Ptr, ByRef rhs As Integer )

Usage

lhs -= rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric type.
rhs
The value to subtract from lhs.
T2
Any numeric type.
T
Any data type.

Description

This operator subtracts and assigns a value to a variable. It is functionally equivalent to:
lhs = lhs - rhs

For numeric types, the right-hand side expression (rhs) will be converted to the left-hand side type (T1).

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol '-=>' can be also used.

Example

Dim n As Double
n = 6
n -= 2.2
Print n
Sleep

Output:
3.8

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Operator - (Subtract)
  • Mathematical Functions


Operator *= (Multiply And Assign)
Multiplies and assigns a value to a variable

Syntax

Declare Operator *= ( ByRef lhs As T1, ByRef rhs As T2 )

Usage

lhs *= rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric type.
rhs
The value to multiply lhs by.
T2
Any numeric type.

Description

This operator multiplies and assigns a value to a variable. It is functionally equivalent to:
lhs = lhs * rhs

The right-hand side expression (rhs) will be converted to the left-hand side type (T1).

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol '*=>' can be also used.

Example

Dim n As Double
n = 6
n *= 2
Print n
Sleep

Output:
12

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Operator * (Multiply)
  • Mathematical Functions


Operator /= (Divide And Assign)
Divides and assigns a value to a variable

Syntax

Declare Operator /= ( ByRef lhs As T1, ByRef rhs As T2 )

Usage

lhs /= rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric type.
rhs
The value to divide lhs by.
T2
Any numeric type.

Description

This operator divides and assigns a value to a variable. It is functionally equivalent to:
lhs = lhs / rhs

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol '/=>' can be also used.

Example

Dim n As Double
n = 6
n /= 2.2
Print n
Sleep

Output:
2.727272727272727

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Operator / (Divide)
  • Mathematical Functions


Operator \= (Integer Divide And Assign)
Integer divides and assigns a value to a variable

Syntax

Declare Operator \= ( ByRef lhs As T1, ByRef rhs As T2 )

Usage

lhs \= rhs

Parameters

lhs
The variable to assign to.
T1
Any numeric type.
rhs
The value to divide lhs by.
T2
Any numeric type.

Description

This operator divides (integer division) and assigns a value to a variable. It is functionally equivalent to:
lhs = lhs \ rhs

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: Similarly to the operator '=[>]' (assign), the alternative symbol '\=>' can be also used.

Example

Dim n As Double
n = 6
n \= 2.2
Print n
Sleep

Output:
3

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Operator \ (Integer Divide)
  • Mathematical Functions


Operator ^= (Exponentiate And Assign)
Exponentiates and assigns a value to a variable

Syntax

Declare Operator ^= ( ByRef lhs As Double, ByRef rhs As Double )

Usage

lhs ^= rhs

Parameters

lhs
The variable to assign to.
rhs
The value to exponentiate lhs by.

Description

This operator exponentiates and assigns a value to a variable. It is functionally equivalent to:
lhs = lhs ^ rhs

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Note: This operator exists in C/C++ with a different meaning - there it performs a Bitwise Xor=.
Note: Similarly to the operator '=[>]' (assign), the alternative symbol '^=>' can be also used.

Example

Dim n As Double
n = 6
n ^= 2
Print n
Sleep

Output:
36

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Operator ^ (Exponentiate)
  • Mathematical Functions


Operator + (Addition)
Sums two expressions

Syntax

Declare Operator + ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator + ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As UInteger

Declare Operator + ( ByRef lhs As Single, ByRef rhs As Single ) As Single
Declare Operator + ( ByRef lhs As Double, ByRef rhs As Double ) As Double

Declare Operator + ( ByRef lhs As T Pointer, ByRef rhs As Integer ) As T Pointer
Declare Operator + ( ByRef rhs As Integer, ByRef lhs As T Pointer ) As T Pointer

Declare Operator + ( ByRef lhs As T, ByRef rhs As Integer ) As T
Declare Operator + ( ByRef lhs As Integer, ByRef rhs As T ) As T

Usage

result = lhs + rhs

Parameters

lhs
The left-hand side expression to sum.
rhs
The right-hand side expression to sum.
T
Any pointer type.

Return Value

Returns the sum of two expressions.

Description

When the left and right-hand side expressions are numeric values, Operator + (Add) returns the sum of the two values.

When the left and right-hand side expressions are string values, Operator + (Add) concatenates the two strings and returns the result.

If an integral value n is added to a T Pointer type, the operator performs pointer arithmetic on the address, returning the memory position of a T value, n indices away (assuming n is within bounds of a contiguous array of T values). This behaves differently from numeric addition, because the Integer value is scaled by SizeOf( T ).

Neither operand is modified in any way.

This operator can be overloaded to accept user-defined types.

Example

Dim n As Single
n = 4.75 + 5.25
Print n

will produce the output:

10

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Operator + (String Concatenation)
  • Mathematical Functions


Operator - (Subtract)
Subtracts two expressions

Syntax

Declare Operator - ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator - ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As UInteger

Declare Operator - ( ByRef lhs As Single, ByRef rhs As Single ) As Single
Declare Operator - ( ByRef lhs As Double, ByRef rhs As Double ) As Double

Declare Operator - ( ByRef lhs As T Pointer, ByRef rhs As T Pointer ) As Integer
Declare Operator - ( ByRef lhs As T Pointer, ByRef rhs As Integer ) As T Pointer

Declare Operator - ( ByRef lhs As T, ByRef rhs As T ) As Integer
Declare Operator - ( ByRef lhs As T, ByRef rhs As Integer ) As T
Declare Operator - ( ByRef lhs As Integer, ByRef rhs As T ) As T

Usage

result = lhs - rhs

Parameters

lhs
The left-hand side expression to subtract from.
rhs
The right-hand side expression to subtract.
T
Any pointer type.

Return Value

Returns the subtraction of two expressions.

Description

When the left and right-hand side expressions are numeric values, Operator - (Subtract) returns the subtraction of the two values.

If the left and right-hand side expressions are both of the T Pointer type, for some type T, the operator performs pointer subtraction on the address, returning the result. This is different from numeric subtraction because the difference is divided by SizeOf( T ).

If an integral value n is subtracted from a T Pointer type, the operator performs pointer arithmetic on the address, returning the memory position of a T value, n indices before (assuming (-n) is within bounds of a contiguous array of T values). This behaves differently from numeric subtraction, because the Integer value is scaled by SizeOf( T ).

Neither operand is modified in any way.

This operator can be overloaded to accept user-defined types.

Example

Dim n As Single
n = 4 - 5
Print n

will produce the output:

-1

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Mathematical Functions


Operator * (Multiply)
Multiplies two numeric expressions

Syntax

Declare Operator * ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator * ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As UInteger

Declare Operator * ( ByRef lhs As Single, ByRef rhs As Single ) As Single
Declare Operator * ( ByRef lhs As Double, ByRef rhs As Double ) As Double

Usage

result = lhs * rhs

Parameters

lhs
The left-hand side multiplicand expression.
rhs
The right-hand side multiplicand expression.

Return Value

Returns the product of two multiplicands.

Description

Operator * (Multiply) returns the product of two multiplicands.

Neither operand is modified in any way.

This operator can be overloaded to accept user-defined types.

Example

Dim n As Double
n = 4 * 5
Print n
Sleep

Output:
20

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Mathematical Functions


Operator / (Divide)
Divides two numeric expressions

Syntax

Declare Operator / ( ByRef lhs As Single, ByRef rhs As Single ) As Single
Declare Operator / ( ByRef lhs As Double, ByRef rhs As Double ) As Double

Usage

result = lhs / rhs

Parameters

lhs
The left-hand side dividend expression.
rhs
The right-hand side divisor expression.

Return Value

Returns the quotient of a dividend and divisor.

Description

Operator / (Divide) returns the quotient of a dividend and divisor.

Neither operand is modified in any way. Unlike with integer division, float division by zero is safe to perform, the quotient will hold a special value representing infinity, converting it to a string returns something like "Inf" or "INF", exact text is platform specific.

This operator can be overloaded to accept user-defined types.

Example

Dim n As Double
Print n / 5
n = 6 / 2.3
Print n
Sleep

Output:
0
2.608695652173913

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Operator \ (Integer Divide)
  • Mathematical Functions


Operator \ (Integer Divide)
Divides two Integer expressions

Syntax

Declare Operator \ ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator \ ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As UInteger

Usage

result = lhs \ rhs

Parameters

lhs
The left-hand side dividend expression.
rhs
The right-hand side divisor expression.

Return Value

Returns the quotient of an Integer dividend and divisor.

Description

Operator \ (Integer division) divides two Integer expressions and returns the result. Float numeric values are converted to Integer by rounding up or down, and the fractional part of the resulting quotient is truncated.

If the divisor (rhs) is zero (0), a division by zero error (crash) will be raised.

Neither of the operands are modified in any way.

This operator can be overloaded for user-defined types.

Example

Dim n As Double
Print n \ 5
n = 7 \ 2.6  '' => 7 \ 3  => 2.33333  => 2
Print n
n = 7 \ 2.4  '' => 7 \ 2 => 3.5 => 3
Print n
Sleep

Output:
0
2
3

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Operator / (Floating-Point Divide)
  • Operator Mod (Modulus)
  • Mathematical Functions


Operator ^ (Exponentiate)
Raises a numeric expression to some power

Syntax

Declare Operator ^ ( ByRef lhs As Double, ByRef rhs As Double ) As Double

Usage

result = lhs ^ rhs

Parameters

lhs
The left-hand side base expression.
rhs
The right-hand side exponent expression.

Return Value

Returns the exponentiation of a base expression raised to some exponent.

Description

Operator ^ (Exponentiate) returns the result of a base expression (lhs) raised to some exponent expression (rhs). ^ works with double float numbers only, operands of other types will be converted into double before performing the exponentiation. Exponent of a fractional value (1/n) is the same as taking nth root from the base, for example, 2 ^ (1/3) is the cube root of 2.

Neither of the operands are modified in any way.

This operator can be overloaded for user-defined types.

This operation is for Double precision data types only (53 significant bits), and suffers from some inaccuracies in the least significant bits of the number, especially when converting from other data types (e.g. 64-bit data types). This inaccuracy is particularly noticeable when the result is expected to be an exact number, but then finding the result is out by a very small amount. For this reason, never assume that an exponentiation expression will be exactly equal to the value expected, but rather close within some small amount of error. Be wary of using rounding methods such as Int and Fix on the result: if you expect the result to be an integer value, then there's a chance that it might be slightly lower, and will round down to a value that is one less than expected.

This operator exists in C/C++ with a different meaning; in C/C++ it performs a Bitwise Xor.

Example

Dim As Double n
Input "Please enter a positive number: ", n
Print
Print n;" squared is "; n ^ 2
Print "The fifth root of "; n;" is "; n ^ 0.2
Sleep

Output:
Please enter a positive number: 3.4

 3.4 squared is 11.56
The fifth root of 3.4 is 1.27730844458754

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Mathematical Functions


Operator - (Negate)
Changes the sign of a numeric expression

Syntax

Declare Operator - ( ByRef rhs As Integer ) As Integer
Declare Operator - ( ByRef rhs As Single ) As Single
Declare Operator - ( ByRef rhs As Double ) As Double

Usage

result = - rhs

Parameters

rhs
The right-hand side numeric expression to negate.

Return Value

Returns the negative of the expression.

Description

Operator - (Negate) is a unary operator that negates the value of its operand.

The operand is not modified in any way.

This operator can be overloaded for user-defined types.

Example

Dim n As LongInt
Print -5
n = 65432568459
n = - n
Print n
Sleep

Output:
-5
-65432568459

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • None

See also

  • Mathematical Functions


Graphics Keyword List
A list of the keywords and procedures of FreeBASIC's graphics library.

Description

If a program intentionally does not use the graphics command surface and needs selected gfxlib command words as normal identifiers, define Fb_No_Gfxlib before those names are parsed.

  • Add
  • Alpha
  • And (Graphics Put)
  • BLoad
  • BSave
  • Circle
  • Color
  • Custom
  • Draw
  • Draw String
  • Event (Message Data From Screenevent)
  • Flip
  • ImageConvertRow
  • ImageCreate
  • ImageDestroy
  • ImageInfo
  • Get (Graphics)
  • GetJoystick
  • GetMouse
  • GetTouch
  • GetTouchCount
  • GetTouchHit
  • Getxpad
  • Inp
  • Line (Graphics)
  • MultiKey
  • Out
  • Or (Graphics Put)
  • Paint
  • Palette
  • PCopy
  • PMap
  • Point
  • PointCoord
  • PReset
  • PSet
  • Pset (Graphics Put)
  • Put (Graphics)
  • RGB
  • RGBA
  • Screen (Graphics)
  • ScreenControl
  • ScreenCopy
  • ScreenEvent
  • ScreenGLProc
  • ScreenInfo
  • ScreenList
  • ScreenLock
  • ScreenPtr
  • ScreenRes
  • ScreenSet
  • ScreenSync
  • ScreenUnlock
  • SetMouse
  • Trans
  • View (Graphics)
  • Wait
  • Window
  • WindowTitle
  • Xor (Graphics Put)


Operator List
List of operators used in FreeBASIC.

Assignment Operators
  • =[>] (Assignment)
  • &= (Concatenate And Assign)
  • += (Add And Assign)
  • -= (Subtract And Assign)
  • *= (Multiply And Assign)
  • /= (Divide And Assign)
  • \= (Integer Divide And Assign)
  • ^= (Exponentiate And Assign)
  • Mod= (Modulus And Assign)
  • And= (Conjunction And Assign)
  • Eqv= (Equivalence And Assign)
  • Imp= (Implication And Assign)
  • Or= (Inclusive Disjunction And Assign)
  • Xor= (Exclusive Disjunction And Assign)
  • Shl= (Shift Left And Assign)
  • Shr= (Shift Right And Assign)
  • Let (Assign)
  • Let() (Assignment)

Type Cast Operators
  • Cast (Operator)
  • CPtr

Arithmetic Operators
  • + (Add)
  • - (Subtract)
  • * (Multiply)
  • / (Divide)
  • \ (Integer Divide)
  • ^ (Exponentiate)
  • Mod (Modulus)
  • - (Negate)
  • Shl (Shift Left)
  • Shr (Shift Right)

Indexing Operators
  • () (Array Index)
  • [] (String Index)
  • [] (Pointer Index)

String Operators
  • + (String Concatenation)
  • & (String Concatenation With Conversion)
  • Strptr (String Pointer)
Relational Operators
  • = (Equal)
  • <> (Not Equal)
  • < (Less Than)
  • <= (Less Than Or Equal)
  • >= (Greater Than Or Equal)
  • > (Greater Than)

Bitwise Operators
  • And (Conjunction)
  • Eqv (Equivalence)
  • Imp (Implication)
  • Not (Complement)
  • Or (Inclusive Disjunction)
  • Xor (Exclusive Disjunction)

Short Circuit Operators
  • Andalso (Short Circuit Conjunction)
  • Orelse (Short Circuit Inclusive Disjunction)

Preprocessor Operators
  • # (Argument Stringize)
  • ## (Argument Concatenation)
  • ! (Escaped String Literal)
  • $ (Non-Escaped String Literal)

Pointer Operators
  • @ (Address Of)
  • * (Value Of)
  • Varptr (Variable Pointer)
  • Procptr (Procedure Pointer And Vtable Index)

Type or Class Operators
  • . (Member Access)
  • -> (Pointer To Member Access)
  • Is (Run-Time Type Information Operator)

Memory Operators
  • New Expression
    • New Overload
  • Placement New
  • Delete Statement
    • Delete Overload

Iteration Operators
  • For, Next, and Step


Operator Cast
Operator to convert a UDT (User Defined Type) variable into a specified data type

Syntax

{ Type | Class | Union } typename
Declare Operator Cast () [ ByRef ] As datatype
...
End { Type | Class | Union }

Operator typename.Cast () [ ByRef ] As datatype [ Export ]
...

Usage

Cast( datatype, expression )

Parameters

typename
The name of the Type, Class, or Union
datatype
The name of the type for conversion into it, a built-in data type (standard data type) or a UDT different from typename
expression
The expression to convert, an instance of typename

Description

Converts an expression (a typename variable) into a different datatype.

Cast Operator must be declared inside the Type, Class, or Union.
As all non-static member procedures, it has passed a hidden This parameter.

The Cast Operator is the only operator (or function) that can be declared multiple times when only the return type differs, but not the same as the Type, Class, or Union they are declared in.

The Cast Operator allows conversions that can also be used on the right-hand side of expressions of construction (with initializer) and assignment (see example 1 below).
In addition to its explicit form usage Cast( datatype, expression ), the Cast Operator allows also implicit conversion into datatype (see same example 1 below). For this not explicit usage, the compiler may decide which cast overload to call based on how the object is used (for example on the Print keyword, the compiler calls the Cast() As String operator if defined).

Note: The overloaded Cast Operator can convert only a UDT instance (not a built-in type variable) because it can be overloaded only as a member Operator (not as a global operator).
To convert a built-in type variable into a UDT (not working syntax: Cast( UDT, built_in_type_variable )), the best way into the UDT is to define a constructor or/and a Let operator, otherwise to use a Cast Operator but with return by reference (by an assignment: Cast( built_in_datatype, UDT_instance ) = built_in_type_variable).
(see example 2 below)

For implicit conversion from one UDT into another UDT, a Cast Operator in the first UDT can replace (in second priority) a constructor and a Let operator in the second UDT (see examples 3 and 4 below).

Warning: For a Cast operator that returns by value (no byref), do not use generally an exit code like Return expression (or Operator = expression) if expression is an instance of typename.
Such a Cast operator code will induce an infinite loop when called, unless an implicit conversion from typename to datatype already exists through a matched constructor (or a let operator) for datatype, so with a higher priority (see example 5 below).

Example

Very simple syntaxic example highlighting the conversion capabilities (explicit and implicit) by using Cast operators:
Type UDT
  Dim As Integer I
  Declare Operator Cast () As Integer
  Declare Operator Cast () As String
End Type

Operator UDT.Cast () As Integer
  Print "UDT.Cast() As Integer",
  Return This.I
End Operator

Operator UDT.Cast () As String
  Print "UDT.Cast() As String",
  Return Str(This.I)
End Operator

Dim As UDT u

u.I = 12
Print Cast(Integer, u)               '' explicit conversion using the defined "Cast() As Integer" operator
Print Cast(String, u)                '' explicit conversion using the defined "Cast() As String" operator
Print u                              '' implicit conversion by compiler using the defined "Cast() As String" operator
Print

u.I = 34
Dim As Integer J = Cast(Integer, u)  '' construction with explicit initialization using the defined "Cast() As Integer" operator
Print J
Dim As Integer K = u                 '' construction with implicit initialization by compiler using the defined "Cast() As Integer" operator
Print K
Print

u.I = 56
J = Cast(Integer, u)                 '' explicit assignment using the defined "Cast() As Integer" operator
Print J
K = u                                '' implicit assignment by compiler using the defined "Cast() As Integer" operator
Print K
Print

u.I = 78
Dim As String S = Cast(String, u)    '' construction with explicit initialization using the defined "Cast() As String" operator
Print S
Dim As String G = u                  '' construction with implicit initialization by compiler using the defined "Cast() As String" operator
Print G
Print

u.I = 90
S = Cast(String, u)                  '' explicit assignment using the defined "Cast() As String" operator
Print S
G = u                                '' implicit assignment by compiler using the defined "Cast() As String" operator
Print G
Print

Sleep

Workarounds for the not working syntax: UDT_instance = Cast( UDT, built_in_type_variable ):
(using constructor, Let operator, and a Cast operator which returns by reference)
Type UDT
  As Integer I
  Declare Constructor ()
  Declare Constructor (ByVal I0 As Integer)
  Declare Operator Let (ByVal I0 As Integer)
  Declare Operator Cast () ByRef As Integer
End Type

Constructor UDT ()
End Constructor

Constructor UDT (ByVal I0 As Integer)
  Print "UDT.Constructor(Byval As Integer)",
  This.I = I0
End Constructor

Operator UDT.Let (ByVal I0 As Integer)
  Print "UDT.Let(Byval As Integer)",,
  This.I = I0
End Operator

Operator UDT.Cast () ByRef As Integer
  Print "UDT.Cast() Byref As Integer",,
  Return This.I
End Operator

Dim As UDT u

'u = Cast(UDT, 12)     '' unsupported - error 20: Type mismatch
u = UDT(34)            '' explicit conversion using the defined "Constructor(Byval As Integer)"
Print u.I
Print

u = 56                 '' implicit conversion by compiler using the defined "Let(Byval As Integer)" operator
Print u.I
Print

Cast(Integer, u) = 78  '' explicit conversion using the defined "Cast() Byref As Integer" operator with byref return
Print u.I
Print

Sleep

Conversion from UDT1 into UDT2, by using a constructor and a Let operator in UDT2:
Type _UDT1 As UDT1

Type UDT2
  Dim As Integer I2
  Declare Constructor ()
  Declare Constructor (ByRef u As _UDT1)
  Declare Operator Let (ByRef u As _UDT1)
End Type

Constructor UDT2 ()
End Constructor

Type UDT1
  Dim As Integer I1
End Type

Constructor UDT2 (ByRef u As UDT1)
  Print "UDT2.Constructor(Byref As UDT1)",
  This.I2 = u.I1
End Constructor

Operator UDT2.Let (ByRef u As UDT1)
  Print "UDT2.Let(Byref As UDT1)",,
  This.I2 = u.I1
End Operator

Dim As UDT1 u1

u1.I1 = 123
Dim As UDT2 u2 = u1  '' implicit conversion by compiler using the defined "UDT2.Constructor(Byref As UDT1)"
Print u2.I2
Print

u1.I1 = 456
u2 = u1              '' implicit conversion by compiler using the defined "UDT2.Let(Byref As UDT1)" operator
Print u2.I2
Print

Sleep

Conversion from UDT1 into UDT2, by using a Cast operator in UDT1:
Type UDT2
  Dim As Integer I2
End Type

Type UDT1
  Dim As Integer I1
  Declare Operator Cast () As UDT2
End Type

Operator UDT1.Cast () As UDT2
  Print "UDT1.Cast() As UDT2",,
  Dim As UDT2 u
  u.I2 = This.I1
  Return u
End Operator

Dim As UDT1 u1

u1.I1 = 123
Dim As UDT2 u2 = u1  '' implicit conversion by compiler using the defined "UDT1.Cast() As UDT2" operator
Print u2.I2
Print

u1.I1 = 456
u2 = u1              '' implicit conversion by compiler using the defined "UDT1.Cast() As UDT2" operator
Print u2.I2
Print

Sleep

Conversion from UDT1 into UDT2, by using in UDT1 a Cast operator exiting by Return This, but without infinite loop thanks to a matched Let operator in UDT2:
Type _UDT1 As UDT1

Type UDT2
  Dim As Integer I2
  Declare Operator Let (ByRef u As _UDT1)
End Type

Type UDT1
  Dim As Integer I1
  Declare Operator Cast () As UDT2
End Type

Operator UDT1.Cast () As UDT2
  Print "UDT1.Cast() As UDT2"
  Return This                       '' implicit conversion by compiler using the defined "UDT2.Let(Byref As UDT1)" operator
End Operator

Operator UDT2.Let (ByRef u As UDT1)
  Print "UDT2.Let(Byref As UDT1)"
  This.I2 = u.I1
End Operator

Dim As UDT1 u1
u1.I1 = 123

Print Cast(UDT2, u1).I2

Sleep

Dialect Differences

  • Only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC.

See also

  • Cast
  • CPtr
  • CInt
  • TypeOf
  • Coercion and Conversion


Operator () (Array Index)
Returns a reference to an element in an array

Syntax

Declare Operator () ( lhs() As T, ByRef rhs As Integer, ... ) ByRef As T

Usage

result = lhs ( rhs [, ...] )
or
lhs ( rhs [, ...] ) = value

Parameters

lhs
An array.
rhs
An index of an element in the array.
T
Any data type.

Description

This operator returns a reference to an element in an array. For multidimensional arrays, multiple indexes must be specified (up to the total number of dimensions of the array).

For any dimension d in array a, any index less than LBound(a, d) or greater than UBound(a, d) will result in a runtime error.

Example

Dim array(0 To 4) As Integer = { 0, 1, 2, 3, 4 }

For index As Integer = 0 To 4
    Print array(index);
Next
Print

will produce the output:

 0 1 2 3 4

Differences from QB

  • None

See also

  • Operator [] (Pointer Index)


Operator [] (String Index)
Returns a reference to the numeric value of a character in a string

Syntax

Declare Operator [] ( ByRef lhs As String, ByRef rhs As Integer ) ByRef As UByte
Declare Operator [] ( ByRef lhs As ZString, ByRef rhs As Integer ) ByRef As UByte
Declare Operator [] ( ByRef lhs As WString, ByRef rhs As Integer ) ByRef As T

Usage

result = lhs [ rhs ]
or
lhs [ rhs ] = value

Parameters

lhs
The string variable or a string reference (not for example a constant or a string returned as a local copy).
rhs
A zero-based offset from the first character.
T
The wide-character type (varies per platform).

Description

This operator returns a reference to the numeric value of a specific character in a string:
    • For a String or a ZString:
a UByte (containing the ASCII value of the character).
    • For a WString:
a numeric type depending on platform, for example UShort for Windows or ULong for Linux (containing the numeric value of the character).

This operator must not be used in case of empty string because reference is undefined (inducing runtime error).
Otherwise, the user must ensure that the index does not exceed the range "[0, Len(lhs) - 1]". Outside this range, results are undefined.

Unlike 'return by value' (where only a copy is returned), 'return by reference' allows you to also modify the referenced variable.
'Return by reference' is implemented under the hood as a pointer implicitly dereferenced:
- In case of a String or a ZString 's':
s[n] is equivalent to *CPtr(Ubyte Ptr, StrPtr(s) + n)
- In case of a WString 's':
s[n] is equivalent to *CPtr(T Ptr, StrPtr(s) + n)

Note: The fact that this operator returns a reference greatly differentiates it from Asc( str [, position ] ) which allows to return the numeric representation of a character, but not to modify it.

Example

Dim a As String = "Hello, world!"
Dim i As Integer

For i = 0 To Len(a) - 1
    Print Chr(a[i]) & " ";
Next i
Print
Print

For i = 1 To 4
    a[i] = a[i] - 32  ' converting lowercase alphabetic characters to uppercase
Next i
For i = 7 To 11
    a[i] = a[i] - 32  ' converting lowercase alphabetic characters to uppercase
Next i
Print a

Will print:
H e l l o ,   w o r l d !

HELLO, WORLD!
Differences from QB

  • New to FreeBASIC

See also

  • String Operators


Operator [] (Pointer Index)
Returns a reference to memory offset from an address

Syntax

Declare Operator [] ( ByRef lhs As T Pointer, ByRef rhs As Integer ) ByRef As T

Usage

result = lhs [ rhs ]
or
lhs [ rhs ] = value

Parameters

lhs
The base address.
rhs
A signed offset from lhs.
T
Any data type.

Description

This operator returns a reference to a value some distance in memory from a base address. It is essentially shorthand for "*(lhs + rhs)" because the reference can be thought of as a pointer having as value the memory location "(lhs + rhs)", and which is implicitly dereferenced; both do exactly the same thing.
Like pointer arithmetic, any type of Pointer can be indexed except for an Any Pointer. Also, like pointer arithmetic, it is up to the user to make sure meaningful data is being accessed.

When indexing a '2-dimensional' pointer (i.e. a T Ptr Ptr), the first (leftmost) index is applied before the second: For example, Pt[I1][I2] = *(Pt[I1] + I2) = *(*(Pt + I1) + I2)
In general, when using an 'n-dimensional' pointer: Pt[I1][I2].....[In], the index order (from left to right) corresponds to the dereferencing order.

This operator must not be used in case of null pointer because reference is undefined (inducing runtime error).
Otherwise, the user must ensure that the offset value (rhs) is in a range that allows an access to valid memory. Outside this range, results are undefined.

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Example

'' initialize a 5-element array
Dim array(4) As Integer = { 0, 1, 2, 3, 4 }

'' point to the first element
Dim p As Integer Ptr = @array(0)

'' use pointer indexing to output array elements
For index As Integer = 0 To 4
    Print p[index];
Next
Print

Will give the output,

 0 1 2 3 4

Differences from QB

  • New to FreeBASIC

See also

  • Pointer Arithmetic
  • Operator * (Value Of)
  • Operator [] (String Index)
  • Operator () (Array Index)
  • Operator + (Add)
  • Operator - (Subtract)
  • Pointer Operators


Operator + (String Concatenation)
Concatenates two strings

Syntax

Declare Operator + ( ByRef lhs As String, ByRef rhs As String ) As String
Declare Operator + ( ByRef lhs As ZString, ByRef rhs As ZString ) As ZString
Declare Operator + ( ByRef lhs As WString, ByRef rhs As WString ) As WString

Usage

result = lhs + rhs

Parameters

lhs
The left-hand side string to concatenate.
rhs
The right-hand side string to concatenate.

Description

This operator concatenates two strings. Unlike Operator & (String Concatenation With Conversion) both expressions must be strings, and may not be converted (in fact, any attempt to concatenate a string with a non-string or two non-strings will result in a type mismatch error, with the exception of when operator overloading is used in a UDT).

Example

Dim As String a = "Hello, ", b = "World!"
Dim As String c
c = a + b
Print c

Output:
Hello, World!

Differences from QB

  • None

See also

  • Operator + (Add)
  • Operator & (String Concatenation With Conversion)
  • Str


Operator & (String Concatenation With Conversion)
Concatenates two strings, converting non-strings to strings as needed

Syntax

Declare Operator & ( ByRef lhs As T, ByRef rhs As U ) As V

Usage

result = lhs & rhs

Parameters

lhs
The left-hand side expression to concatenate.
T
Any standard data type or user-defined type that can be converted to a standard data type.
rhs
The right-hand side expression to concatenate.
U
Any standard data type or user-defined type that can be converted to a standard data type.
V
The resultant string type (varies with operands).

Description

This operator concatenates two expressions. If either of the expressions is not a string type, it is converted to String with Str.

If either of the expressions is a WString, a WString is returned, otherwise a String is returned.

This operator can be overloaded to accept user-defined types.

Note: This operator exists in C/C++ with a different meaning - there it performs a bitwise And.

Example

Dim As String A,C
Dim As Single B
A="The result is: "
B=124.3
C=A & B
Print C
Sleep

Output:
The result is: 124.3

Differences from QB

  • New to FreeBASIC

See also

  • Operator + (String Concatenation)
  • Str


Operator = (Equal)
Compares two expressions for equality

Syntax

Declare Operator = ( ByRef lhs As Byte, ByRef rhs As Byte ) As Integer
Declare Operator = ( ByRef lhs As UByte, ByRef rhs As UByte ) As Integer
Declare Operator = ( ByRef lhs As Short, ByRef rhs As Short ) As Integer
Declare Operator = ( ByRef lhs As UShort, ByRef rhs As UShort ) As Integer
Declare Operator = ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator = ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As Integer
Declare Operator = ( ByRef lhs As LongInt, ByRef rhs As LongInt ) As Integer
Declare Operator = ( ByRef lhs As ULongInt, ByRef rhs As ULongInt ) As Integer

Declare Operator = ( ByRef lhs As Single, ByRef rhs As Single ) As Integer
Declare Operator = ( ByRef lhs As Double, ByRef rhs As Double ) As Integer

Declare Operator = ( ByRef lhs As String, ByRef rhs As String ) As Integer
Declare Operator = ( ByRef lhs As ZString, ByRef rhs As ZString ) As Integer
Declare Operator = ( ByRef lhs As WString, ByRef rhs As WString ) As Integer

Declare Operator = ( ByRef lhs As T, ByRef rhs As T ) As Integer

Declare Operator = ( ByRef lhs As Boolean, ByRef rhs As Boolean ) As Boolean

Usage

result = lhs = rhs

Parameters

lhs
The left-hand side expression to compare to.
rhs
The right-hand side expression to compare to.
T
Any pointer type.

Return Value

Returns negative one (-1) if expressions are equal, or zero (0) if unequal.

Description

Operator = (Equality) is a binary operator that compares two expressions for equality and returns the result - a boolean value mainly in the form of an Integer: negative one (-1) for true and zero (0) for false. Only if the left and right-hand side types are both Boolean, the return type is also Boolean. The arguments are not modified in any way.

This operator can be overloaded to accept user-defined types as well.

Operator = (Equality) should not be confused with initializations or assignments, both of which also use the "=" symbol.

Example

Dim i As Integer = 0    '' initialization: initialise i with a value of 0
i = 420                 '' assignment: assign to i the value of 420

If (i = 69) Then        '' equation: compare the equality of the value of i and 69
    Print "serious error: i should equal 420"
    End -1
End If

Operator <> (Inequality) is complement to Operator = (Equality), and is functionally identical when combined with Operator Not (Bit-wise Complement).

   If (420 = 420) Then Print "(420 = 420) is true."
   If Not (69 <> 69) Then Print "not (69 <> 69) is true."

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • none

See also

  • Operator <> (Inequality)
  • Operator =[>] (Assignment)


Operator <> (Not Equal)
Compares two expressions for inequality

Syntax

Declare Operator <> ( ByRef lhs As Byte, ByRef rhs As Byte ) As Integer
Declare Operator <> ( ByRef lhs As UByte, ByRef rhs As UByte ) As Integer
Declare Operator <> ( ByRef lhs As Short, ByRef rhs As Short ) As Integer
Declare Operator <> ( ByRef lhs As UShort, ByRef rhs As UShort ) As Integer
Declare Operator <> ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator <> ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As Integer
Declare Operator <> ( ByRef lhs As LongInt, ByRef rhs As LongInt ) As Integer
Declare Operator <> ( ByRef lhs As ULongInt, ByRef rhs As ULongInt ) As Integer

Declare Operator <> ( ByRef lhs As Single, ByRef rhs As Single ) As Integer
Declare Operator <> ( ByRef lhs As Double, ByRef rhs As Double ) As Integer

Declare Operator <> ( ByRef lhs As String, ByRef rhs As String ) As Integer
Declare Operator <> ( ByRef lhs As ZString, ByRef rhs As ZString ) As Integer
Declare Operator <> ( ByRef lhs As WString, ByRef rhs As WString ) As Integer

Declare Operator <> ( ByRef lhs As T, ByRef rhs As T ) As Integer

Declare Operator <> ( ByRef lhs As Boolean, ByRef rhs As Boolean ) As Boolean

Usage

result = lhs <> rhs

Parameters

lhs
The left-hand side expression to compare to.
rhs
The right-hand side expression to compare to.
T
Any pointer type.

Return Value

Returns negative one (-1) if expressions are not equal, or zero (0) if equal.

Description

Operator <> (Not equal) is a binary operator that compares two expressions for inequality and returns the result - a boolean value mainly in the form of an Integer: negative one (-1) for true and zero (0) for false. Only if the left and right-hand side types are both Boolean, the return type is also Boolean. The arguments are not modified in any way.

This operator can be overloaded to accept user-defined types as well.

Example

Dim As String a = "hello", b = "world"
Dim As Integer i = 10, j = i

If (a <> b) Then
  Print a & " does not equal " & b
End If

If (i <> j) Then
  Print "error: " & i & " does not equal " & j
End If

Operator = (Equal) is complement to Operator <> (Not equal), and is functionally identical when combined with Operator Not (Bit-wise Complement).

   If (69 <> 420) Then Print "(69 <> 420) is true."
   If Not (69 = 420) Then Print "not (69 = 420) is true."

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • none

See also

  • Operator = (Equal)


Operator < (Less Than)
Compares an expression less than another expression

Syntax

Declare Operator < ( ByRef lhs As Byte, ByRef rhs As Byte ) As Integer
Declare Operator < ( ByRef lhs As UByte, ByRef rhs As UByte ) As Integer
Declare Operator < ( ByRef lhs As Short, ByRef rhs As Short ) As Integer
Declare Operator < ( ByRef lhs As UShort, ByRef rhs As UShort ) As Integer
Declare Operator < ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator < ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As Integer
Declare Operator < ( ByRef lhs As LongInt, ByRef rhs As LongInt ) As Integer
Declare Operator < ( ByRef lhs As ULongInt, ByRef rhs As ULongInt ) As Integer

Declare Operator < ( ByRef lhs As Single, ByRef rhs As Single ) As Integer
Declare Operator < ( ByRef lhs As Double, ByRef rhs As Double ) As Integer

Declare Operator < ( ByRef lhs As String, ByRef rhs As String ) As Integer
Declare Operator < ( ByRef lhs As ZString, ByRef rhs As ZString ) As Integer
Declare Operator < ( ByRef lhs As WString, ByRef rhs As WString ) As Integer

Declare Operator < ( ByRef lhs As T, ByRef rhs As T ) As Integer

Usage

result = lhs < rhs

Parameters

lhs
The left-hand side expression to compare to.
rhs
The right-hand side expression to compare to.
T
Any pointer type.

Return Value

Returns negative one (-1) if the left-hand side expression is less than the right-hand side expression, or zero (0) if greater than or equal.

Description

Operator < (Less than) is a binary operator that compares two expressions for inequality and returns the result - a boolean value in the form of an Integer: negative one (-1) for true and zero (0) for false. The arguments are not modified in any way.

When this operator is applied to string type data, then a lexicographic/alphabetic order comparison is performed (ordered from the numeric ASCII values of the characters of the two strings).

This operator can be overloaded to accept user-defined types as well.

Example

Const size As Integer = 4
Dim array(size - 1) As Integer = { 1, 2, 3, 4 }

Dim index As Integer = 0
While (index < size)
   Print array(index)
   index += 1
Wend

Operator >= (Greater than or equal) is complement to Operator < (Less than), and is functionally identical when combined with Operator Not (Bit-wise Complement).

   If (69 < 420) Then Print "(69 < 420) is true."
   If Not (69 >= 420) Then Print "not (69 >= 420) is true."

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • none

See also

  • Operator >= (Greater than or equal)


Operator <= (Less Than Or Equal)
Compares an expression less than or equal to another expression

Syntax

Declare Operator <= ( ByRef lhs As Byte, ByRef rhs As Byte ) As Integer
Declare Operator <= ( ByRef lhs As UByte, ByRef rhs As UByte ) As Integer
Declare Operator <= ( ByRef lhs As Short, ByRef rhs As Short ) As Integer
Declare Operator <= ( ByRef lhs As UShort, ByRef rhs As UShort ) As Integer
Declare Operator <= ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator <= ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As Integer
Declare Operator <= ( ByRef lhs As LongInt, ByRef rhs As LongInt ) As Integer
Declare Operator <= ( ByRef lhs As ULongInt, ByRef rhs As ULongInt ) As Integer

Declare Operator <= ( ByRef lhs As Single, ByRef rhs As Single ) As Integer
Declare Operator <= ( ByRef lhs As Double, ByRef rhs As Double ) As Integer

Declare Operator <= ( ByRef lhs As String, ByRef rhs As String ) As Integer
Declare Operator <= ( ByRef lhs As ZString, ByRef rhs As ZString ) As Integer
Declare Operator <= ( ByRef lhs As WString, ByRef rhs As WString ) As Integer

Declare Operator <= ( ByRef lhs As T, ByRef rhs As T ) As Integer

Usage

result = lhs <= rhs

Parameters

lhs
The left-hand side expression to compare to.
rhs
The right-hand side expression to compare to.
T
Any pointer type.

Return Value

Returns negative one (-1) if the left-hand side expression is less than or equal to the right-hand side expression, or zero (0) if greater than.

Description

Operator <= (Less than or Equal) is a binary operator that compares an expression less than or equal to another expression and returns the result - a boolean value in the form of an Integer: negative one (-1) for true and zero (0) for false. The arguments are not modified in any way.

When this operator is applied to string type data, then a lexicographic/alphabetic order comparison is performed (ordered from the numeric ASCII values of the characters of the two strings).

This operator can be overloaded to accept user-defined types as well.

Example

Operator > (Greater than) is complement to Operator <= (Less than or Equal), and is functionally identical when combined with Operator Not (Bit-wise Complement).

   If (69 <= 420) Then Print "(69 <= 420) is true."
   If Not (60 > 420) Then Print "not (420 > 69) is true."

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • none

See also

  • Operator > (Greater than)


Operator >= (Greater Than Or Equal)
Compares an expression greater than or equal to another expression

Syntax

Declare Operator >= ( ByRef lhs As Byte, ByRef rhs As Byte ) As Integer
Declare Operator >= ( ByRef lhs As UByte, ByRef rhs As UByte ) As Integer
Declare Operator >= ( ByRef lhs As Short, ByRef rhs As Short ) As Integer
Declare Operator >= ( ByRef lhs As UShort, ByRef rhs As UShort ) As Integer
Declare Operator >= ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator >= ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As Integer
Declare Operator >= ( ByRef lhs As LongInt, ByRef rhs As LongInt ) As Integer
Declare Operator >= ( ByRef lhs As ULongInt, ByRef rhs As ULongInt ) As Integer

Declare Operator >= ( ByRef lhs As Single, ByRef rhs As Single ) As Integer
Declare Operator >= ( ByRef lhs As Double, ByRef rhs As Double ) As Integer

Declare Operator >= ( ByRef lhs As String, ByRef rhs As String ) As Integer
Declare Operator >= ( ByRef lhs As ZString, ByRef rhs As ZString ) As Integer
Declare Operator >= ( ByRef lhs As WString, ByRef rhs As WString ) As Integer

Declare Operator >= ( ByRef lhs As T, ByRef rhs As T ) As Integer

Usage

result = lhs >= rhs

Parameters

lhs
The left-hand side expression to compare to.
rhs
The right-hand side expression to compare to.
T
Any pointer type.

Return Value

Returns negative one (-1) if the left-hand side expression is greater than or equal to the right-hand side expression, or zero (0) if less than.

Description

Operator >= (Greater than or Equal) is a binary operator that compares an expression greater than or equal to another expression and returns the result - a boolean value in the form of an Integer: negative one (-1) for true and zero (0) for false. The arguments are not modified in any way.

When this operator is applied to string type data, then a lexicographic/alphabetic order comparison is performed (ordered from the numeric ASCII values of the characters of the two strings).

This operator can be overloaded to accept user-defined types as well.

Example

Operator < (Less than) is complement to Operator >= (Greater than or Equal), and is functionally identical when combined with Operator Not (Bit-wise Complement).

   If (420 >= 69) Then Print "(420 >= 69) is true."
   If Not (420 < 69) Then Print "not (420 < 69) is true."

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • none

See also

  • Operator < (Less than)


Operator > (Greater Than)
Compares an expression greater than another expression

Syntax

Declare Operator > ( ByRef lhs As Byte, ByRef rhs As Byte ) As Integer
Declare Operator > ( ByRef lhs As UByte, ByRef rhs As UByte ) As Integer
Declare Operator > ( ByRef lhs As Short, ByRef rhs As Short ) As Integer
Declare Operator > ( ByRef lhs As UShort, ByRef rhs As UShort ) As Integer
Declare Operator > ( ByRef lhs As Integer, ByRef rhs As Integer ) As Integer
Declare Operator > ( ByRef lhs As UInteger, ByRef rhs As UInteger ) As Integer
Declare Operator > ( ByRef lhs As LongInt, ByRef rhs As LongInt ) As Integer
Declare Operator > ( ByRef lhs As ULongInt, ByRef rhs As ULongInt ) As Integer

Declare Operator > ( ByRef lhs As Single, ByRef rhs As Single ) As Integer
Declare Operator > ( ByRef lhs As Double, ByRef rhs As Double ) As Integer

Declare Operator > ( ByRef lhs As String, ByRef rhs As String ) As Integer
Declare Operator > ( ByRef lhs As ZString, ByRef rhs As ZString ) As Integer
Declare Operator > ( ByRef lhs As WString, ByRef rhs As WString ) As Integer

Declare Operator > ( ByRef lhs As T, ByRef rhs As T ) As Integer

Usage

result = lhs > rhs

Parameters

lhs
The left-hand side expression to compare to.
rhs
The right-hand side expression to compare to.
T
Any pointer type.

Return Value

Returns negative one (-1) if the left-hand side expression is greater than the right-hand side expression, or zero (0) if less than or equal.

Description

Operator > (Greater than) is a binary operator that compares an expression greater than another expression and returns the result - a boolean value in the form of an Integer: negative one (-1) for true and zero (0) for false. The arguments are not modified in any way.

When this operator is applied to string type data, then a lexicographic/alphabetic order comparison is performed (ordered from the numeric ASCII values of the characters of the two strings).

This operator can be overloaded to accept user-defined types as well.

Example

Operator <= (Less than or equal) is complement to Operator > (Greater than), and is functionally identical when combined with Operator Not (Bit-wise Complement).

   If (420 > 69) Then Print "(420 > 69) is true."
   If Not (420 <= 69) Then Print "not (420 <= 69) is true."

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • none

See also

  • Operator <= (Less than or equal)


Operator # (Preprocessor Stringize)
Preprocessor operator to convert macro arguments to strings

Syntax

#macro_argument

Description

This operator converts the macro_argument into a string whose value is the name of the argument. This substitution is made during the macro expansion, previous to compilation.

Note: because of this feature, care should be taken when using file-handling statements in a macro. Because of potential ambiguity with file-handling statements that take a "#filenum" parameter, if filenum is one of the macro parameters, it may be necessary to wrap the filenum expression in parenthesis (e.g. "#(filenum)"), to separate it from the # sign. Otherwise, filenum will be stringized in the macro.

Example

#define SEE(x) Print #x ;" = "; x
Dim variable As Integer, another_one As Integer
variable=1
another_one=2
SEE(variable)
SEE(another_one)

Output:
variable = 1
another_one = 2

Differences from QB

  • New to FreeBASIC

See also

  • Preprocessor


Operator ## (Preprocessor Concatenate)
Preprocessor operator to concatenate strings

Syntax

text##text

Description

This operator creates a new token by concatenating the texts at both sides of it. This text can be recognized by other macros and further expanded. One use, is to create a macro that expands to different macro names, variable names, and function names depending on the arguments received.

Note:
In macro/define's use '##_' to escape line continuation character '_' to allow multiple lines of macro expanded code to be combined into a single statement.

Example

#define Concat(t,n) t##n

Print concat (12,34)

Dim Concat (hello,world) As Integer
Concat (hello,world)=99
Print helloworld

Output:
1234
99
Version

  • Before fbc 1.08.0, '##_' did not allow to escape line continuation character '_'.

Differences from QB

  • New to FreeBASIC

See also

  • Preprocessor


Operator ! (Escaped String Literal)
Explicitly indicates that a string literal should be processed for escape sequences.

Syntax

!"text"

Parameters

!
The preprocessor escaped string operator
"text"
The string literal containing escape characters

Description

This operator explicitly indicates that the string literal following it (wrapped in double quotes) should be processed for escape sequences. This a preprocessor operator and can only be used with string literals at compile time.

The default behavior for string literals is that they not be processed for escape sequences. Option Escape can be used in the -lang fblite dialect to override this default behaviour causing all strings to be processed for escape sequences.

Use the $ Operator (Non-Escaped String Literal) operator to explicitly indicate that a string should not be processed for escape sequences.

Example

Print "Some escape sequence examples:"
Print !"1.\tsingle quote (\\\') : \'"
Print !"2.\tdouble quote (\\\") : \""
Print !"3.\tbackslash    (\\\\) : \\"
Print !"4.\tascii char   (\\65): \65"

'' OUTPUT:
''
'' Some escape sequence examples:
'' 1. single quote (\') : '
'' 2. double quote (\") : "
'' 3. backslash    (\\) : \
'' 4. ascii char   (\65): A

Differences from QB

  • New to FreeBASIC

See also

  • Operator $ (Non-Escaped String Literal)
  • Option Escape
  • Preprocessor
  • Literals
  • Escape Sequences


Operator $ (Non-Escaped String Literal)
Explicitly indicates that a string literal should not be processed for escape sequences.

Syntax

$"text"

Parameters

$
The preprocessor non-escaped operator
"text"
The string literal

Description

This operator explicitly indicates that the string literal following it (wrapped in double quotes) should not be processed for escape sequences. This a preprocessor operator and can only be used with string literals at compile time.

The default behavior for string literals is that they not be processed for escape sequences. However, Option Escape in the -lang fblite dialect can be used to override this default behaviour causing all strings to be processed for escape sequences.

Use the ! Operator (Escaped String Literal) to explicitly indicate that a string should be processed for escape sequences.

Example

'' Compile with -lang fblite or qb

#lang "fblite"

Print "Default"
Print "Backslash  : \\"
Print !"Backslash !: \\"
Print $"Backslash $: \\"
Print

Option Escape

Print "Option Escape"
Print "Backslash  : \\"
Print !"Backslash !: \\"
Print $"Backslash $: \\"
Print

'' OUTPUT:

'' Default
'' Backslash  : \\
'' Backslash !: \
'' Backslash $: \\

'' Option Escape
'' Backslash  : \
'' Backslash !: \
'' Backslash $: \\

Differences from QB

  • New to FreeBASIC

See also

  • Operator ! (Escaped String Literal)
  • Option Escape
  • Preprocessor
  • Literals
  • Escape Sequences


Operator @ (Address Of)
Returns the address of a string literal, variable, object or procedure

Syntax

Declare Operator @ ( ByRef rhs As T ) As T Pointer

Usage

result = @ rhs

Parameters

rhs
The string literal, variable, object or procedure to retrieve the address of.
T
Any standard, user-defined or procedure type.

Return Value

Returns the address of the right-hand side (rhs) operand.

Description

Operator @ (Address of) returns the memory address of its operand.

When the operand is of type String, the address of the internal string descriptor is returned. Use Operator Strptr (String pointer) to retrieve the address of the string data.

The operand cannot be an array, but may be an array element. For example, "@myarray(0)" returns the address of "myarray(0)".

This operator can be overloaded for user-defined types as a member Operator using the appropriate syntax.

Example

'This program demonstrates the use of the @ operator.

Dim a As Integer
Dim b As Integer

Dim addr As Integer Ptr

a = 5   'Here we place the values 5 and 10 into a and b, respectively.
b = 10

'Here, we print the value of the variables, then where in memory they are stored.
Print "The value in A is ";a;" but the pointer to a is ";@a
Print "The value in B is ";b;" but the pointer to b is ";@b

'Now, we will take the integer ptr above, and use @ to place a value into it.
'Note that the * will check the value in the ptr, just as @ checked the ptr
'for a normal variable.

addr = @a

Print "The pointer addr is now pointing at the memory address to a, value: ";*addr

addr = @b

Print "The pointer addr is now pointing at the memory address to b, value: ";*addr

'This program demonstrates how the @ symbol can be used
'to create pointers to subroutines.

Declare Sub mySubroutine ()

Dim say_Hello As Sub()

say_Hello = @mySubroutine   'We tell say_Hello to point to mySubroutine.
                            'The sub() datatype acts as a pointer here.

say_Hello() 'Now we can run say_Hello just like mySubroutine.

Sub mySubroutine
    Print "hi"
End Sub

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • VarPtr
  • ProcPtr
  • Operator * (Value Of)
  • Pointers


Operator * (Value Of)
Dereferences a pointer

Syntax

Declare Operator * ( ByRef rhs As T Pointer ) ByRef As T

Usage

result = * rhs
or
* rhs = value

Parameters

rhs
The address to dereference.
T
Any standard, user-defined or procedure type.

Return Value

Returns a reference to the value stored at the address rhs.

Description

Operator * (Value of) returns a reference to the value stored at an address, and is often called the dereference operator. The operand is not modified in any way.
Any type of Pointer can be dereferenced except for an Any Pointer.

As a reference, the result of this operator can be used on the left-hand side of assignments.

This operator must not be used in case of null pointer because reference is undefined (inducing runtime error).
Otherwise, the user must ensure that the pointer value induces access to a valid memory. Otherwise, results are undefined.

This operator can be overloaded for user-defined types.

Example

'This program demonstrates the use of * to utilize the value a pointer points to.
Dim a As Integer
Dim pa As Integer Ptr

pa=@a 'Here, we use the @ operator to point our integer ptr at 'a'.
' 'a' is, in this case, a standard integer variable.

a=9     'Here we give 'a' a value of 9.

Print "The value of 'a' is";*pa 'Here, we display the value of 'a' using a pointer.

*pa = 1 'Here we use our pointer to change the value of 'a'
Print "The new value of 'a' is";a 'Here we display the new value of 'a'.

Output:
The value of 'a' is 9
The new value of 'a' is 1

Dialect Differences

  • In the -lang qb dialect, this operator cannot be overloaded.

Differences from QB

  • New to FreeBASIC

See also

  • Operator @ (Address Of)
  • Operator [] (Pointer Index)
  • Pointers


Operator . (Member Access)
Returns a reference to a member from a reference to an object

Syntax

Declare Operator . ( ByRef lhs As T ) ByRef As U

Usage

result = lhs . rhs

Parameters

lhs
An object.
T
A user-defined type.
rhs
The name of a member to access.
U
The type that rhs refers to.

Return Value

Returns a reference to the member specified by rhs.

Description

Operator . (Member access) returns a reference to a member of an object.

Operator . (Member access) can also be used to access members of an implicit object inside a With..End With block.

This operator cannot be overloaded.

Example

Type T
    As Integer a, b
End Type

Dim x As T

'' Access the member 'a' of x.
x.a = 10

'' Access the member 'b' of x.
With x
    .b = 20
End With

Dialect Differences

  • None

Differences from QB

  • None

See also

  • Operator -> (Pointer To Member Access)
  • Operator @ (Address Of)
  • Operator * (Value Of)
  • With..End With


Operator -> (Pointer To Member Access)
Returns a reference to a member from a pointer to an object

Syntax

Declare Operator -> ( ByRef lhs As T Ptr ) ByRef As U

Usage

result = lhs -> rhs

Parameters

lhs
The address of an object.
T
A user-defined type.
rhs
The name of a member to access.
U
The type that rhs refers to.

Return Value

Returns a reference to the member specified by rhs.

Description

Operator -> (Pointer to member access) returns a reference to a member of an object through a pointer to that object. It has the effect of dereferencing a pointer to an object, then using Operator . (Member Access). For example, "p->member" is equivalent to "x.member", if x is an object of user-defined type and p is a pointer to an object of the same type. "p->member" is equivalent to "(*p).member".

This operator can be overloaded for user-defined types.

Example

Type rect
    x As Integer
    y As Integer
End Type

Dim r As rect
Dim rp As rect Pointer = @r

rp->x = 4
rp->y = 2

Print "x = " & rp->x & ", y = " & rp->y
Sleep

Dialect Differences

  • Not available in the -lang qb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Operator . (Member Access)
  • Operator @ (Address Of)
  • Operator * (Value Of)


Operator New Overload
Member operator to overload dynamic memory allocation process part provided by Operator New Expression when applying to a UDT (User Defined Type).

Syntax

Declare Operator New ( size As UInteger ) As Any Ptr
Declare Operator new[] ( size As UInteger ) As Any Ptr

Parameters

size
Number of bytes to allocate.
Return Value

A pointer of type Any Ptr to the start of the newly allocated memory.

Description

The member operator New Overload overloads the dynamic memory allocation process part provided by the New Expression operator when applying to a UDT (User Defined Type). So the user can define its own dynamic memory allocation process part.
But after that, the UDT instance construction process part provided by the New Expression operator is not modified.

New[] Overload operator is the (one-dimensional) array-version of the New Overload operator and overloads the dynamic memory allocation process provided by the New[] Expression operator when applying to a UDT (User Defined Type).

Memory allocated with New Overload operator must be freed by also defining a Delete Overload operator. Memory allocated with New[] Overload operator must be freed by also defining a Delete[] Overload operator, the array-version of Delete Overload operator. You cannot mix and match the different versions of the operators.

Member operators New Overload, and New[] Overload are always static, even if not explicitly declared (Static keyword is unnecessary but allowed). Thus, they do not have an implicit This instance argument passed to them (because instance not yet been constructed).

Example

Dynamic allocation displayer for UDT, by using the member operators "New([]) Overload" and "Delete([]) Overload" (very simple example, only for syntax usage):
    • displaying of memory allocations: addresses, sizes,
    • displaying of memory deallocations: addresses.

Type UDTdisplayer
  '' user UDT fields:
    Dim As Byte b(1 To 1024*1024)
  '' display fields:
    Public:
      Declare Operator New (ByVal size As UInteger) As Any Ptr
      Declare Operator Delete (ByVal buf As Any Ptr)
      Declare Operator New[] (ByVal size As UInteger) As Any Ptr
      Declare Operator Delete[] (ByVal buf As Any Ptr)
    Private:
      Declare Static Function allocation (ByRef N As String, ByVal size As UInteger) As Any Ptr
      Declare Static Sub deallocation (ByRef D As String, ByVal p As Any Ptr)
End Type

Operator UDTdisplayer.New (ByVal size As UInteger) As Any Ptr
  Return UDTdisplayer.allocation("New", size)
End Operator

Operator UDTdisplayer.Delete (ByVal buf As Any Ptr)
  UDTdisplayer.deallocation("Delete", buf)
End Operator

Operator UDTdisplayer.New[] (ByVal size As UInteger) As Any Ptr
  Return UDTdisplayer.allocation("New[]", size)
End Operator

Operator UDTdisplayer.Delete[] (ByVal buf As Any Ptr)
  UDTdisplayer.deallocation("Delete[]", buf)
End Operator

Function UDTdisplayer.allocation (ByRef N As String, ByVal size As UInteger) As Any Ptr
  Dim As Any Ptr p = Allocate(size)
  Print "memory allocation for " & size & " bytes from '" & N & "' at address: " & p
  Return p
End Function

Sub UDTdisplayer.deallocation (ByRef D As String, ByVal p As Any Ptr)
  Print "memory deallocation from '" & D & "' at address " & p
  Deallocate p
End Sub

Dim As UDTdisplayer Ptr pu1 = New UDTdisplayer
Dim As UDTdisplayer Ptr pu2 = New UDTdisplayer[3]
Delete pu1
Delete[] pu2

Sleep

Output example:
memory allocation for 1048576 bytes from 'New' at address: 32677920
memory allocation for 3145728 bytes from 'New[]' at address: 33775648
memory deallocation from 'Delete' at address 32677920
memory deallocation from 'Delete[]' at address 33775648

Aligned memory allocator:
    • by using the member operators "New Overload" and "Delete Overload", any created User object is aligned to a multiple of "ALIGN" bytes (256 bytes in this example),
    • the real pointer of the allocated memory is saved just above the User pointer, in the padding block.

Const ALIGN = 256

Type UDT
  Dim As Byte a(0 To 10 * 1024 * 1024 - 1) '' 10 megabyte fixed array
  Declare Operator New (ByVal size As UInteger) As Any Ptr
  Declare Operator Delete (ByVal buffer As Any Ptr)
  Declare Constructor ()
  Declare Destructor ()
End Type

Operator UDT.New (ByVal size As UInteger) As Any Ptr
  Print "  Overloaded New operator, with parameter size = &h" & Hex(size)
  Dim pOrig As Any Ptr = CAllocate(ALIGN-1 + SizeOf(UDT Ptr) + size)
  Dim pMin As Any Ptr = pOrig + SizeOf(UDT Ptr)
  Dim p As Any Ptr = pMin + ALIGN-1 - (CUInt(pMin + ALIGN-1) Mod ALIGN)
  Cast(Any Ptr Ptr, p)[-1] = pOrig
  Operator = p
  Print "  real pointer = &h" & Hex(pOrig), "return pointer = &h" & Hex(p)
End Operator

Operator UDT.Delete (ByVal buffer As Any Ptr)
  Print "  Overloaded Delete operator, with parameter buffer = &h" & Hex(buffer)
  Dim pOrig As Any Ptr = Cast(Any Ptr Ptr, buffer)[-1]
  Deallocate(pOrig)
  Print "  real pointer = &h" & Hex(pOrig)
End Operator

Constructor UDT ()
  Print "  Constructor, @This = &h" & Hex(@This)
End Constructor

Destructor UDT ()
  Print "  Destructor, @This = &h" & Hex(@This)
End Destructor

Print "'Dim As UDT Ptr p = New UDT'"
Dim As UDT Ptr p = New UDT

Print "  p = &h" & Hex(p)

Print "'Delete p'"
Delete p

Sleep

Output example:
'Dim As UDT Ptr p = New UDT'
  Overloaded New operator, with parameter size = &hA00000
  real pointer = &h420020   return pointer = &h420100
  Constructor, @This = &h420100
  p = &h420100
'Delete p'
  Destructor, @This = &h420100
  Overloaded Delete operator, with parameter buffer = &h420100
  real pointer = &h420020

Dynamic allocation manager for UDT, by using the member operators "New[] Overload" and "Delete[] Overload":
    • monitoring of memory allocations/deallocations: addresses, sizes and total memory used,
    • detection of abnormal deallocation requests,
    • detection of a failed allocation (Allocate() returning null pointer),
    • detection of total allocated memory size exceeding a threshold,
    • the last two detection cases induces an automatic memory freeing before forcing the program to end.
The principle is to manage a dynamic list of successful allocations, but not yet freed, containing the allocated addresses with their requested sizes:

Type UDTmanager
  '' user UDT fields:
    Dim As Byte b(1 To 1024*1024)
  '' manager fields:
    Public:
      Declare Operator New[] (ByVal size As UInteger) As Any Ptr
      Declare Operator Delete[] (ByVal buf As Any Ptr)
      Static As UInteger maxmemory
    Private:
      Static As Any Ptr address()
      Static As UInteger bytes()
      Static upbound As UInteger
      Declare Static Function printLine (ByRef text As String, ByVal index As UInteger, ByVal sign As Integer) As UInteger
      Declare Static Sub endProgram ()
End Type

Dim As UInteger UDTmanager.maxmemory = 3 * 1024 * 1024 * 1024
ReDim UDTmanager.address(0)
ReDim UDTmanager.bytes(0)
Dim UDTmanager.upbound As UInteger = 0

Function UDTmanager.printLine (ByRef text As String, ByVal index As UInteger, ByVal sign As Integer) As UInteger
  Dim As UInteger total = 0
  For I As UInteger = 1 To UDTmanager.upbound
    If I <> index OrElse Sgn(sign) > 0 Then
      total += UDTmanager.bytes(I)
    End If
  Next I
  Print text, "&h" & Hex(UDTmanager.address(index), SizeOf(Any Ptr) * 2),
  If sign <> 0 Then
    Print Using " +####.## MB"; Sgn(sign) * Cast(Integer, UDTmanager.bytes(index) / 1024) / 1024;
  Else
    Print Using "( ####.## MB)"; UDTmanager.bytes(index) / 1024 / 1024;
  End If
  Print,
  Print Using "###.## GB"; total / 1024 / 1024 / 1024
  Return total
End Function

Sub UDTmanager.endProgram ()
  Do While UDTmanager.upbound > 0
    Deallocate UDTmanager.address(UDTmanager.upbound)
    UDTmanager.printLine("memory deallocation forced", UDTmanager.upbound, -1)
    UDTmanager.upbound -= 1
    ReDim Preserve UDTmanager.address(UDTmanager.upbound)
    ReDim Preserve UDTmanager.bytes(UDTmanager.upbound)
  Loop
  Print "end program forced"
  Print
  Sleep
  End
End Sub

Operator UDTmanager.New[] (ByVal size As UInteger) As Any Ptr
  Dim As Any Ptr p = Allocate(size)
  If p > 0 Then
    UDTmanager.upbound += 1
    ReDim Preserve UDTmanager.address(UDTmanager.upbound)
    ReDim Preserve UDTmanager.bytes(UDTmanager.upbound)
    UDTmanager.address(UDTmanager.upbound) = p
    UDTmanager.bytes(UDTmanager.upbound) = size
    If UDTmanager.printLine("memory allocation", UDTmanager.upbound, +1) > UDTmanager.maxmemory Then
      UDTmanager.address(0) = p
      UDTmanager.bytes(0) = size
      Print
      UDTmanager.printLine("memory allocation exceeded", 0, 0)
      UDTmanager.endProgram()
    End If
    Return p
  Else
    UDTmanager.address(0) = p
    UDTmanager.bytes(0) = size
    Print
    UDTmanager.printLine("memory allocation failed", 0, 0)
    UDTmanager.endProgram()
  End If
End Operator

Operator UDTmanager.Delete[] (ByVal buf As Any Ptr)
  Dim As UInteger found = 0
  For I As UInteger = 1 To UDTmanager.upbound
    If UDTmanager.address(I) = buf Then
      Deallocate buf
      UDTmanager.printLine("memory deallocation", I, -1)
      For J As UInteger = I + 1 To UDTmanager.upbound
        UDTmanager.address(J - 1) = UDTmanager.address(J)
        UDTmanager.bytes(J - 1) = UDTmanager.bytes(J)
      Next J
      UDTmanager.upbound -= 1
      ReDim Preserve UDTmanager.address(UDTmanager.upbound)
      ReDim Preserve UDTmanager.bytes(UDTmanager.upbound)
      found = 1
      Exit For
    End If
  Next I
  If found = 0 Then
    UDTmanager.address(0) = buf
    UDTmanager.bytes(0) = 0
    UDTmanager.printLine("deallocation not matching", 0, 0)
  End If
End Operator

Print "Message",, "Address" & Space(SizeOf(Any Ptr)), "Size", "Total"
Print
Randomize
Dim As UDTmanager Ptr pu1 = New UDTmanager[CUInt(Rnd() * 256 + 1)]
Dim As UDTmanager Ptr pu2 = New UDTmanager[CUInt(Rnd() * 256 + 1)]
Dim As UDTmanager Ptr pu3 = Cast(UDTmanager Ptr, 1)
Delete[] pu2
Delete[] pu3
Delete[] pu2
Delete[] pu1
Do
  Dim As UDTmanager Ptr pu = New UDTmanager[CUInt(Rnd() * 512 + 1)]
Loop

Output for fbc 32-bit (maximum dynamic data < 2 GB).
Here, program is stopped because of memory allocation failed:
Message                     Address       Size          Total

memory allocation           &h020E0020       +99.00 MB    0.10 GB
memory allocation           &h083F3020        +3.00 MB    0.10 GB
memory deallocation         &h083F3020        -3.00 MB    0.10 GB
deallocation not matching   &h00000001    (    0.00 MB)   0.10 GB
deallocation not matching   &h083F3020    (    0.00 MB)   0.10 GB
memory deallocation         &h020E0020       -99.00 MB    0.00 GB
memory allocation           &h020ED020      +103.00 MB    0.10 GB
memory allocation           &h087F2020      +106.00 MB    0.20 GB
memory allocation           &h0F20D020      +230.00 MB    0.43 GB
memory allocation           &h1D812020      +137.00 MB    0.56 GB
memory allocation           &h2612C020      +377.00 MB    0.93 GB
memory allocation           &h3DA30020      +275.00 MB    1.20 GB
memory allocation           &h4ED40020      +220.00 MB    1.41 GB
memory allocation           &h5C958020      +229.00 MB    1.64 GB

memory allocation failed    &h00000000    (  142.00 MB)   1.64 GB
memory deallocation forced  &h5C958020      -229.00 MB    1.41 GB
memory deallocation forced  &h4ED40020      -220.00 MB    1.20 GB
memory deallocation forced  &h3DA30020      -275.00 MB    0.93 GB
memory deallocation forced  &h2612C020      -377.00 MB    0.56 GB
memory deallocation forced  &h1D812020      -137.00 MB    0.43 GB
memory deallocation forced  &h0F20D020      -230.00 MB    0.20 GB
memory deallocation forced  &h087F2020      -106.00 MB    0.10 GB
memory deallocation forced  &h020ED020      -103.00 MB    0.00 GB
end program forced
Output for fbc 64-bit (maximum dynamic data < virtual memory).
Here, program is stopped because of total allocated memory size > 3 GB (adjustable threshold):
Message                     Address                     Size          Total

memory allocation           &h0000000001EA5040            +105.00 MB    0.10 GB
memory allocation           &h00000000087BC040             +93.00 MB    0.19 GB
memory deallocation         &h00000000087BC040             -93.00 MB    0.10 GB
deallocation not matching   &h0000000000000001          (    0.00 MB)   0.10 GB
deallocation not matching   &h00000000087BC040          (    0.00 MB)   0.10 GB
memory deallocation         &h0000000001EA5040            -105.00 MB    0.00 GB
memory allocation           &h0000000001EA1040            +155.00 MB    0.15 GB
memory allocation           &h000000000B9BF040            +165.00 MB    0.31 GB
memory allocation           &h0000000015ED8040            +382.00 MB    0.69 GB
memory allocation           &h000000002DCE7040            +458.00 MB    1.13 GB
memory allocation           &h000000004A6FB040            +255.00 MB    1.38 GB
memory allocation           &h000000005A607040             +96.00 MB    1.48 GB
memory allocation           &h000000006061B040            +426.00 MB    1.89 GB
memory allocation           &h000000007FFF9040            +221.00 MB    2.11 GB
memory allocation           &h000000008DD03040            +119.00 MB    2.22 GB
memory allocation           &h0000000095413040            +147.00 MB    2.37 GB
memory allocation           &h000000009E727040            +217.00 MB    2.58 GB
memory allocation           &h00000000AC03C040            +334.00 MB    2.91 GB
memory allocation           &h00000000C0E4B040            +280.00 MB    3.18 GB

memory allocation exceeded  &h00000000C0E4B040          (  280.00 MB)   3.18 GB
memory deallocation forced  &h00000000C0E4B040            -280.00 MB    2.91 GB
memory deallocation forced  &h00000000AC03C040            -334.00 MB    2.58 GB
memory deallocation forced  &h000000009E727040            -217.00 MB    2.37 GB
memory deallocation forced  &h0000000095413040            -147.00 MB    2.22 GB
memory deallocation forced  &h000000008DD03040            -119.00 MB    2.11 GB
memory deallocation forced  &h000000007FFF9040            -221.00 MB    1.89 GB
memory deallocation forced  &h000000006061B040            -426.00 MB    1.48 GB
memory deallocation forced  &h000000005A607040             -96.00 MB    1.38 GB
memory deallocation forced  &h000000004A6FB040            -255.00 MB    1.13 GB
memory deallocation forced  &h000000002DCE7040            -458.00 MB    0.69 GB
memory deallocation forced  &h0000000015ED8040            -382.00 MB    0.31 GB
memory deallocation forced  &h000000000B9BF040            -165.00 MB    0.15 GB
memory deallocation forced  &h0000000001EA1040            -155.00 MB    0.00 GB
end program forced
Dialect Differences

  • Only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • New Expression
  • Delete Overload
  • Allocate


Operator Delete Overload
Member operator to overload memory deallocation process part provided by Operator Delete Statement when applying to a UDT (User Defined Type).

Syntax

Declare Operator Delete ( buf As Any Ptr )
Declare Operator delete[] ( buf As Any Ptr )

Parameters

buf
A pointer to memory that has been allocated by New Overload operator or New[] Overload operator, the array-version of New Overload operator.

Description

The member operator Delete Overload overloads the memory deallocation process part provided by the Delete Statement operator when applying to a UDT (User Defined Type). So the user can define its own memory deallocation process part.
But before that, the UDT instance destruction process part provided by the Delete Statement operator is not modified.

Delete[] Overload operator is the array-version of Delete Overload operator and overloads the memory deallocation process provided by the Delete[] Statement operator when applying to a UDT (User Defined Type).

Memory freed with Delete Overload operator must have be allocated by also defining a New Overload operator. Memory freed with Delete[] Overload operator must have been allocated by also defining a New[] Overload operator, the array-version of New Overload operator. You cannot mix and match the different versions of the operators.

Member operators Delete Overload, and Delete[] Overload are always static, even if not explicitly declared (Static keyword is unnecessary but allowed). Thus, they do not have an implicit This instance argument passed to them (because instance already been destroyed).

Example

See the New Overload operator examples.

Dialect Differences

  • Only available in the -lang fb dialect.

Differences from QB

  • New to FreeBASIC

See also

  • Delete Statement
  • New Overload
  • Deallocate


Operator For (Iteration)
Declares or defines operators used by a For...Next loop with user defined type variables

Syntax

{ Type | Class | Union } typename
Declare Operator For ()
Declare Operator For ( [ ByRef | ByVal ] stp As typename )
...
End { Type | Class | Union }

Usage

For iterator [ As typename ] = start_value To end_value [ Step step_value ]
[ ...statements... ]
Next

Parameters

(including arguments)
typename
name of the Type, Class, or Union
stp, step_value
a typename object used as an incremental value
iterator
a typename object used as an iterator
end_value
a typename object used as a loop-terminating value
start_value
a typename object used to copy construct or assign to the iterator initially

Description

Operator For, Operator Next and Operator Step can be overloaded in user-defined type definitions to allow objects of that type to be used as iterators and step values in For...Next loops.
As all non-static member procedures, they have passed a hidden This parameter that allows to access by reference to the iterator object in the code body of the 3 operators.

Operator For is called once after copy constructing or assigning to the iterator object, and allows the object to perform any additional initialization needed in preparation for the loop.

The first version of Operator For is used if no step value is given in the For...Next statement. If a step value is given, the second version is used and is passed the step value because eventual additional initialization may use it.

Advanced usage
The above description seems to imply that the 3 arguments start_value, end_value, and step_value must be of the same type as the iterator (this is the more obvious use), but it is not quite true:
- The start_value, end_value, and step_value arguments can be of any type (of different types among themselves and also of different types from the one of the iterator).
- The only constraint is that the iterator could be constructed (in case of local iterator) or assigned (in case of global iterator) from the start_value argument (because the iterator is implicitly constructed or assigned under the hood).
- Similarly the other parameters end_value, and step_value must be able to be converted into objects of the same type as the iterator.

Example

See the Operator Step examples.

Dialect Differences

  • Only available in the -lang fb dialect.

See also

  • Operator Next
  • Operator Step
  • For...Next


Variable Declarations
Statements to declare and allocate space for variables.

Dim
Declares a variable at the current scope.
Const
Declares a non-modifiable variable.
Scope
Begins a new scope block.
Static
Declares variables in a procedure that retain their value between calls.
Shared
Used with Dim allows variables to be visible throughout a module.
Var
Declares variables where the data type is implied from an initializer.
Byref (Variables)
Used with Dim or Static or Var allows to declare references.


User Defined Types

Declaration
Declaring and describing user defined types
Referencing
Accessing data in a user defined type
Member Procedures
Declaring and defining methods related to a user defined type
Member Access Control
Controlling when data and member procedures are accessed

Declarations
Enum...End Enum
User defined enumeration of values
Type...End Type
User defined structure of non overlapping data and member procedures
Class...End Class
Not implemented. Keyword reserved.
Union...End Union
User defined structure of overlapping data
Extends
Extends an user defined type to derive another
Extends Wstring
Extends an user defined type to inherits Wstring behavior
Extends Zstring
Extends an user defined type to inherits Zstring behavior
Implements
Not implemented. Keyword reserved.
Field
Specifies field alignment within a user defined type
Object
Built-in type providing run-time type information

Referencing
Temporary Types
Creates a temporary copy of a user defined type
This
Built-in, hidden, parameter passed to non-static member procedures to access the user defined type instance
Base (Member Access)
Built-in, hidden, variable to access the base user defined type instance in derived user defined types
Type Alias
Declares a user defined type from other user defined or standard data types
With
Compound statement to access the data and members of a user defined type

Member Procedures
Base (Initialization)
Specifies an initializer for the base user defined type in derived user defined type constructors
Constructor
Declares or defines a member procedure that is automatically called when a user defined type is created
Destructor
Declares or defines a member procedure that is automatically called when a user defined type is destroyed or goes out of scope
Function
Declares or defines a member procedure returning a value
Operator
Declares or defines an overloaded operator
Override
Member method attribute that specifies that the method is expected to override a virtual method in the base user defined type
Property
Declares or defines property member procedures for a user defined type
Sub
Declare or defines a member procedure
Static (Member)
Declares or defines a member procedure or variable is static
Virtual
Member method attribute that declares that a member must have an implementation
Abstract
Member method attribute that declares that a member must be implemented in a derived user defined type
Const (Member)
Member method attribute that declares or defines that the method is readonly and does not modify the user defined types's data

Member Access Control
Public: (Access Control)
Data and members in a user defined type have public visibility
Private: (Access Control)
Data and members in a user defined type have private visibility
Protected: (Access Control)
Data and members in a user defined type have protected visibility


Standard Data Types
Built-in data types

Integer types
Types that store integer values, whose range is determined by the size of the data type and its signedness.
Floating-point types
Types that store real number values, whose range and precision is determined by the size of the data type.
Boolean types
Types that store boolean values.
Procedure Types
Types that store pointers to procedures
Data Type Modifiers
Specifies additional characteristics of a standard or user-defined data type.
String types
Types that store or point to an array of characters.
Class types
Types that provide special capabilities to be used directly or to be extended by user-defined types

Integer types
Byte and UByte
8-bit wide data types that store integer values.
Short and UShort
16-bit wide data types that store integer values.
Long and ULong
32-bit wide data types that store integer values.
Integer and UInteger
32-bit or 64-bit wide data types that store integer values.
LongInt and ULongInt
64-bit wide data types that store integer values.

Floating-point types
Single
32-bit wide data types that store real number values.
Double
64-bit wide data types that store real number values.

Boolean types
Boolean
1-bit wide data types that store boolean values.

Procedure Types
Function Pointer
Types that store a pointer to a function procedure
Sub Pointer
Types that store a pointer to a sub procedure
Data Type Modifiers
Const
Specifies a read only type.
Pointer and Ptr (Shortcut For 'Pointer')
Modifies types to be pointer types.
Unsigned
Specifies an unsigned integer type.
Alias (Modifier)
Modifies how a datatype is linked with other languages (Name mangling).

String types
String
Fixed-length and variable-length strings with built-in memory management.
ZString
Fixed-length and variable-length null-terminated strings.
WString
Fixed-length and variable-length null-terminated strings of wide characters.

Class types
Object
Super class providing run-time type information

See also

  • Variable types and limits
  • Pointers to Procedures


Standard Data Type Limits
Standard variable types and limits.

Numeric Types

Type | Size in bits | Format | Minimum Value | Maximum Value | Literal Suffix | Sig. Digits
BYTE | 8 | signed integer | -128 | +127 |  | 2+
UBYTE | 8 | unsigned integer | 0 | +255 |  | 2+
SHORT | 16 | signed integer | -32768 | +32767 |  | 4+
USHORT | 16 | unsigned integer | 0 | 65535 |  | 4+
LONG | 32 | signed integer | -2147483648 | +2147483647 | &, l | 9+
ULONG | 32 | unsigned integer | 0 | +4294967295 | ul | 9+
INTEGER | 32/64 [*] | signed integer | [*]32bit: -2147483648, 64bit: -9223372036854775808 | [*]32bit: +2147483647, 64bit: +9223372036854775807 | % | [*]
UINTEGER | 32/64 [*] | unsigned integer | 0 | [*]32bit: +4294967295, 64bit: +18446744073709551615 | u | [*]
LONGINT | 64 | signed integer | -9223372036854775808 | +9223372036854775807 | ll | 18+
ULONGINT | 64 | unsigned integer | 0 | +18446744073709551615 | ull | 19+
SINGLE | 32 | floating point | [**]+/-1.401 298 E-45 | [**]+/-3.402 823 E+38 | !, f | 6+
DOUBLE | 64 | floating point | [**]+/-4.940 656 458 412 465 E-324 | [**]+/-1.797 693 134 862 316 E+308 | #, d | 15+
enums | 32/64 [*] | signed integer | [*]32bit: -2147483648, 64bit: -9223372036854775808 | [*]32bit: +2147483647, 64bit: +9223372036854775807 |  | [*]

[*] Integer and UInteger data types vary with platform, matching the size of pointers.
[**] The minimum and maximum values for the floating-point types Single and Double are, respectively, the values closest to zero and the values closest to positive and negative infinity.

String Types

Type | Character Size (in bytes) | Minimum Size (in characters) | Maximum Size (in characters), including terminating padding | Literal Suffix
String | 1 | 0 | [**]32bit: +2147483647, 64bit: +9223372036854775807 | $
String*N | 1 | 1 | [**,***]+2147483647 | $
Zstring*N | 1 | 1 | [**,***]+2147483647 | [N/A]
Wstring*N | [*] | [*]1 | [*,**,***]+2147483647 | [N/A]

[*] Unicode, or "wide", characters vary in both size and availability with platform.
[**] All runtime library string procedures take and produce Integer values for sizes and positions. The actual maximum size will vary (smaller) with storage location and/or platform.
[***] Before fbc version 1.20.0, fixed-length strings had the same maximum size limits as variable strings.

Boolean Type

Type | Internal size in bits | Value
Boolean | 1-bit wide data (in a Byte) | 'False' or 'True'

Arrays

Platform | Maximum Subscript Range | Maximum Elements per Dimension | Minimum/Maximum Dimensions | Maximum Size (in bytes)
32bit | [*][-2147483648, +2147483647] | [*]+2147483647 | 1/8 | [*]+2147483647
64bit | [*][-9223372036854775808, +9223372036854775807] | [*]+9223372036854775807 | 1/8 | [*]+9223372036854775807

[*] All runtime library array procedures take and produce Integer values for subscripts and indexes. The actual limits will vary (smaller) with the number of dimensions, element size, storage location and/or platform.

UDTs

Platform | Maximum Size (in bytes)
32bit | [*]+2147483647
64bit | [*]+2147483647

[*] The actual maximum size will vary (smaller) with storage location and/or platform.

See also


Converting Data Types
Operators and procedures that convert between different types.

Generic conversions
Operators to convert between arbitrary types.
Conversions to integral types
Operators to convert to integral types.
Conversions to floating-point types
Operators to convert to floating-point types.
Conversions to/from string types
Operators to convert top an from string types.
Conversion to boolean types
Operators to convert to boolean types.

Generic conversions
Cast and CPtr
Converts expressions between different types.

Conversions to integral types
CByte and CUByte
Converts numeric expressions to 8-bit values.
CShort and CUShort
Converts numeric expressions to 16-bit values.
CLng and CULng
Converts numeric expressions to 32-bit values.
CInt and CUInt
Converts numeric expressions to 32-bit or 64-bit values.
CLngInt and CULngInt
Converts numeric expressions to 64-bit values.
CSign
Converts a numeric expression to a signed-type value.
CUnsg
Converts a numeric expression to an unsigned-type value.
Conversions to floating-point types
CSng and CDbl
Converts a numeric or string expression to floating-point values.

Conversions to/from string types
Str and WStr
Converts numeric expressions or booleans to their string representation.
Val
Converts a numeric string expression to a floating-point value.
ValInt and ValUInt
Converts numeric string expressions to integer values.
ValLng and ValULng
Converts numeric string expressions to long values.

Conversion to boolean types
CBool
Converts a numeric or string expression to a boolean value.


Operators
Procedures that operate on one or more operands.

FreeBASIC has numerous operators that perform a certain function with their operands. Many operators use a "operand operator operand" syntax, like Operator = (Assignment) or Operator +, while others are called like normal procedures, like Operator Strptr.

Assignment operators
Operators which assign the value of one operand to the other.
Arithmetic operators
Operators that perform mathematical computations on their operands and return the result.
Conditional operators
Operators that compare the relationship between their operands.
Logical operators
Operators that perform bitwise computations with their operands and return the result.
Short circuit operators
Operators that perform short circuit evaluations with their operands and return the result.
Indexing operators
Operators that return references to variables or objects based on an index value.
String operators
Operators overloaded to work with strings.
Preprocessor operators
Operators that control preprocessor behavior.
Pointer operators
Operators that work with pointers and addresses.
Type or Class operators
Operators that provide access to Type or Class members.
Memory operators
Operators that allocate memory for and construct objects.
Iterating operators
Operators that use iterator objects in For...Next statements.


Assignment Operators
Operators that assign values to operands

The assignment operators perform an assignment to the first, or left-hand side, operand based on the value of the second, or right-hand side, operand. Most of the assignment operators are combination operators, in that they first perform a mathematical or bitwise operation on the two operands, then assign the result to the left-hand side operand.

Operator =[>] (Assignment)
Assigns the value of one operand to the other.
Operator &= (Concatenate And Assign)
Assigns the value of a concatenation between two operands.
Operator += (Add And Assign)
Assigns the value of an addition between two operands.
Operator -= (Subtract And Assign)
Assigns the value of a subtraction between two operands.
Operator *= (Multiply And Assign)
Assigns the value of a multiplication between two operands.
Operator /= (Divide And Assign)
Assigns the value of a division between two operands.
Operator \= (Integer Divide And Assign)
Assigns the value of an integer divide between two operands.
Operator ^= (Exponentiate And Assign)
Assigns the value of a exponentiation between two operands.
Operator Let (Assignment)
Assigns the value of one user defined type to another.
Operator Let() (Assignment)
Assigns the fields of a user defined type to a list of variables.
Operator Mod= (Modulus And Assign)
Assigns the value of a modulus between two operands.
Operator And= (Conjunction And Assign)
Assigns the value of a bitwise conjunction between two operands.
Operator Eqv= (Equivalence And Assign)
Assigns the value of a bitwise equivalence between two operands.
Operator Imp= (Implication And Assign)
Assigns the value of a bitwise implication between two operands.
Operator Or= (Inclusive Disjunction And Assign)
Assigns the value of a bitwise inclusive or between two operands.
Operator Xor= (Exclusive Disjunction And Assign)
Assigns the value of a bitwise exclusive or between two operands.
Operator Shl= (Shift Left And Assign)
Assigns the value of a bitwise shift left of an operand.
Operator Shr= (Shift Right And Assign)
Assigns the value of a bitwise shift right of an operand.


Arithmetic Operators
Operators that can be used in mathematical expressions

The mathematical operators perform mathematical operations with the values of their operands and return the results.

Operator + (Add)
Returns the result of an addition of two operands.
Operator - (Subtract)
Returns the result of a subtraction of two operands.
Operator * (Multiply)
Returns the result of a multiplication of two operands.
Operator / (Divide)
Returns the result of a division of two operands.
Operator \ (Integer Divide)
Returns the result of an integer divide of two operands.
Operator ^ (Exponentiate)
Returns the result of an exponentiation of two operands.
Operator Mod (Modulus)
Returns the result of a modulus of two operands.
Operator - (Negate)
Returns the result of a negation of an operand.
Operator Shl (Shift Left)
Returns the result of a bitwise shift left of an operand.
Operator Shr (Shift Right)
Returns the result of a bitwise shift right of an operand.


Relational Operators
Operators that compare relationships

The relational operators perform comparisons between the values of two operands. Each operator returns a boolean result that is true (-1) if the relationship holds true, or false (0) if not.

Operator = (Equal)
Compares the equal relation of two operands.
Operator <> (Not Equal)
Compares the inequality relation of two operands.
Operator < (Less Than)
Compares the less than relation of two operands.
Operator <= (Less Than Or Equal)
Compares the less than or equal relation of two operands.
Operator >= (Greater Than Or Equal)
Compares the greater than or equal relation of two operands.
Operator > (Greater Than)
Compares the greater than relation of two operands.
Operator Is (Run-Time Type Information)
Checks whether an object is of a certain type.


Logical Operators
Operators that perform bitwise logic

The logical operators perform logical operations on the values of their operands, and return the results. These operators are bitwise operators, in that the results are found by performing logical operations on each bit of their operands.

Operator And (Conjunction)
Returns the result of a bitwise conjunction of two operands.
Operator Eqv (Equivalence)
Returns the result of a bitwise equivalence of two operands.
Operator Imp (Implication)
Returns the result of a bitwise implication of two operands.
Operator Not (Complement)
Returns the result of a bitwise complement of an operand.
Operator Or (Inclusive Disjunction)
Returns the result of a bitwise inclusive or of two operands.
Operator Xor (Exclusive Disjunction)
Returns the result of a bitwise exclusive or of two operands.


Short Circuit Operators
Operators that perform a short circuit logical evaluation.

The short circuit operators perform a evaluation on the left hand operand, and depending on the result, may go on to evaluate the right hand side. The evaluations take place logically, in a comparison to zero.

Operator Andalso (Short Circuit Conjunction)
Returns the result of a short circuit conjunction of two operands.
Operator Orelse (Short Circuit Inclusive Disjunction)
Returns the result of a short circuit inclusive or of two operands.


Indexing Operators
Operators that return references based on an index

The indexing operators return references to some memory based on the value of their second, or right-hand side, operand. This operand is used as an index, or offset, from the beginning of some memory represented by the first, or left-hand side, operand.

Operator () (Array Index)
Returns a reference to an element in an array.
Operator [] (String Index)
Returns a reference to the numeric value of a character in a string.
Operator [] (Pointer Index)
Returns a reference to memory offset from a base address.


String Operators
Operators that work with strings

These operators provide conversion to string, concatenation and retrieval of character data.

Operator + (String Concatenation)
Concatenates two strings.
Operator & (String Concatenation With Conversion)
Concatenates two values converted to strings.
Operator Strptr (String Pointer)
Returns the address of a string's character data.


Preprocessor Operators
Operators that are executed by the preprocessor

These operators control how text is interpreted by the preprocessor.

Operator # (Stringize)
Returns a text operand converted to a String literal.
Operator ## (Concatenation)
Concatenates two text operands.
Operator ! (Escaped String Literal)
Indicates string literal immediately following must be processed for escape sequences.
Operator $ (Non-Escaped String Literal)
Indicates string literal immediately following must not be processed for escape sequences.


Pointer Operators
Operators that work with pointers

The pointer operators provide the ability to retrieve the addresses in memory of their operands, and to use, or dereference, that memory.

Operator Varptr (Variable Pointer)
Returns the memory address of a variable.
Operator Strptr (String Pointer)
Returns the memory address of a string's character data.
Operator Procptr (Procedure Pointer And Vtable Index)
Returns the memory address of a procedure.
Operator @ (Address Of)
Returns the memory address of a variable, object or procedure.
Operator * (Value Of)
Returns a reference to a variable or object at some memory address.


Type or Class Operators
Operators that work with objects

These operators return references to members of objects, given an object or its memory address.

Operator . (Member Access)
Returns a reference to a member.
Operator -> (Pointer To Member Access)
Returns a reference to a member from a pointer.
Operator Is (Run-Time Type Information)
Checks whether an object is compatible to a type derived from its runtime-type.


Memory Operators
Operators that work with memory

The memory operators provide a way to dynamically allocate and deallocate variables and objects.

Operator New Expression
Allocates memory for and constructs objects.
Operator New Overload
Overloads memory allocation process of Operator New Expression when applying to UDT.
Operator Placement New
Constructs objects at a specified memory location.
Operator Delete Statement
Destroys and deallocates memory for objects.
Operator Delete Overload
Overloads memory deallocation process of Operator Delete Statement when applying to UDT.


Iterating Operators
Operators that work with iterator objects

These operators allow objects of user-defined types to be used as iterators and step values in For...Next statements.

Operator For
Allows an iterator a chance to prepare for the loop.
Operator Step
Increments an iterator object.
Operator Next
Determines if the loop should terminate or continue iterating.


Operator Step (Iteration)
Increments the iterator of a For...Next loop

Syntax

{ Type | Class | Union } typename
Declare Operator Step ()
Declare Operator Step ( [ ByRef | ByVal ] stp As typename )
...
End { Type | Class | Union }

Usage

For iterator [ As typename ] = start_value To end_value [ Step step_value ]
[ ...statements... ]
Next

Parameters

(including arguments)
typename
name of the Type, Class, or Union
stp, step_value
a typename object used as an incremental value
iterator
a typename object used as an iterator
end_value
a typename object used as a loop-terminating value
start_value
a typename object used to copy construct or assign to the iterator initially

Description

Operator For, Operator Next and Operator Step can be overloaded in user-defined type definitions to allow objects of that type to be used as iterators and step values in For...Next loops.
As all non-static member procedures, they have passed a hidden This parameter that allows to access by reference to the iterator object in the code body of the 3 operators.

Operator Step is called to increment the iterator object immediately after all statements in the For...Next body are executed, if any.

The first version of Operator Step is used if no step value is given in the For...Next statement. If a step value is given, the second version is used and is passed the step value to increment the iterator object.

Advanced usage
The above description seems to imply that the 3 arguments start_value, end_value, and step_value must be of the same type as the iterator (this is the more obvious use), but it is not quite true:
- The start_value, end_value, and step_value arguments can be of any type (of different types among themselves and also of different types from the one of the iterator).
- The only constraint is that the iterator could be constructed (in case of local iterator) or assigned (in case of global iterator) from the start_value argument (because the iterator is implicitly constructed or assigned under the hood).
- Similarly the other parameters end_value, and step_value must be able to be converted into objects of the same type as the iterator.

Example

'' Example Type
Type T
  '' value is set by the constructor
  value As Double
  Declare Constructor( ByVal x As Double = 0 )

  Declare Operator For( ByRef stp As T )
  Declare Operator Step( ByRef stp As T )
  Declare Operator Next( ByRef cond As T, ByRef stp As T ) As Integer
End Type

Constructor T ( ByVal x As Double )
  Print "T iterator constructed with value " & x
  value = x
End Constructor

Operator T.for( ByRef stp As T )
End Operator

Operator T.step( ByRef stp As T )
  Print " incremented by " & stp.value & " in step."
  value += stp.value
End Operator

Operator T.next( ByRef cond As T, ByRef stp As T ) As Integer
  '' iterator's moving from a high value to a low value (step >= 0)
  If( stp.value < 0 ) Then
    Return( value >= cond.value )
  Else
  '' iterator's moving from a low value to a high value (step < 0)
    Return( value <= cond.value )
  End If
End Operator

'' Example Usage. It looks like we are working with numbers, but the iterators
'' have overloaded constructors. The 10, 1, and -1 are all of type T.
For i As T = 10 To 1 Step -1
  Print i.value;
Next i

A more practical example demonstrating file iteration based on cha0s' file iteration class:
'' a class which iterates through files
Type FileIter
    As String pathName, fileName
    Declare Constructor( ByRef pathName As String )

    Declare Operator For()
    Declare Operator Step()
    Declare Operator Next( ByRef endCond As FileIter) As Integer
End Type

Constructor FileIter( ByRef pathName As String )
    this.pathName = pathName
End Constructor

Operator FileIter.for( )
    fileName = Dir(pathName & "/*.*")
End Operator

Operator FileIter.step( )
    fileName = Dir("")
End Operator

Operator FileIter.next( ByRef endCond As FileIter ) As Integer
    Return(fileName <> endCond.pathName)
    '' the c'tor sets the path name and so we check against that
End Operator

'' example code
'' change it to any directory
For i As FileIter = "./" To ""
    Print i.fileName
Next

Another example working with strings:
Type CharIterator
    '' used to build a step var
    Declare Constructor( ByVal r As ZString Ptr )

    '' implicit step versions
    Declare Operator For ( )
    Declare Operator Step( )
    Declare Operator Next( ByRef end_cond As CharIterator ) As Integer

    '' explicit step versions
    Declare Operator For ( ByRef step_var As CharIterator )
    Declare Operator Step( ByRef step_var As CharIterator )
    Declare Operator Next( ByRef end_cond As CharIterator, ByRef step_var As CharIterator ) As Integer

    '' give the current "value"
    Declare Operator Cast( ) As String

    Private:
        '' data
        value As String

        '' This member isn't necessary - we could use
        '' the step variable on each iteration -
        '' but we choose this method, since we have
        '' to compare strings otherwise. See below.
        is_up As Integer
End Type

Constructor CharIterator( ByVal r As ZString Ptr )
    value = *r
End Constructor

Operator CharIterator.cast( ) As String
    Operator = value
End Operator

'' implicit step versions
''
'' In this example, we interpret implicit step
'' to always mean 'up'
Operator CharIterator.for( )
    Print "implicit step"
End Operator

Operator CharIterator.step( )
    value[0] += 1
End Operator

Operator CharIterator.next( ByRef end_cond As CharIterator ) As Integer
    Return this.value <= end_cond.value
End Operator

'' explicit step versions
''
'' In this example, we calculate the direction
'' at FOR, but since the step var is passed to
'' each operator, we have the choice to also calculate
'' it "on-the-fly". For strings such as this, repeated comparison
'' may penalize, but if you're working with simpler types,
'' then you may prefer to avoid the overhead of
'' an 'is_up' variable.
Operator CharIterator.for( ByRef step_var As CharIterator )
    Print "explicit step"
    is_up = (step_var.value = "up")
End Operator

Operator CharIterator.step( ByRef step_var As CharIterator )
    If( is_up ) Then
        value[0] += 1
    Else
        value[0] -= 1
    End If
End Operator

Operator CharIterator.next( ByRef end_cond As CharIterator, ByRef step_var As CharIterator ) As Integer
    If( this.is_up ) Then
        Return this.value <= end_cond.value
    Else
        Return this.value >= end_cond.value
    End If
End Operator

For i As CharIterator = "a" To "z"
    Print i; " ";
Next
Print "done"

For i As CharIterator = "a" To "z" Step "up"
    Print i; " ";
Next
Print "done"

For i As CharIterator = "z" To "a" Step "down"
    Print i; " ";
Next
Print "done"

For i As CharIterator = "z" To "a" Step "up"
    Print i; " ";
Next
Print "done"

Iterating with fractions:
Type fraction
    '' Used to build a step var
    Declare Constructor( ByVal n As Integer, ByVal d As Integer )

    '' Implicit step versions
    Declare Operator For ( )
    Declare Operator Step( )
    Declare Operator Next( ByRef end_cond As fraction ) As Integer

    '' Explicit step versions
    Declare Operator For ( ByRef step_var As fraction )
    Declare Operator Step( ByRef step_var As fraction )
    Declare Operator Next( ByRef end_cond As fraction, ByRef step_var As fraction ) As Integer

    '' Give the current "value"
    Declare Operator Cast( ) As Double
    Declare Operator Cast( ) As String

    Private:
        As Integer num, den
End Type

Constructor fraction( ByVal n As Integer, ByVal d As Integer )
    This.num = n : This.den = d
End Constructor

Operator fraction.Cast( ) As Double
    Operator = num / den
End Operator

Operator fraction.Cast( ) As String
    Operator = num & "/" & den
End Operator

'' Some fraction functions
Function gcd( ByVal n As Integer, ByVal m As Integer ) As Integer
    Dim As Integer t
        While m <> 0
            t = m
            m = n Mod m
            n = t
        Wend
    Return n
End Function

Function lcd( ByVal n As Integer, ByVal m As Integer ) As Integer
    Return (n * m) / gcd( n, m )
End Function

''
'' Implicit step versions
''
'' In this example, we interpret implicit step
'' to mean 1
''
Operator fraction.For( )
    Print "implicit step"
End Operator

Operator fraction.Step( )
    Dim As Integer lowest = lcd( This.den, 1 )
    Dim As Double mult_factor = This.den / lowest
    Dim As fraction step_temp = fraction( 1, 1 )

    This.num *= mult_factor
    This.den *= mult_factor

    step_temp.num *= lowest
    step_temp.den *= lowest

    This.num += step_temp.num
End Operator

Operator fraction.Next( ByRef end_cond As fraction ) As Integer
    Return This <= end_cond
End Operator

''
'' Explicit step versions
''
Operator fraction.For( ByRef step_var As fraction )
    Print "explicit step"
End Operator

Operator fraction.Step( ByRef step_var As fraction )
    Dim As Integer lowest = lcd( This.den, step_var.den )
    Dim As Double mult_factor = This.den / lowest
    Dim As fraction step_temp = step_var

    This.num *= mult_factor
    This.den *= mult_factor

    mult_factor = step_temp.den / lowest

    step_temp.num *= mult_factor
    step_temp.den *= mult_factor

    This.num += step_temp.num
End Operator

Operator fraction.Next( ByRef end_cond As fraction, ByRef step_var As fraction ) As Integer
    If(( step_var.num < 0 ) Or ( step_var.den < 0 ) ) Then
        Return This >= end_cond
    Else
        Return This <= end_cond
    End If
End Operator

For i As fraction = fraction(1,1) To fraction(4,1)
    Print i; " ";
Next
Print "done"

For i As fraction = fraction(1,4) To fraction(1,1) Step fraction(1,4)
    Print i; " ";
Next
Print "done"

For i As fraction = fraction(4,4) To fraction(1,4) Step fraction(-1,4)
    Print i; " ";
Next
Print "done"

For i As fraction = fraction(4,4) To fraction(1,4)
    Print i; " ";
Next
Print "done"

Dialect Differences

  • Only available in the -lang fb dialect.

See also

  • Operator For
  • Operator Next
  • For...Next


Operator Next (Iteration)
Determines if a For...Next loop should be terminated

Syntax

{ Type | Class | Union } typename
Declare Operator Next ( [ ByRef | ByVal ] cond As typename ) As Integer
Declare Operator Next ( [ ByRef | ByVal ] cond As typename, [ ByRef | ByVal ] stp As typename ) As Integer
...
End { Type | Class | Union }

Usage

For iterator [ As typename ] = start_value To end_value [ Step step_value ]
[ ...statements... ]
Next

Parameters

(including arguments)
typename
name of the Type, Class, or Union
cond, end_value
a typename object used as a loop-terminating value
stp, step_value
a typename object used as an incremental value
iterator
a typename object used as an iterator
start_value
a typename object used to copy construct or assign to the iterator initially

Description

Operator For, Operator Next and Operator Step can be overloaded in user-defined type definitions to allow objects of that type to be used as iterators and step values in For...Next loops.
As all non-static member procedures, they have passed a hidden This parameter that allows to access by reference to the iterator object in the code body of the 3 operators.

Operator Next is called every time the iterator object needs to be checked against the end value. This happens immediately after the call to its Operator For, and immediately after any calls to its Operator Step. Operator Next should return zero (0) if the loop should be terminated, or non-zero if the loop should continue iterating. The first time Operator Next is called, no statements in the For...Next body, if any, have been executed yet.

The first version of Operator Next is used if no step value is given in the For...Next statement. If a step value is given, the second version is used and is passed the step value because testing for iterating end may depend on it.

Advanced usage
The above description seems to imply that the 3 arguments start_value, end_value, and step_value must be of the same type as the iterator (this is the more obvious use), but it is not quite true:
- The start_value, end_value, and step_value arguments can be of any type (of different types among themselves and also of different types from the one of the iterator).
- The only constraint is that the iterator could be constructed (in case of local iterator) or assigned (in case of global iterator) from the start_value argument (because the iterator is implicitly constructed or assigned under the hood).
- Similarly the other parameters end_value, and step_value must be able to be converted into objects of the same type as the iterator.

Example

See the Operator Step examples.

Dialect Differences

  • Only available in the -lang fb dialect.

See also

  • Operator For
  • Operator Step
  • For...Next


Operator Precedence
When several operations occur in a single expression, each operation is evaluated and resolved in a predetermined order. This is called the order of operation or operator precedence.

If an operator in an expression has a higher precedence, it is evaluated before an operator of lower precedence.

If operators have equal precedence, they then are evaluated in the order in of their associativity. The associativity may be Left-to-Right or Right-to-Left order.

As a rule, binary operators (such as +, ^) and unary postfix operators (such as (), ->) are evaluated Left-to-Right, and unary prefix operators (such as Not, @) are evaluated Right-to-Left.

Operators that have an associativity of "N/A" indicate that there is no expression in which the operator can be used where its order of operation would need to be checked, either by precedence or by associativity. Function-like operators such as Cast are always the first to be evaluated due to the parentheses required in their syntax. And assignment operators are always the last to be evaluated.

Parentheses can be used to override operator precedence. Operations within parentheses are performed before other operations. Within the parentheses normal operator precedence is used.

The following table lists operator precedence from highest to lowest. Breaks in the table mark the groups of operators having equal precedence.

Highest Precedence
Operator | Description | Associativity
CAST | Type Conversion | N/A
PROCPTR | Procedure pointer | N/A
STRPTR | String pointer | N/A
VARPTR | Variable pointer | N/A
[] | String index | Left-to-Right
[] | Pointer index | Left-to-Right
() | Array index | Left-to-Right
() | Function Call | Left-to-Right
. | Member access | Left-to-Right
-> | Pointer to member access | Left-to-Right
@ | Address of | Right-to-Left
* | Value of | Right-to-Left
New | Allocate Memory | Right-to-Left
Delete | Deallocate Memory | Right-to-Left
^ | Exponentiate | Left-to-Right
- | Negate | Right-to-Left
* | Multiply | Left-to-Right
/ | Divide | Left-to-Right
\ | Integer divide | Left-to-Right
MOD | Modulus | Left-to-Right
SHL | Shift left | Left-to-Right
SHR | Shift right | Left-to-Right
+ | Add | Left-to-Right
- | Subtract | Left-to-Right
& | String concatenation | Left-to-Right
Is | Run-time type information check | N/A
= | Equal | Left-to-Right
<> | Not equal | Left-to-Right
< | Less than | Left-to-Right
<= | Less than or equal | Left-to-Right
>= | Greater than or equal | Left-to-Right
> | Greater than | Left-to-Right
NOT | Complement | Right-to-Left
AND | Conjunction | Left-to-Right
OR | Inclusive Disjunction | Left-to-Right
EQV | Equivalence | Left-to-Right
IMP | Implication | Left-to-Right
XOR | Exclusive Disjunction | Left-to-Right
ANDALSO | Short Circuit Conjunction | Left-to-Right
ORELSE | Short Circuit Inclusive Disjunction | Left-to-Right
=[>] | Assignment | N/A
&= | Concatenate and Assign | N/A
+= | Add and Assign | N/A
-= | Subtract and Assign | N/A
*= | Multiply and Assign | N/A
/= | Divide and Assign | N/A
\= | Integer Divide and Assign | N/A
^= | Exponentiate and Assign | N/A
MOD= | Modulus and Assign | N/A
AND= | Conjunction and Assign | N/A
EQV= | Equivalence and Assign | N/A
IMP= | Implication and Assign | N/A
OR= | Inclusive Disjunction and Assign | N/A
XOR= | Exclusive Disjunction and Assign | N/A
SHL= | Shift Left and Assign | N/A
SHR= | Shift Right and Assign | N/A
LET | Assignment | N/A
LET() | Assignment | N/A

In some cases, the order of precedence can cause confusing or counter-intuitive results. Here are some examples:
'' trying to raise a negated number to a power
-2 ^ 2
Desired result: (-2) ^ 2 = 4
Actual result:   -(2 ^ 2) = -4

'' trying to test a bit in a number
n And 1  <>  0
Desired result: (n And 1) <> 0
Actual result:   n And (1 <> 0)

'' trying to shift a number by n+1 bits
a Shl n+1
Desired result: a Shl (n + 1)
Actual result: (a Shl n) + 1

For expressions where the operator precedence may be ambiguous, it is recommended to wrap parts of the expression in parentheses, in order both to minimise the possibility of error and to aid comprehension for people reading the code.

See also

  • Operators


Bitwise Operators Truth Tables
Computed values for the bitwise logical operators.

Binary operators
Operators that take two operands.
Unary operator
Operator that take a single operand.

These logical operators return a value based on the value of their operand(s). For the binary operators, each bit in the left-hand side value is applied logically to the corresponding bit in the right-hand side value. The result of this operation is returned. For the unary operator, (Operator Not), the logic is applied to its right-hand side operand only.

Binary operators

Operator And (Conjunction)
Bits in the result are set if and only if both of the corresponding bits in the left and right-hand side operands are set.

Lhs | 0 | 0 | 1 | 1
Rhs | 0 | 1 | 0 | 1
Result | 0 | 0 | 0 | 1

Operator Eqv (Equivalence)
Bits in the result are set if and only if both of the corresponding bits in the left and right-hand side operands are both either set or unset.

Lhs | 0 | 0 | 1 | 1
Rhs | 0 | 1 | 0 | 1
Result | 1 | 0 | 0 | 1

Operator Imp (Implication)
Bits in the result are set if and only if the corresponding bit in the left-hand side operand implies the bit in the right-hand side operand.

Lhs | 0 | 0 | 1 | 1
Rhs | 0 | 1 | 0 | 1
Result | 1 | 1 | 0 | 1

Operator Or (Inclusive Disjunction)
Bits in the result are set if either of the corresponding bits in the left and right-hand side operands are set.

Lhs | 0 | 0 | 1 | 1
Rhs | 0 | 1 | 0 | 1
Result | 0 | 1 | 1 | 1

Operator Xor (Exclusive Disjunction)
Bits in the result are set if and only if one of the corresponding bits in the left and right-hand side operands is set.

Lhs | 0 | 0 | 1 | 1
Rhs | 0 | 1 | 0 | 1
Result | 0 | 1 | 1 | 0

Unary operators

Operator Not (Complement)
Bits in the result are set if the corresponding bits in the right-hand side operand are unset, and unset if they are set.

Rhs | 0 | 1
Result | 1 | 0


Control Flow Statements
Statements that direct the flow of program execution.

Transferring Statements
Statements that transfer control to another part of a program.
Branching Statements
Statements that execute one of a number of code branches.
Looping Statements
Statements that execute code repeatedly.

Transferring Statements
Goto
Transfers execution to another point in code defined by a text label.
GoSub
Temporarily transfers execution to another point in code, defined by a text label.
On Goto
Transfers execution to one of a number of points in code defined by text labels, based on the value of an expression.
On Gosub
Temporarily transfers execution to one of a number of points in code defined by text labels, based on the value of an expression.
Return (From Procedure)
Returns from a procedure returning a value.
Return (From Gosub)
Returns from a call using GoSub.
Exit Sub, Exit Function, Exit Operator,
Exit Constructor, Exit Destructor and Exit Property
Prematurely leaves a procedure code block.

Branching Statements
If..End If
Executes a block of statements if a condition is met.
..Else If..
Executes a block of code if a condition is met and all previous conditions weren't met.
..Else..
Executes a block of code if all previous conditions weren't met.
Select..End Select
Executes one of a number of statement blocks using a set of conditions.
..Case..
Executes a block of code if a condition is met.
..Case Else..
Executes a block of code if all previous conditions weren't met.

Intra-branch control
Exit Select
Prematurely breaks out of a Select..End Select statement.
Looping Statements
While..Wend
Executes a block of statements while a condition is met.
For..Next
Executes a block of statements while an iterator is less than or greater than an expression.
Do..Loop
Executes a block of statements while or until a condition is met.

Intra-loop control
Continue While, Continue For and Continue Do
Prematurely re-enters a loop.
Exit While, Exit For and Exit Do
Prematurely breaks out of a loop.


Procedures
Keywords that work with procedures.

Description

These keywords control the declaration and definition of both module-level procedures and member procedures, how they are called, how arguments are passed and how their names are seen externally to other modules. Procedures can also be declared to be executed automatically before any module-level code is executed.

Declaration
Keywords that declare and define procedures.
Linkage
Keywords that specify how procedure names are seen by external modules.
Calling conventions
Keywords that specify how arguments are used when calling procedures.
Parameter passing conventions
Keywords that specify how arguments are passed to procedures.
Variadic Procedures
Macros that allow for an arbitrary number of arguments to be passed to a procedure.
Automatic execution
Keywords that specify automatic execution of procedures.
Miscellaneous
Miscellaneous keywords.

Declaration
Declare
Declares a module-level or member procedure.
Sub
Specifies a procedure that does not return an argument.
Function
Specifies a procedure that returns an argument.
Overload
Specifies that the procedure name can be used in other procedure declarations.
Static
Specifies static storage for all variables and objects in the procedure body.
Const (Member)
Specifies a const member procedure in user-defined type definitions.
Static (Member)
Specifies a static member procedure in user-defined type definitions.

Linkage
Public
Specifies external linkage for a procedure.
Private
Specifies internal linkage for a procedure.
Alias
Specifies an alternate external name for a procedure.
Export
Specifies a procedure is to be exported from a shared library.
Lib
Specifies automatic loading of a library.

Calling conventions
stdcall
Specifies the standard calling convention for BASIC languages, including FreeBASIC.
cdecl
Specifies the standard calling convention in the C and C++ languages.
pascal
Specifies the standard calling convention in the Fortran, Pascal and Microsoft QuickBASIC/QBasic languages.
Fastcall
Specifies the fastcall calling convention for 32-bit procedures.
Thiscall
Specifies the thiscall calling convention for 32-bit member procedures.
Parameter passing conventions
ByRef
Specifies passing an argument by reference.
ByVal
Specifies passing an argument by value.
Any
Disables type-checking on arguments.

Variadic Procedures
... (Ellipsis)
Indicates a variadic procedure in a declaration.
Cva_Arg
Macro to obtain the next argument from a variadic argument list object
Cva_Copy
Macro to copy a variadic argument list object variable
Cva_End
Macro to end using a variadic argument list object variable
Cva_List
Variadic argument list object type
Cva_Start
Macro to initialize variadic argument list object variable
va_first
Macro to obtain the argument list in a variadic procedure.
va_arg
Macro to obtain the current argument in a variadic procedure.
va_next
Macro to move to the next argument in a variadic procedure.

Automatic execution
Constructor (Module)
Indicates a procedure is to be executed before module-level code.
Destructor (Module)
Indicates a procedure is to be executed after module-level code.

Miscellaneous
Byref (Function Results)
Specifies that a function returns by reference rather than by value.
Call
Invokes a procedure.
Naked
Specifies that a function body is not to be given any prolog/epilog code


Modularizing
Keywords helpful when writing modular programs.

  • Common
  • DyLibFree
  • DyLibLoad
  • DyLibSymbol
  • Export
  • Extern
  • Extern...End Extern
  • Import
  • Namespace
  • Private
  • Public
  • Using (Namespaces)


Preprocessor
Commands that control the preprocessor.

Description

Preprocessor commands are sent to the compiler to control what gets compiled and how. They can be used to choose to compile one block of code rather than another for cross-platform compatibility, include headers or other source files, define small inline functions called macros, or alter how the compiler handles variables.

Conditional Compilation
Commands that allow for branches in compilation based on conditions.
Text Replacement
Commands that create text-replacement macros.
Feature-control Defines
User-defined symbols that affect how selected compiler features are parsed.
File Directives
Commands that indicate to the compiler how other files relate to the source file.
Control Directives
Commands that set compile options, control compilation, and report compile time information.
Metacommands
Commands that are kept for backward compatibility.

Conditional Compilation
#if
Compiles the following code block based on a condition.
#ifdef
Compiles the following code block if a symbol is defined.
#ifndef
Compiles the following code block if a symbol is not defined.
#elseif
Compiles the following code block if a condition is true and the previous conditions was false.
#elseifdef
Compiles the following code block if a symbol is defined and the previous conditions was false.
#elseifndef
Compiles the following code block if a symbol is not defined and the previous conditions was false.
#else
Compiles the following code block if previous conditions were false.
#endif
Signifies the end of a code block.
defined
Returns "-1" if a symbol is defined, otherwise "0".

Text Replacement
#define
Creates a single-line text-replacement macro.
#macro and #endmacro
Creates a multi-line text-replacement macro.
#undef
Undefines a symbol.
# Preprocessor Stringize
Converts text into a string literal.
## Preprocessor Concatenate
Concatenates two pieces of text.
! Escaped String Literal
Indicates string literal immediately following must be processed for escape sequences.
$ Non-Escaped String Literal
Indicates string literal immediately following must not be processed for escape sequences.
Feature-control Defines
Fb_No_Gfxlib
Allows selected graphics-library command words to be used as normal identifiers.
Fb_No_Sfxlib
Allows selected sound-library command words to be used as normal identifiers.
File Directives
#include
Inserts text from a file.
#inclib
Includes a library in the linking processes.
#libpath
Includes a path to search for libraries in the linking process.

Control Directives
#pragma
Sets compiling options.
#Pragma Reserve
Reserves symbol name.
#Cmdline
Sets compiler command options from source.
#lang
Sets dialect from source.
#print
Outputs a messages to standard output while compiling.
#error
Outputs a messages to standard output and stops compilation.
#assert
Stops compilation with an error message if a given condition is false.
#line
Sets the current line number and file name.

Metacommands
'$Include
Alternate form of the #include directive.
'$Dynamic
Alternate form of the Option Dynamic statement.
'$Static
Alternate form of the Option Static statement.
'$Lang
Alternate form of the #lang directive.
See also

  • Intrinsic Defines


Escape Sequences
Escape sequences can be used in string literals by using the operator ! .

Usage

result = !"text"

Description

The accepted escape sequences in text are:
\a | beep
\b | backspace
\f | formfeed
\l or \n | newline
\r | carriage return
\t | tab
\unnnn | unicode char in hex
\v | vertical tab
\nnn | ascii char in decimal
\&hnn | ascii char in hex
\&onnn | ascii char in octal
\&bnnnnnnnn | ascii char in binary
\\ | backslash
\" | double quote
\' | single quote

Note: The zero-character (\000 = \&h00 = \&o000 = \&b00000000) is the null terminator. Only characters before the first null terminator can be seen when the literal is used as a String. To get a zero character in a string use Chr(0) instead.

See also

  • Operator ! (Escaped String)
  • Operator $ (Non-Escaped String)
  • Option Escape
  • String
  • Chr
  • Literals


Compiler Switches
Statements that affect how code is compiled.

Description

These statements affect how the compiler declares variables, arrays and procedures, parses string literals, passes procedure parameters and more.

Metacommands
  • '$Dynamic
  • '$Include
  • '$Static
  • '$Lang

Compiler Options
  • Option Base
  • Option ByVal
  • Option Dynamic
  • Option Escape
  • Option Explicit
  • Option Gosub
  • Option Nogosub
  • Option NoKeyword
  • Option Private
  • Option Static
Set Default Datatypes
  • DefByte
  • DefDbl
  • DefInt
  • DefLng
  • DefLongInt
  • DefShort
  • DefSng
  • DefStr
  • DefUByte
  • DefUInt
  • Defulongint
  • DefUShort
Dialect Differences

  • DefLongInt and Defulongint available only in the -lang fblite dialect.
  • OPTION statements are available only in the -lang fblite and -lang qb dialects only.

See also

  • Preprocessor


Intrinsic Defines
Preprocessor symbols defined by the compiler.

Description

Intrinsic defines are set by the compiler and may be used as any other defined symbol. Intrinsic defines often convey information about the state of the compiler, either in general or at a specific point in the compilation process. Most intrinsic defines are associated with a value.

Platform Information
Defines that provide information on the system.
Version Information
Defines that provide information on the fbc compiler version being used.
Command-line switches
Defines that provide information with the command-line switches used with fbc.
Environment Information
Defines that provide information about the operating system environment.
Context-specific Information
Defines that provide context information about the compilation process.
Basic-macros
Built-in basic-macros.
Constants
Built-in constants.

Platform Information
__FB_WIN32__
Defined if compiling for Windows.
__FB_LINUX__
Defined if compiling for Linux.
__FB_DOS__
Defined if compiling for DOS.
__FB_CYGWIN__
Defined if compiling for Cygwin.
__FB_FREEBSD__
Defined if compiling for FreeBSD.
__FB_NETBSD__
Defined if compiling for NetBSD.
__FB_OPENBSD__
Defined if compiling for OpenBSD.
__FB_DARWIN__
Defined if compiling for Darwin.
__FB_XBOX__
__Fb_Wii__
Defined if compiling for Xbox.
__FB_BIGENDIAN__
Defined if compiling on a system using big-endian byte-order.
__FB_PCOS__
Defined if compiling for a common PC OS (e.g. DOS, Windows, OS/2).
__FB_UNIX__
Defined if compiling for a Unix-like OS.
__FB_64BIT__
Defined if compiling for a 64bit target.
__FB_ARM__
Defined if compiling for the ARM architecture.
__FB_PPC__
Defined if compiling for the PowerPC architecture.
__Fb_Riscv64__
Defined if compiling for the 64-bit RISC-V architecture.
__Fb_S390X__
Defined if compiling for the s390x architecture.
__Fb_Loongarch64__
Defined if compiling for the 64-bit LoongArch architecture.
__FB_X86__
Defined if compiling for the X86 / X86_64 architecture.
__FB_JS__
Defined if compiling for emscripten target.
__FB_ANDROID__
Defined if compiling for android target.

Version Information
__FB_VERSION__
Defined as a string literal of the compiler version.
__FB_VER_MAJOR__
Defined as an integral literal of the compiler major version number.
__FB_VER_MINOR__
Defined as an integral literal of the compiler minor version number.
__FB_VER_PATCH__
Defined as an integral literal of the compiler patch number.
__FB_MIN_VERSION__
Macro to check for a minimum compiler version.
__FB_BUILD_DATE__
Defined as a string literal of the compiler build date in "mm-dd-yyyy" format.
__FB_BUILD_DATE_ISO__
Defined as a string literal of the compiler build date in "yyyy-mm-dd" format.
__FB_SIGNATURE__
Defined as a string literal of the compiler signature.
__FB_BUILD_SHA1__
Defined as a string literal of the compiler's source revision sha-1.
__FB_BUILD_FORK_ID__
Defined as a string literal of the custom defined project fork identifier name.

Command-line switches
__FB_ASM__
Defined to either "intel" or "att" depending on -asm.
__FB_BACKEND__
Defined to either "gas" or "gcc" depending on -gen.
__FB_GCC__
True (-1) if -gen gcc is used, false (0) otherwise.
__FB_OPTIMIZE__
Defined to the optimization level depending on -O.
__FB_GUI__
True (-1) if GUI subsystem mode is active, false (0) otherwise.
__FB_MAIN__
Defined if compiling a module with an entry point.
__FB_DEBUG__
True (-1) if the "-g" switch was used, false (0) otherwise.
__FB_ERR__
Zero (0) if neither the "-e", "-ex" or "-exx" switches were used.
__FB_FPMODE__
Defined as "fast" if compiling for fast SSE math, "precise" otherwise.
__FB_FPU__
Defined as "sse" if compiling for SSE floating point unit, or "x87" for normal x87 floating-point unit.
__FB_LANG__
Defined to a string literal of the "-lang" dialect used.
__FB_MT__
True (-1) if the "-mt" switch was used, false (0) otherwise.
__FB_OUT_DLL__
True (-1) in a module being compiled and linked into a shared library, false (0) otherwise.
__FB_OUT_EXE__
True (-1) in a module being compiled and linked into an executable, false (0) otherwise.
__FB_OUT_LIB__
True (-1) in a module being compiled and linked into a static library, zero (0) otherwise.
__FB_OUT_OBJ__
True (-1) in a module being compiled only, zero (0) otherwise.
__FB_PROFILE__
Set to an integer to indicate the profiling method.
__FB_SSE__
Defined if compiling for SSE floating point unit.
__FB_VECTORIZE__
Defined as the level of automatic vectorization (0 to 2)
Environment Information
__FB_ARGC__
Defined as an integer literal of the number of command-line arguments passed to the program.
__FB_ARGV__
Defined as a ZString Ptr Ptr to the command line arguments passed to the program.
__DATE__
Defined as a string literal of the compilation date in "mm-dd-yyyy" format.
__DATE_ISO__
Defined as a string literal of the compilation date in "yyyy-mm-dd" format.
__TIME__
Defined as a string literal of the compilation time.
__PATH__
Defined as a string literal of the absolute path of the module.

Context-specific Information
__FILE__ and __FILE_NQ__
Defined as the name of the module.
__FUNCTION__ and __FUNCTION_NQ__
Defined as the name of the procedure where it's used.
__LINE__
Defined as an integer literal of the line of the module where it's used.
__FB_OPTION_BYVAL__
True (-1) if parameters are declared by value by default, zero (0) otherwise.
__FB_OPTION_DYNAMIC__
True (-1) if all arrays are variable-length, zero (0) otherwise.
__FB_OPTION_ESCAPE__
True (-1) if string literals are processed for escape sequences, zero (0) otherwise.
__FB_OPTION_GOSUB__
True (-1) if gosub support is enabled, zero (0) otherwise.
__FB_OPTION_EXPLICIT__
True (-1) if variables and objects need to be explicitly declared, zero (0) otherwise.
__FB_OPTION_PRIVATE__
True (-1) if all procedures are private by default, zero (0) otherwise.
__FB_OPTION_PROFILE__
True (-1) if profiling code is generated, zero (0) otherwise.

Basic-macros
__FB_ARG_COUNT__
Counts the number of arguments in an argument list.
__FB_ARG_EXTRACT__
Returns nth argument from an argument list.
__FB_ARG_LEFTOF__
Returns left token based on separator.
__Fb_Arg_Listexpand__
Expands a macro one or more time on an argument list
__FB_ARG_RIGHTOF__
Returns right token based on separator.
__FB_EVAL__
Evaluates an argument (expression) at compile time.
__FB_IIF__
Returns an expression depending on the result of a comparison expression evaluated at compile time.
__FB_JOIN__
Joins two token arguments together as one.
__Fb_Query_Symbol__
Queries a fbc's symbol internals.
__FB_QUOTE__
Converts the argument to a string.
__FB_UNIQUEID__
Gets the identifier at the top of a stack.
__FB_UNIQUEID_POP__
Pops an identifier off of a stack.
__FB_UNIQUEID_PUSH__
Pushes a new unique identifier on to a stack.
__FB_UNQUOTE__
Takes a literal string and converts it back to tokens.

Constants
False and True
Intrinsic constants for the Boolean data type.


Error Handling
Handling runtime errors.

FreeBASIC can handle the errors in the following ways:
  • By default the program does nothing with the errors - they are silently ignored and code continues. In this case code should process possible errors in the next line by using the Err function.
  • If compiled with -e, -ex or -exx options, FreeBASIC uses QB-like error handling.
  • Future OOP versions of FreeBASIC may have a java-like TRY..CATCH...FINALLY exception handler implemented.

NOTE: The following information is valid unless the error produces an OS General Protection Fault (for example if the program writes outside the process memory area). In these cases the OS will immediately stop the program and issue an error: nothing can avoid it from inside FreeBASIC.

Default error handling

The default FreeBASIC behavior is to set the ERR variable and continue.

Dim As Integer e
Open "xzxwz.zwz" For Input As #1
e = Err
Print e
Sleep

(The example program supposes there is no xzxwz.zwz file). The program does not stop; it sets the ERR variable and continues. The error can be processed in the next line.

Some IO functions such as Open and Put #... can be used in function form, returning an error number or zero if successful.

Print Open ("xzxwz.zwz" For Input As #1)
Sleep

QuickBASIC-like error handling

If the -e, -ex or -exx switch is used at compile time, the program is expected to have a QB-like error handler enabled. If no handler processes the error, the program stops with an error.

Notice: if QB-Like error handling is used, the programmer should be prepared to handle all error conditions.

'' Compile with QB (-lang qb) dialect

'$lang: "qb"

On Error Goto FAILED
Open "xzxwz.zwz" For Input As #1
On Error Goto 0
Sleep
End

FAILED:
Dim e As Integer
e = Err
Print e
Sleep
End

On Error sets an error handling routine which the program will jump to when an error is found. On Error Goto 0 disables the error handling.

If an error handling routine is not set when an error occurs, the program will stop and send the console an error message.
Aborting program due to runtime error 2 (file not found)
The error handler routine can be at the end of the program, as in QB. The On Local Error statement allows the setting of a local error handler routine at the end of the same Sub or Function in which the error occurs.

'' Compile with -e
'' The -e command line option is needed to enable error handling.

Declare Sub foo
  foo
Sleep

Sub foo

    Dim filename As String
    Dim errmsg As String
    filename = ""
    On Local Error Goto fail
  Open filename For Input Access Read As #1
    Print "No error"
    On Local Error Goto 0
    Exit Sub

  fail:
  errmsg = "Error " & Err & _
           " in function " & *Erfn & _
           " on line " & Erl
  Print errmsg

End Sub

If the -e switch is used (whatever the -lang dialect), the error handler must terminate the program.
With (-ex or -exx) and -lang qb dialect only, the error routine can end by using Resume (retries the statement that caused the error) or Resume Next (continues at the next instruction) .

Error codes

See Runtime Error Codes for a listing of runtime error numbers and their associated meaning.

No user error code range is defined. If Error is used to set an error code it is wise to use high values to avoid collisions with the list of built-in error codes. (This built-in list may be expanded later.)

'On [Local] Error Goto' statement use

'On [Local] Error Goto label' causes a program jump to a specified label as soon as an error occurs. Such errors can be triggered by built-in statements such as 'Open', 'Get', 'Put', or when the Error statement is used.
The error checking for built-in statements is only enabled if the program is compiled with one of the -e, -ex or -exx options. Otherwise, no jump will be performed, but the command will still consume processor time.
When triggered by the only Error statement, 'On [Local] Error Goto label' remains always working even when none of these compile options are used.
'On Error Goto 0' deactivates the current error handler. If an error occurs, FreeBASIC will not jump.

The optional Local keyword (authorized only inside Sub/Function) was intended to be used to define an error handler only in the same procedure the 'On Local Error' is in (for compatibility with PDS 7.1 and VB Dos 1.0 for example). In this case, FreeBASIC should have searched for the label in the current procedure only.
But presently, the Local clause is ignored by the compiler, and the error handler can be either in the scope of the same procedure the 'On [Local] Error' is in, or in the main part of the module (if defined before the procedure).
Exception when -gen gcc is used (or for the fbc 64-bit): the Local clause seems to be rightly taken into account, but except inside a local scope!

'On [Local] Error Goto label' is not the best way to catch errors from a built-in procedure when a syntax in the form of a function is available. The return error code can be directly tested (using the returned error code from function inhibits the QuickBASIC-like error checking and statement 'On Error Goto').

It is advisable to try to write programs compatible with the -lang fb dialect and -exx option, because to test programs with option -exx for debugging purposes is a great helping:
- Avoid to use the statement Resume or Resume Next, because it is not at all supported by -lang fb (compilation error).
- On the other hand, sometimes when it is useful (when no form of a function is available), use the statement 'On [Local] Error Goto', because it runs with any option among -e, -ex, -exx. Otherwise (no error checking option), 'On [Local] Error Goto' is inactive (without compilation error), but it consumes CPU time.

Accordingly and if necessary, usually write QuickBasic-like error handling ('On [Local] Error Goto label' ..... 'label:' .....), with conditional assembly directive depending on the value of __FB_ERR__, in order not to penalize the execution speed (if not error checking option is used).

The behavior of statement 'On Error Goto' (-lang fb) regarding compilation options (none, -e/-ex/-exx) is enlighten with the following program including several examples (4), to be compiled with or without error checking option (4*2=8 tests):
#define Config 1
'#DEFINE Config 2
'#DEFINE Config 3
'#DEFINE Config 4

#if Config = 1 '-----------------------------------------------------------

Open "does_not_exist" For Input As #1

Print "main end"
Sleep
System

' - with compiler option 'none' :
'     console output :
'       'main end'
'
' - with compiler option '-e' or '-ex' or '-exx' :
'     console output :
'       'Aborting due to runtime error 2 (file not found) at line 10 of .....'

#endif '-------------------------------------------------------------------

#if Config = 2 '-----------------------------------------------------------

Dim As Integer Result = Open("does_not_exist" For Input As #1)
If Result <> 0 Then
    Print "error code returned: " & Result
    Print "file not found (processed by 'Result = Open(.....)')"
End If

Print "main end"
Sleep
End

' - with compiler option 'none' or '-e' or '-ex' or '-exx' :
'     console output :
'       'error code returned: 2'
'       'file not found (processed by 'Result = Open(.....)')'
'       'main end'

#endif '-------------------------------------------------------------------

#if Config = 3 '-----------------------------------------------------------

On Error Goto Error_Handler
Open "does_not_exist" For Input As #1

Print "main end"
Sleep
End

error_handler:
Print "file not found (processed by 'On Error Goto')"
On Error Goto 0
Print "QB-like error handling end"
Sleep
End

' - with compiler option 'none' :
'     console output :
'       'main end'
'
' - with compiler option '-e' or '-ex' or '-exx' :
'     console output :
'       'file not found (processed by 'On Error Goto')'
'       'QB-like error handling end'

#endif '-------------------------------------------------------------------

#if Config = 4 '-----------------------------------------------------------

On Error Goto error_handler
Dim As Integer Result = Open("does_not_exist" For Input As #1)
If Result <> 0 Then
    Print "error code returned: " & Result
    Print "file not found (processed by 'Result = Open(.....)')"
End If

Print "main end"
Sleep
End

error_handler:
Print "file not found (processed by 'On Error Goto')"
On Error Goto 0
Print "QB-like error handling end"
Sleep
End

' - with compiler option 'none' or '-e' or '-ex' or '-exx' :
'     console output :
'       'error code returned: 2'
'       'file not found (processed by 'Result = Open(.....)')'
'       'main end'

#endif '-------------------------------------------------------------------

The Config=2 and Config=4 sections highlight that when FB function is called using explicitly its returned error code (-lang fb dialect), 'On Error Goto' is by-passed whatever the error checking level ('none', '-e', '-ex', '-exx').

See also

  • Error Handling Functions
  • Runtime Error Codes


Array Functions
Statements and procedures for working with arrays.

Defining Arrays
Statements that create arrays.
Clearing Array Data
Procedures that work with array memory.
Retrieving Array Size
Procedures that return the array's length/size and bounds for any array's dimension.
Retrieving Array Descriptor
Procedures that return a pointers to an array's descriptor.

Defining Arrays
Option Dynamic
Forces arrays to be defined as variable-length arrays.
'$Dynamic
Alternate form of the Option Dynamic statement.
Option Static
Reverts a previous Option Dynamic command.
'$Static
Alternate form of the Option Static statement.
Dim
Defines any type of array.
ReDim
Defines and resizes variable-length arrays.
Preserve
Preserves array contents when used with ReDim.

Clearing Array Data
Erase
Destroys variable-length array elements and initializes fixed-length array elements.
Retrieving Array Size
Arraylen
Returns the total number of array elements.
Arraysize
Returns the total array size (in bytes).
LBound
Returns the lower bound of an array's dimension.
UBound
Returns the upper bound of an array's dimension.

Retrieving Array Descriptor
Array[Const]Descriptorptr
Returns a [constant] pointer to array's descriptor (FBARRAY).


Bit Manipulation
Macros that work with the bits and bytes of numbers.

Description

The macros documented here provide access to the individual bits, bytes and words of integer values.

Byte Manipulation Macros
Gets the value of individual bytes or words of UInteger values.
Bit Manipulation Macros
Gets the state of individual bits of numeric values.

Byte Manipulation Macros
LoByte
Gets the least significant byte (LSB, or lo-byte) value of an UInteger value.
HiByte
Gets the most significant byte (MSB, or hi-byte) value of the least significant word (LSW, or lo-word) of an UInteger value.
LoWord
Gets the least significant word (LSW, or lo-word) value of an UInteger value.
HiWord
Gets the most significant word (LSW, or hi-word) value of an UInteger value.
Bit Manipulation Macros
Bit
Gets the state of an individual bit in an integer value.
BitReset
Gets the value of an integer with a specified bit cleared.
BitSet
Gets the value of an integer with a specified bit set.


Console Functions
Procedures that work with the console.

Description

These procedures provide ways to output text to the console, as well as control where and how text is output.

Configuring the Console
Statements that affect how text is displayed.
Cursor Color and Positioning
Procedures that move the cursor and change its color.
Writing Text to the Console
Procedures that output text to the console.

Configuring the Console
Cls
Clears the entire screen or text viewport.
Width
Sets or returns the number of rows and columns of the console display.
View Print
Sets the printable area of the console screen.

Cursor Color and Positioning
Color
Changes the foreground and background color of text to be written.
CsrLin
Returns the row position of the cursor.
Pos
Returns the column position of the cursor.
Locate
Sets the row and column position of the cursor and its visibility.
Screen (Console)
Gets the character or color attribute at a given location.
Writing Text to the Console
Print
? (Shortcut For 'Print')
Writes text to the console.
Print Using
? Using (Shortcut For 'Print Using')
Writes formatted text to the console.
Write
Writes a list of items to the console.
Spc
Skips a number of spaces when writing text.
Tab
Skips to a certain column when writing text.


Date and Time Functions
Procedures that work with dates and time.

Description

These procedures provide ways to deal with date and time intervals in a consistent way. Additional procedures are provided to set and get the current system date and time, and to retrieve a time stamp for sensitive timing algorithms.

VisualBasic compatible procedures
Procedures for working with so-called date serials, similar to those used in Visual Basic(r).
Date and time procedures
Procedures for working with the system date and time.

VisualBasic compatible procedures
Now
Gets a date serial of the current date and time.

Creating Date serials
DateSerial
Gets the date serial representation of a date.
TimeSerial
Gets the date serial representation of a time.
DateValue
Gets the date serial representation of a date expressed as a String.
TimeValue
Gets the date serial representation of a time expressed as a String.

Extracting information from Date serials
Second
Gets the seconds of the hour from a date serial.
Minute
Gets the minutes of the hour from a date serial.
Hour
Gets the hour of the day from a date serial.
Day
Gets the day of the month from a date serial.
Weekday
Gets the day of the week from a date serial.
Month
Gets the month of the year from a date serial.
Year
Gets the year from a date serial.
DatePart
Gets a time interval from a date serial.

Extracting information from Date serials
DateAdd
Gets the result of a time interval added to a date serial.
DateDiff
Gets a time interval between two date serials.

Miscellaneous
IsDate
Tests if a String can be converted to a date serial.
MonthName
Gets the month name of its integer representation.
WeekdayName
Gets the weekday name of its integer representation.
Date and time procedures
Date
Gets the String representation of the current system date.
Time
Gets the String representation of the current system time.
SetDate
Sets the current system date.
SetTime
Sets the current system time.
Timer
Gets a counter expressed in seconds.


Error Handling Functions
Statements and procedures that provide runtime error-handling capabilities.

Description

These statements and procedures provide ways of dealing with runtime errors. Specific modules, procedures and source code lines can be retrieved, and error handlers can be set up.

Determining Errors
Procedures that retrieve information about an error.
Handling Errors
Statements that allow handling of errors.

Determining Errors
Erl
Gets the line in source code where the error occurred.
Erfn
Gets the name of the function where the error occurred.
Ermn
Gets the name of the source file where the error occurred.
Err
Gets the error number of the last error that occurred.
Error
Generates an error using an error number.
Handling Errors
On Error
Sets a global error handler using a label.
On Local Error
Sets a local error handler using a label.
Resume
Resumes execution at the line where the error occurred.
Resume Next
Resumes execution at the line after where the error occurred.
See also

  • Error Handling
  • Runtime Error Codes


File I/O Functions
Statements and procedures for working with files and devices.

Description

These statements and procedures provide file and device i/o capabilities. So called file numbers can be bound to files or devices, which can be read or written to using formatted (text mode) or unformatted (binary mode) data. In binary mode, files and devices can be read from or written to in arbitrary locations. For multithreaded applications, files and devices can also be locked.

Opening Files or Devices
Procedures and other keywords that provide read or write access to a file or device.
Reading from and Writing to Files or Devices
Procedures that read and write data to an opened file or device.
File Position and other Info
Procedures that determine where reading and writing will take place within an opened file.

Opening Files or Devices
FreeFile
Gets an available file number that can be used to read or write from files or devices.
Open
Binds a file number to a physical file to provide reading and writing capabilities.
Open Com
Binds a file number to a communications port.
Open Cons
Binds a file number to the standard input and output streams.
Open Err
Binds a file number to the standard input and error streams.
Open Lpt
Binds a file number to a printer device.
Open Pipe
Binds a file number to the input and output streams of a process.
Open Scrn
Binds a file number directly to the console.
Open Tcp
Binds a file number to a TCP client connection.
Open Tcp Server
Binds a file number to a listening TCP server socket.
Close
Unbinds a file number from a file or device.
Reset
Unbinds all active file numbers.

File I/O modes
Input (File Mode)
Text data can be read from the file.
Output
Text data can be written to the file.
Append
Text data is added to the end of a file when output.
Binary
Arbitrary data can be read from or written to the file.
Random
Blocks of data of certain size can be read from and written to the file.

File access privileges
Access
An overview of file access privileges.
Read (File Access)
Binary data can only be read from the file.
Write (File Access)
Binary data can only be written to the file.
Read Write(File Access)
Binary data can be read from and written to the file.

Character encoding
Encoding
Specifies the character encoding of a file.
Reading from and Writing to Files or Devices
Input #
Reads a list of values from a file or device.
Write #
Writes a list of values to a file or device.
Input()
Reads a number of characters from a file or device.
Winput()
Reads a number of wide characters from a file or device.
Line Input #
Reads a line of text from a file or device.
Print #
? # (Shortcut For 'Print #')
Writes text data to a file or device.
Put #
Writes arbitrary data to a file or device.
Get #
Reads arbitrary data from a file or device.
Tcp Accept
Accepts a TCP client from a server file number.
fbnetwire.bi portable wire helpers
Provides fixed-size little-endian value helpers for files and TCP handles.

File Position and other Info
LOF
Gets the length (in bytes) of a file.
LOC
Gets the file position of the last read or write operation.
EOF
Returns true if all of the data has been read from a file.
Eoc
Returns true if a TCP connection has been closed by the peer.
Seek (Statement)
Sets the file position of the next read or write operation.
Seek (Function)
Gets the file position of the next read or write operation.
Lock
Restricts read or write access to a file or portion of a file.
Unlock
Remove read or write restrictions from a previous Lock command.


fbnetwire.bi portable wire helpers

fbnetwire.bi provides small source-level helpers for writing fixed-size values to files or Open Tcp handles in a predictable byte order.

It is meant for simple binary protocols where both sides need to agree on exactly how numbers and strings are represented on the wire. The helpers use little-endian byte order and fixed-width value sizes instead of relying on the target CPU's native byte order or native Integer size.

Syntax

#include once "fbnetwire.bi"

Description

The include supplies private helper procedures and functions. Because they are declared private, each source file that includes fbnetwire.bi gets its own local helpers without exporting public symbols.

The helpers work with normal FreeBASIC file numbers. That means they can be used with ordinary binary files, pipes, or TCP sockets opened through Open Tcp and Open Tcp Server.

fbnetwire.bi does not create sockets, accept clients, decide whether Eof or Eoc should be used, or implement a full packet protocol. It only provides byte encoding and small read/write helpers.

Wire format

Values are encoded in little-endian order:
  • Short and UShort values use 2 bytes.
  • Long and ULong values use 4 bytes.
  • LongInt and ULongInt values use 8 bytes.
  • Single values use 4 bytes.
  • Double values use 8 bytes.
  • Strings are stored as a 32-bit little-endian byte length followed by that many string bytes.
The string helpers store bytes, not characters. If the text has to be portable across machines and languages, choose an explicit encoding such as UTF-8 before sending it.

Helpers

Encoding and decoding helpers:
  • FbNetEncodeInt16LE / FbNetDecodeInt16LE
  • FbNetEncodeUInt16LE / FbNetDecodeUInt16LE
  • FbNetEncodeInt32LE / FbNetDecodeInt32LE
  • FbNetEncodeUInt32LE / FbNetDecodeUInt32LE
  • FbNetEncodeInt64LE / FbNetDecodeInt64LE
  • FbNetEncodeUInt64LE / FbNetDecodeUInt64LE
  • FbNetEncodeSingleLE / FbNetDecodeSingleLE
  • FbNetEncodeDoubleLE / FbNetDecodeDoubleLE
File-number helpers:
  • FbNetPutBytes / FbNetGetBytes
  • FbNetPutInt16LE / FbNetGetInt16LE
  • FbNetPutInt32LE / FbNetGetInt32LE
  • FbNetPutInt64LE / FbNetGetInt64LE
  • FbNetPutSingleLE / FbNetGetSingleLE
  • FbNetPutDoubleLE / FbNetGetDoubleLE
  • FbNetPutStringLE / FbNetGetStringLE
The file-number helpers return 1 for success and 0 for failure.

Examples

Writing and reading a small binary record:

#include once "fbnetwire.bi"

dim as integer f = freefile()

if open( "packet.bin" for binary as #f ) <> 0 then
    end 1
end if

if FbNetPutInt32LE( f, 12345 ) = 0 then end 1
if FbNetPutDoubleLE( f, 3.14159 ) = 0 then end 1
if FbNetPutStringLE( f, "hello", 1024 ) = 0 then end 1

close #f

f = freefile()
if open( "packet.bin" for binary as #f ) <> 0 then
    end 1
end if

dim as long id
dim as double value
dim as string text

if FbNetGetInt32LE( f, id ) = 0 then end 1
if FbNetGetDoubleLE( f, value ) = 0 then end 1
if FbNetGetStringLE( f, text, 1024 ) = 0 then end 1

close #f

print id, value, text

The same helpers can be used with a TCP client file number:

#include once "fbnetwire.bi"

dim as integer sock = freefile()

if open tcp( "host=127.0.0.1,port=9000,timeout=5000", as #sock ) <> 0 then
    end 1
end if

if FbNetPutStringLE( sock, "ping", 4096 ) = 0 then
    close #sock
    end 1
end if

close #sock

Limits and policy

The string helpers take a max_bytes argument. On write, text longer than max_bytes is truncated before being sent. On read, any incoming string length below zero or above max_bytes is rejected and the helper returns 0.

These helpers deliberately do not define a whole message protocol. Real protocols usually need message types, versioning, limits, timeout policy, and recovery behavior. fbnetwire.bi gives you predictable primitive values to build on.

Platform Differences

  • The byte encoding helpers are source-level and do not depend on a particular operating system.
  • TCP examples require targets with Open Tcp support. Some targets, including DOS and JavaScript, do not provide TCP support.
  • Floating-point helpers assume the target stores Single and Double in the normal IEEE-style layout used by supported FreeBASIC targets.

Differences from QB

  • New to FreeBASIC.

See also

  • Open Tcp
  • Open Tcp Server
  • Tcp Accept
  • Get #
  • Put #
  • File I/O Functions

File I/O Functions

Table of Contents


Mathematical Functions
Procedures that work with numbers mathematically.

Description

This set of procedures provide basic algebraic and trigonometric function. Random numbers can also be retrieved, using a variety of random number generators.

Algebraic Procedures
Absolute values, logarithms, square roots and more.
Trigonometry Procedures
Sine, Cosine and other trigonometry-related procedures.
Miscellaneous Procedures
Miscellaneous procedures.

Algebraic Procedures
Abs
Returns the absolute value of a number.
Exp
Returns e raised to some power.
Log
Returns the natural logarithm of a number.
Sqr
Returns the square root of a number.
Fix
Returns the integer part of a number.
Frac
Returns the fractional part of a number.
Int
Returns the largest integer less than or equal to a number.
Sgn
Returns the sign of a number.
Trigonometric Procedures
Sin
Returns the sine of an angle.
Asin
Returns the arcsine of a number.
Cos
Returns the cosine of an angle.
Acos
Returns the arccosine of a number.
Tan
Returns the tangent of an angle.
Atn
Returns the arctangent of a number.
Atan2
Returns the arctangent of the ratio between two numbers.

Miscellaneous Procedures
Randomize
Seeds the random number generator used by Rnd.
Rnd
Returns a random Double in the range [0, 1).


Memory Functions
Procedures that work with static and dynamic memory.

Description

These procedures provide access to the free store, or heap. Memory from the free store can be reserved and freed, and procedures are provided to read and write directly to that memory.

Working with Dynamic Memory
Procedures that reserve, resize or free dynamic memory.
Miscellaneous Procedures
Procedures that read or write values to and from addresses in memory.

Working with Dynamic Memory
Allocate
Reserves a number of bytes of uninitialized memory and returns the address.
CAllocate
Reserves a number of bytes of initialized (zeroed) memory and returns the address.
Reallocate
Changes the size of reserved memory.
Deallocate
Returns reserved memory back to the system.
Miscellaneous Procedures
Peek
Reads some type of value from an address.
Poke
Writes some type of value to an address.
Clear
Clears data in an array with a specified value.
Fb_Memcopy
Copies a block of memory from a location to another.
(memory areas must not overlap)
fb_MemCopyClear
Copies the first part of a block of memory from a location to another and clears the rest.
(memory areas must not overlap)
Fb_Memmove
Copies a block of memory from a location to another.
(memory areas may overlap)
Swap
Exchange the contents of two variables.
SAdd
Returns the address for the data in a zstring/wstring variable.
See also

  • Memory Operators


Operating System Functions
Statements and procedures for working with files, directories and the system.

Description

The statements and procedures listed here provide access to the operating system environment. They transfer execution to external programs, get information about files and directories, manipulate the file system and send commands to the command shell.

Working with Files
Procedures that deal with files.
Working with Directories
Various directory management procedures.
File Properties
Get information about files.
System Procedures
Procedures for working with the environment.

Working with Files
Exec and Chain
Temporarily transfers control to another program.
Run
Transfers control to another program.
Kill
Deletes an existing file.
Name
Renames an existing file.

File Properties
FileAttr
Gets information about a file bound to a file number.
FileCopy
Copies a file.
FileDateTime
Gets the last modified date and time of a file.
FileExists
Tests for the existence of a file.
FileLen
Gets the length (in bytes) of a file.
FileSetEof
Sets the length of an open file bound to a file number.
FileFlush
Flushes application or system buffers for an open file bound to a file number.
Working with Directories
CurDir
Gets the current working directory.
ChDir
Sets the current working directory.
Dir
Gets the names of files or directories matching certain attributes.
ExePath
Gets the directory of the current running program.
MkDir
Creates a new directory.
RmDir
Deletes an existing directory.

System Procedures
Fre
Gets the amount of free memory (in bytes) available.
Command
Gets the command-line parameters passed to the program.
Environ
Gets the value of an environment variable.
IsRedirected
Checks whether stdin or stdout is redirected to a file or not.
SetEnviron
Sets the value of an environment variable.
Shell
Sends a command to the system command interpreter.
System
Closes all open files and exits the program.


Sound Library Reference
Procedures, command families, examples, and backend notes for FreeBASIC's built-in sound library.

Description

The sound library, or sfxlib, is the built-in audio layer used by FreeBASIC's sound commands. It covers generated tones, PLAY-string music, short sound effects, music file playback, MIDI commands, output device selection, and audio capture where the target backend supports it.

Programs that use these commands automatically request the sfxlib runtime. If a program does not use the sound command surface and needs words such as Music, Sfx, or Device as normal identifiers, define FB_NO_SFXLIB before those names are parsed.

For headless tests and package smoke tests, set SFXLIB_DRIVER=null before the program starts to use the null output driver.

Quick example

sound 440, 0.25
play "t140 o4 cdefgab"
sfx load 1, "click.wav"
sfx play 1
sleep 1000, 1

Generated Sound

  • BEEP: produces a beep or short pitched note.
  • SOUND: plays a generated tone.
  • TONE: plays a generated tone on a selected channel.
  • NOISE: plays generated noise.
  • NOTE: plays a named musical note.
  • PLAY: plays BASIC PLAY strings.
  • TEMPO: sets or returns PLAY-string tempo.
  • CHANNEL: sets or returns the default generated-sound channel.
  • OCTAVE: sets or returns the default note octave.
  • VOICE: sets or returns the default instrument.
  • VOL: sets master volume.
  • VOLUME: sets or returns master or channel volume.
  • BALANCE: sets or returns master stereo balance.
  • PAN: sets or returns channel stereo pan.
  • WAVE: defines a generated waveform.
  • ENVELOPE: defines an ADSR envelope.
  • INSTRUMENT: defines or assigns an instrument.

Digital Audio and MIDI

  • MUSIC: loads, plays, loops, pauses, resumes, stops, and queries the current music stream.
  • SFX: loads and controls short sound effects by id or channel.
  • MIDI: opens MIDI output, plays MIDI files, and sends MIDI messages where supported.

Devices, Backends, and Capture

  • DEVICE: lists, selects, and queries sfxlib output devices.
  • sfxlib backends: summarizes the output backends used on supported targets.
  • CAPTURE: starts, pauses, resumes, stops, reads, and saves audio capture where available.


String Functions
Statements and Procedures that work with strings.

Description

These statements and procedures provide many ways to create and manipulate strings and substrings. Numbers can be converted to strings and vice-versa. Procedures are also provided to aid in serialization of numeric data, perhaps for persistent storage.

Creating Strings
String data types and procedures that create new strings.
Character Conversions
Procedures that convert from character codes to strings and back.
Numeric/Boolean to String Conversions
Procedures that convert numeric values to strings.
String to Numeric Conversions
Procedures that convert strings to numeric values.
Numeric Serializations
Procedures that convert raw numeric data to and from strings suitable for storage.
Working with Substrings
Procedures that return subsets of strings, or that modify subsets of strings.

Creating Strings
String
Standard data type: 8 bit character string.
String (Function)
Returns a String of multiple characters.
ZString
Standard data type: null terminated 8 bit character string.
WString
Standard data type: wide character string.
Wstring (Function)
Returns a WString of multiple characters.
Space
Returns a String consisting of spaces.
WSpace
Returns a WString consisting of spaces.
Len
Returns the length of a string in characters.

Character Conversion
Asc
Returns an Integer representation of an character.
Chr
Returns a string of one or more characters from their ASCII Integer representation.
WChr
Returns a WString of one or more characters from their Unicode Integer representation.

Numeric/Boolean to String Conversions
Bin
Returns a binary String representation of an integral value.
WBin
Returns a binary WString representation of an integral value.
Hex
Returns a hexadecimal String representation of an integral value.
WHex
Returns a hexadecimal WString representation of an integral value.
Oct
Returns an octal String representation of an integral value.
WOct
Returns an octal WString representation of an integral value.
Str
Returns the String representation of numeric value or boolean.
WStr
Returns the WString representation of numeric value.
Format
Returns a formatted String representation of a Double.

String to Numeric Conversions
Val
Returns the Double conversion of a numeric string.
ValInt
Returns the Integer conversion of a numeric string.
ValLng
Returns the Long conversion of a numeric string.
ValUInt
Returns the UInteger conversion of a numeric string.
ValULng
Returns the ULong conversion of a numeric string.
Numeric Serialization
MKD
Returns an eight character String representation of a Double.
MKI
Returns a four character String representation of a Integer.
MKL
Returns a four character String representation of a Long.
MKLongInt
Returns an eight character String representation of a LongInt.
MKS
Returns a four character String representation of a Single.
MKShort
Returns a two character String representation of a Short.
CVD
Returns a Double representation of an eight character String.
CVI
Returns an Integer representation of a four character String.
CVL
Returns a Long representation of a four character String.
CVLongInt
Returns a LongInt representation of an eight character String.
CVS
Returns a Single representation of a four character String.
CVShort
Returns a Short representation of a two character String.

Working with Substrings
Left
Returns a substring of the leftmost characters in a string.
Mid (Function)
Returns a substring of a string.
Right
Returns a substring of the rightmost characters in a string.
LCase
Returns a copy of a string converted to lowercase alpha characters.
UCase
Returns a copy of a string converted to uppercase alpha characters.
LTrim
Removes surrounding substrings or characters on the left side of a string.
RTrim
Removes surrounding substrings or characters on the right side of a string.
Trim
Removes surrounding substrings or characters on the left and right side of a string.
InStr
Returns the first occurrence of a substring or character within a string.
InStrRev
Returns the last occurrence of a substring or character within a string.
Mid (Statement)
Copies a substring to a substring of a string.
LSet
Left-justifies a string.
RSet
Right-justifies a string.


Threading Support Functions
Procedures for working with multithreaded applications.

Description

These procedures allow for multithreaded programming. Threads and conditional variables can be created and destroyed, and so-called mutexes can be obtained to protect thread-sensitive data.

Threads
Procedures that start and wait for threaded procedures.
Mutexes
Procedures that deal with mutexes.
Conditional Varables
Procedures that create and signal conditional variables.

Threads
ThreadCall
Starts a procedure with parameters in a separate thread of execution.
ThreadCreate
Starts a procedure in a separate thread of execution.
ThreadWait
Waits for a thread to finish and releases the thread handle.
ThreadDetach
Releases a thread handle without waiting for the thread to finish.
ThreadSelf
Returns the thread handle of the current thread.

Mutexes
MutexCreate
Creates a mutex.
MutexLock
Acquires a lock on a mutex.
MutexUnlock
Releases a lock on a mutex.
MutexDestroy
Destroys a mutex that is no longer needed.
Conditional Variables
CondCreate
Creates a conditional variable.
CondWait
Pauses execution of a threaded procedure.
CondSignal
Resumes execution of a threaded procedure waiting for a conditional.
CondBroadcast
Resumes all threaded procedures waiting for a conditional.
CondDestroy
Destroys a conditional variable that is no longer needed.
Platform Differences

  • These procedures are not supported in DOS.


User Input (Runtime Library)
Statements and procedures (from runtime library) that get input from the user.

Description

These statements and procedures allow access to the keyboard buffer, and provide ways of getting input from the user.

Reading keys from the keyboard buffer
Procedures that read individual keys from the keyboard buffer.
Reading values from the keyboard buffer
Procedures that read characters and values from the keyboard buffer.
Detecting key status by keyboard scancode
Procedure that detects the status of a key by its scancode.

Reading values from the keyboard buffer
Input
Reads values from the keyboard buffer.
Line Input
Reads a line of text from the keyboard buffer.
Input()
Reads a number of characters from the keyboard buffer, file or device.
Winput()
Reads a number of wide characters from the keyboard buffer, file or device.
Reading keys from the keyboard buffer
Inkey
Gets the first key, if any, waiting in the keyboard buffer.
GetKey
Gets and waits for the first key in the keyboard buffer.

Detecting key status by keyboard scancode
MultiKey
Detects the status of a key by its scancode.


2D Drawing Functions
Statements and procedures for working with 2D graphics.

Description

The statements and procedures listed here provide ways of drawing to the screen. Image buffers can be created and blitted to the screen using a variety of blending methods. Palette colors can be retrieved or set in graphics modes that support them.

Working with Color
Procedures that control the color used by other drawing procedures.
Drawing to Image Buffers
Procedures that draw shapes and text onto image buffers or to the screen.
Image Buffer Creation
Procedures that create, free and save image buffers.
Blitting Image Buffers
Procedures that draw image buffers onto other image buffers or to the screen.

Working with Color
Color
Sets the foreground and background color to use with the drawing procedures.
Palette
Gets or sets color table information in paletted modes.
RGB
Returns a color value for hi/truecolor modes.
RGBA
Returns a color value including alpha (transparency) for hi/truecolor modes.
Point
Gets a pixel value from an image buffer or screen.

Drawing to Image Buffers
PSet and PReset
Plots a single pixel on an image buffer or screen.
Line (Graphics)
Plots a line of pixels on an image buffer or screen.
Circle
Plots circles and ellipses on an image buffer or screen.
Draw
Draws in a sequence of commands on an image buffer or screen.
Draw String
Writes text to an image buffer or screen.
Paint
Fills an area with color on an image buffer or screen.

Image Buffer Creation
Get (Graphics)
Creates an image buffer from a portion of another image buffer or screen.
ImageCreate
Creates an image buffer of a certain size and pixel depth.
ImageDestroy
Frees an image buffer resource.
ImageConvertRow
Converts a row of pixels in an image buffer to a different color depth.
ImageInfo
Retrieves useful information about an image buffer
BLoad
Creates an image buffer from a file.
BSave
Saves an image buffer to a file.
Blitting Image Buffers
Put (Graphics)
Blits an image buffer to another image buffer or screen.

Blending Methods
Add
Saturated addition of the source and target components.
Alpha
Blend using a uniform transparency or the image buffer's alpha channel.
And (Graphics Put)
Combine the source and target components using a bitwise And
Or
Combine the source and target components using a bitwise Or
PSet
Directly copy pixel colors from the source to the destination.
Trans
Pixels matching the transparent mask color are not blitted.
Custom
Allows a custom blending procedure to be used.
Xor
Combine the source and target components using a bitwise Xor


User Input Functions (Graphics Library)
Procedures (from graphics library) for working with mice, touch devices, gaming devices and keyboards.

Description

These procedures provide access to external devices such as keyboards, mice, touch screens and gamepads.

Mouse, Touch and Joystick Input
Procedures that provide state information for mouse, touch and joystick input.
Keyboard Input
Procedures that provide keyboard state information.

Mouse, Touch and Joystick Input
GetMouse
Gets button and axis information for the mouse.
SetMouse
Sets position and visibility of the mouse cursor.
GetTouchCount
Gets the number of active touch contacts.
GetTouch
Gets position and identifier information for an active touch contact.
GetTouchHit
Tests whether any active touch contact is inside a rectangle or circle.
GetJoystick
Gets button and axis information for gaming devices.
Getxpad
Gets Xbox-style controller state for gaming devices.
Stick
Gets axis position for gaming devices.
Strig
Gets button state for gaming devices.
Keyboard Input
MultiKey
Gets key information for the keyboard.


Screen Functions
Statements and procedures that work with the graphics display.

Description

These statements and procedures control the graphics capabilities of the FreeBASIC graphics library. Screen modes can be set with varying resolutions and color depths, window events can be handled, and specific OpenGL procedures can be retrieved.

Working with screen modes
Procedures for setting and retrieving information about screen modes.
Working with pages
Procedures that manipulate screen pages.
Working video memory
Procedures that provide direct access to framebuffer memory.
Screen Metrics
Procedures that control the way coordinates are interpreted.
Screen Data Types
Data types and data definitions for screen functions.

Working with screen modes
ScreenList
Gets the available fullscreen resolutions.
Screen (Graphics) and ScreenRes
Sets a new graphics display mode.
ScreenInfo
Gets information about the system desktop or current display mode.
ScreenControl
Gets or sets internal graphics library settings.
ScreenEvent
Gets system events.
ScreenGLProc
Returns the address of an OpenGL procedure.
WindowTitle
Sets the running program's window caption.

Working with pages
Cls
Clears the entire screen or viewport.
ScreenSet
Sets the current work and visible pages.
ScreenCopy and PCopy and Flip
Copies pixel data from one page to another.
ScreenSync
Waits for the vertical refresh of the monitor.
Working video memory
ScreenPtr
Gets the address of the working page's framebuffer.
ScreenLock
Locks the current working page's framebuffer for direct access.
ScreenUnlock
Reverts a previous ScreenLock command.

Screen Metrics
View (Graphics)
Sets a clipping region for all drawing and blitting procedures.
Window
Sets a new coordinate mapping for the current viewport.
PMap
Converts coordinates between physical and view mappings.
PointCoord
Queries Draw's pen position.

Screen Data Types
Event
Data type for ScreenEvent function.


GfxLib - FreeBASIC graphics library overview
GfxLib is the built-in graphics and input library used by FreeBASIC programs that open a graphics screen with Screen, ScreenRes, or related commands. It implements the built-in drawing commands, graphics pages, palette handling where the target supports it, keyboard input, mouse input, touch input on targets that expose it, and the platform-specific code needed to display the program's framebuffer.

GfxLib is not one single display driver. The common runtime owns the program-visible framebuffer and dispatches presentation, input, mode enumeration, window handling, and OpenGL entry points to a driver selected for the current target.

ScreenControl can be used with SET_DRIVER_NAME before the first graphics screen is opened to request a specific driver. The same choice can be made from the environment by setting FBGFX. Driver names are matched case-insensitively. ScreenControl with GET_DRIVER_NAME can be used after a graphics screen has opened to see which driver was selected.

The available driver names depend on the target and build configuration:
  • All targets:
    • Null - keeps the GfxLib framebuffer in memory without opening a real display. This is useful for headless tests, image processing code, or targets where no display driver can be initialized.
  • Windows:
    • Direct2D - preferred by normal Win32 builds when enabled and available.
    • DirectX - the DirectDraw based driver.
    • GDI - the Win32 GDI fallback driver.
    • OpenGL - the Win32 OpenGL driver.
    • Cygwin builds use the Win32-compatible path but do not necessarily include every native Windows driver.
  • Unix/X11 family:
    • X11 - the normal X11 driver.
    • OpenGL - OpenGL through X11/GLX when enabled.
    • FBDev - Linux framebuffer device support when enabled on Linux.
  • Darwin/macOS:
    • Darwin - the native Darwin graphics backend.
    • Null - fallback.
  • Haiku:
    • Haiku - the native Haiku graphics backend.
    • Null - fallback.
  • Android:
    • AndroidModern - the normal Android native-window driver.
    • AndroidLegacy - compatibility path for older Android behavior.
  • JavaScript/Emscripten:
    • asmjs - browser canvas driver.
    • WebGL - browser WebGL driver for OpenGL requests.
  • Wii:
    • wii - libogc based TV framebuffer driver.
  • Xbox:
    • xbox - original Xbox/nxdk framebuffer driver.
  • DOS:
    • VESA linear - VESA 2.0 linear framebuffer driver.
    • VESA banked - banked VESA compatibility driver.
    • VGA - standard VGA driver.
    • ModeX - tuned 320x240x8bpp VGA mode.
    • BIOS - BIOS graphics fallback.
Platform Differences

  • Driver availability is a compile-time and run-time property. A driver can be present in the binary but still fail to initialize if the operating system, display server, hardware, emulator, or browser environment does not provide what it needs.
  • On DOS, windowing and OpenGL-related options are not available. The refresh-rate setting is also not generally available. Programs should check whether the requested screen was opened successfully before using ScreenPtr or drawing into the screen.
  • On DOS, the requested resolution must match a mode supported by the video hardware or emulator. Safe fallback modes such as 640x480 or 800x600 are more portable than vendor-specific modes.
  • On JavaScript targets, the browser owns the real window, event loop, and presentation timing. GfxLib presents through browser canvas/WebGL rather than a native desktop window.
  • On console-style targets such as Wii and Xbox, the backend presents to the target's video output and scales the GfxLib framebuffer as needed. Controller input is exposed through the target-specific input functions instead of pretending every controller is a PC keyboard.
  • On Android, touch input is available through the GfxLib touch functions, and mouse-style compatibility is provided where it makes sense.
Differences from QB

  • QuickBASIC graphics code wrote directly to VGA memory. FreeBASIC keeps a GfxLib framebuffer and lets the selected driver copy or present that framebuffer using the current platform's display API.
  • Multiple graphics pages are still exposed to the program, but the runtime manages those pages internally instead of relying on the video card to provide QB-style page memory.
  • Some drivers update the visible screen from an event loop, timer, callback, explicit unlock, or input polling boundary. Portable graphics code should use the normal GfxLib drawing commands, ScreenLock, ScreenUnlock, ScreenPtr, ScreenSet, and ScreenCopy rather than mixing GfxLib with target-specific raw video memory access.
See also

  • GFX Functions Index
  • Screen (Graphics) The QB-style way to set a graphics mode
  • ScreenRes A more flexible way to open a graphics screen
  • ScreenList Check display modes available to GfxLib
  • ScreenControl Select drivers and query graphics state
  • ScreenLock
  • ScreenUnlock
  • ScreenPtr Direct access to the active framebuffer
  • ScreenSet
  • ScreenCopy
  • ScreenInfo
  • ScreenGLProc
  • Internal pixel formats


DOS Keyboard Scancodes
Listing of keyboard scancodes.

Description

Here follows a list of hardware keyboard scancodes accepted by the MultiKey function. These are equal to DOS scancodes, and are guaranteed to be always recognized on all platforms.

These constants are also defined in the fbgfx.bi include file you can use in your programs. If you are using the lang fb dialect then everything inside fbgfx.bi is enclosed in the FB Namespace. To use these constants in lang fb, either prepend "FB." to the constant name, or put "Using FB" after the #include line.
The hexadecimal code is not required and provided only for reference.

SC_ESCAPE       &h01
SC_1            &h02
SC_2            &h03
SC_3            &h04
SC_4            &h05
SC_5            &h06
SC_6            &h07
SC_7            &h08
SC_8            &h09
SC_9            &h0A
SC_0            &h0B
SC_MINUS        &h0C
SC_EQUALS       &h0D
SC_BACKSPACE    &h0E
SC_TAB          &h0F
SC_Q            &h10
SC_W            &h11
SC_E            &h12
SC_R            &h13
SC_T            &h14
SC_Y            &h15
SC_U            &h16
SC_I            &h17
SC_O            &h18
SC_P            &h19
SC_LEFTBRACKET  &h1A
SC_RIGHTBRACKET &h1B
SC_ENTER        &h1C
SC_CONTROL      &h1D
SC_A            &h1E
SC_S            &h1F
SC_D            &h20
SC_F            &h21
SC_G            &h22
SC_H            &h23
SC_J            &h24
SC_K            &h25
SC_L            &h26
SC_SEMICOLON    &h27
SC_QUOTE        &h28
SC_TILDE        &h29
SC_LSHIFT       &h2A
SC_BACKSLASH    &h2B
SC_Z            &h2C
SC_X            &h2D
SC_C            &h2E
SC_V            &h2F
SC_B            &h30
SC_N            &h31
SC_M            &h32
SC_COMMA        &h33
SC_PERIOD       &h34
SC_SLASH        &h35
SC_RSHIFT       &h36
SC_MULTIPLY     &h37
SC_ALT          &h38
SC_SPACE        &h39
SC_CAPSLOCK     &h3A
SC_F1           &h3B
SC_F2           &h3C
SC_F3           &h3D
SC_F4           &h3E
SC_F5           &h3F
SC_F6           &h40
SC_F7           &h41
SC_F8           &h42
SC_F9           &h43
SC_F10          &h44
SC_NUMLOCK      &h45
SC_SCROLLLOCK   &h46
SC_HOME         &h47
SC_UP           &h48
SC_PAGEUP       &h49
SC_LEFT         &h4B
SC_CLEAR        &h4C
SC_CENTER       &h4C
SC_RIGHT        &h4D
SC_PLUS         &h4E
SC_END          &h4F
SC_DOWN         &h50
SC_PAGEDOWN     &h51
SC_INSERT       &h52
SC_DELETE       &h53
SC_F11          &h57
SC_F12          &h58
'' Extra scancodes not compatible with DOS scancodes
SC_LWIN         &h5B
SC_RWIN         &h5C
SC_MENU         &h5D

See also

  • MultiKey


Default Palettes
Default color values for FreeBASIC graphics and text screen modes.

FreeBASIC initializes the palette indexes with the colors in the tables below. The colors are the same as in QB. Colors in graphics mode can be changed using the Palette statement. There is no portable way of changing the palette in console mode.

Screen mode 1
4 colors: Black and white, and two others
Screen modes 2, 10 and 11
Monochromatic: black and white.
Screen modes 7, 8, 9, 12, and Console (Screen 0)
Two sets of 8 colors: normal and intense (bright)
Screen 13 and 8-bit modes
Multiple color and grayscale bands

Screen mode 1

Value | Name
0 | black
1 | cyan
2 | magenta
3 | white

Screen modes 2, 10 and 11

Value | Name
0 | black
1 | white

Screen modes 7, 8, 9, 12, and Console (Screen 0)

Normal Value | Normal Name | Intense Value | Intense Name
0 | black | 8 | dark grey
1 | blue | 9 | bright blue
2 | green | 10 | bright green
3 | cyan | 11 | bright cyan
4 | red | 12 | bright red
5 | pink | 13 | bright pink
6 | yellow | 14 | bright yellow
7 | grey | 15 | white

Screen 13 and 8-bit modes

Screen 12 color band

Colors 0 through 15 are the same as screen 12 mode.

Grayscale band
Colors 16 through 31 are grayscale from black to white.

Brightness/saturation bands
3 bands of decreasing brightness, each containing 3 bands of decreasing saturation, each containing 24 hues of color starting and ending at blue.

Name | HB/HS | HB/MS | HB/LS | MB/HS | MB/MS | MB/LS | LB/HS | LB/MS | LB/LS
blue | 32 | 56 | 80 | 104 | 128 | 152 | 176 | 200 | 224
magenta | 36 | 60 | 84 | 108 | 132 | 156 | 180 | 204 | 228
red | 40 | 64 | 88 | 112 | 136 | 160 | 184 | 208 | 232
yellow | 44 | 58 | 92 | 116 | 140 | 164 | 188 | 212 | 236
green | 48 | 72 | 96 | 120 | 144 | 168 | 192 | 216 | 240
cyan | 52 | 76 | 100 | 124 | 148 | 172 | 194 | 220 | 244

Black band
Colors 248 through 255 are black.


Programmer's Guide
FreeBASIC programmer's guide.

Work in Progress: New pages created for this guide should use the ProPg* prefix.

Getting Started
Hello World
FreeBASIC Primer #1

Source Files
Source Files (.bas)
Header Files (.bi)
Using Prebuilt Libraries
Manage Reusable Procedures by Including Source vs Compiled Modules

Lexical Conventions
Comments
Identifier Rules
Literals
Labels
Line Continuation
Line Separator

Variables and Datatypes
Constants and Enumerations
Numeric Types
Strings (string, zstring, and wstring)
Coercion and Conversion
Constants
Variables

Arrays
Overview
Fixed-length Arrays
Variable-length Arrays
Array Indexing
Passing Arrays to Procedures

Pointers
Overview
Pointer Arithmetic

References
From Pointers to References
Using References

Declarations
Implicit Declarations
Initialization
Storage Classes
Variable Scope
Simple Variable Lifetime vs Scope
Dynamic Object and Data Lifetime
Namespaces
Variable and Procedure Linkage

User Defined Types
Overview
Type Aliases
Temporary Types
Type (UDT/Alias/Temporary) and Union
Constructors and Destructors (basics)
Member Procedures
Properties
Variable-length member data
Member Access Rights and Encapsulation
Operator Overloading
Iterators
New and Delete
Types as Objects
Composition, Aggregation, Inheritance
Inheritance Polymorphism
Constructors, '=' Assignment-Operators, and Destructors (advanced, part #1)
Constructors, '=' Assignment-Operators, and Destructors (advanced, part #2)

Statements and Expressions
Differences between Expressions and Statements
Constant Expressions
Assignments
Operator List
Operator Precedence
Control Flow Statements

Procedures
Procedures Overview
Passing Arguments to Procedures
Returning Values
Procedure Scopes
Calling Conventions
Recursion
Callback
Constructors and Destructors
Pointers to Procedures
Variadic Arguments

Graphics
FreeBASIC GfxLib overview
Graphics Mode Refresh and Anti-Flickering
Internal Graphics Formats
External Graphics File Formats (page to be completed)
Lite regulation function to be integrated into user loop for FPS control
Fine-grain procedure for waiting and in-loop procedure for fine-regulating FPS

Multi-Threading
Overview
Threads
Mutual Exclusion
Conditional Variables
Critical Sections
Critical Sections FAQ

Making Binaries
Executables
Static Libraries
Shared Libraries (DLLs)
Profiling with fb's profiler
Profiling for gmon/gprof

Preprocessor
Overview
Conditional Compilation
Macros

Other Topics
(And topics that need to get placed elsewhere)
ASCII
Date Serials
Radians
Error Handling
Event Handling
Intrinsic Defines
Identifier Look-ups in namespaces and types

Technical Articles
Inline Asm
C Standard Library Functions
File I/O with FreeBASIC
Dynamic memory management with FreeBASIC
Replace Recursion with Iteration
OBJECT built-in and RTTI info
Embed and Access binary Data in Executable
Use Implicit / Overload New([]) and Delete([]) Operators with Inheritance Polymorphism
Emulate a TLS (Thread Local Storage) and a TP (Thread Pooling) feature

NOTE: Existing CatPg pages should be recreated as ProPg pages providing a general overview to the grouping of keywords.


Hello World
This example is a classic in any programming language.

More as a sanity check than anything else, a good place to start with any programming language is to try a very simple program to test that the compiler is installed correctly and that a valid executable can be made.

Open up any editor capable of saving text files and type in the following source code:
Print "Hello World"

Save the file with a '.bas' extension. For example 'hello.bas'

From a command prompt or shell in the directory where 'hello.bas' was saved, type the following command:
fbc hello.bas
Depending on the operating system, this should create an executable file in the same directory as 'hello.bas'. It might be named 'hello.exe' or './hello', for example.

Run the executable, and we should have the following output:
Hello World

See also

  • Freebasic FAQ
  • Main Features
  • Requirements
  • Installing
  • Running


FreeBASIC Primer #1
This primer is intended for beginning beginners, for those who are just starting to learn how to program and using FreeBASIC do to it.

Learning the language

Learning a programming language means learning the words to write it and knowing what they mean when they are written. We don't need to learn them all at once. But learning a few important words that do something will help us get started. Here we are just going to concentrate on these keywords:

  • Dim
  • Print
  • Input
  • For...Next
  • If...Then
  • Do...Loop

Hello World!

No beginners reference is complete without this example.

Print "Hello World!"

The text between the pair of double quotes is a literal string. The Print statement is used to output text to the display. If you can edit, compile, and execute this example, you are on your way.

Note: A program terminates immediately after executing the last line of code. User can add a Sleep statement to induce a pause until pressing any key to complete instruction (see last example for usage).

Using a Variable to Store Data

Sometimes in a program we will want to store some information somewhere, in memory, and then use it later. To store something in memory we use a variable. All variables in FreeBASIC are of some specific type, like a number or a string. We use the Dim statement to declare a variable name and specify what type of information we want to store in it.

Dim text As String
text = "Hello World!"
Print text

We are using Dim to let the compiler know that we want to use a variable named text in our program and that we will be putting String data in it. We then assign (copy) "Hello World!" in to the variable. Finally, we use Print to output it to the display.

Using a Variable in an Expression

An expression is a generic term for describing a part of the source code that can be evaluated. After an expression is evaluated, we can then do something with it, like assign (copy) it to a variable.

Dim As String a, b, text
a = "Hello"
b = "World"
text = a + " " + b + "!"
Print text

We are assigning the variables a and b with some data. We are then using the variables a and b in an expression which is then assigned to text. Finally, we output the result to the display.

Getting Input from the User

Often, we have no idea what data is needed for a program unless the user gives it to us. We can't put it in our source code since we won't know what it is until the user runs the program and tells us what it is.

Dim answer As String
Input "Type something and press enter:", answer
Print "You typed: '"; answer; "'"

Here the Input statement will first, output some information to the display, and then wait for the user to give the program some data. In this example, we just output back to the display, exactly what the user typed in.

Doing Some Math

Variables and expressions are not just limited to strings. Most early languages didn't handle strings very well if at all. Writing mathematical expressions is similar to how they might be written with pencil and paper.

Dim As Integer a, b, c

a = 5
b = 7
c = a + b

Print "a = "; a
Print "b = "; b
Print "a + b = "; c

We are assigning values to the variables a, b and c. We are using Integer for the variables' data type. An integer can be positive or negative, but not have any fractions.

Doing Some Math with Input

This is similar to the previous example, except we will let the user choose the numbers we are going to add together.

Dim As Integer a, b, r
Input "Enter a number:", a
Input "Enter another number:", b

r = a + b
Print "The sum of the numbers is "; r

Dim lets the compiler know which variable names we want to use and that they are going to hold Integer data. We are using Input to get the numbers from the user, and Print to display the results.

Doing More Math with Input

Numeric variables are not limited to just integers. We can also use Single or Double precision data types which can represent fractions. In this example we will take some input from the user to convert a weight in pounds to kilograms.

Dim As Single lb, kg
Input "Enter a weight in pounds:", lb

kg = lb * 0.454
Print lb; " lb. is equal to "; kg; " kg"

Repeating Statements

Using For...Next statement we can tell the program to do something repeatedly a set number of times. For example lets say we wanted to add up all the numbers from 1 to 100.

Dim total As Integer
Dim number As Integer
total = 0
For number = 1 To 100
  total = total + number
Next
Print "The sum of number from 1 to 100 is "; total

Making a Decision

A program can choose which statements to execute using a conditional statement like If...Then. We can use the value of a variable or the result of an expression to decide if we should, or should not, execute one or more statements.

Dim number As Integer
Input "Enter a number : ", number
Print "Your number is ";
If number < 0 Then
  Print "negative"
ElseIf number > 0 Then
  Print "positive"
Else
  Print "zero"
End If

After getting a number from the user, we are going to output a word ( positive, negative, or zero ) based on which condition matches the statement.

Repeating Statements (Again)

Here we will use another looping structure Do...Loop to repeat some statements. How will the program know to stop repeating the statements? We will use If...Then to make the decision when to get out of the loop.

Dim As Single   total, count, number   ' multi variable declaration (same type)
Dim As String   text

Print "This program will calculate the sum and average for a"
Print "list of numbers. Enter an empty value to see results."
Print

Do
  Input "Enter a number: ", text       ' get user input
  If text = "" Then Exit Do            ' if empty -> quit Do/Loop
  count += 1                           ' increment count by: 1
  total += Val(text)                   ' add and assign new value
Loop

Print
Print "You entered:    "; count; "  number(s)"
Print "The sum is:     "; total

If count > 0 Then Print "The average is: "; total / count

Print
Print "Any keypress ends program. ";

Sleep

See also

  • Dim
  • Print
  • ?
  • Input
  • For...Next
  • If...Then
  • Do...Loop


Source Files (.bas)
Text files read by FreeBASIC and compiled into executable code.

A source file is a text file that contains FreeBASIC language statements. A program might be made from just one source file or possibly hundreds. Source files are read by the compiler and compiled into object code. Object code is then linked to create an executable or can be stored for later use as a library.

FreeBASIC by default, automatically takes care of compiling sources and linking object modules in to executables, so normally it is possible to make an executable program by just passing the names of the source files on the fbc command line. For example, assuming we had three source files that together made a program, we could create an executable for the program by running fbc, the FreeBASIC compiler on a command line as follows:

fbc myprog.bas tools.bas funcs.bas

Unicode support

  • Besides ASCII files with Unicode escape sequences (\u), FreeBASIC can parse UTF-8, UTF-16LE, UTF-16BE, UTF-32LE and UTF-32BE source (.bas) or header (.bi) files as long as they were saved with Byte Order Mark (BOM), they can be freely mixed with other sources/headers in the same project (also with other ASCII files).
  • Unicode files must be saved with Byte Order Mark (BOM), otherwise fbc does not recognize them as Unicode.
  • Literal strings can be typed in the original non-Latin alphabet, just use a text-editor that supports one of the Unicode formats listed above.

Note: The most reliable cross-platform code is obtained by encoding without BOM in ASCII/UTF-8 characters.

Implicit main()

Some languages require a special main() procedure be defined as an entry point to the program which define the first statements that will be executed when the program starts. FreeBASIC allows executable statements in module level code and normally the first source file passed to fbc on the command line will be used as the "main" module. The main module can be explicitly named by passing -m filename on the command line, where filename is the name of the main module without the .bas extension.
    '' sample.bas
    Declare Sub ShowHelp()

    '' This next line is the first executable statement in the program
    If Command(1) = "" Then
        ShowHelp
        End 0
    End If

    Sub ShowHelp()
        Print "no options specified."
    End Sub

Header Files

A header file is a special kind of source file that typically only contains declarations and has a .bi extension. See Header Files (.bi).

See also

  • fbc command-line
  • Header Files (.bi)


Header Files (.bi)
Provides an interface for a module.

A header file is a special kind of source file that typically only contains preprocessor statements, defines, declarations, prototypes, constants, enumerations, or similar types of statements, however, a header file can contain any valid source code if the purpose suits. What makes them different from other module (.bas) source files, is instead of being compiled directly, they are included by another source file (module or header) using the #include preprocessor directive. All compiled libraries typically have one or more header files that can be included in another source file and will introduce to the compiler all the names of the procedures usable in a particular library.

FreeBASIC Header Files

Some of the keywords, constants, and procedures documented in this manual are not normally available when compiling a source code unless a specific header file is included in the source first.
  • inc/fbc-int/array.bi
  • inc/datetime.bi
  • inc/dir.bi
  • inc/fbgfx.bi
  • inc/fbio.bi
  • inc/fblimits.bi
  • inc/fbprng.bi
  • inc/fbthread.bi
  • inc/file.bi
  • inc/fbc-int/symbol.bi
  • inc/crt/string.bi
  • inc/vbcompat.bi

Case Sensitivity

Although the FreeBASIC language itself is not case-sensitive, the file system on which it is running might be. If a header file can not be found, check that FreeBASIC is searching for it the correct location and ensure that name of both the directory and filename of the header file specified in the #include statement is using the correct upper and lower case letters.

Path Separators

FreeBASIC will automatically switch backslash ( \ ) and forward slash ( / ) characters as needed for a given platform. This allows source code to be easily cross compatible.

Including a header only once

It is common that header files need to #include other header files to compile correctly. FreeBASIC offers three methods for guarding against including a header file more than once.
  • #ifndef guards in the header file
  • #include Once where the file is included
  • #pragma Once in the header file itself

#ifndef guards in the header file

The use of #ifndef and #define is a common practice in nearly any language that supports preprocessing. The first time a file is included, a unique symbol is defined. The next time the same header file is included, the definition of the symbol is checked, and if it is already defined, the contents of the header file are skipped.
'' header.bi
#ifndef __HEADER_BI__
#define __HEADER_BI__

#print These statements will only be included Once,
#print even though header.bi might be included more
#print than Once in the same source file.

#endif

#include once

At the point in the source code where the header file is included, the optional "once" specifier of the #include directive can tell the compiler to only include the source file one time.
'' header.bi
#include Once "fbgfx.bi"

'' module.bas
#include Once "fbgfx.bi"
#include Once "header.bi"

#pragma once

#pragma Once can be used in a header file to indicate that the header file should only be included once.
'' header.bi
#pragma Once
#print This header will only ever be included Once per module

Version

  • Since fbc 1.10.0, symbol.bi and fblimits.bi are added.
  • Before fbc 1.10.0 (for fbc 1.08.0 and fbc 1.09.0), fbprng.bi was named fbmath.bi.
  • Since fbc 1.08.0, array.bi is added.
  • Before fbc 1.08.0, the standard fbmath.bi header did not exist.

See also

  • Source Files (.bas)
  • Header Files Index


Using Prebuilt Libraries
FreeBASIC is distributed with many headers for common or popular libraries. The headers allow a programmer to use functions available in these existing static or shared libraries (DLLs).

The libraries themselves are not distributed with FreeBASIC, but most can be downloaded from the web and readily installed. Some other libraries may need to be first compiled from sources to be used. Please see the documentation for the specific library on how to configure, install, and use them.
Note that the system architecture of the library has to match the architecture of the application. For example, to successfully use a library in a x86 (32-bit) application, a x86 (32-bit) library is required.

Some static or shared libraries (DLLs) may be already present on the system since they might be part of FreeBASIC itself or the operating system.

Although many headers can be used on any of the platforms supported by FreeBASIC, some headers are platform specific and will not be usable on other platforms.

FreeBASIC headers

There are a few headers that are specific to FreeBASIC and expose some functions that are otherwise not available:
  • inc/fbc-int/array.bi - Declaration for Fbarray (Array Descriptor Structure And Access), Arraylen, Arraysize.
  • inc/datetime.bi - Declarations for DateSerial, DateValue, IsDate, Year, Month, Day, Weekday, TimeSerial, TimeValue, Hour, Minute, Second, Now, DateAdd, DatePart, DateDiff, MonthName, WeekdayName.
  • inc/dir.bi - Constants to be used with Dir.
  • inc/fbgfx.bi - Additional constants and structures to be used with graphics commands such as MultiKey, ScreenControl, and ScreenEvent, ImageCreate.
  • inc/fbio.bi - Declaration for IsRedirected.
  • inc/fblimits.bi - Constants for minimum and maximum values of built-in number.
  • inc/fbprng.bi - Constants to be used with Randomize, and also unfinished structures for other random number generators.
  • inc/fbthread.bi - Declaration for ThreadDetach and ThreadSelf.
  • inc/file.bi - Declarations for FileCopy, FileAttr, FileLen, FileExists, FileDateTime.
  • inc/fbc-int/symbol.bi - Declaration for __Fb_Query_Symbol__ and its associated sub-macros.
  • inc/crt/string.bi - Declarations for Format.
  • inc/vbcompat.bi - Includes datetime.bi, dir.bi, file.bi, and string.bi plus additional constants compatible with Microsoft Visual Basic.

C Runtime (CRT)

Where possible cross-platform compatible headers have been provided for the C runtime (CRT). For example,
#include Once "crt.bi"
printf( !"Hello World\n" )

To include a specific CRT header, prefix the name of the header file with "crt/". For example:
#include Once "crt/stdio.bi"
Dim f As FILE Ptr
f = fopen("somefile.txt", "w")
fprintf( f, "Hello File\n")
fclose( f )

Windows API

Many (many) headers for the Windows API are available for inclusion in FreeBASIC source code. In most cases the only include file needed is "windows.bi". For example,
#include Once "windows.bi"
MessageBox( null, "Hello World", "FreeBASIC", MB_OK )

To include a specific Windows API header, prefix the name of the header with "win/" for example:
#include Once "win/ddraw.bi"

Browse the "inc/win/" directory where FreeBASIC was installed to see all of the available Windows API headers.

Other Headers Provided

See the External Libraries Index for an overview of the additional headers shipped with FreeBASIC. The page also describes the file(s) that need to be included to use the library. Alternatively one can browse the "inc/" directory located where FreeBASIC was installed to find other headers. It is possible that headers might be available for a library you need to use. Some headers are located in "inc/" and others might be located in a sub-directory. To include headers located in a subdirectory of "inc/", prefix the name of the header with the name of the directory where it is located.
For example:
'' located at inc/curl.bi
#include Once "curl.bi"

'' located at inc/GL/gl.bi
#include Once "GL/gl.bi"

Requirements for Using Prebuilt Static Libraries

  • The source code must include the appropriate headers using #include.
  • The static library must be linked at compile time by using either #inclib in the source code or by using the -l option on the command line to specify the name of the library.

Requirements for Using Prebuilt Shared Libraries

  • The source code must include the appropriate headers using #include.
  • The shared library (.DLL) must be present on the host computer where the compiled program will run.

Version

  • Since fbc 1.10.0, symbol.bi and fblimits.bi are added.
  • Before fbc 1.10.0 (for fbc 1.08.0 and fbc 1.09.0), fbprng.bi was named fbmath.bi.
  • Since fbc 1.08.0, array.bi is added.
  • Before fbc 1.08.0, the standard fbmath.bi header did not exist.

See also

  • Static Libraries
  • Shared Libraries (DLLs)


Manage Reusable Procedures by Including Source vs Compiled Modules
How to manage FreeBASIC reusable procedures by including source modules versus including compiled modules.
(written with help of Josep Roca's posts)

Preamble:

When old OSs (like DOS) were used, compiling by separate modules was mandatory as soon as the program was not very short, because of the small memory size available for the compiler (about 200 KB with Quick Basic 4.5).
Now, with modern PC and OS, this compilation limit is pushed back by a factor of about 10000, and the compile time for a large file in one go has become acceptable especially with FreeBASIC.
This allows to use another method (than a library of compiled files) to manage the reusable user procedures.

First, the reusable user procedures source codes are grouped into different source modules, for example by functionality.
Then the process is different depending on each method used.

To simplify the explanation that follows, it is considered that there is only one source file containing the main program, which calls the various user procedures contained in source modules for reuse.
But one can easily complete these methods to take into account a main program spread over several source files.

First method: The compiled modules stored in a library file are included in the linking process with the compiled main program

The principle of this method is by only accessing to the compiled user procedures.

The different modules (containing the sources of the user procedures to be reused) are turned in to object files and then stored into a library file (using the -lib compile option).
Finally the source file of the main program is compiled, then linked to the library, to make an executable (using the -l < libname > compile option, or the #Inclib "Libname" directive put at beginning of the main program source code).

For each compiled module, if at least one procedure is called, then the entire module will be added in the final executable.

Thus, the granularity of the added code to the executable is at the module level (coarse granularity).

Second method: The source modules are included directly in the main source program to be compiled in one go

The principle of this method is by fully accessing to the sources of user procedures.

The different modules (containing the sources of the user procedures to be reused) are directly included in the source of the main program (using the #Include "File" directive for each module, put at the beginning of the main program source code).
Finally, the big resulting source file is compiled in one go to make it an executable.

Since the compiler processes a single source file, all reusable user procedures can be declared as Private (which is obviously impossible when using library because the external links are required during the linkage).
As a result, only the Private procedures really called will be kept in the executable.

Thus, the granularity of the code added to the executable is at the elementary procedure level (fine granularity).

Note:
The compiler removes the Private procedures that are not called, but this does not currently work for Private procedures that are only called by other Private procedures that are not called themselves, because the first ones appear as being called.
The problem is that the one-pass compiler only uses a simple flag to track the "used" state of a procedure, which is set whenever the procedure is accessed, no matter from where.

See also

  • fbc command-line
  • Source Files (.bas)
  • Header Files (.bi)
  • Using Prebuilt Libraries


Comments
Comments are regions of text that the compiler will ignore but may contain information that is useful to the programmer. One exception are metacommands which may appear in certain types of comments.

Single Line comments

The single quote character (') may be used to indicate a comment and may appear after other keywords on a source line. The rest of the statement will be treated as a comment.
' comment text

The comment statement: Rem

A source code statement beginning with Rem indicates that the line is comment and will not be compiled. In FreeBASIC, Rem may also appear after other keywords on a source line and the behavior is the same as above (only the rest of the statement will be treated as a comment).
Rem comment

Multi-line comments

Multi-line comments are marked with the tokens /' and '/. All text between the two markers is considered comment text and is not compiled.

Multi-line comments can span several lines, and can also be used in the middle of statements. After the end of the comment, the statement will continue to be parsed as normal (even if the comment crosses line breaks).
/' Multi-line
   comment '/

Print "Hello" /' embedded comment'/ " world"

Note: If FreeBASIC encounters a close-comment marker while it's not in a multi-line comment, it will treat it as a normal single-line comment due to the single quote.

Nested Comments

A multi-line comment can contain other multi-line comments inside it. Each inner comment has its own open- and close-comment markers.

/'
 This is a comment.
 /'
  This is a comment inside a comment
 '/
    This Is a comment.
'/

A multi-line comment can contain unlimited levels of nested comments. FreeBASIC will continue to parse the multi-line comment for more markers until the number of close-comment markers reaches the number of open-comment markers, i.e. when it has closed all the comments it has opened.

Comments after line continuation

A single-line comment may appear after the line continuation character ( _ ) in a multi-line statement. FreeBASIC does not parse the text after the line continuation character, though, so you can't open multi-line comments after them.

Print _ ' line
    "This is part of the previous line's statement"

Metacommands

Metacommands, such as $Static and $Include, can be placed in single-line comments. The $ sign and the keyword must be the first two things in the statement, not including white space.

Rem compile With -lang fblite Or qb

#lang "fblite"

Rem $Static
' $include: 'vbcompat.bi'

Single-line comment parsing

When you make a single-line comment, FreeBASIC will parse the comment, to check for a metacommand. If it finds a multi-line comment, it will treat it as usual, and continue parsing the single-line comment after the close-comment marker.

If you want to prevent FreeBASIC parsing the single-line comment, put another single quote ('), at the start of the comment. FreeBASIC will treat the rest of the line, including multi-line comment markers and metacommands, as ordinary text, and will ignore it. Other words encountered in a comment will also stop the parsing.
    • Note: As of version 0.21.0, this will not longer apply in the -lang fb dialect, and multi-line comment markers will be completely ignored inside single-line comments

'' $static <-- will not get parsed
'' this multiline comment marker ("/'") will be ignored
Print "This line is not a comment."

Example

/' this is a multi line
comment as a header of
this example '/

Rem This Is a Single Line comment

'this is a single line comment

Dim a As Integer   'comment following a statement

Dim b As /' can comment in here also '/    Integer

#if 0
    before version 0.16, This was the
    only way of commenting Out sections
    With multiple lines of code.
#endif

See also

  • Rem


Identifier Rules
Naming conventions for FreeBASIC symbols.

Description

An identifier is a symbolic name which uniquely identifies a variable, Type, Union, Enum, Function, Sub, or Property, within its scope or Namespace.

Identifiers may contain only uppercase and lowercase Latin characters a-z and A-Z), digits (0-9), and the underscore character (_). The first character of an identifier must be a letter or underscore, not a digit; if an identifier's length exceeds 128 characters, it will be truncated.

Identifiers are case-insensitive: FOO and foo (and all other permutations of uppercase and lowercase) refer to the same symbol.

In the -lang qb and -lang fblite dialects, identifiers may have a type suffix at the end indicating one of the standard data types:

  • % for Integer
  • & for Long
  • ! for Single
  • # for Double
  • $ for String

The use of these suffixes is generally discouraged and is not allowed in the -lang fb dialect (the default).

The alternative is to be explicit - for example, Dim As Integer foo or Dim foo As Integer instead of Dim foo%.

In the -lang qb and -lang fblite dialects, identifiers may contain one or more periods (.).

Platform Differences

  • Warning:
- For 64-bit compiler only and regarding the choice of user module level procedure identifier names, an additional restriction to the above normal 'Identifier Rules' should also exclude in the global namespace all register names or other symbols issued from the only 'intel' format assembler when it is used (the one by default, no problem with 'att' format), because they causes assembler errors or runtime bugs (due to the 64-bit compiler not decorating module level procedure names).
- Since fbc version 1.09.0, for x86 and x86_64 only (with any assembler format), the use of an inline asm symbol or a global/external/backend symbol induces a warning if such a symbol is used for a module level procedure or a shared variable in the global namespace.

Dialect Differences

  • Periods in symbol names are only supported in the -lang qb and -lang fblite dialects.

Differences from QB

  • Support for the underscore character (_) in symbol names is new to FreeBASIC.

See also

  • Variables
  • Identifier Look-ups in namespaces and types


Labels
Defines a location in a program.

Syntax

symbolname :
or
literalnumber

Description

A label can define a place in a program where Goto or GoSub can jump to.
A label can also mark Data blocks, allowing Restore to change the Read sequence.

A label can be a positive integer line number or a symbolname. In both cases, the label must start at the first column of line. A symbolname label must end with a colon (:) character.

Example

'' Compile with -lang fblite or qb

#lang "fblite"

beginning:
3 Print "Hello World!"
Goto beginning

'' compile with -lang qb

'$lang: "qb"

'' Labels can be used to "bookmark" DATA blocks, allowing RESTORE to alter the READ sequence.
Read a,b,c
Restore here
Read d,e
Print a,b,c,d,e

Data 1,2,3,4,5
here:
Data 6,7,8

Output:
1,2,3,6,7

Dialect Differences

  • Line numbers with decimals is available only in the -lang qb dialect.

Differences from QB

  • None if compiled in the -lang qb dialect.

See also

  • GoSub
  • Goto
  • Restore


Line Continuation
Line Continuation character(s) allow to spread out a single line of code into multiple lines.

Description

A single '_' (underscore) character at the end of a line of code tells the compiler that the line continues in the next line. This allows a single statement (line of code) to be spread across multiple lines in the input file, which can be a nice formatting help:
'' This Dim statement is spread across multiple lines, using the '_' character
Dim myvariable _
As Integer

This is often used to make very long lines of code easier to read, for example procedure declarations with a lot of parameters:
'' Here's an example:

Declare Sub drawRectangle( ByVal x As Integer, ByVal y As Integer, ByVal w As Integer, ByVal h As Integer )

'' which can also be written as:

Declare Sub drawRectangle( ByVal x As Integer, ByVal y As Integer, _
                           ByVal w As Integer, ByVal h As Integer )

'' or:

Declare Sub drawRectangle _
    ( _
        ByVal x As Integer, _
        ByVal y As Integer, _
        ByVal w As Integer, _
        ByVal h As Integer _
    )

'' (or any other formatting you like)

The '_' line continuation character can be inserted at pretty much any point in a line of code. It does not work inside comments though.

Be careful when adding the '_' line continuation character right behind an identifier or keyword. It should be separated with at least one space character, otherwise it would be treated as part of the identifier or keyword:
'' Declare variable "a_"
'' (no line continuation happening, because the '_' character is part of
'' the "a_" identifier)
Dim As Integer a_

'' Declare variable "a" and initialize to value 5
'' (line continuation happening, because the '_' character
'' was separated from the identifier "a" with a space character)
Dim As Integer a _
= 5

Warning: When an erroneous code line is spread over a multiple lines block by using the '_' line continuation character, the error message refers only to the last line of the block.

Note for fbc version >= 1.08:
In macro/define's use '##_' to escape line continuation character '_' to allow multiple lines of macro expanded code to be combined into a single statement.

See also

  • Line Separator


Line Separator
Line Separator character(s) allow to place multiple statements on the same line.

Description

Statements normally take up a single line with no terminator.
But statements can even be stacked by using a colon (:) to separate them.

To place multiple statements on the same line, separate the statements with a colon (:), as in the following example:
'' These 5 statements are stacked on the same line, using the ':' character as a separator
Dim As String text = "Hello!" : Color 14, 3 : Print text : Color 7, 0 : Sleep

See also

  • Line Continuation


Constants and Enumerations
Enumeration, a special User-Defined Type composed of a number of named constants.

Syntax

Enum [typename [ Explicit ] ]
symbolname [= expression] [, ...]
...
End Enum

Parameters

typename
Name of the Enum
symbolname
Name of the constant
expression
A constant expression
Explicit
Requires that symbols must be explicitly referred to by typename.symbolname

Description

An enumeration is a User-Defined Type that consists of a set of named integer constants.
Enumerations are useful for defining sequences and states, particularly when there is a natural progression through those states.

An enumeration provides context to describe a range of values which are represented as named constants through symbols.
Unlike namespaces, enumerations can be also defined in any scope blocks, and their symbols are only visible throughout the scope in which the enumeration is declared.

Enumerations lend themselves to more maintainable code because they are symbolic, allowing you to work with integer values ​​while using an explicit name.
Enumerations are value Types, which means they contain their own value, can not inherit or be inherited from.

Enum can not contain any member procedure or member data (only symbols), but it can be included (named or unnamed) in a Type by having.

Usage

Every symbol in an enumeration is implicitly assigned an integer value (positive or negative) that corresponds to its place in the order of the values in the enumeration.
Every symbol is treated as a constant.

By default, the first value is assigned 0, the next one is assigned 1, and so on.
But you can explicitly set the value of any symbol (and the next symbol will be implicitly set to the previous value plus an increment of one).

The values given to the symbols do not have to be unique.

When an enumeration is qualified as Explicit, access to any symbolname must be always prefixed by typename.
Prefixing can also be used to solve ambiguity with other entities.

An Enum instance can be passed, as any User-Defined Type instance, to a procedure (including for the definition of overloaded operators).
The size of an Enum instance will be always that of an Integer (no matter how many defined symbols are just declarations for the compiler assignment).

An Enum instance (or Enum symbol) can be implicitly converted to an integer.
Note:
- In many languages, no integer variable is implicitly convertible into a scoped enumeration type instance (and vice versa), the latter being strongly typed (declaration equivalent to the Enum qualified as Explicit with FreeBASIC), and an explicit cast is required for such a conversion.
- But FreeBASIC accepts it (even if the numeric value does not match any Enum symbol defined). This is just one of FreeBASIC's many shortcomings over normal Enum functionality.
- Therefore, there is not much interest in declaring a real ENUM instance (which is sized as an INTEGER), but otherwise any pre-built integer variable can be declared instead.
- If the Explicit qualifier is not used (namespace prefix not imposed), there is not much point in using an ENUM structure compared to a simple discrete list of constants, except for the auto-increment of values.

Example

Simple example of use:
Enum Colors
    black
    blue
    green
    cyan
    red
    pink
    yellow
    grey
    dark_grey
    bright_blue
    bright_green
    bright_cyan
    bright_red
    bright_pink
    bright_yellow
    white
End Enum

Sub print_fbc (ByVal foreground As Colors, ByVal background As Colors)
    Color foreground, background
    Print " " & __FB_SIGNATURE__ & " "
End Sub

Dim As Colors std_foreground, std_background
std_foreground = LoWord(Color())
std_background = HiWord(Color())

Dim As Colors my_foreground, my_background
my_foreground = bright_yellow
my_background = cyan

print_fbc(my_foreground, my_background)

Color std_foreground, std_background
Print "end"

Sleep

Same result, but with a Type interfacing Enum to impose explicit casting when assigning numeric values to Enum instances (see 'Note' above):
Enum Colors Explicit
    black
    blue
    green
    cyan
    red
    pink
    yellow
    grey
    dark_grey
    bright_blue
    bright_green
    bright_cyan
    bright_red
    bright_pink
    bright_yellow
    white
End Enum

Type Console_Colors
    Public:
        Declare Property foreground () As Colors
        Declare Property foreground (ByVal c As Colors)
        Declare Property background () As Colors
        Declare Property background (ByVal c As Colors)
    Private:
        Dim As Colors _foreground
        Dim As Colors _background
End Type

Property Console_Colors.foreground () As Colors
    Return This._foreground
End Property

Property Console_Colors.foreground (ByVal c As Colors)
    This._foreground = c
End Property

Property Console_Colors.background () As Colors
    Return This._background
End Property

Property Console_Colors.background (ByVal c As Colors)
    This._background = c
End Property

Sub print_fbc (ByVal foreground As Colors, ByVal background As Colors)
    Color foreground, background
    Print " " & __FB_SIGNATURE__ & " "
End Sub

Dim As Console_Colors std_colors
std_colors.foreground = Cast(Colors, LoWord(Color()))  '' explicit cast mandatory because of property declaration
std_colors.background = Cast(Colors, HiWord(Color()))  '' explicit cast mandatory because of property declaration

Dim As Console_Colors my_colors
my_colors.foreground = Colors.bright_yellow
my_colors.background = Colors.cyan

print_fbc(my_colors.foreground, my_colors.background)

Color std_colors.foreground, std_colors.background
Print "end"

Sleep

See also

  • Enum
  • Type
  • Constants


Numeric Types
Of all built-in data-types, Numeric Types are those dedicated to the representation of all kinds of numbers.

FreeBASIC supplies several numeric data types for handling numbers in various representations.
Of these, integer types represent only integer numbers (positive, negative, and zero), and floating-point types represent real numbers.

Integer types

Integer types are those that represent only integer numbers:
    • Signed integer types:
- Byte 8-bit
- Short 16-bit
- Long 32-bit
- Integer 32-bit or 64-bit (*)
- LongInt 64-bit
- Integer<8|16|32|64> 8|16|32|64-bit
    • Unsigned integer types:
- UByte 8-bit
- UShort 16-bit
- ULong 32-bit
- UInteger 32-bit or 64-bit (*)
- ULongInt 64-bit
- Uinteger<8|16|32|64> 8|16|32|64-bit

Floating-point types

Floating-point types are those that represent real numbers:
    • Single precision:
- Single 32-bit
    • Double precision:
- Double 64-bits

Pseudo-integer types

Other types that also represent but indirectly integer numbers:
    • Enumerated type:
- Enum (32-bit or 64-bit) (*)
    • Boolean type:
- Boolean (1-bit useful in 8-bit)

Pointer types

Pointers are types whose values (uinteger) are addresses in memory (they are said to 'point' to this memory).
The type of data that is pointed to depends on the type of pointer (an Integer Pointer points to Integer data).
Pointers are declared like any other variable, with the suffix pointer or ptr following the type name:
    • Pointer type:
- {DataType|Any} {Pointer|Ptr} (32-bit or 64-bit) (*)

(*) INTEGER, UINTEGER, and ENUM data types vary with platform, matching the size of POINTER.

Example

Size (in bits) of all types above:
Print Using "A  BYTE     is ##"; SizeOf(Byte)     * 8; : Print "-bit"
Print Using "A  SHORT    is ##"; SizeOf(Short)    * 8; : Print "-bit"
Print Using "A  LONG     is ##"; SizeOf(Long)     * 8; : Print "-bit"
Print Using "An INTEGER  is ##"; SizeOf(Integer)  * 8; : Print "-bit"
Print Using "A  LONGINT  is ##"; SizeOf(LongInt)  * 8; : Print "-bit"
Print Using "An UBYTE    is ##"; SizeOf(UByte)    * 8; : Print "-bit"
Print Using "An USHORT   is ##"; SizeOf(UShort)   * 8; : Print "-bit"
Print Using "An ULONG    is ##"; SizeOf(ULong)    * 8; : Print "-bit"
Print Using "An UINTEGER is ##"; SizeOf(UInteger) * 8; : Print "-bit"
Print Using "An ULONGINT is ##"; SizeOf(ULongInt) * 8; : Print "-bit"
Print
Print Using "A  SINGLE   is ##"; SizeOf(Single)   * 8; : Print "-bit"
Print Using "A  DOUBLE   is ##"; SizeOf(Double)   * 8; : Print "-bit"
Print
Enum myENUM : option1 = 1 : option2 : End Enum
Print Using "An ENUM     is ##"; SizeOf(myENUM)   * 8; : Print "-bit"
Print
Print Using "A  BOOLEAN  is ##"; SizeOf(Boolean)  * 8; : Print "-bit"
Print
Print Using "A  POINTER  is ##"; SizeOf(Any Ptr)  * 8; : Print "-bit"

Sleep

Output example for win64:
A  BYTE     is  8-bit
A  SHORT    is 16-bit
A  LONG     is 32-bit
An INTEGER  is 64-bit
A  LONGINT  is 64-bit
An UBYTE    is  8-bit
An USHORT   is 16-bit
An ULONG    is 32-bit
An UINTEGER is 64-bit
An ULONGINT is 64-bit

A  SINGLE   is 32-bit
A  DOUBLE   is 64-bit

An ENUM     is 64-bit

A  BOOLEAN  is  8-bit

A  POINTER  is 64-bit
See also

  • Standard Data Types
  • Standard Data Type Limits
  • Coercion and Conversion


Strings (string, zstring, and wstring)
Of all built-in data-types, Strings types are those dedicated to the representation of character chains.

FreeBASIC supplies several strings data types for handling characters chains in various representations.
The fixed-length strings types (string, zstring and wstring) represent fixed-length chains of characters, while the variable-length string type represents a variable-length chain of characters.

Fixed-length strings

There are 3 types of fixed-length strings:
    • Fixed-length string type for 8 bit character (QB-style fixed-length string):
- String * size
since fbc version 1.20.0, total length of the string: 'size' characters = 'size' bytes ('size' useful characters)
before fbc version 1.20.0, total length of the string: 'size+1' characters = 'size+1' bytes ('size' useful characters + '1' final null character)
    • Fixed-length zstring type for 8 bit character:
- ZString * size
total length of the zstring: 'size' characters = 'size' bytes ('size-1' useful characters + '1' final null character)
or
- ZString Ptr
total length of the pointed zstring: depends on the character chain referenced by the pointer (up to and including the final null character)
    • Fixed-length wstring type for wide character:
- WString * size
total length of the wstring: 'size' characters = 'size' x k(*) bytes ('size-1' useful characters + '1' final null character)
or
- WString Ptr
total length of the pointed wstring: depends on the character chain referenced by the pointer (up to and including the final null character)

(*) The number of bytes 'k' used by a WSTRING character depends on the platform.

Variable-length string

There is only one type of variable-length string:
    • Variable-length string type for 8 bit character:
- String
total length of the string: dynamic length depending on the assigned data characters
(string referenced by an internal descriptor of 1 pointer + 2 uinteger length)

Example

Size (in bytes) of different strings from all types above:
'  Result depending on fbc version (before/since 1.20.0)

Dim As String * 20 s20 = "FreeBASIC manual"

Dim As ZString * 20 z20 = "FreeBASIC manual"
Dim As ZString Ptr pz = @"FreeBASIC manual"

Dim As WString * 20 w20 = "FreeBASIC manual"
Dim As WString Ptr pw = @WStr("FreeBASIC manual")

Dim As String s = "FreeBASIC manual"

Print Using "'FIXED-LENGTH STRING * 20': ## bytes in total, ## useful characters available"; SizeOf(s20); Len(s20)
#if __FB_VERSION__ < "1.20.0"
Print Using "    containing ## user characters of # byte(s) each"; IIf(InStr(s20, Chr(0)) > 0, InStr(s20, Chr(0)) - 1, Len(s20)); SizeOf(s20[0])
#endif
Print
Print Using "'FIXED-LENGTH ZSTRING * 20': ## bytes in total, ## useful characters available"; SizeOf(z20); SizeOf(z20) \ SizeOf(z20[0]) - 1
Print Using "    containing ## user characters of # byte(s) each"; Len(z20); SizeOf(Z20[0])
Print "'ZSTRING PTR': dereferencing pointer -> "; """" & *pz & """"
Print Using "    containing ## user characters of # byte(s) each"; Len(*pz); SizeOf((*pz)[0])
Print
Print Using "'FIXED-LENGTH WSTRING * 20': ## bytes in total, ## useful characters available"; SizeOf(w20); SizeOf(w20) \ SizeOf(w20[0]) - 1
Print Using "    containing ## user characters of # byte(s) each"; Len(w20); SizeOf(w20[0])
Print "'WSTRING PTR': dereferencing pointer -> "; """" & *pw & """"
Print Using "    containing ## user characters of # byte(s) each"; Len(*pw); SizeOf((*pw)[0])
Print
Type descriptor : Addr As ZString Ptr : UC As UInteger : AC As UInteger : End Type
Print Using "'STRING': ## bytes in descriptor, memory allocated for ## characters right now"; SizeOf(s); Cast(descriptor Ptr, @s)->AC
Print Using "    containing ## user characters of # byte(s) each"; Len(s); SizeOf(s[0])

Sleep

Note: For the fixed-length string type only (QB-style fixed-length string), the 'Len()' keyword always returns the declared constant number of characters, regardless of the number of characters assigned to it by user.
(hence the formula only for fixed-length string type before fbc version 1.20.0: 'user_characters_length = IIf(InStr(s, Chr(0)) > 0, InStr(s, Chr(0)) - 1, Len(s))')

Output example before fbc version 1.20.0 and for win64 (a wstring character uses 2 bytes):
'FIXED-LENGTH STRING * 20': 21 bytes in total, 20 useful characters available
    containing 16 user characters of 1 byte(s) each

'FIXED-LENGTH ZSTRING * 20': 20 bytes in total, 19 useful characters available
    containing 16 user characters of 1 byte(s) each
'ZSTRING PTR': dereferencing pointer -> "FreeBASIC manual"
    containing 16 user characters of 1 byte(s) each

'FIXED-LENGTH WSTRING * 20': 40 bytes in total, 19 useful characters available
    containing 16 user characters of 2 byte(s) each
'WSTRING PTR': dereferencing pointer -> "FreeBASIC manual"
    containing 16 user characters of 2 byte(s) each

'STRING': 24 bytes in descriptor, memory allocated for 32 characters right now
    containing 16 user characters of 1 byte(s) each
Implicit conversion from Zstring to Ubyte

When any operand of a binary operator (as assignment, equal, +, *, ...) consists in dereferencing a 'Zstring Ptr' pointer ('pz'), this can give a 'Zstring' string or a 'Ubyte' variable, depending on the other operand:
- If the other operand is numeric, so the dereferenced 'Zstring Ptr' pointer ('*pz') will be treated as a 'Ubyte' reference to the one character pointed.
- If in this case a 'Zstring' pointer indexing '[]' operator is used as dereferencing syntax ('pz[n]'), it is basically a short-cut version of the 'String' indexing '[]' operator ('(*pz)[n]').

Since '(*pz)[i]' and 'pz[i][k]' are always numeric (each an 'Ubyte'), such equalities are true:
pz[i] = (*pz)[i]
(*pz)[i] = pz[i]
and even:
pz[i+k] = pz[i][k]
pz[i][k] = pz[i+k]

Simple code example with a dereferenced 'Zstring Ptr' converted to an 'Ubyte' reference (as operand of an 'assignment' operator and a '+' operator):
Dim As ZString * (14+1) z = "FreeBASIC     "
Dim As ZString Ptr pz = @z

Print z
z[10] = Asc("2")
*(pz + 11) = Asc("0")                '' *(pz + 11) is converted to an Ubyte reference
(*pz)[12] = Asc("1")
pz[13] = Asc("8")                    '' pz[13] is converted to an Ubyte reference
Print z

For I As Integer = 10 To 13
    Print Str(z[I] - Asc("0"));
Next I
Print
For I As Integer = 10 To 13
    Print Str((*pz)[I] - Asc("0"));
Next I
Print
For I As Integer = 10 To 13
    Print Str(pz[I] - Asc("0"));     '' pz[I] is converted to an Ubyte reference
Next I
Print

Sleep

Output:
FreeBASIC
FreeBASIC 2018
2018
2018
2018
See also

  • String Functions
  • Standard Data Type Limits


Coercion and Conversion
Transfer the data of a given type variable to a variable of another type requires conversion.

Coercion of Numeric Data Types in Expressions and Arguments

For the integer types, the rule depends on the following ranking:
in 32bit:
byte < ubyte < short < ushort < long < integer < ulong < uinteger < longint < ulongint
in 64bit:
byte < ubyte < short < ushort < long < ulong < longint < integer < ulongint < uinteger
First, in accordance to the above ranking, all arguments smaller than Integer are converted to Integer.
Then, if arguments have different size, the smaller argument is converted to match the bigger one. Next, if arguments have different signedness, the signed argument is converted to unsigned to match the other one.
Finally, the (U)Integer types replace:
in 32bit:
the (U)Long types
in 64bit:
the (U)Longint types

On the other hand, if one of the arguments is a Single or a Double, both arguments are converted and/or promoted to Double.

Conversion of Numeric Data Types

A type conversion will occur implicitly when an expression or variable is assigned, passed as a parameter to a procedure, or returned as a result from a procedure. Conversions may also be explicit when using CAST or one of the built-in conversion functions (among standard functions).

Integer To Integer, any combination of Signed and Unsigned
    • Any integer type to a smaller integer type: least significant bits are retained
    • Any integer type to a larger integer type: sign extended to fill most significant bits
Integer to Single or Double
    • Possible loss of precision
Double to Single
    • Possible loss of precision
    • If the value of the Double exceeds the range of a Single result is +/- INF
Double or Single to Integer
    • Possible loss of precision
    • If the value of the floating point number exceeds the range of the target type are results are undefined. A run-time error is not raised.

Conversions using User Data Type constructors and operators

For conversion between built-in types (among standard types like between numeric types as above or between string types), the compiler knows what to do without the need for instructions from user.
This is called the implicit internal conversion (or coercion).

When one of the two types is at least a UDT (User Defined Type), the user has to code some UDT procedures to define how do the conversion.
Then, the conversion execution can be explicit if the user specifies what UDT procedure must be used, or implicit if the user leaves the choice to compiler.

In the world of UDTs, conversions can be controlled by means of three member procedures:
    • Single-argument constructor: allow conversion from a particular type to initialize an object.
    • Assignment operator: allow conversion from a particular type on assignment.
    • Type-cast operator: allow conversion to a particular type.

For a construction with implicit initialization ('Dim As UDT u = v'), the compiler searches by priority:
    • Firstly a matched constructor (for u type from v type).
    • Secondly a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
    • Thirdly finally a matched cast operator (for v type to u type).
(a matched let operator (for u type from v type) is not searched by compiler on a construction with implicit initialization)

For an implicit copy-construction ('Byval As u_type') when passing a v type parameter, the compiler searches by priority:
    • Firstly a matched constructor (for u type from v type).
    • Secondly a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
    • Thirdly finally a matched cast operator (for v type to u type).
(a matched let operator (for u type from v type) is not searched by compiler on a construction with implicit initialization)

For an implicit reference-passing ('Byref As u_type') when passing a v type parameter, the compiler searches by priority:
    • Firstly a matched cast operator (for v type to u type).
    • Secondly a matched constructor (for u type from v type).
    • Thirdly finally a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
(a matched let operator (for u type from v type) is not searched by compiler on an implicit reference-passing)

For an implicit assignment ('u = v'), or an implicit return from function by assigning ('Function = v') with function returning u type, the compiler searches by priority:
    • Firstly a matched let operator (for u type from v type).
    • Secondly a cast operator (for v type to special type (*)) and its matching let operator (for u type from special type (*)).
    • Thirdly a matched cast operator (for v type to u type).
    • Fourthly finally a matched constructor (for u type from v type) and an explicit copy-let operator (u type).

For an implicit return from function by exiting immediately ('Return v') with function returning u type, the compiler searches by priority:
    • Firstly a matched constructor (for u type from v type).
    • Secondly a cast operator (for v type to special type (*)) and its matching conversion-constructor (for u type from special type (*)).
If an explicit copy-constructor (u type) exists:
    • Thirdly a matched cast operator (for v type to u type).
    • Fourthly finally a matched let operator (for u type from v type).
Else (an explicit copy-constructor (u type) does not exist):
    • Thirdly a matched let operator (for u type from v type).
    • Fourthly finally a matched cast operator (for v type to u type).

special type (*) : pointer or string, or UDT if there is no explicit copy-constructor / explicit copy-assignment operator for u type

Note:
Only the member procedures directly impacted in the conversion process itself are cited above:
- the copy-constructor (u type) and the copy-let operator (u type) are not cited when only their implicit definition is sufficient and not their explicit definition,
- the default constructor (u type) is not cited whereas an implicit version, otherwise explicit version if another explicit constructor (u type) is defined, is always required in the 'Function = v' case, but not for the conversion process itself.

Example

Very simple syntaxic example highlighting the conversion capabilities by using Cast operator (explicit and implicit), Constructor (explicit and implicit) and Let operator (implicit):
Type UDT
  Dim As Integer I
  Declare Operator Cast () As Integer
  Declare Constructor (ByVal I0 As Integer)
  Declare Operator Let (ByVal I0 As Integer)
End Type

Operator UDT.Cast () As Integer
  Return This.I
End Operator

Constructor UDT (ByVal I0 As Integer)
  This.I = I0
End Constructor

Operator UDT.Let (ByVal I0 As Integer)
  This.I = I0
End Operator

Dim As Integer J = 12
Dim As UDT u1 = UDT(J)  '' construction with explicit initialization using the defined "Constructor(Byval As Integer)" operator
Print u1.I
Dim As UDT u2 = J       '' construction with implicit initialization by compiler using the defined "Constructor(Byval As Integer)" operator
Print u2.I
Print

u1.I = 34
J = Cast(Integer, u1)  '' explicit assignment using the defined "Cast() As Integer" operator
Print J
Dim As Integer K
K = u1                 '' implicit assignment by compiler using the defined "Cast() As Integer" operator
Print K
Print

J = 56
u1 = J                 '' implicit assignment by compiler using the defined "Let(Byval As Integer)" operator
Print u1.I
Print

Sleep

See also

  • Standard Data Types
  • Variable Types
  • Casting and Conversion Functions
  • User Defined Types


Constants
Constants are identifiers that represent literals or constant expressions that can not be changed after they are defined.

For example, Const PI = 3.1415926535897932 will always mean that the identifier PI refers to the number 3.1415926535897932. The identifier PI can be used instead of repeating the full number in source.

Global constants once defined, are available globally and the identifier can be used to refer to the constant anywhere in the source program.

After an identifier is defined with the Const statement, the symbol cannot be altered. If code tries to alter a constant, an error message will result upon code compilation.

Built-in constants include the Boolean data type values True and False.

A constant definition differs from a variable definition. Variables hold values that can be changed.

Example

Declare Sub PrintConstants ()

Const FirstNumber = 1
Const SecondNumber = 2
Const FirstString = "First string."
Const FirstBoolean = False
Const SecondBoolean = True

Print FirstNumber, SecondNumber 'This will print 1      2
Print FirstString 'This will print First string.
Print FirstBoolean, SecondBoolean 'This will print false      true
Print

PrintConstants ()

Sub PrintConstants ()
    Print FirstNumber, SecondNumber 'This will also print 1        2
    Print FirstString 'This will also print First string.
    Print FirstBoolean, SecondBoolean 'This will also print false      true
End Sub

See also

  • Constant Expressions
  • Literals
  • Const
  • Enum


Variables
Symbols representing data in memory.

Description

Variables are name symbols which can be manipulated. They are declared and referenced using names composed of letters, numbers, and character "_". These reference names cannot contain most other symbols because such symbols are part of the FreeBASIC programming language. They also cannot contain spaces. See Indentifier Rules.

In FreeBASIC, variables can be defined using the Dim statement.

Variables are available for later access depending on where and how the Dim declaration for that variable is given. Depending on the scope of a variable, a defined variable can be available within the main area of a program, within a procedure, through an entire module, or through out an entire program. See Variable Scope.

Variables are also made available when they are passed as parameters to a procedure such as Function or Sub.

After a variable is declared with the Dim statement, they can be assigned, passed to procedures, and used in expressions wherever their Standard Data Type is similar. Sometimes variables are automatically converted to other data types before being used in expressions, or passed as parameters to procedures. See Coercion and Conversion.

Example

' compile with -lang qb or fblite

'$lang: "qb"

Declare Sub PrintConstants()

Dim FirstNumber As Integer
Dim Shared SecondNumber As Integer

FirstNumber = 1
SecondNumber = 2

PrintConstants ()
Print FirstNumber, SecondNumber, ThirdNumber 'This will print 1 2 0

Sub PrintConstants ()
    Dim ThirdNumber As Integer
    ThirdNumber = 3
    Print FirstNumber, SecondNumber, ThirdNumber 'This will print 0 2 3
End Sub

See also

  • Coercion and Conversion
  • Dim
  • Identifier Rules
  • Variable Scope


Arrays
Multi-dimensional container types.

Overview

Arrays are special kinds of variables which act as containers for a number of values, or elements. An array can store elements of any type, and all of its elements share the same type. For example, an array can store Integer elements or Single elements, but not both. These elements are accessed--read from or written to--through an Integer value representing their position in the array. Arrays have lengths, or sizes, which are equal to the number of elements they are storing at any given time. Fixed-length arrays have constant sizes throughout their lifetimes, while the sizes of variable-length arrays can change dynamically.

Elements and positions

The values that an array stores are its elements. Each element of an array has a corresponding position, which is an Integer value ranging from the array's lower bound to its upper bound, inclusive. These positions are used to access individual elements in the array using Operator (), which takes a position and returns a reference to the element at that position. A valid position in an array is greater than or equal to its lower bound, and less than or equal to its upper bound.
' Create an array of 3 elements all having the value zero (0.0f).
Dim array(1 To 3) As Single

' Assign a value to the first element.
array(1) = 1.2

' Output the values of all the elements ("1.2 0 0").
For position As Integer = 1 To 3
    Print array(position)
Next

Sizes and bounds

The size of an array is equal to the number of elements it stores at any given time. An array can have a size of zero (0), meaning it's not storing any values at the moment--it's empty. If an array's size is greater than zero, that many elements are being stored. An array's size is equal to one more than the difference between its upper and lower bounds, or UBound(array) - LBound(array) + 1.

The lower and upper bounds not only determine the size of an array, but also the valid positions of individual elements. For example, an array with a lower bound of zero (0) and an upper bound of four (4), stores five (5) elements, the first element being at position 0, the last at position 4. These bounds may be specified when the array is declared, or, for some arrays, changed by resizing the array. An array's lower and upper bounds can be retrieved using LBound and UBound, respectively.

When creating or resizing an array, if a lower bound is not specified it defaults to zero (0).
' Declares and initializes an array of four integer elements.
Dim array(3) As Integer = { 10, 20, 30, 40 }

' Outputs all of the element values (" 10 20 30 40").
For position As Integer = LBound(array) To UBound(array)
    Print array(position) ;
Next

Fixed-length and variable-length

There are two fundamental kinds of arrays: fixed-length and variable-length. The primary difference between the two is that the bounds of fixed-length arrays can never change, that is, they always store the same number of elements in the same positions. Variable-length array bounds can be changed, affecting the number of elements stored and/or the positions of the elements.

Since fixed-length arrays never change size, the compiler chooses to make room for--or, allocate--the memory for the array elements either in static storage or on the program stack, depending on the array's storage class. This can be an advantage, since the cost of creating these kinds of arrays doesn't include any adverse run-time penalty. Fixed-length arrays are declared using Extern, Static and Dim. At least an upper bound must be specified, and all bounds must be compile-time constant values, such as numeric literals, Const variables or Enum enumerators.

Variable-length arrays can change in size, so the compiler chooses to allocate the memory for the array elements at run-time, in the free store. The advantage here of course is being able to dynamically resize the arrays, however, run-time performance could vary when they are created, resized or destroyed. Variable-length arrays are declared using Extern, Static, Dim and ReDim. When using Extern, Static or Dim, the lower and upper bounds can be left unspecified--resulting in an empty array--or either one must have a variable value, such as an Integer variable or Function result. ReDim can be used to resize an existing variable-length array, by giving it different lower and/or upper bounds.
' Creates a fixed-length array that holds 5 single elements.
Const totalSingles = 5
Dim flarray(1 To totalSingles) As Single

' Creates an empty variable-length array that holds integer values.
Dim vlarray() As Integer

' Resizes the array to 10 elements.
ReDim vlarray(1 To 10)

Multi-dimensional arrays

The arrays discussed so far have been one-dimensional, that is, the elements are accessed through a single position. One-dimensional arrays can be thought of as a simple row of elements. Arrays can also have more than one dimension; an individual element of the array is accessed using two or more positions. Two-dimensional arrays use two positions--a row and a column position--to refer to individual elements, like a grid or table. Three-dimensional arrays use three positions--a row, column and perhaps depth position--to refer to individual elements, like a cube. Four-dimensional arrays can be thought of as one or more three-dimensional arrays, and so on. Multi-dimensional arrays are declared just like one-dimensional arrays, except that more than one lower and upper bound range is specified.
The total number of elements and the total size (in bytes) of a multi-dimensional array can be directly obtained using Arraylen and Arraysize, respectively.
' Take Care while initializing multi-dimensional array
Dim As Integer multidim(1 To 2,1 To 5) = {{0,0,0,0,0},{0,0,0,0,0}}

Detailed use of LBound and UBound

LBound / UBound returns the lower / upper values of size information for an array: the dimension of the array (number of usable indexes) and the index bounds for each dimension. The array name is specified in the first parameter, and the dimension selection in the second parameter.
These sizing information are available both for a fixed-length array (sizing fixed at compile-time) and for a variable-length array (some sizing adjustable during run-time).

- Usage:
result = (L|U)Bound( array [, dimension ] )
with as parameters:
array : array name, without parentheses
dimension : dimension number to get the bound, ordered from left to right compared to index position when accessing array
(1 = first dimension)

- Return value:
For the specific dimension value '0':
LBound returns always '1', and UBound returns the number of dimensions
if the array is not fully sized (declared as 'array( )' or 'array( Any [, Any...] )'), UBound returns '0'

For any valid dimension value of the array (from 1 to the last one):
(L|U)Bound returns the lowest|highest value that can be used as an index for the specified dimension
if the array is not fully sized (declared as 'array( )' or 'array( Any [, Any...] )'), LBound returns '0' and UBound returns '-1'

For any other dimension value (dimension number not relevant):
LBound returns always '0', and UBound returns always '-1'

Summarizing table example:
Array definition |      array( )       | array( Any , Any )  | array( 4 , 5 To 9 ) |
Dimension nb (x) |                     |        (1)   (2)    |       (1)    (2)    |
-----------------|---------------------|---------------------|---------------------|
Lbound(array, 0) |      1              |      1              |      1              |
Ubound(array, 0) |      0              |      0 (but not 2!) |      2              |
-----------------|---------------------|---------------------|---------------------|
Lbound(array, 1) |      0              |      0              |      0 (by default) |
Ubound(array, 1) |     -1              |     -1              |      4              |
-----------------|---------------------|---------------------|---------------------|
Lbound(array, 2) |      0              |      0              |      5              |
Ubound(array, 2) |     -1              |     -1              |      9              |
-----------------|---------------------|---------------------|---------------------|
Lbound(array, k) |      0              |      0              |      0              |
Ubound(array, k) |     -1              |     -1              |     -1              |
  for k<0 or k>2 |                     |                     |                     |
-----------------|---------------------|---------------------|---------------------|
- Use cases:
So UBound( array , 0 ) value allows to easily determine if an array is sized (value > 0) or not (value = 0), and if yes the number of dimensions (the positive value).
Remark:
( ( LBound( array ) = 0 ) AND ( UBound( array ) = -1 ) )
OR:
( @array( LBound( array ) ) = 0 ) - Note: No longer supported since fbc 1.20.0
would allow to only know that the array is un-sized.

Once the number of dimensions is determined, the information on the two boundaries of each dimension is safely accessible.

Example of a macro that displays all the sizing information for an array:
#macro PRINT_ARRAY_SIZING (array)
    If UBound( array , 0 ) = 0 Then
        Print "'" & #array & "' un-sized"
    Else
        Print "'" & #array & "' sized with " & UBound( array , 0 ) & " dimension";
        If UBound( array , 0 ) > 1 Then
            Print "s";
        End If
        Print
        For I As Integer = 1 To UBound( array , 0 )
            Print "   dimension nb: " & I
            Print "      lower bound: " & LBound( array , I )
            Print "      upper bound: " & UBound( array , I )
        Next I
    End If
#endmacro

Dim As Integer array1( )
PRINT_ARRAY_SIZING( array1 )
Print

Dim As Single array2( Any )
PRINT_ARRAY_SIZING( array2 )
Print

Dim As String array3( 4 , 5 To 9 )
PRINT_ARRAY_SIZING( array3 )
Print

Type UDT
    Dim As Double array4( Any, Any, Any )
End Type

Dim As UDT u
PRINT_ARRAY_SIZING( u.array4 )
Print

ReDim u.array4( -7 To -3, -2 To 5, 6 To 9 )
PRINT_ARRAY_SIZING( u.array4 )
Print

Erase u.array4
PRINT_ARRAY_SIZING( u.array4 )
Print

Sleep

Output:
'array1' un-sized

'array2' un-sized

'array3' sized with 2 dimensions
   dimension nb: 1
      lower bound: 0
      upper bound: 4
   dimension nb: 2
      lower bound: 5
      upper bound: 9

'u.array4' un-sized

'u.array4' sized with 3 dimensions
   dimension nb: 1
      lower bound: -7
      upper bound: -3
   dimension nb: 2
      lower bound: -2
      upper bound: 5
   dimension nb: 3
      lower bound: 6
      upper bound: 9

'u.array4' un-sized
- Notes:
The maximum number of dimensions of a multidimensional array is 8.

At the contrary to QB, FB stores the multidimensional arrays data in this definite order: values differing only in the last index are contiguous in memory (row-major order).

An array declared with 'Any' instead of the bound fields is considered presently to be fully un-sized from UBound ('UBound( array , 0 ) = 0'), while at least its number of dimensions is already fixed (and locked).
Moreover this value of the number of dimensions is well set in the array descriptor.
In this case, UBound( array , 0 ) could return this value while remaining with LBound (array , 0 ) = 0 and UBound( array , 0 ) = -1.
A bug report (#853) has been filled in, about this behavior.

See also

  • Fixed-length Arrays
  • Variable-length Arrays
  • Array Indexing
  • Passing Arrays to Procedures
  • Variable Scope


Fixed-length Arrays
Fixed-size homogeneous data structures.

Overview

Fixed-length arrays are arrays that have a fixed constant size throughout the execution of a program. The memory used by a fixed-length array to store its elements is allocated at compile-time, either on the stack or in the .BSS or .DATA sections of the executable, depending on whether Static (or Shared) was used to define it. This may allow for quicker program execution since the memory for the array is already allocated, unlike variable-length arrays, whose element memory isn't allocated until runtime.

Fixed-length arrays with automatic storage, have their elements allocated on the program stack, and pointers to these elements remain valid only while the array is in scope. The elements of fixed-length arrays with static storage are allocated in the .DATA or .BSS sections of the executable, depending on whether or not they are initialized when defined, so pointers to these elements remain valid for the entire execution of the program. Fixed-length arrays of any storage class cannot be resized during program execution, only variable-length arrays can.

Fixed-length arrays may also be used as data members inside user-defined types, in which case the array is directly allocated as part of the user-defined type structure.

Declaration

A fixed-length array is declared with either the Dim or Static keywords, followed by a variable identifier, a parenthesized list of boundaries and an element data type.

'' Defines a one-dimensional fixed-length array of type INTEGER having automatic storage.
Dim arrayOfIntegers(69) As Integer

'' Defines a one-dimensional fixed-length array of type SHORT having static storage.
Static arrayOfShorts(420) As Short

There are various ways to specify an array's amount of elements. Each array can have between 1 or 8 dimensions. Each dimension has a lower bound and an upper bound.

Dim a(1) As Integer  '' 1-dimensional, 2 elements (0 and 1)
Dim b(0 To 1) As Integer  '' 1-dimensional, 2 elements (0 and 1)
Dim c(5 To 10) As Integer  '' 1-dimensional, 6 elements (5, 6, 7, 8, 9 and 10)

Dim d(1 To 2, 1 To 2) As Integer  '' 2-dimensional, 4 elements: (1,1), (1,2), (2,1), (2,2)
Dim e(9, 9, 9, 9) As Integer '' 4-dimensional, 10 * 10 * 10 * 10 elements

For an array to be declared fixed-length, the boundaries must be specified using only number literals or Const values or Enum constants.

Const myLowerBound = -5
Const myUpperBound = 10

'' Declares a one-dimensional fixed-length array, holding myUpperBound - myLowerBound + 1 String objects.
Dim arrayOfStrings(myLowerBound To myUpperBound) As String

'' Declares a one-dimensional fixed-length array of bytes,
'' big enough to hold an INTEGER.
Dim arrayOfBytes(0 To SizeOf(Integer) - 1) As Byte

Declaration with initializer

The fixed-length arrays may be given values at the time of their declaration by following the array declaration with an initializer (variable-length arrays declarations can't use initializers).
These initializing array values are given in comma-delimited values enclosed by curly brackets. These methods of initializing array values can be nested within one another for complex assignments, allowing for arrays of any dimension to be initialized.

'' Declare an array of 2 by 5 elements followed by an initializer
Dim array(1 To 2, 1 To 5) As Integer => {{1, 2, 3, 4, 5}, {1, 2, 3, 4, 5}}

Any upper bound can be an ellipsis "..." (3 dots). This will cause to upper bound to be set automatically based on the number of elements found in the initializer. When ellipsis is used in this manner, an initializer must be used.

'' Declare (with one ellipsis) an array of 2 by 5 elements followed by an initializer
Dim array(1 To 2, 1 To ...) As Integer => {{1, 2, 3, 4, 5}, {1, 2, 3, 4, 5}}

'' Declare (with two ellipsis) an array of 2 by 5 elements followed by an initializer
Dim array(1 To ..., 1 To ...) As Integer => {{1, 2, 3, 4, 5}, {1, 2, 3, 4, 5}}


Variable-length Arrays
Resizable homogeneous data structures. Also known as "dynamic arrays".

Overview

Variable-length arrays are arrays that can, during program execution, either be resized to hold more or less elements, or have their dimension[s] use a different subscript range. The memory used by a variable-length array to store its elements is allocated at runtime in the heap, as opposed to fixed-length arrays whose data is either allocated on the program stack or in the .BSS or .DATA sections of the executable, depending on whether they were defined with Static (or Shared).

Variable-length arrays may also be used as data members inside user-defined types. As opposed to fixed-length arrays though, the array is not allocated as part of the user-defined type structure, because user-defined types cannot be variable-length. Instead, the user-defined type only contains the array descriptor that is used to hold and access the variable-length array behind the scenes, and the array is still allocated on the heap, as with variable-length array variables.

Variable-length arrays are often called "dynamic arrays" because their size can change dynamically at runtime, instead of being fixed-size.

Declaration

A variable-length array is declared with either the Dim or ReDim keywords, followed by a variable identifier, a parenthesized list of boundaries and an element data type. For an array to be declared variable-length, it must be declared with unknown boundaries, or with variable (non-constant) boundaries. ReDim always defines variable-length arrays, whether the specified boundaries are constant or not.

'' Declares a one-dimensional variable-length array of integers, with initially 2 elements (0 and 1)
ReDim a(0 To 1) As Integer

'' Declares a 1-dimensional variable-length array without initial bounds.
'' It must be resized using Redim before it can be used for the first time.
Dim b(Any) As Integer

'' Same, but 2-dimensional
Dim c(Any, Any) As Integer

Dim myLowerBound As Integer = -5
Dim myUpperBound As Integer = 10

'' Declares a 1-dimensional variable-length array by specifying variable (non-constant) boundaries.
'' The array will have myUpperBound - myLowerBound + 1 elements.
Dim d(myLowerBound To myUpperBound) As Integer

'' Declares a variable-length array whose amount of dimensions will be determined
'' by the first Redim or array access found. The array has no initial bounds and must
'' be resized using Redim before it can be used for the first time.
Dim e() As Integer

Resizing

Resizing a variable-length array refers to "redefining" the array with different boundaries, allowing the array to grow or shrink. Elements outside the new subscript range[s] are erased; object elements will be destroyed. If the array is resized to a larger size, new elements are added initialized with a zero or null value; object elements are default-constructed. Variable-length arrays are resized using the ReDim keyword following the same form as definition. In this case the element data type may be omitted from the ReDim statement.

'' Define an empty 1-dimensional variable-length array of SINGLE elements...
Dim array(Any) As Single

'' Resize the array to hold 10 SINGLE elements...
ReDim array(0 To 9) As Single

'' The data type may be omitted when resizing:
ReDim array(10 To 19)

Resizing an array cannot change its amount of dimensions, but only the boundaries of each dimension.

By default, element values of a variable-length array are lost when resized. To retain the previous element values during a resize, use the Preserve keyword.

See also

  • Fixed-length Arrays
  • Passing Arguments to Procedures
  • Passing Arrays to Procedures
  • ... (Ellipsis)


Array Index
An array index is the number used to access an Array of Variables created using the Dim command.

Description

The following examples illustrate the use of array elements.

If we have an array myArray with elements of 1 to 10, filled with random data:
Index        Data
1              5
2              2
3              6
4              5
5              9
6              1
7              0
8              4
9              5
10             7

One can access each piece of data separately by pointing to the Index of the array element:
    Print myArray(5)

Printing the data contained in the fifth element of myArray results in an output of:
    9
To change the contents of an array, use it like any other Variable:
    myArray(3) = 0

To print the contents of myArray(3), use the command:
    Print myArray(3)

Which results in an output of:
    0
Array elements can be indexed using another Variable. In this example we set all elements in our array to zero:
    Dim a As Integer
    For a = 1 To 10
      myArray(a) = 0
    Next a

To change a random array element to a random value:
    Dim Index As Integer
    Dim Value As Integer
    index = Int(Rnd(1) * 10) + 1 'This line will simply return a random value between 1 and 10
    Value = Int(Rnd(1) * 10) + 1 'This line will do the same
    myArray(index) = Value

Example

Declare Sub PrintArray()

Dim Numbers(1 To 10) As Integer
Dim Shared OtherNumbers(1 To 10) As Integer
Dim a As Integer

Numbers(1) = 1
Numbers(2) = 2
OtherNumbers(1) = 3
OtherNumbers(2) = 4

PrintArray ()

For a = 1 To 10
 Print Numbers(a)
Next a

Print OtherNumbers(1)
Print OtherNumbers(2)
Print OtherNumbers(3)
Print OtherNumbers(4)
Print OtherNumbers(5)
Print OtherNumbers(6)
Print OtherNumbers(7)
Print OtherNumbers(8)
Print OtherNumbers(9)
Print OtherNumbers(10)

Sub PrintArray ()
 Dim a As Integer
 For a = 1 To 10
   Print otherNumbers(a)
 Next a
End Sub

See also

  • Arrays Overview
  • Dim
  • Function
  • Sub
  • Variables
  • Variable Scope
  • Standard Data Type Limits


Passing Arrays to Procedures
Declaring/defining array parameters and passing array arguments to procedures

Syntax for array symbol name

As parameter in procedure declaration:
( [ Any [, Any...] ] ) As datatype
array_name( [ Any [, Any...] ] ) As datatype
As parameter in procedure definition:
array_name( [ Any [, Any...] ] ) As datatype
As argument in procedure call:
array_name()

Parentheses (even empty) are mandatory to specify that the parameter/argument is an array.

Usage of array symbol name

When declaring procedure with array parameter:
Declare { Sub | Function } proc_name ... ( ( [ Any [, Any...] ] ) As datatype , ... ) [ ... ]
Declare { Sub | Function } proc_name ... ( array_parameter_name( [ Any [, Any...] ] ) As datatype , ... ) [ ... ]
When defining procedure with array parameter:
{ Sub | Function } proc_name ... ( array_parameter_name( [ Any [, Any...] ] ) As datatype , ... ) [ ... ]
When passing array argument to procedure:
sub_name ( array_argument_name() , ... )
sub_name array_argument_name() , ...
funct_name ( array_argument_name() , ... )
funct_name array_argument_name() , ...
... funct_name ( array_argument_name() , ... ) [ ... ]

Note (same rules as for any parameter/argument list):
When declaring/defining a procedure, parentheses surrounding a non empty parameter list are required.
When calling a subroutine, parentheses surrounding an argument list (empty or non empty) are optional.
When calling a function as a subroutine (without using the return variable), the same rules as for subroutine apply.
When calling a function in an expression (which uses the return value), parentheses surrounding a non empty argument list are required.
But it is a common convention to always use parentheses (empty or non empty) after the procedure name, to signify a procedure call.

Description

Array parameter can not have a ByVal or ByRef keyword before them, because arrays don't get passed the same way as normal parameters. While variables get passed by value or by reference, arrays get passed by descriptor (see below). In fact when an array is passed to a procedure, it is a reference to its descriptor which is passed.
All the elements of a passed array can be modified, and those changes are reflected at the calling level. Perhaps ByRef should be allowed by similarity with variable-length string (which are also passed by descriptor).

There is no direct possibility of passing an array by value. Declaring a passed array parameter As Constant only forbids any modification in the procedure body. A workaround would be to pass a user copy of the array.

Note:
A function return can not be an array type variable.
A fixed-length String array can not be passed to a procedure.
A ZString/WString array can not be simply passed to a procedure because the zstring/wstring size is not passed (the "as zstring/wstring * 1" type is taken by default by compiler in procedure body). It is a bug at the moment because compiler just fails silently while it badly computes in the procedure body the address of each array element (except the first obviously).

Array descriptor (for information purposes only):
For a fixed-length array, the descriptor is only used for passing the array to a procedure (otherwise the compiler does not use it because knowing the fixed characteristics of the array).
For a variable-length array, the descriptor is always used (the array descriptor is the only defining the array characteristics).

The array descriptor has the following structure (each item is coded on an 'Any Ptr' or an 'Uinteger', except on an 'Integer' for lbound and ubound):
- pointer to the real or virtual element: @array(0, 0, ...)
- pointer to the first real element: @array(lbound1, lbound2, ...)
- "global" size in bytes: (ubound1 - lbound1 + 1) * (ubound2 - lbound2 + 1) * ... * (size of 1 element in bytes)
- size of one element in bytes
- number of dimensions
- flags(*): array points to fixed-length memory (1-bit) / array has nb of dimensions defined at first declaration / nb of dimensions allocated in descriptor (4-bit)
then for each dimension:
- number of elements: (ubound - lbound + 1)
- lbound
- ubound

(*): Additional member field (an 'Uinteger') for fbc versions >= 1.08
(see the include file ./inc/fbc-int/array.bi for fbc versions >= 1.07)

For more information, see Fbarray (Array Descriptor Structure And Access).

Example

Declare Sub splitString(ByVal As String, (Any) As String, ByVal As UByte = Asc(","))

Dim As String s = "Programmer's Guide/Variables and Datatypes/Arrays/Passing Arrays to Procedures"
Dim As String array(Any)

splitString(s, array(), Asc("/"))

Print "STRING TO SPLIT:"
Print s
Print
Print "RESULT ARRAY FROM SPLITTING:"
For i As Integer = LBound(array) To UBound(array)
    Print i, array(i)
Next i

Sleep

Sub splitString(ByVal source As String, destination(Any) As String, ByVal delimitor As UByte)
    Do
        Dim As Integer position = InStr(1, source, Chr(delimitor))
        ReDim Preserve destination(UBound(destination) + 1)
        If position = 0 Then
            destination(UBound(destination)) = source
            Exit Do
        End If
        destination(UBound(destination)) = Left(source, position - 1)
        source = Mid(source, position + 1)
    Loop
End Sub

See also

  • Passing Arguments to Procedures
  • Calling Conventions
  • Returning Values


Pointers
Data types whose values are addresses in memory.

Declaration

Pointers are Variables whose values are addresses in memory, and they are said to 'point' to this memory. The type of data that is pointed to depends on the type of pointer (an Integer Pointer points to Integer data). Pointers are declared like any other variable, with the suffix "pointer" or "ptr" following the type name.

Assigning pointer variables

A pointer is a memory address and the value of the pointer itself is that memory address. To assign a pointer variable is to assign it a memory address to something. One way to assign a pointer a memory address is to take the address of some other variable in the program using Operator @ (Address of).

Accessing pointed to data

The data pointed to by a pointer can be accessed with Operator * (Value of). This operator returns a reference to the data that its operand points to. The following,

Dim myInteger As Integer = 10
Dim myPointer As Integer Pointer = @myInteger
*myPointer = 20
Print myInteger

defines an Integer variable called myInteger and an Integer pointer called myPointer that points to the location in memory where myInteger is stored. Operator @ (Address of) is used to retrieve the address of myInteger. The value of 20 is assigned to the location at which myPointer points - the address of myInteger, or @myInteger. Changes to *myPointer directly affect the value of myInteger (the expression "*myPointer" is the same thing as "myInteger").

Pointers to user-defined types

Pointers to user-defined types are defined and used like all other pointers. Accessing a member of a Type or Class requires one of the following two methods:

Type myType
    a As Integer
    b As Double
End Type

Dim x As myType
Dim p As myType Pointer = @x

'' 1) dereference the pointer and use the member access operator:
(*p).a = 10
(*p).b = 12.34

'' 2) use the shorthand form of the member access operator:
Print p->a
Print p->b

The first method uses Operator . (Member Access). This operator accesses members from references, so the pointer is dereferenced first. The member access operator has higher priority over the dereference operator, so parenthesis are needed to dereference the pointer before using it with the member access operator.

The second method uses Operator -> (Pointer To Member Access). This operator accesses members from pointers, which are automatically dereferenced. This can make code a little clearer, although both forms produce identical results.

See also

  • Operator @ (Address Of)
  • Operator * (Value Of)
  • Operator . (Member Access)
  • Operator -> (Pointer To Member Access)
  • VarPtr
  • StrPtr
  • ProcPtr


Pointer Arithmetic
Manipulating address values mathematically.

Overview
Adding and subtracting from pointers
Incrementing and decrementing pointers
Distance between two pointers

Overview

It is often useful to iterate through memory, from one address to another. Pointers are used to accomplish this. While the type of a pointer determines the type of variable or object retrieved when the pointer is dereferenced (using Operator * (Value Of)), it also determines the distance, in bytes, its particular type takes up in memory. For example, a Short takes up two (2) bytes in memory, while a Single needs four (4) bytes.

Adding and subtracting from pointers

Pointers can be added to and subtracted from just like a numeric type. The result of this addition or subtraction is an address, and the type of pointer determines the distance from the original pointer.

For example, the following,

Dim p As Integer Ptr = New Integer[2]

*p = 1
*(p + 1) = 2

will assign the values "1" and "2" to each integer in the array pointer to by p. Since p is an Integer Pointer, the expression "*(p + 1)" is saying to dereference an Integer four/eight (4/8 on 32/64bit systems) bytes from p; the "1" indicates a distance of "1 * the size of an Integer", or four/eight (4/8 on 32/64bit systems) bytes.

Subtraction follows the exact same principle. Remember, a - b = a + -b.

Incrementing and decrementing pointers

Sometimes it is more convenient to modify the pointer itself, in which case the combination addition and subtraction operators will work just like above. For example, the following,

Dim array(5) As Short = { 32, 43, 66, 348, 112, 0 }
Dim p As Short Ptr = @array(0)

While (*p <> 0)
    If (*p = 66) Then Print "found 66"
    p += 1
Wend

iterates through an array until it finds an element with the value of "0". If it finds an element with the value "66" it displays a nice message.

Distance between two pointers

The distance between two pointers is retrieved with Operator - (Subtract), and is measured in values, not bytes. For example, the following,

Type T As Single

Dim array(5) As T = { 32, 43, 66, 348, 112, 0 }
Dim p As T Ptr = @array(0)

While (*p <> 0)
    p += 1
Wend
Print p - @array(0)

will output "5" regardless of what type T is. This is because there is a five (5) element difference between the first element of array (32) and the element pointed to by p (0).

Specifically, if a and b are both pointers of type T, the distance between them is the number of bytes between them, divided by the size, in bytes, of T, or

( cast(byte ptr, a) - cast(byte ptr, b) ) / sizeof(T)

See also

  • Operator + (Add)
  • Operator - (Subtract)
  • Operator @ (Address Of)
  • Operator * (Value Of)
  • Pointer Operators


From Pointers to References
References do not fully offer the same capabilities as Pointers, but using them instead can simplify coding.

Preamble:

References and pointers are closely related.
Indeed, a variable and its different references hold the same address, since they allow access to the same object.

The reference encapsulates the manipulation of the address of the object and is used as a dereferenced pointer.
The difference lies here in the fact that one does not have to perform the dereferencing.

References are much easier to handle than pointers, so they make code much safer.

Reminder on pointers

An address is a value. User can store this value in a variable. Pointers are precisely variables that contain the address of other objects, for example the address of another variable.
The value of a pointer may change. This does not usually mean that the pointed variable is moved to memory, but rather that the pointer points to something else.
In order to precise what is pointed by a pointer, the pointers have a type. This type is usually constructed from the type of the pointed object. This allows the compiler to verify that the manipulations made in memory via the pointer are valid.

The most common use of pointers is when sent in the parameters of a procedure. They make it possible to manipulate in a simple way data which can be important (instead of providing to a procedure a very big data block, one could for example provide a pointer to this one).
Another use is when doing dynamic allocation in memory, with the '[C]Allocate' and 'New' keywords which return the address of the memory that has been allocated. This address must be stored somewhere and know how to use it.
Finally, pointers can also be used to manipulate tables, but their interest is lower with FB because user has the capacity to declare static and dynamic arrays and to use the dedicated '()' (Array Index) operator to access their elements.

It is also possible to create pointers to procedures, and to use these pointers to parameterize an algorithm, the behavior of which will depend on the procedures thus pointed out.

It is very important to make sure that the pointers that user is manipulating are all initialized (that is, they contain the address of a valid object, not just anything). Indeed, to access a variable by means of an uninitialized pointer amounts to read or, more seriously, to write in the memory at a completely random place (according to the initial value of the pointer at the time of its creation).
In general, pointers are initialized as soon as they are created, or if they are to be used later, they are initialized with the null value. This will allow future tests on the validity of the pointer or at least to detect errors. Indeed, using a pointer with a null value to access a variable can often generate a program protection fault, but will always generate an error message at run-time if the program was compiled with the option '-exx'.

With the dereferencing operators '*' and '[]', it is possible to access the variables from pointers.
The 'Any Ptr' pointers are a special type of pointer. They can point to a variable of any type. You can not use the '*' and '[]' dereferencing operators on an 'Any Ptr' pointer. It must first be converted into a pointer of a given type.
Similarly, a typed pointer can also be converted into a pointer of any other type (if necessary converting first through an 'Any Ptr' pointer).
For object pointers ('po'), it's more convenient to use the single '->' operator rather than combining the "*" and "." to access object members (use 'po->member' instead of '(*po).member').

A pointer can also be declared with a type different but compatible with the address types of objects to be pointed (as a simple example, 'Zstring Ptr' and 'Ubyte Ptr' are two compatible pointer types in both directions). In a more evolved way in case of inheritance structure, derived type objects can be referenced with pointers constructed from the type of one among those of their common bases (allowing to activate the polymorphism when a virtual and overridden method is called on such pointers).

Pointers have their own arithmetic. Increment/decrement is special for this type. This is done by multiplying the size of the type of the pointer by the value that one wants to add/remove to it. This makes it possible to properly move the pointer forward or back from the number of elements indicated.
The only operation allowed between pointers is the subtraction, provided the pointers are of the same type. This operation only makes sense if the pointers point to the same homogeneous structure of elements (of the type of the pointers), because the result corresponds to a difference in number of elements.

Notion of reference

In higher-level languages, the use of pointers tends to be suppressed, in favor of references and dynamic arrays managed by the compiler. References fulfill some pointer functions by removing explicit user access to memory. This avoids many problems, in return some uses and optimizations are no longer possible.

In addition to the two classic ways to access data represented in memory: use its name (if it is a variable or a constant) or dereference a pointer containing its address, there is a third method, the use of references, which is absolutely essential in some cases and provides, in addition, an alternative to the use of a pointer.

A reference is a way to associate a new name with an already existing object.
It is not a means to create a new object, even if the syntax to create a reference variable leads to an expression strangely resembling the definition of an initialized variable.
This assignment of a new name does not deprive the object concerned of its original name, if it had one. The new name simply becomes a synonym for the old one, meaning that they both refer to the same object.
To pass/return a variable by reference instead of doing it by value avoids making a copy of the transmitted object. When the object in question is very large, the copy operation can be expensive in run-time and in memory.

In the manner of a pointer, a reference has its own type, which must be identical to that of the referenced object, otherwise compatible at least. In this last case only (type compatible but not the same), references (like pointers) can not do all same operations than with the original object.

The use of a reference to an object makes it possible to obtain the advantages associated with the use of a "pointer to object" (speed and memory savings) while avoiding the heaviness of writing implied by the use of an object pointer (usage of the '@' operator, and '*' or '[]' operators).

Note:
- The arrays of references are not supported yet in FB.
- The non-static reference fields for UDT are not supported yet in FB.
- The references to procedures are not supported in FB (only references to pointers to procedures).

Using references

See the next page of this "References" section:
- the 'Using References' page.

See also

  • Allocate, CAllocate, New (Expression)
  • Operator @ (Address Of), Operator * (Value Of), Operator [] (Pointer Index)
  • Operator . (Member Access), Operator -> (Pointer To Member Access)
  • Pointer Overview
  • Pointer Arithmetic
  • Using References


Using References
The syntax for declaring the References and Using them instead of pointers.

Preamble:

The different syntaxes used to declare a reference all use the 'Byref' keyword.
Since a pointer is a variable, it is possible to modify its contents, and the same pointer can allow successive access to different variables. The association between a reference and the object that it designates is, however, fixed when it is declared.

The 'Byref' keyword indicates a variable that is declared by reference. It is used in three different contexts:
- In a procedure signature, to pass an argument by reference (byref parameter).
- In a function signature, to return a variable to the caller by reference (byref return).
- In the body of the code, to define a reference variable (byref variable).

Table of Contents
1. Passing parameter by reference to procedure (byref parameter)
2. Returning variable by reference from function (byref return)
3. Defining reference variable in code (byref variable)
4. References versus pointers by comparative examples
5. Hacking on usage of references with the additional syntaxes allowed by FreeBASIC

1. Passing parameter by reference to procedure (byref parameter)

  • Syntax of declaration:
{Sub|Function} procedure_name (ByRef parameter As [Const] datatype, ...

When used in the parameter list of a procedure, the Byref keyword indicates that an argument is passed by reference, not by value. The consequence is that any modification made to the argument in the called procedure is reflected in the body of the call.
If the procedure does not need or must not to modify the transmitted object, the Const qualifier can be used in the declaration (before the declaration of the datatype) so that the compiler checks in the body of the procedure that the passed object is not modified in any place (otherwise, a compiler error message is issued).

  • Full syntax example for passing a parameter by reference:
Declare Sub passbyref (ByRef ref As Double, ByVal value As Double)  '' declaration for passing by reference

Dim As Double X = 0
Print X
passbyref(X, 1.23)
Print X

Sleep

Sub passbyref (ByRef ref As Double, ByVal value As Double)  '' declaration for passing by reference
    ref = value
End Sub

Output:
 0
 1.23
Note: A pointer can be passed directly (without first dereferencing it) to Byref procedure parameter if in argument term the Byval keyword is specified in front of the pointer name.

Back to top

2. Returning variable by reference from function (byref return)

  • Syntax of declaration:
Function function_name (...) ByRef As [Const] datatype

When used in the return type of a function, the Byref keyword indicates that the variable is returned by reference, not by value. The consequence is that the caller can modify the variable returned by the function and the modification is reflected in the state of the variable that the function processes.
If the caller does not need or must not to modify the transmitted object, the Const qualifier can be used in the declaration (before the declaration of the datatype) so that the compiler checks in the body of the caller that the returned object is not modified in any place (otherwise, a compiler error message is issued).

Operators (member or global), when used as functions, have also the capability to return results by reference, by using the similar syntax.

  • Full syntax example for returning a variable by reference:
Declare Function returnbyref () ByRef As Double  '' declaration for returning by reference

Print returnbyref()
returnbyref() = 4.56
Print returnbyref()

Sleep

Function returnbyref () ByRef As Double  '' declaration for returning by reference
    Static As Double X = 0
    Return X
End Function

Output:
 0
 4.56
As for the arguments list, it should always be surrounded with parentheses even if empty.

Specific syntax:
On the left-hand side of an assignment expression using the '=' symbol, the result of the function (returned by reference) must be enclosed in parentheses when the function calls one single argument, in order to solve the parsing ambiguity.
From fbc version 0.90, '=>' can be used for assignments, in place of '=', same as for initializers, allowing to avoid parsing ambiguity (without parentheses):
Declare Function transitbyref( ByRef _s As String ) ByRef As String

Dim As String s

s = "abcd"
Print s

'' the enclosing parentheses are required here.
( transitbyref( s ) ) = transitbyref( s ) & "efgh"
Print s

'' the enclosing parentheses are not required here.
transitbyref( s ) => transitbyref( s ) & "ijkl"
Print s

Sleep

Function transitbyref( ByRef _s As String ) ByRef As String
    '' This var-len string will transit by reference (input and output), no copy will be created.
    Return _s
End Function

Output:
abcd
abcdefgh
abcdefghijkl
Note: A pointer can be returned directly (without first dereferencing it) for a Byref function return if in identifier= or Function= or Return statement the Byval keyword is specified in front of the pointer name.

Back to top

3. Defining reference variable in code (byref variable)

  • Syntax of declaration:
{Dim|Static} [Shared] ByRef As [Const] datatype ref = variable
or
[Static] Var [Shared] ByRef ref = variable

Unlike pointers, the reference variable must be assigned as soon as the declaration using an initializer.
datatype must be the same type as that of the variable, or a compatible type (for example one from the types of its Bases in case of inheritance):
- Only when the two types are identical (or using the second syntax with Var), a reference variable can be considered as an alias of the variable. One can do the same operations through such a reference variable as one can do with the original variable.
- Otherwise (types compatible but not identical), one can not do all same operations than with the original variable:
For example, a base type reference variable referring to a derived type object allows to activate polymorphism when a virtual method is called on it, similarly to a base type pointer referring to a derived type object. One can do the same operations through such a reference variable as one can do with a dereferenced pointer of same type (but for both not the same operations as using directly the derived type instance).

If the code does not need or must not to modify the referred object, the Const qualifier can be used in the declaration (before the declaration of the data_type in the first syntax) so that the compiler checks in the code that the object is not modified, through the reference variable, in any place (otherwise, a compiler error message is issued).

There is no interaction between the life of a reference and the life of the object who is referred (similarly to a pointer: destroy an object does not destroy its pointer(s)).
Once created, each one lives his life independently.

  • Full syntax example for defining a reference variable in code:
Dim As Double X = 0

Dim ByRef As Double refX = X  '' declaration for defining a reference
Print X
refX = 7.89
Print X

Sleep

Output:
 0
 7.89

Back to top

4. References versus pointers by comparative examples

  • Function returning the greater variable between two integer variables:
- Using pointers (by passing/returning pointer variables):
Function maxPtr (ByVal p1 As Integer Ptr, ByVal p2 As Integer Ptr) As Integer Ptr
    If *p1 > *p2 Then
        Return p1
    Else
        Return p2
    End If
End Function

Dim As Integer i1 = 1, i2 = 2
Print i1, i2
*maxPtr(@i1, @i2) = 3
Print i1, i2

Sleep

Output:
 1             2
 1             3
- Using references (by passing/returning reference variables):
Function maxRef (ByRef r1 As Integer, ByRef r2 As Integer) ByRef As Integer
    If r1 > r2 Then
        Return r1
    Else
        Return r2
  End If
End Function

Dim As Integer i1 = 1, i2 = 2
Print i1, i2
maxRef(i1, i2) = 3
Print i1, i2

Sleep

Output:
 1             2
 1             3

  • Inheritance structure with overriding subroutine and overriding function with covariant return:
- Using pointers to objects:
Type myBase Extends Object
    Declare Virtual Function clone () As myBase Ptr
    Declare Virtual Sub Destroy ()
End Type

Function myBase.clone () As myBase Ptr
    Dim As myBase Ptr pp = New myBase(This)
    Print "myBase.clone() As myBase Ptr", pp
    Function = pp
End Function

Sub myBase.Destroy ()
    Print "myBase.Destroy()", , @This
    Delete @This
End Sub

Type myDerived Extends myBase
    Declare Function clone () As myDerived Ptr Override  '' overriding member function with covariant return
    Declare Sub Destroy () Override                      '' overriding member subroutine
End Type

Function myDerived.clone () As myDerived Ptr      '' overriding member function with covariant return
    Dim As myDerived Ptr pc = New myDerived(This)
    Print "myDerived.clone() As myDerived Ptr", pc
    Function = pc
End Function

Sub myDerived.Destroy ()                '' overriding member subroutine
    Print "myDerived.Destroy()", , @This
    Delete @This
End Sub

Dim As myDerived c

Dim As myBase Ptr ppc = @c                '' base type pointer to derived object c
Dim As myDerived Ptr pcc = @c             '' derived type pointer to derived object c

Dim As myBase Ptr ppc1 = ppc->clone()     '' base type pointer to clone of object c
'                                              (through its base type pointer and polymorphism)
Dim As myDerived Ptr pcc1 = pcc->clone()  '' derived type pointer to derived object c
'                                              (through its derived type pointer and covariance of return value)
Print
ppc1->Destroy()                           '' using base type pointer and polymorphism
pcc1->Destroy()                           '' using derived type pointer

Sleep

Output example:
myDerived.clone() As myDerived Ptr        4663904
myDerived.clone() As myDerived Ptr        4663952

myDerived.Destroy()                       4663904
myDerived.Destroy()                       4663952
- Using references to objects:
Type myBase Extends Object
    Declare Virtual Function clone () ByRef As myBase
    Declare Virtual Sub Destroy ()
End Type

Function myBase.clone () ByRef As myBase
    Dim As myBase Ptr pp = New myBase(This)
    Print "myBase.clone() Byref As myBase", pp
    Function = *pp
End Function

Sub myBase.Destroy ()
    Print "myBase.Destroy()", , @This
    Delete @This
End Sub

Type myDerived Extends myBase
    Declare Function clone () ByRef As myDerived Override  '' overriding member function with covariant return
    Declare Sub Destroy () Override                        '' overriding member subroutine
End Type

Function myDerived.clone () ByRef As myDerived      '' overriding member function with covariant return
    Dim As myDerived Ptr pc = New myDerived(This)
    Print "myDerived.clone() Byref As myDerived", pc
    Function = *pc
End Function

Sub myDerived.Destroy ()                '' overriding member subroutine
    Print "myDerived.Destroy()", , @This
    Delete @This
End Sub

Dim As myDerived c

Dim ByRef As myBase rpc = c                '' base type reference to derived object c
Dim ByRef As myDerived rcc = c             '' derived type reference to derived object c

Dim ByRef As myBase rpc1 = rpc.clone()     '' base type reference to clone of object c
'                                               (through its base type reference and polymorphism)
Dim ByRef As myDerived rcc1 = rcc.clone()  '' derived type reference to derived object c
'                                               (through its derived type reference and covariance of return value)
Print
rpc1.Destroy()                             '' using base typpe reference and polymorphism
rcc1.Destroy()                             '' using derived type reference

Sleep

Output example:
myDerived.clone() Byref As myDerived      9775712
myDerived.clone() Byref As myDerived      9775760

myDerived.Destroy()                       9775712
myDerived.Destroy()                       9775760
Back to top

5. Hacking on usage of references with the additional syntaxes allowed by FreeBASIC

In FB, a reference is implemented under the hood through an internal pointer which holds the address of the variable.

The access to this internal pointer is presently allowed for user, in read, and also in write for a reference variable (unlike many other languages):
- Therefore, the address of the referred variable (the value of the internal pointer) can be get by using the '@' operator applied on the reference variable symbol name:
variable_address = @ref
- And even, a reference variable can be reassigned (by modifying the value of the internal pointer) to refer to another variable (of compatible type) by doing:
@ref = @other_variable
- The address of the internal pointer of a reference variable can even be obtained:
internal_pointer_address = @@ref
Note:
- A reference variable can also be re-initialized to a "null" reference:
@ref = 0
- A reference variable can even be directly declared as a "null" reference:
Dim ByRef As datatype ref = *CPtr(datatype Ptr, 0)

Thus, by always using the same reference symbol name, one can mix the pure syntax on the reference with the syntax on its internal pointer.

  • Example of hacking on reference symbol name:
Declare Function resizeZstring (ByRef refZstring As ZString, ByVal length As Integer) ByRef As ZString
Declare Sub prntZstring (ByRef refZstring As ZString)

Dim ByRef As ZString refZ = *CPtr(ZString Ptr, 0)  '' "null" reference declaration

Const cz1 = "FB"
@refZ = @(resizeZstring(refZ, Len(cz1)))           '' reference (re-)inititialization
refZ = cz1
prntZstring(refZ)

Const cz2 = "FreeBASIC"
@refZ = @(resizeZstring(refZ, Len(cz2)))           '' reference re-inititialization
refZ = cz2
prntZstring(refZ)

Const cz3 = "FreeBASIC 1.06.0"
@refZ = @(resizeZstring(refZ, Len(cz3)))           '' reference re-inititialization
refZ = cz3
prntZstring(refZ)

Const cz4 = ""
@refZ = @(resizeZstring(refZ, Len(cz4)))           '' reference re-inititialization to "null" reference
refZ = cz4
prntZstring(refZ)

Sleep

Function resizeZstring (ByRef refZstring As ZString, ByVal length As Integer) ByRef As ZString
    If length > 0 Then
        If @refZstring = 0 Then
            Print "Zstring memory buffer allocation"
        Else
            Print "Zstring memory buffer re-allocation"
        End If
        length += 1
    Else
        Print "Zstring memory buffer de-allocation"
    End If
' Return *Cptr(Zstring Ptr, Reallocate(@refZstring, length * Sizeof(Zstring)))
' '' Using the "Return Byval ..." syntax allows to avoid casting + dereferencing as above
    Return ByVal Reallocate(@refZstring, length * SizeOf(ZString))
End Function

Sub prntZstring (ByRef refZstring As ZString)
    Print "  " & @refZstring, "'" & refZstring & "'"
    Print
End Sub

Output example:
Zstring memory buffer allocation
  9513600     'FB'

Zstring memory buffer re-allocation
  9513600     'FreeBASIC'

Zstring memory buffer re-allocation
  9513600     'FreeBASIC 1.06.0'

Zstring memory buffer de-allocation
  0           ''
Back to top

See also

  • Byref (Parameters), Byref (Function Results), Byref (Variables)
  • Operator @ (Address Of), Operator * (Value Of)
  • From Pointers to References


Implicit Declarations
Lazy declaration of variables.

The qb and fblite FreeBASIC language dialects allow variable names to be used without declaring them first. This is called implicit or lazy declaration since the actual declaration is inferred from how the name is first used.

Variable Type

When a variable is implicitly declared, its type depends on one of two things: the most recent default implicit type directive, if any, or the variable type suffix symbol used, if any.

Default type

In the qb dialect, implicitly declared variables default to Single type, while in the fblite dialect they default to Integer type.

Default implicit type directives

"DEFxxx" directives dictate the new default type for any following implicit variable declarations. These directives are: DefByte, DefUByte, DefShort, DefUShort, DefInt, DefUInt, DefLng, DefSng, DefDbl and DefStr.

Variable type suffix symbols

Variable names suffixed with one of a certain set of symbols will be implicitly declared of a certain type. These symbols are: '%' for Integer, '&' for Long, '!' for Single, '#' for Double and '$' for String. These symbols override previous "DEFxxx" directives, if any.

Implicit Array Declaration

Currently, FreeBASIC does not support implicit declaration of arrays.

Debugging

For full debugging support, all variables must be explicitly declared and suffixes should not be used. The use of Option Explicit is recommended to turn of support for implicit declarations, so that mistyped variable names are caught at compile time by the compiler.

See also

  • Option Explicit
  • FreeBASIC Language Dialects


Variable Initializers
Variable initializers are supported for initializing pre-built type variables, UDT objects and arrays.

Preamble:

Pre-built type variables, user defined type (UDT) objects and arrays are initialized to zero (or 'False' for 'Boolean') or null strings by default when they are created.

To avoid the overhead of default variable initialization, the 'Any' initializer can be used with 'Dim' to tell the compiler to only reserve the place for the variable in memory but not initialize it, so the variable will contain garbage. In this case the programmer should not make assumptions about the initial values.

Pre-built type variables, UDT objects and arrays may be given a value at the time of their declaration using 'Dim ...', with the syntaxes shown below.
Pre-built type variables, UDT objects and arrays are initialized as they would in a normal assignment, using an '=' sign. The '=>' sign can also be used, allowing to avoid the declaration resembling an expression for example when declaring fixed length strings.

Table of Contents
1. Initializer syntax and validity for pre-built type variable declaration
2. Initializer syntax and validity for UDT object declaration
3. Initializer syntax and validity for array declaration
4. Nested Initializer syntax with another

1. Initializer syntax and validity for pre-built type variable declaration

The main pre-built types are:
- Integer types
- Floating-point types
- Boolean types
- String types

Initializer syntaxes
4 basic syntaxes are described below.

Static allocations with initializer:
(1) Dim variable_symbol [As PreBuiltType] = expression
or:
(2) Dim ByRef ref_variable_symbol [As PreBuiltType] = variable

Dynamic allocations with initializer:
(3) Dim ptr_variable_symbol As PreBuiltType Ptr = New PreBuiltType( expression )
or:
(4) Dim ByRef ref_variable_symbol As PreBuiltType = *New PreBuiltType( expression )

Initializer validity
- expression:
Must produce an evaluated value of 'PreBuiltType' type or compatible.
- variable:
'PreBuiltType' variable or compatible, when 'As PreBuiltType' is specified.

Declaration syntax for global symbol
If using global symbols when declaring pre-built type variable with initializer ('Dim Shared' or 'Static [Shared]' or 'Static Var [Shared]', instead of 'Dim'), the initializer argument must at least be able to be evaluated at the start of the program so that it can be placed in the .data section:
- The initializer syntaxe for variable-length string becomes no longer valid because the 'variable_symbol' must refer to a dynamic memory block in the heap.
- For the other pre-built type variables, the initializer syntaxe remains valid if the provided argument can be evaluated at compile time.

Example
Dim d As Double = 1234.56789

Print d

Sleep

Note:
In the -lang fb only, 'Var' can be used instead of 'Dim' (removing the explicit type declaration), except for an initializer value of 'WString' type.

Back to top

2. Initializer syntax and validity for UDT object declaration

The UDT (User Defined Type) is a type structure defined by the user, in addition to the already existing pre-built types.

All the following assumes that any statement below (1 to 7) has the access rights to any constructor explicitly defined in the UDT, if any exists.

Initializer syntaxes
7 basic syntaxes are described below.

Static allocations with initializer:
(1) Dim udt_symbol As UdtName = ( argument_list )
or:
(2) Dim udt_symbol As UdtName = udt_instance
or:
(3) Dim ByRef ref_udt_symbol As UdtName = udt_instance
or:
(4) Dim udt_symbol As UdtName = UdtName( argument_list )
or:
(5) Dim udt_symbol As UdtName = Type[<UdtName>]( argument_list )

Dynamic allocations with initializer:
(6) Dim ptr_udt_symbol As UdtName Ptr = New UdtName( argument_list )
or:
(7) Dim ByRef ref_udt_symbol As UdtName = *New UdtName( argument_list )

Parameters
- argument_list:
List of any argument type (with comma delimited items).
If there is only one argument, the '= Type[<UdtName>]( argument )' initializer (if valid) can be shortened into '= ( argument )' or even '= argument' (case of the conversion-constructor).
- udt_instance:
Instance of 'UdtName' or compatible (derived type).

Initializer validity
- Initializer syntax line (1):
Valid only if exists none constructor (neither implicit nor explicit).
- Initializer syntax line (2 or 3):
Always valid.
Case of the copy-construction (2) and reference declaration (3).
- Initializer syntax line (4):
Valid only if exists an implicit or explicit constructor matching the 'argument_list'.
- Initializer syntax line (5):
Valid only if exists none constructor (neither implicit nor explicit), or otherwise if exists at least a constructor (implicit or explicit) matching the 'argument_list'.
- Initializer syntax line (6 or 7):
Its validity follows the same rules as that of the initializer syntax line (5) above.

But the most complex are the rules determining the existence of an implicit constructor depending on the type structure:
- Apart from the default-constructor and the default copy-constructor, any other type of constructor exists only if it is explicitly defined.
- For the implicit default-constructor and the implicit copy-constructor, it depends on the type structure, for example they both exist for (main cases):
- type having or inheriting a member field with a implicit/explicit default-constructor (including member string),
- or type having a base with an implicit/explicit default-constructor (including type derived from Object),
- .....,
- otherwise for example, only a variable member with an initializer itself induces only an implicit default-constructor. but no implicit copy-constructor.

Description
When a UDT has an implicit constructor (due to a string member, or a variable member with an initializer itself, or a data member with constructor, or UDT derived from Object, or since fbc version 1.20.0 containing a STRING*N field plus another data type field) or an explicit constructor, then a simple initializer as '= ( argument_list )', the first initializer syntax in the list (1), becomes not valid.
In that case, an advanced initialization can be applied by using a constructor matching the initialization expression of the last four syntaxes in the list (4 to 7).

The simpler initializer syntax, the first in the list (1), can always be replaced by one of the last four initializer syntaxes in the list (4 to 7), but the contrary does not work.

The second initializer syntax in the list (2) is the special case of copy-construction. This initializer syntax always works, either by default copy-construction or by the way of a copy-constructor (implicit or explicit).

The third initializer syntax in the list (3) is the special case of reference declaration which must always have an initializer. This initializer syntax always works if 'udt_instance' is a reference (or a dereferenced pointer).

Declaration syntax for global symbol
If using global symbols when declaring UDT object with initializer ('Dim Shared' or 'Static [Shared]' or 'Static Var [Shared]', instead of 'Dim'), the initializer argument(s) must at least be able to be evaluated at the start of the program so that it can be placed in the .data section:
- The initializer syntaxes (6) and (7) in the list become no longer valid because the symbol must refer to a dynamic memory block in the heap.
- The other initializer syntaxes remain valid if the provided argument(s) can be evaluated at compile time. If the initializer must call an existing constructor (implicit or explicit), it is the constructor code, called when the program starts, which writes the "initial" values into the .data section.

Commented example
Type UDT1
    Dim As Integer I
    Dim As Integer J
End Type

Dim As UDT1 u11 = (1, 2)                  '' default-construction + initialization
'Dim As UDT1 u12 = UDT1(1, 2)             '' not valid: no Constructor(As Integer, As Integer)
Dim As UDT1 u13 = Type<UDT1>(1, 2)        '' default-construction + initialization
Dim As UDT1 Ptr pu14 = New UDT1(1, 2)     '' default-construction + initialization
    Delete pu14
Dim ByRef As UDT1 ru15 = *New UDT1(1, 2)  '' default-construction + initialization
    Delete @ru15

Dim As UDT1 u16 = u13                     '' default copy-construction
'Dim As UDT1 u17 = UDT1(u13)              '' not valid: no implicit Constructor(As UDT1)
Dim As UDT1 u18 = Type<UDT1>(u13)         '' default-construction + initialization
Dim As UDT1 Ptr pu19 = New UDT1(u13)      '' default-construction + initialization
    Delete pu19
Dim ByRef As UDT1 ru110 = *New UDT1(u13)  '' default-construction + initialization
    Delete @ru110
Print

Type UDT2
    Dim As Integer I = Any
    Dim As Integer J
    Declare Constructor ()
    Declare Constructor (ByVal _I As Integer, ByVal _J As Integer)
End Type
Constructor UDT2 ()
    Print "UDT2.Constructor()"
End Constructor
Constructor UDT2 (ByVal _I As Integer, ByVal _J As Integer)
    Print "UDT2.Constructor(Byval As Integer, Byval As Integer)"
    This.I = _I
    This.J = _J
End Constructor

'Dim As UDT2 u21 = (1, 2)                  '' not valid: exist constructor (due at least to '= Any' initialiser)
Dim As UDT2 u22 = UDT2(1, 2)               '' call Constructor(As Integer, As Integer)
Dim As UDT2 u23 = Type<UDT2>(1, 2)         '' call Constructor(As Integer, As Integer)
Dim As UDT2 Ptr pu24 = New UDT2(1, 2)      '' call Constructor(As Integer, As Integer)
    Delete pu24
Dim ByRef As UDT2 ru25 = *New UDT2(1, 2)   '' call Constructor(As Integer, As Integer)
    Delete @ru25

Dim As UDT2 u26 = u23                      '' default copy-construction
'Dim As UDT2 u27 = UDT2(u23)               '' not valid: no implicit Constructor(As UDT2)
'Dim As UDT2 u28 = Type<UDT2>(u23)         '' not valid: no implicit Constructor(As UDT2)
'Dim As UDT2 Ptr pu29 = New UDT2(u23)      '' not valid: no implicit Constructor(As UDT2)
'Dim Byref As UDT2 ru210 = *New UDT2(u23)  '' not valid: no implicit Constructor(As UDT2)
Print

Type UDT3
    Dim As Integer I
    Dim As String S
    Declare Constructor ()
    Declare Constructor (ByVal _I As Integer, ByRef _S As Const String)
End Type
Constructor UDT3 ()
    Print "UDT3.Constructor()"
End Constructor
Constructor UDT3 (ByVal _I As Integer, ByRef _S As Const String)
    Print "UDT3.Constructor(Byval As Integer, Byref As Const String)"
    This.I = _I
    This.S = _S
End Constructor

'Dim As UDT3 u31 = (1, "2")                 '' not valid: exist constructor (due at least to string member)
Dim As UDT3 u32 = UDT3(1, "2")              '' call Constructor(As Integer, As String)
Dim As UDT3 u33 = Type<UDT3>(1, "2")        '' call Constructor(As Integer, As String)
Dim As UDT3 Ptr pu34 = New UDT3(1, "2")     '' call Constructor(As Integer, As String)
    Delete pu34
Dim ByRef As UDT3 ru35 = *New UDT3(1, "2")  '' call Constructor(As Integer, As String)
    Delete @ru35

Dim As UDT3 u36 = u33                       '' default copy-construction
Dim As UDT3 u37 = UDT3(u33)                 '' call implicit Constructor(As UDT3)
Dim As UDT3 u38 = Type<UDT3>(u33)           '' call implicit Constructor(As UDT3)
Dim As UDT3 Ptr pu39 = New UDT3(u33)        '' call implicit Constructor(As UDT3)
    Delete pu39
Dim ByRef As UDT3 ru310 = *New UDT3(u33)    '' call implicit Constructor(As UDT3)
    Delete @ru310
Print

Type UDT4 Extends Object
    Dim As Integer I
    Dim As Integer J
    Declare Constructor ()
    Declare Constructor (ByVal _I As Integer, ByVal _J As Integer)
End Type
Constructor UDT4 ()
    Print "UDT4.Constructor()"
End Constructor
Constructor UDT4 (ByVal _I As Integer, ByVal _J As Integer)
    Print "UDT4.Constructor(Byval As Integer, Byval As Integer)"
    This.I = _I
    This.J = _J
End Constructor

'Dim As UDT4 u41 = (1, 2)                 '' not valid: exist constructor (due at least to Object as base)
Dim As UDT4 u42 = UDT4(1, 2)              '' call Constructor(As Integer, As Integer)
Dim As UDT4 u43 = Type<UDT4>(1, 2)        '' call Constructor(As Integer, As Integer)
Dim As UDT4 Ptr pu44 = New UDT4(1, 2)     '' call Constructor(As Integer, As Integer)
    Delete pu44
Dim ByRef As UDT4 ru45 = *New UDT4(1, 2)  '' call Constructor(As Integer, As Integer)
    Delete @ru45

Dim As UDT4 u46 = u43                     '' default copy-construction
Dim As UDT4 u47 = UDT4(u43)               '' call implicit Constructor(As UDT4)
Dim As UDT4 u48 = Type<UDT4>(u43)         '' call implicit Constructor(As UDT4)
Dim As UDT4 Ptr pu49 = New UDT4(u43)      '' call implicit Constructor(As UDT4)
    Delete pu49
Dim ByRef As UDT4 ru410 = *New UDT4(u43)  '' call implicit Constructor(As UDT4)
    Delete @ru410
Print

' Note for static UDT declaration + initializer:
'    When the initializer expression calling the constructor has only one parameter 'x', example:
'    'Dim As UDT u = UDT(x)', in this case, 'UDT(x)' can be shortened into '(x)' or even 'x', like:
'    'Dim As UDT u = (x)' or even 'Dim As UDT u = x', but all these statements call the constructor.
'    (a constructor with one parameter is called a conversion-constructor)

' The six below declarations + initialisers all call only the conversion-constructor

Type UDT5
    Dim As Integer I
    Declare Constructor ()
    Declare Constructor (ByVal _I As Integer)
End Type
Constructor UDT5 ()
    Print "UDT5.Constructor()"
End Constructor
Constructor UDT5 (ByVal _I As Integer)
    Print "UDT5.Constructor(Byval As Integer)"
    This.I = _I
End Constructor

Dim As UDT5 u51 = UDT5(1)                  '' call Constructor(As Integer)
Dim As UDT5 u52 = Type<UDT5>(1)            '' call Constructor(As Integer)
Dim As UDT5 u53 = (1)                      '' call Constructor(As Integer)
Dim As UDT5 u54 = 1                        '' call Constructor(As Integer)
Dim As UDT5 Ptr pu55 = New UDT5(1)         '' call Constructor(As Integer)
    Delete pu55
Dim ByRef As UDT5 ru56 = *New UDT5(1)      '' call Constructor(As Integer)
    Delete @ru56

Dim As UDT5 u57 = u54                      '' default copy-construction
'Dim As UDT5 u58 = UDT5(u54)               '' not valid: no implicit Constructor(As UDT5)
'Dim As UDT5 u59 = Type<UDT5>(u54)         '' not valid: no implicit Constructor(As UDT5)
'Dim As UDT5 Ptr pu510 = New UDT5(u54)     '' not valid: no implicit Constructor(As UDT5)
'Dim Byref As UDT5 ru511 = *New UDT5(u54)  '' not valid: no implicit Constructor(As UDT5)
Print

Sleep

Note:
In the -lang fb only, 'Var' can be used instead of 'Dim' (removing the explicit type declaration), but it must not have ambiguity on the type of the initializer value.
In the previous example, only 'Var u11 = (1, 2)' does not work, but 'Var u13 = Type<UDT1>(1, 2)' works.

Back to top

3. Initializer syntax and validity for array declaration

The array can be of any DataType.

Initializer syntax
1 basic syntax is described below.
(arrays of references is not supported presently)

Dim array_symbol ([lbound To] ubound) [AS DataType] = { expression [, ...] }

Parameter
- 'lbound', 'ubound':
Constant numeric values.
- 'expression(s)':
List given in comma delimited items, then enclosed by curly brackets.
Initialization list ordered from 'lbound' to 'ubound'.
Must produce evaluated values of 'DataType' type or compatible.

Initializer validity
'lbound' and 'ubound' must be constant numeric values because variable-length (dynamic) array declaration does not support any initializer.
Only fixed-length (static) array declaration supports an initializer.

Declaration syntax for global symbol
If using global symbols when declaring array variable with initializer ('Dim Shared' or 'Static [Shared]', instead of 'Dim'), the initializer argument(s) must at least be able to be evaluated at the start of the program so that it can be placed in the .data section:
- The initializer syntaxe for fixed-length (static) array of variable-length string become no longer valid because the symbol must refer to a dynamic memory block in the heap.
- For the fixed-length (static) arrays of other pre-built type variables, the initializer syntaxe remains valid if the provided expression(s) can be evaluated at compile time.

Example
Dim array(0 To 4) As String = {"array(0)", "array(1)", "array(2)", "array(3)", "array(4)"}

For I As Integer = 0 To 4
    Print array(I),
Next I
Print

Sleep

Note:
Even in the -lang fb, 'Var' can not be used instead of 'Dim', because 'Var' does not support the array declaration.

Back to top

4. Nested Initializer syntax with another

These methods of initializing variables can be nested within one another for complex assignments.
For instance, to initialize a multidimensional array:
Dim array(1 To 3, 1 To 5) As Integer = _
    { _
        {11, 12, 13, 14, 15}, _
        {21, 22, 23, 24, 25}, _
        {31, 32, 33, 34, 35} _
    }

For I As Integer = 1 To 3
    For J As Integer = 1 To 5
        Print array(I, J),
    Next J
    Print
Next I

Sleep

In this declaration, the values for the left-most dimension are given as 5-index arrays.
Nesting allows for arrays of any dimension to be initialized.

UDTs and arrays can be nested within each other as well.
For instance, the following code declares and initializes an array of UDTs:
Type mytype
    var1 As Double
    var2 As Integer
    var3 As ZString Ptr
End Type

Dim MyVar(0 To 1) As mytype = _
    { _
        (1.1, 1, @"Hello"), _
        (2.2, 2, @"GoodBye") _
    }

For I As Integer = 0 To 1
    Print MyVar(I).var1, MyVar(I).var2, *MyVar(I).var3
Next I

Sleep

Note:
Even in the -lang fb, 'Var' can not be used instead of 'Dim', because 'Var' does not support the array declaration.

Back to top

Dialect Differences

  • In the -lang qb dialect, variables cannot be initialised.
  • In the -lang fblite dialect, the explicit type declaration can be omitted in 'Dim ... = ...', but only for the Integer type (initializer value must match the Integer type).

Differences from QB

  • Variable Initializers are new to FreeBASIC.
  • The alternate syntax 'Dim [Byref] As DataType symbolname = ...' is new to FreeBASIC.

See also

  • Implicit declarations
  • Constant Expressions
  • Dim
  • Shared
  • Static
  • Var


Storage Classes
Visibility and lifetime of variables, objects and arrays

A variable, object or array's storage class determines when and where memory is allocated for it and when that memory is destroyed. There are 2 storage classes in FreeBASIC: automatic and static.

Automatic

Automatic variable, object and array lifetimes begin at the point of declaration and end when leaving the scope they are declared in.

Automatic entities are guaranteed to have unique storage for each instance of the block in which they are declared. For example, the automatic variables declared within a procedure will be allocated at different addresses and have unique state (value) for each call to the procedure.

Automatic variables, objects and arrays are defined using the Dim, ReDim and Var keywords without the Shared specifier.

The memory for automatic variables, objects and arrays is allocated on the program stack.

Automatic variables, objects and arrays have no linkage.

Static

Static variable, object and array lifetimes begin at program creation and end with program termination.

Static entities are guaranteed to have the same storage for each instance of the block in which they are declared. For example, the static variables declared within a procedure will be allocated at the same address, and retain their state (value) across each call to the procedure.

Static variables, objects and arrays are declared using the Static keyword. Entities declared using the Shared specifier are implicitly static. All entities declared within a procedure that is declared using the Static specifier are also implicitly static.

The memory for static variables, objects and arrays is allocated in the .BSS section of the executable, or in the .DATA section if they are initialized when defined. Static variable-length arrays must be declared empty, with an empty subscript range list; their element data is still allocated in the free store (when they are resized), but the internal array data is allocated in the .DATA section of the executable to allow the element data to persist throughout program execution.

Static variables, objects and arrays have internal linkage by default, unless previously declared using the Extern or Common keywords.

Platform Differences

  • In DOS and Windows platforms, the size of the program stack can be adjusted at compile-time using the -t command-line switch. In Linux platforms, the size of the program stack can be adjusted at load-time by modifying /etc/security/limits.conf, or on a per-thread basis using the shell builtin ulimit.

Differences from QB

  • QuickBASIC allows static entities to be declared within procedures and DEF FN routines only.

See also

  • Extern, Common
  • Dim, ReDim, Var, Shared, Byref (Variables)
  • Static
  • Linkage


Variable Scope
Visibility and access rules for variables and objects

A variable's scope refers to its visibility in a program. A variable is not visible (cannot be accessed) outside the scope in which it was declared. Where and how a variable is declared determines its scope.

In FreeBASIC, there are 4 categories of scope: local, shared, common and common shared. Each of these scopes has different visibility rules, which are detailed below.

Local Scope

Variables declared in the local scope are visible only in the most local instance of the IF, SELECT, WITH, FOR, WHILE, DO, SCOPE, procedure, or module block in which they are declared.

  • Sub, Function, the main body, and each compound statement implicitly define a new local scope block.
  • Explicitly declared variables using Dim or ReDim or Static take the scope of the local most block in which they are declared.
  • Implicit variables take the scope of the the local most Scope...End Scope block in which they are first used, otherwise take the scope of the Sub, Function, or main body in which they are used.

In the local scope, there is no visibility between module-level code and procedure level code. Furthermore, variables dimensioned within a block decision or loop statement will only be visible within the block in which they are dimensioned. Variables declared in the local scope of a module are not visible in any of the procedures within that module. Similarly, local variables declared inside procedures are not visible in the module-level code, nor any other procedure within the module.

Variables declared inside Scope blocks may only be declared of local scope, and are not visible outside the block. Scope blocks, however, inherit the surrounding scope, so local variables declared outside the Scope block will be visible inside (see example program).

You can declare a variable to be of local scope explicitly by using the Dim statement, or implicitly (for only -lang qb and -lang fblite dialects) by simply introducing the variable (see Implicit Declarations). The example program local.bas demonstrates visibility rules for the local scope.

local.bas
'' visible only in this module
Dim As Integer local_moduleLevel1

'' OK.
Print local_moduleLevel1

Scope
  '' OK; SCOPE Blocks inherit outer scope
  Print local_moduleLevel1

  '' visible only in this SCOPE Block
  Dim As Integer local_moduleLevel2

  '' OK.
  Print local_moduleLevel2
End Scope

'' Error; can't see inner-SCOPE vars
'' print local_moduleLevel2

Function some_function( ) As Integer
  '' visible only in this function
  Dim As Integer local_functionLevel

  '' OK.
  Print local_functionLevel

  '' Error; can't see local module-level vars
  '' print local_moduleLevel1

  '' Error; can't see local module-level vars
  '' print local_moduleLevel2

  Function = 0

End Function

'' print local_functionLevel                    '' Error; can't see function_level vars
End 0

Shared Scope

Variables declared in the shared scope of a module are visible to both the module and all procedures of that module.

Unlike the local scope, the shared scope makes module-level variables visible to procedures of that module. In other words, the module shares its declarations with its procedures.

Variables can only be declared to be of shared scope at the module-level. Ie., only modules can share variables. Neither procedures nor Scope blocks can declare variables in the shared scope, thus variables declared there can only be local to that procedure or block.

You can declare a variable to be of shared scope by using the DIM (or REDIM or STATIC) statement with the Shared keyword. The example program shared_scope.bas demonstrates visibility rules for the shared scope.

shared.bas
'' visible throughout this module
Dim Shared As Integer shared_moduleLevel1

'' OK.
Print shared_moduleLevel1

Scope
  '' OK; can see outer-scope vars
  Print shared_moduleLevel1

  '' Error; SCOPE-level vars cannot be shared
  '' dim shared as integer shared_ModuleLevel2
End Scope

End 0

Function some_function( ) As Integer
  '' OK; can see shared module-level vars
  Print shared_moduleLevel1

  '' Error; function-level vars cannot be shared
  '' dim shared as integer sharedFunctionLevel

  Function = 0
End Function

Common Scope

Variables declared in the common scope are visible to all modules.

Variables declared with Common are visible to other modules with a matching Common variable declaration. The variable name declared must match from between modules.

module1.bas
'' compile with:
''    fbc -lang qb module1.bas module2.bas

'$lang: "qb"

Declare Sub Print_Values()
Common m1 As Integer
Common m2 As Integer
                      ' This is executed after all other modules
m1 = 1

Print "Module1"
Print "m1 = "; m1     ' m1 = 1 as set in this module
Print "m2 = "; m2     ' m2 = 2 as set in module2

Print_Values

module2.bas
Common m1 As Integer
Common m2 As Integer

m2 = 2

Print "Module2"       ' This is executed first
Print "m1 = "; m1     ' m1 = 0 (by default)
Print "m2 = "; m2     ' m2 = 2

Sub Print_Values()
  Print "Module2.Print_Values"
  Print "m1 = "; m1   ' Implicit variable = 0, because '-lang qb' use
  Print "m2 = "; m2   ' Implicit variable = 0, because '-lang qb' use
End Sub

Output:
  Module2
  m1 = 0
  m2 = 2
  Module1
  m1 = 1
  m2 = 2
  Module2.Print_Values
  m1 = 0
  m2 = 0

Common Shared Scope

Variables declared in the common shared scope are visible to all modules and all procedures of those modules.

Variables declared with Common are visible to other modules with a matching Common variable declaration. The variable name declared must match from between modules. Within a module the Shared declaration modifier gives the variable module scope and makes the variable visible to all subs and functions.

module3.bas
'' compile with:
''    fbc module3.bas module4.bas

Declare Sub Print_Values()
Common m1 As Integer
Common m2 As Integer

'' This is executed after all other modules
m1 = 1

Print "Module3"
Print "m1 = "; m1     '' m1 = 1 as set in this module
Print "m2 = "; m2     '' m2 = 2 as set in module2

Print_Values

module4.bas
Common Shared m1 As Integer
Common Shared m2 As Integer

m2 = 2

Print "Module4"       '' This is executed first
Print "m1 = "; m1     '' m1 = 0 (by default)
Print "m2 = "; m2     '' m2 = 2

Sub Print_Values()
  Print "Module4.Print_Values"
  Print "m1 = "; m1   '' m1 = 1
  Print "m2 = "; m2   '' m2 = 2
End Sub

Output:
  Module4
  m1 = 0
  m2 = 2
  Module3
  m1 = 1
  m2 = 2
  Module4.Print_Values
  m1 = 1
  m2 = 2

Example

See examples above.

See also

  • Scope
  • Dim
  • Common
  • Shared
  • Variables
  • Implicit Declarations
  • Simple Variable Lifetime vs Scope
  • Dynamic Object and Data Lifetime


Simple Variable Lifetime vs Scope
Lifetime of Simple Variable, created from declaration keyword for static memory allocation, relative to its Scopes.

Preamble:

  • The Lifetime of a variable is the time period in which the variable has valid memory (the Scope referring to the program part where its name is visible). The value of a variable may change during its lifetime, but it retains some consistent value.
  • The simple variables considered are the predefined variables including the raw pointers, and the fixed-length strings/arrays, but excluding the variable-length strings/arrays. Instances of simple UDT (without any dynamic data allocated) are also considered.
  • The declaration keywords for static memory allocation are: 'Dim', 'Static', 'Var'.

For such variables allocated in static way as defined above, the lifetime generally matches the surrounding scope, otherwise it can be greater than this one.

Declaration syntax for a lifetime matching the surrounding scope

For such variables declared anywhere, as follows (or similar syntax):
(1) Dim Shared As datatype [Ptr] variablename ...
or
(2) Var Shared variablename = expression
(equivalent to: 'Dim Shared As TypeOf((expression)) = expression')
or
(3) {Dim|Static} Shared As datatype [Ptr] variablename ...
or
(4) [Static] Var Shared variablename = expression
(equivalent to: '{Dim|Static} Shared As TypeOf((expression)) = expression')
or
(5) Common [Shared] As datatype [Ptr] variablename
they always have a lifetime matching their surrounding scope (global scope, or scope block, or compound statement block, or procedure scope).

With (1) or (2) syntax, the local variable is always allocated on the program stack at the time of its declarations, and is automatically deallocated when going out its scope.
With (3) or (4) or (5) syntax, the global variable is always allocated in the .BSS or .DATA section of the executable (its scopes and lifetimes begins at program creation and ends with program termination).

Declaration syntax for a lifetime that may be greater than the surrounding scope

For such variables declared anywhere, as follows (or similar syntax):
Static Shared As datatype [Ptr] variablename ...
Static Var Shared variablename = expression
they always have a lifetime equal to the program duration, so greater than their surrounding scope if there are declared in any local scope block (matching their surrounding scope if there are declared in the global scope).

The static variable is always allocated in the .BSS or .DATA section of the executable (its lifetimes begins at program creation and ends with program termination).

Interest of declaring such static variables in a compound instruction block or in a procedure scope:
- As for 'Dim', the 'Static' keyword is used in a compound statement block or in a procedure scope to declare variables whose scope stops at the end of the compound statement block or the procedure.
- However, unlike 'Dim', the lifetime differs because the variables declared with the 'Static' keyword retain their value between the successive loops of the compound instruction block or the successive calls to the procedure.
- In summary, a declared static variable has a local scope, but its lifetime is comparable to that of a global scope variable.
- So, static variables with the same name can be declared in several different compound statement blocks and in different procedure scopes. Each of these variables therefore remains independent and retains its own value in its own local scope.

Example

Lifetime's comparison between different variables declared in a local scope:
local variable vs static variable, both declared in a procedure scope
Dim Shared As ZString Ptr pzl ' global variable to memorize the local Zstring address
Dim Shared As ZString Ptr pzs ' global variable to memorize the static Zstring address

Declare Sub prntSubString (ByVal p As ZString Ptr, ByVal size As Integer)

Sub s ()                                          ' beginning of procedure scope
    Dim As ZString * 15 zl = "local variable"     ' declare/initialize a local Zstring
    pzl = @zl                                     ' memorize the local Zstring address

    Static As ZString * 16 zs = "static variable" ' declare/initialize a static Zstring
    pzs = @zs                                     ' memorize the static Zstring address

    Print "     From inside the procedure scope:"
    prntSubString(pzl, 14)                        ' display address/content of the local zstring
    prntSubString(pzs, 15)                        ' display address/content of the static zstring
End Sub                                           ' end of procedure scope

Print "Lifetimes comparison between local/static variables declared in a local scope:"
s() ' call the procedure

Print "     From outside the procedure scope:"
prntSubString(pzl, 14) ' display address/content of the local zstring after going out its scope
prntSubString(pzs, 15) ' display address/content of the static zstring after going out its scope

Sleep

Sub prntSubString (ByVal p As ZString Ptr, ByVal size As Integer)
    Print , "&h" & Hex(p, SizeOf(Any Ptr) * 2),
    Print """";
    For I As Integer = 0 To size - 1
        Dim As UByte u = (*p)[I]
        If u < Asc(" ") Then
            Print " ";
        Else
            Print Chr(u);
        End If
    Next I
    Print """"
End Sub

Output example:
Lifetimes comparison between local/static variables declared in a local scope:
     From inside the procedure scope:
              &h0019FE74    "local variable"
              &h00407004    "static variable"
     From outside the procedure scope:
              &h0019FE74    " p@ Çp@ ¿■  Y "
              &h00407004    "static variable"

See also

  • Dim, Static, Var
  • Storage Classes
  • Variable Scope
  • Dynamic Object and Data Lifetime


Dynamic Object and Data Lifetime
Lifetime of Dynamic Object and its Data, created from declaration keyword for dynamic memory allocation.

Preamble:

  • The Lifetime of an object (and of its data) is the time period in which its identifier variable exists (and refers to valid data). But in absolute terms, the identifier variable of the object and its associated data can have two independent lifetimes (the Scope referring to the program part where the identifier variable is visible).
  • The dynamic objects considered are the predefined pseudo-objects such as the variable-length strings/arrays, and the instances of complex UDT (with its own dynamic data allocated).
  • Simple variables but allocated in a dynamic way are also considered, and finally the dynamic objects which are allocated as well in a dynamic way.
  • The declaration keywords for dynamic memory allocation are: 'Allocate'/'Callocate'/'Reallocate', 'New', 'ImageCreate' (for deallocation: 'Deallocate', 'Delete', 'ImageDestroy').

For such objects and data dynamically allocated as defined above, the lifetime of the identifier variable of the object generally matches the surrounding scope (otherwise it can be greater than this one), but the lifetime of associated data may mismatch this one because the allocation/deallocation of associated data is triggered by the user himself.

Case of predefined pseudo-objects allocated in a static way by user

Even though these predefined type variables (variable-length strings (1), or variable-length arrays (2)) are allocated in a static way as follows (or similar syntax):
(1) Dim [Shared] As String stringname ...
or
(2) Dim [Shared] As datatype arrayname() ...
these variables can be considered as dynamic pseudo-objects because they are assemblies of two entities:
- a descriptor associated to the identifier variable (stringname (1), or arrayname() (2)), the first entity
- referencing a dynamic allocation in memory (the string data (1), or the array data (2)), the second entity (unnamed).

The descriptor is allocated in the .BSS or .DATA section, if 'Shared' is used, otherwise on the program stack.
The string data are allocated/reallocated/deallocated in the heap by string assigning, also updating the descriptor accordingly (assigning an empty string does not destroy the descriptor but just resets it).
The array data are allocated/reallocated in the heap by 'Redim' and deallocated by 'Erase', also updating the descriptor accordingly ('Erase' does not destroy the descriptor but just re-initializes it).
So whatever such a user command applied, the identifier variable remains always defined in its scope, at cons the memory allocation can be dynamically modified/freed in this same scope (accordingly to the user command).

Case of dynamic objects allocated in a static way by user

The user can also define a dynamic object through a complex UDT with member procedures to allocate/reallocate/deallocate dynamic data associated to it.
The member procedures normally used to perform this are the constructors (for allocation), the assignment operators (for reallocation) and the destructor (for deallocation).

Even if the object identifier variable is allocated in a static way (similarly to above):
Dim [Shared] As complexUDT instancename ...
inducing automatic allocation and deallocation of object data following the identifier variable scope (by means of implicit call to UDT constructor then destructor), between the two, the dynamic data allocation can be deeply impacted by user commands (such as explicit calls to operators overloaded for the UDT).

Case of simple variables but allocated in a dynamic way by user

The keywords ('Allocate', 'Reallocate', 'New', 'ImageCreate'), used to declare a dynamic allocation, create an unnamed entity whose the lifetime depends on other user commands ('Deallocate', 'Delete', 'ImageDestroy').
Generally, these allocation keywords are included in expressions used to initialize (1|3), or assign (2|4), a simple variable (a pointer (1|2), or a reference (3|4)), as for example:
(1) Dim As datatype Ptr DATApointername = New datatype ...
or
(2) Dim [Shared] As datatype Ptr DATApointername
(2) .....
(2) DATApointername = New datatype ...
or
(3) Dim ByRef As datatype DATAreferencename = *New datatype ...
or
(4) Dim [Shared] ByRef As datatype DATAreferencename = *CPtr(datatype Ptr, 0)
(4) .....
(4) @DATAreferencename = New datatype ...

Therefore, in this case, there are two distinct entities:
- a named pointer (1|2) or a reference (3|4), the first entity,
- pointing (1|2) or referring (3|4) to an allocated memory, the second entity (unnamed).

Do not confuse the two entities, each has its own lifetime.
'Deallocate', 'Delete', 'ImageDestroy', deallocating only the second entity (not the first), as for example using:
(1|2) Delete DATApointername
or
(3|4) Delete @DATAreferencename

Case of dynamic objects allocated as well in a dynamic way by user

The dynamic object (complex UDT) can also be allocated as well in a dynamic way (similarly to above), by initializing (1|3), or assigning (2|4), a simple variable (a pointer (1|2), or a reference (3|4)), as for example:
(1) Dim As complexUDT Ptr UDTpointername = New complexUDT ...
or
(2) Dim [Shared] As complexUDT Ptr UDTpointername
(2) .....
(2) UDTpointername = New complexUDT ...
or
(3) Dim ByRef As complexUDT UDTreferencename = *New complexUDT ...
or
(4) Dim [Shared] ByRef As complexUDT UDTreferencename = *CPtr(complexUDT Ptr, 0)
(4) .....
(4) @UDTreferencename = New complexUDT ...

Therefore, in this last case, three entities can be considered:
- a named pointer (1|2) or a reference (3|4), the first entity,
- pointing (1|2) or referring (3|4) to the allocated fields of the object, the second entity (unnamed),
- and addressing the dynamic allocated associated data, the third entity (unnamed).

Do not confuse the three entities, each has its own lifetime.
'Delete' deallocates the second entity (not the first), which begins to deallocate the third at first (by calling its destructor), as for example using:
(1|2) Delete UDTpointername
or
(3|4) Delete @UDTreferencename

Example

Dynamic object (complex UDT) allocated as well in a dynamic way by user:
- first entity: the UDT reference, statically allocated on the program stack,
- second entity: the UDT instance with its zstring pointer field (referred by the UDT reference), dynamically allocated in the heap by the user,
- third entity: the zstring data (referred by the zstring pointer field), dynamically reallocated in the heap by the UDT procedure members (constructors, let operator, destructor).
Type complexUDT
    Public:
        Declare Constructor ()
        Declare Constructor (ByVal p As ZString Ptr)
        Declare Operator Let (ByVal p As ZString Ptr)
        Declare Operator Cast () As String
        Declare Property info () As String ' allocation address, allocation size, string length
        Declare Destructor ()
    Private:
        Dim As ZString Ptr pz
End Type

Declare Sub prntInfo_printString (ByRef u As complexUDT)

Print "'Dim Byref As complexUDT ref = *New complexUDT(""Beginning"")':"
Dim ByRef As complexUDT ref = *New complexUDT("Beginning")
prntInfo_printString(ref)

Print "'ref = """"':"
ref = ""
prntInfo_printString(ref)

Print "'ref = ""FreeBASIC""':"
ref = "FreeBASIC"
prntInfo_printString(ref)

Print "'ref = ""Programmer's Guide / Declarations / Dynamic Object and Data Lifetime""':"
ref = "Programmer's Guide / Declarations / Dynamic Object and Data Lifetime"
prntInfo_printString(ref)

Print "'ref.Destructor()':"
ref.Destructor()
prntInfo_printString(ref)

Print "'ref.Constructor()':"
ref.Constructor()
prntInfo_printString(ref)

Print "'ref.Constructor(""End"")':"
ref.Constructor("End")
prntInfo_printString(ref)

Print "'Delete @ref':"
Delete @ref
@ref = 0 ' systematic safety to avoid double-delete on same allocation

Sleep

Constructor complexUDT ()
    Print "    complexUDT.Constructor()"
    This.pz = Reallocate(This.pz, 1)
    (*This.pz)[0] = 0
End Constructor

Constructor complexUDT (ByVal p As ZString Ptr)
    Print "    complexUDT.Constructor(Byval As Zstring Ptr)"
    This.pz = Reallocate(This.pz, Len(*p) + 1)
    *This.pz = *p
End Constructor

Operator complexUDT.Let (ByVal p As ZString Ptr)
    Print "    complexUDT.Let(Byval As Zstring Ptr)"
    This.pz = Reallocate(This.pz, Len(*p) + 1)
    *This.pz = *p
End Operator

Operator complexUDT.Cast () As String
    Return """" & *This.pz & """"
End Operator

Property complexUDT.info () As String
    Return "&h" & Hex(This.pz, SizeOf(Any Ptr) * 2) & ", " & _     ' allocation address
            Len(*This.pz) + Sgn(Cast(Integer, This.pz)) & ", " & _ ' allocation size
            Len(*This.pz)                                          ' string length
End Property

Destructor complexUDT ()
    Print "    complexUDT.Destructor()"
    This.pz = Reallocate(This.pz, 0)
End Destructor

Sub prntInfo_printString (ByRef u As complexUDT)
    Print "        " & u.info
    Print "        " & u
    Print
End Sub

Output:
'Dim Byref As complexUDT ref = *New complexUDT("Beginning")':
    complexUDT.Constructor(Byval As Zstring Ptr)
        &h001F2AD0, 10, 9
        "Beginning"

'ref = ""':
    complexUDT.Let(Byval As Zstring Ptr)
        &h001F2AD0, 1, 0
        ""

'ref = "FreeBASIC"':
    complexUDT.Let(Byval As Zstring Ptr)
        &h001F2AD0, 10, 9
        "FreeBASIC"

'ref = "Programmer's Guide / Declarations / Dynamic Object and Data Lifetime"':
    complexUDT.Let(Byval As Zstring Ptr)
        &h001F2AD0, 69, 68
        "Programmer's Guide / Declarations / Dynamic Object and Data Lifetime"

'ref.Destructor()':
    complexUDT.Destructor()
        &h00000000, 0, 0
        ""

'ref.Constructor()':
    complexUDT.Constructor()
        &h001F2AD0, 1, 0
        ""

'ref.Constructor("End")':
    complexUDT.Constructor(Byval As Zstring Ptr)
        &h001F2AE0, 4, 3
        "End"

'Delete @ref':
    complexUDT.Destructor()
See also

  • Allocate, CAllocate, Reallocate, Deallocate
  • New (Expression), Delete (Statement)
  • ImageCreate, ImageDestroy
  • ReDim, Erase
  • Storage Classes
  • Variable Scope
  • Simple Variable Lifetime vs Scope


Namespaces
Namespace, a container for identifiers so that they don't conflict with those in other Namespaces or the global scope.

Syntax

Namespace identifier [ Alias "aliasname" ]
statements
End Namespace

Parameters

identifier
The name of the Namespace (including nested names specifier).
aliasname
An alternate external name for the Namespace.

Description

Namespaces are declaration fields that allow to delimit the search for the names of identifiers by the compiler. Their purpose is essentially to group the identifiers logically and to avoid name conflicts between several parts of the same project.
This type of conflict stems from the fact that only one global scope is provided by default, in which there should be no name conflict. With Namespaces, this type of problem can be more easily avoided, because defining global objects in the global scope can be avoid.

Namespaces are not allowed to contain code directly, only inside procedures declared in that Namespace. That is because a Namespace is not a scope, it is not something that is executed, it is just something that can be used to hold declarations.

Any variable declared in Namespace is implicitly static and visible throughout the entire program (Static and Shared keywords are useless). Therefore only an initializer with a constant is authorized.

Usage

Unlike another declarative region such as a Type, a Namespace can be split into several pieces. The first piece serves as declaration, and the following ones as extensions. The syntax for a Namespace extension is exactly the same as that for the declaration part.
Identifiers declared or defined within the same Namespace must not conflict. They may have the same names, but only as part of the overloading. A Namespace therefore behaves exactly like the declaration fields of Types and the global scope.

Access to Namespace identifiers is through the resolution operator ("."), by prefixing the name of the identifier to use from the name of its Namespace. However, this prefixing is useless inside the Namespace itself, just like members inside their Type.
Namespace member procedures can be defined inside this space. They can also be set outside this space, if the resolution operator is used (prefixing from the name of its Namespace). The procedures thus defined must appear after their declarations in the Namespace.

It is possible to define a Namespace within another Namespace. However, this declaration must occur at the outermost declarative level of the Namespace that will contain the Namespace. Namespace declarations can not be put inside a procedure body or inside a Type block.

When a Namespace has a very complicated name, it may be advantageous to define an alias for that name. The alias will then have a simpler name.
Names given to Namespace aliases must not conflict with the names of other identifiers in the same Namespace, whether this is the global scope or not.

Note: The parser allows to define anonymous Namespaces (without identifier term), but this is the only similarity with the actual C++ capability: The FB compiler automatically generates multiple separate anonymous Namespaces instead of one only per module in such a case.
The FB anonymous Namespaces are almost unusable because all their declarations are inaccessible, even from the body of the module that contains them. Apart from encapsulating module constructors/destructors also inside, nothing else can be done with them.

'Using (Namespaces)' command
Using (Namespaces) allows to use a identifier from a Namespace in a simplified way, without having to specify its full name (that is, the Namespace name followed by the "." operator then the identifier name).
Each Using command allows to directly use all the identifiers of the referred Namespace.

- Syntax:
Using identifier [, identifier [, ...] ]

- Parameters:
identifier: The name of the Namespace to use.

- Usage:
After a Using command, it is still possible to use the full names of the identifiers from a Namespace, but this is no longer necessary. The Using commands are valid from the line where they are declared until the end of the current scope block.
If a Namespace is extended after a Using directive, the identifiers defined in the Namespace extension can be then used exactly as the identifiers defined before the using directive (that is, without the full expression of their Namespace names).

When entering Using command(s) for several Namespace names, name conflicts may occur. In this case, no error is reported from the Using command(s), but an error occurs if one of the identifiers for which there is a conflict is used (using full name of the expected identifier solves the conflict).

Example

Namespace extension:
Namespace A  ' Declaration of Namespace A
    Dim As Integer i
End Namespace

Namespace B  ' Declaration of Namespace B
    Dim As Integer i
End Namespace

Namespace A  ' Extension of Namespace A
    Dim As Integer j
End Namespace

Access to Namespace members:
Dim As Integer i  ' Declare i in the global scope

Namespace A
    Dim As Integer i = 2  ' Declare i in Namespace A
    Dim As Integer j = 3  ' Declare j in Namespace A
End Namespace

i = 1    ' Use i from global scope (.i)
A.i = 4  ' Use i from Namespace A (A.i)

External definition of a function declared in a Namespace:
Namespace A
    Declare Function f () As Integer  ' Declaration of f() in Namespace A (A.f())
End Namespace

Function A.f () As Integer  ' Definition of f() from Namespace A (A.f())
    Return 0
End Function

Definition of nested Namespace:
Namespace A
    Dim As Integer i  ' (A.i)
    Namespace B
        Dim As Integer j  ' (A.B.j)
    End Namespace
End Namespace

Access with 'Using (Namespaces)' command:
Namespace A
    Dim As Integer i  ' Declaration of A.i
    Dim As Integer j  ' Declaration of A.j
End Namespace

Using A  ' Namespace A identifiers are also used
i = 1  ' Equivalent to A.i
j = 1  ' Equivalent to A.j

Extension of Namespace after 'Using (Namespace)' command:
Namespace A
    Dim As Integer i
End Namespace

Using A

Namespace A
    Dim As Integer j
End Namespace

i = 0  ' Initialize A.i
j = 0  ' Initialize A.j

Conflict between local identifiers with 'Using (Namespaces)' command:
Namespace A
    Dim As Integer i  ' Declare A.i
    Dim As Integer j  ' Declare A.j
End Namespace

Namespace B
    Dim As Integer i  ' Declare B.i
    Dim As Integer j  ' Declare B.j
    Using A           ' A.i/j and B.i/j are in conflict, but no error is given
End Namespace

Dim As Integer j  ' Declare also j the global scope

Using B
'i = 1   ' error: Ambiguous symbol access, explicit scope resolution required (between B.i and A.i)
B.i = 1  ' ambiguity resolution solved by using full name
j = 2    ' ambiguity (between .j, B.j, A.j) solved by compiler, by choosing override .j in the global scope

See also

  • Namespace
  • Using (Namespaces)
  • Scope...End Scope


Variable and Procedure Linkage
Name visibility within and between modules
Linkage refers to the visibility of the name of a variable, object or procedure between one or more modules of a program. In other words, a linkage dictates how a name is shared between modules. There are two main types of linkage a name can have: internal and external.

Internal linkage

Names with internal linkage only refer to variables, objects or procedures defined within their own module; they are not outwardly visible to other modules. This means that two or more modules can refer to different things using the same name. Note that linkage only refers to visibility of a name, and depending on storage class and lifetime, a variable, object or procedure with internal linkage may be shared between modules using its address.

Module-scope declarations

Variable and object names declared at module-scope have internal linkage unless otherwise declared with Extern or Common. For example, variable names first introduced with Dim or Static have internal linkage, and those variables can only be referred to by name within the module in which they are defined. Note that using Shared only allows name visibility within the module's procedures, and does not contribute to the name's linkage.
Procedure names declared with Private have internal linkage.

Local-scope declarations

All variable and object names declared at local-scope (in a Do loop, or procedure body, for instance) have internal linkage.

External linkage

Names with external linkage may refer to variables, objects or procedures defined within their module or in another module. Having external linkage means that a name is outwardly visible to other modules, and all modules that use that same external name all refer to the same variable, object or procedure. Thus, only one module may define an external name (the compiler will complain about a duplicated definition if it finds an additional definition of a name with external linkage).

Module-scope declarations

Variable and object names declared at module-scope are declared to have external linkage with Extern or Common.
Extern declares the variable having external linkage, but does not define it. This external declaration must come before any definition of the same name (a declaration without Extern specifies internal linkage, and currently, any further external declarations of that name signify a duplicated definition). Variable and object names with external linkage declared using Extern are always in the shared scope, and so can be referred to within procedure bodies.
Common declares the variable having external linkage as well as defining the variable. But, it is different from Extern in that the Common definition of the variable may appear in more than one module. When used with arrays, only variable-length arrays without subscripts may be declared, and the array must be sized at run-time using Dim or ReDim before it can be used. Variable and object names with external linkage declared using Common are only in the shared scope if the Shared scope specifier is also given. Shared variables can be referred to within procedure bodies.
When both Extern and Common are both used to declare and define a variable, the effect is that the meaning of Common statement is altered to behave as though it were a Dim declaration. So it is generally, not recommended to mix Extern and Common on the same variable in the same module. However, variables may be declared and defined with Common in one module and then referenced with Extern in another module without confusion.

Procedure names are declared to have external linkage by default. Declarations using Public explicitly specify external linkage.

Local-scope declarations

Currently, names declared at local-scope cannot have external linkage.


User Defined Types
Custom types.

Overview

User-Defined Types are special kinds of variables which can be created by the programmer. A User-Defined Type (UDT) is really just a container that contains a bunch of other variables, like an array, but unlike arrays, UDTs can hold different variable types (whereas arrays always hold many variables of the same type). In fact, UDTs can even have procedures inside of them!

Members

The different variables and/or procedures stored inside a UDT are called "members", or more generally, items. Members can be variables of just about any type, including numerical types, strings, pointers, Enums, and even arrays. Variables are created in UDTs much the same way variables are created normally, except that the Dim keyword is optional. UDT members are accessed via the . Operator, so for example if you created a variable called someVar in a UDT you would access it with the name of the UDT variable followed by ".someVar". Here is an example:

'Define a UDT called myType, with an Integer member named someVar
Type myType
  As Integer someVar
End Type

'Create a variable of that type
Dim myUDT As myType

'Set the member someVar to 23, then display its contents on the screen
myUDT.someVar = 23
Print myUDT.someVar

Notice that the Type...End Type does not actually create a variable of that type, it only defines what variables of that type contain. You must create a variable of that type to actually use it!

UDT Pointers

UDT Pointers are, as the name implies, pointers to UDTs. They are created like regular pointers, but there is a special way to use them. To access the member of a UDT pointed to by a pointer, you use the -> Operator. For example, if myUDTPtr is a pointer to a UDT which has a member someVar, you would access the member as myUDTPtr->someVar, which is a much cleaner shorthand for the equally valid (*myUDTPtr).someVar.

Type rect
    x As Integer
    y As Integer
End Type

Dim r As rect
Dim rp As rect Pointer = @r

rp->x = 4
rp->y = 2

Print "x = " & rp->x & ", y = " & rp->y
Sleep

UDT Instantiation

When creating an object of a UDT:
- only non-static data members induce a specific memory allocation to each object instance,
- static data members are allocated only once for the UDT and are therefore common to all object instances,
- procedure members are also defined only once for the UDT and their code is not duplicated for each object instance (and this, even for the non-static procedure members).

That is why a static variable declared inside any procedure member is allocated only once for the UDT.
Therefore this static variable is indeed permanent but shared by all UDT instances executing the code (it is not an instance-specific variable).

See also

  • Type Aliases
  • Temporary Types
  • Constructors and Destructors (basics)
  • Member Procedures
  • Member Access Rights and Encapsulation
  • Operator Overloading


Type Aliases
Additional names for variable or object types

Overview
Declaration
Overload resolution
Pointers to procedure pointers
Type forwarding
Incomplete types

Overview

Type aliases are alternative names for a type. They can be used to facilitate a mass change from one type to another, save typing, or make circular dependency possible.

Declaration

Type aliases are declared using the Type keyword much like declaring variables or objects with Extern or Dim.

The following example declares a type alias to Single called "float", a procedure, and defines and initializes two variables of that type:

Type float As Single

Declare Function Add (a As float, b As float) As float

Dim foo As float = 1.23
Dim bar As float = -4.56

Procedure pointer type aliases are declared in the same fashion, as shown in the following example:

Declare Function f (ByRef As String) As Integer

Type func_t As Function (ByRef As String) As Integer

Dim func As func_t = @f

Function f (ByRef arg As String) As Integer
    Function = CInt(arg)
End Function

Overload resolution

Type aliases are just that - aliases. For all intents and purposes, a type alias is the type it aliases. So as far as procedure overload resolution is concerned, a procedure declared with a parameter of type "alias_to_T" is the same as a procedure declared with a parameter of type "T" (the same applies to overloading member procedures as well).

In other words, it is an error - duplicated definition - to declare a procedure where parameters differ only in a type and its alias, as the following example shows:

Type float As Single

Declare Sub f Overload (a As Single)

'' If following line is uncommented, this will generate a duplicated definition error
'' Declare Sub f (a As float)

Pointers to procedure pointers

Pointers to procedure pointers are just like any other pointer type, except they point to procedure pointers. Because the syntax for declaring procedure pointers doesn't allow directly creating a pointer to procedure pointer when the procedure is a function (because ptr applies on return type and not on procedure), a type alias is used.

The following example declares a pointer to a procedure returning an integer pointer, and then a pointer to a pointer to a procedure returning an integer:

Dim pf As Function() As Integer Ptr

Type pf_t As Function() As Integer
Dim ppf As pf_t Ptr

Type forwarding

Type aliases can be forward referencing: an alias can refer to some other type not yet fully defined.

Type foo As bar

Type sometype
  f   As foo Ptr
End Type

Type bar
  st  As sometype
  a   As Integer
End Type

Using a type alias and forward referencing allows circular dependencies between types.

Type list As list_

Type listnode
  parent As list Ptr
  text As String
End Type

Type list_
  first As listnode Ptr
  count As Integer
End Type

Incomplete types

A type is considered incomplete until the size of it, that is the number of bytes it would need to occupy in memory is known, and the offsets of all of its fields are known. It is not possible to allocate space for an incomplete type. It is not possible to declare a variable having the data type of an incomplete type, pass an incomplete type as a parameter, or access the members of an incomplete type.

However, pointers to incomplete types may be allocated, declared as members in other types, or passed as parameters to a procedures since the size of a pointer is known.

Type sometype As sometype_

'' Not allowed since size of sometype is unknown
'' TYPE incomplete
''   a AS sometype
'' END TYPE

'' Allowed since size of a pointer is known
Type complete
  a As sometype Ptr
End Type
Dim x As complete

'' Not allowed since size of sometype is still unknown
'' DIM size_sometype AS INTEGER = SIZEOF( sometype )

'' Complete the type
Type sometype_
  value As Integer
End Type

'' Allowed since the types are now completed
Dim size_sometype As Integer = SizeOf( sometype )

Type completed
  a As sometype
End Type

Dim size_completed As Integer = SizeOf( completed )


Type (UDT/Alias/Temporary) and Union
The different keywords Type (UDT), Type (Alias), Type (Temporary), and Union.

Definition

There may be confusion in the user mind between the different keywords Type and Union (despite the documentation that distinguishes them from each other through some different pages):
    • Type (Udt):
Declares/Defines a user structure of non overlapping data with possibly member procedures:
- Named Type: Type that has a name that can be referenced to declare the data type.
- Anonymous Type: Type that does not have a name and so cannot be referenced by a name.
    • Type(Alias):
Declares a user defined type name from other user defined or standard data types.
    • Type (Temporary):
Creates a temporary instance of a user defined type.
    • Union:
Declares/Defines a user structure of overlapping data with possibly member procedures but no object fields (or bases) with constructors or destructors (directly on indirectly):
- Named Union: Union that has a name that can be referenced to declare the data type.
- Anonymous Union: Union that does not have a name and so cannot be referenced by a name.

Complexity level for a Type/Union structure:
- Trivial structure: Named or Anonymous structure that has no member procedures (directly on indirectly).
- Complex structure: Named Structure that has member procedure(s) (directly or indirectly).
- Extended structure: Named structure that inherits from another named structure.

Type/Union structure can be also nested inside another Type/Union structure:
- Nested Named Type: Named Type that is nested inside another Named Type/Union structure.
- Nested Anonymous Type: Anonymous Type that is nested inside another Named or Anonymous Union structure.
- Nested Named Union: Named Union that is nested inside another Named Type/Union structure.
- Nested Anonymous Union: Anonymous Union that is nested inside another Named or Anonymous Type structure.

Type (Alias) declaration can be also nested inside a Type/Union structure:
- Nested Type (Alias) declaration allows declaration of an inner alias inside a Type/Union and according to the access right of the place.

Description

The main purpose of Nested Named Types/Unions is:
- To allow relationships between Types and Unions to be expressed in an understandable way.
- Such a Type or Union has its own namespace in which to organize related symbols - and provide visibility access (Public/Protected/Private) for Types only - to give finer-grained control over access to members.
- When there are circular dependencies between Types/Unions, Nested Named Type/Union usage can avoid using Type (Alias) and forward referencing.
Without Nested Named Types/Unions:
- Relationships can still be expressed between Types/Unions but have the disadvantage that all Types/Unions must be publicly accessible.
- Namespace use can help organize scope of visibility but the problem remains that *all* Types/Unions must be visible.
- In some situations this allows a user (or the author himself) to inadvertently use a Type/Union in a way that it was never intended, potentially creating valid but error-prone code.
- This problem can be reduced by hiding Types/Unions through of forward declarations (via Type (Alias)) and putting private symbols in a separate module, but creates new problems by fragmenting the code.

Anonymous Type (or Union):
- Does not have a name and cannot be referenced by name.
- May only appear nested within a Named Union (or Type) or another Anonymous Union (or Type) .
- Declares fields within the structure that nests it, either at separate memory locations (Nested Type) or overlapping memory locations (Nested Union).
- Fields can be referenced.
- Cannot contain any static members or procedures declarations (directly or indirectly) => Trivial structure.

Named Type (or Union):
- Has a name that can be referenced to declare the data type for fields, variables, parameters, arrays, etc.
- Can appear on its own or nested inside another Named Type (or Union).
- Declaration of the Type (or Union) only.
- Does not declare any fields within the structure that nests it, so it does not add any size to it.
- Can contain static member(s) or procedure declaration(s) (directly or indirectly) => Trivial/Complex structure.

Complexity level for Type/Union structure versus scope location:
- Trivial structure: Named or Anonymous Type/Union that can be declared at global / namespace / nested scopes or inside local scopes (like Scope or Sub).
- Complex structure: Named Type/Union that must be declared at the global / namespace / nested scope with definitions at the global / namespace scope.
- Extended structure: Named Type/Union declared in a scope that has visibility to its Base's scope (currently in same scope than its Base).

The main structure (Type/Union) must be always Named, the other (Nested) structures can be Anonymous or Named.
Mostly everything it can be done in a Named Type/Union can also be done in a Nested Named Type/Union, including the inheritance capability (as base or as derived).
Nested Anonymous Type/Union can not have procedure members or static data members (same restriction than for a Named Type/Union inside a local scope).

Example

Example: 'Named Type' <- 'Nested Anonymous Union' <- 'Nested Anonymous Type':
Type T  '' named
    Union  '' anonymous
        Dim a As Short
        Type  '' anonymous
            Dim b1 As Byte
            Dim b2 As Byte
        End Type
    End Union
    Declare Sub proc()
End Type

Sub T.proc()
    b1 = 1
    b2 = 2
    Print b1, b2, a
End Sub

Dim x As T
x.proc()

Sleep

Example of circular dependencies between two main types solved by Nested Named Type usage (to avoid using type aliases and forward referencing):
'' example of two dependent types, with a type alias and a forward referencing:
'
'Type list As list_
'
'Type listnode
'    text As String
'    parent As list Ptr
'End Type
'
'Type list_
'    first As listnode Ptr
'    count As Integer
'End Type

'' same example of two dependent types, but with simply one of them a Nested Named Type:

Type list
    count As Integer
    Type listnode
        text As String
        parent As list Ptr
    End Type
    first As listnode Ptr
End Type

Example to check access syntaxes and access rights:
Type UDT1
    Private:
        Dim As Integer I1 = 123
        Type UDT2
            Private:
                Dim As Integer I2 = 456
            Public:
                Declare Function returnI2() As Integer
        End Type
    Public:
        Type UDT3
            Private:
                Dim As Integer I3 = 789
            Public:
                Declare Function returnI3() As Integer
        End Type
        Declare Function returnI1() As Integer
        Dim As UDT2 t2
        Dim As UDT3 t3
End Type

Function UDT1.returnI1() As Integer
    Return This.I1
End Function

Function UDT1.UDT2.returnI2() As Integer
    Return This.I2
End Function

Function UDT1.UDT3.returnI3() As Integer
    Return This.I3
End Function

Dim As UDT1 t11

'Print t11.I1            '' error 202: Illegal member access
                         ''    I1 is in the private section of UDT1

Print t11.returnI1()     '' OK, returnI1() is in the public section of UDT1

'Print t11.t2.I2         '' error 202: Illegal member access
                         ''    t2 is in the public section of UDT1, but I2 is in the private section of UDT2

Print t11.t2.returnI2()  '' OK, t2 is in the public section of UDT1, and returnI2() is in the public section of UDT2

'Print t11.t3.I3         '' error 202: Illegal member access
                         ''    t3 is in the public section of UDT1, but I3 is in the private section of UDT3

Print t11.t3.returnI3()  '' OK, t3 is in the public section of UDT1, and returnI3() is in the public section of UDT3
Print

'Dim As UDT1.UDT2 t21    '' error 202: Illegal member access
                         ''    UDT2 is in the private section of UDT1

Dim As UDT1.UDT3 t31     '' OK, UDT3 is in the public section of UDT1

'Print t31.I3            '' error 202: Illegal member access
                         ''    I3 is in the private section of UDT3

Print t31.returnI3()     '' OK, returnI3() is in the public section of UDT3

Sleep

Example of what we can do with nested named types.
This is just the declaration part, but gives a few things worth pointing out:
Type MemoryTable
    Private:
        '' private members visible within MemoryTable only
        Const default_alignment = 16
        m_bytes_per_node As UInteger = 0
        m_nodes_per_chunk As UInteger = 0
        Type Chunk
            m_owner As MemoryTable Ptr
            m_nodes As UInteger
            m_prevChunk As Chunk Ptr
            m_data As Any Ptr
        End Type
        Declare Sub allocChunk()
        m_headChunk As Chunk Ptr = 0
    Protected:
        '' protected members visible within MemoryTable and derived types
        Declare Constructor()
        Type Iterator
            Private:
                m_chunk As Chunk Ptr
                m_index As Integer
            Protected:
                Declare Constructor()
                Declare Constructor(ByVal table As MemoryTable Ptr)
                Declare Property item() As Any Ptr
            Public:
                Declare Property hasItem() As Boolean
                Declare Sub nextItem()
        End Type
        Declare Function newNode() As Any Ptr
    Public:
        '' public member visible from outside the type
        Declare Constructor(ByVal bytes As UInteger, nodes As UInteger)
        Declare Destructor()
End Type

See also

  • Overview (User Defined Types)
  • Type Aliases
  • Temporary Types
  • Constructors and Destructors (basics)
  • Member Access Rights and Encapsulation


Constructors and Destructors (basics)
In charge of the creation and destruction of objects.

Overview
Declaration
Default constructors
Copy constructors
Calling constructors
Compiler-provided constructors and destructors

Overview

Constructors and destructors are responsible for creating and destroying objects, respectively. In general, constructors give objects their initial state, that is, they give meaningful values to their objects' member data. Destructors perform the opposite function; they make sure any resources owned by their objects are properly freed.

Simply, constructors are special member procedures that are called when an object is created, and destructors are special member procedures called when an object is destroyed. Both constructors and destructors are called automatically by the compiler whenever an object is created or destroyed, whether explicitly with the use of the Dim or New Expression/Delete Statement keywords, or implicitly by passing an object to a procedure by value or through an object going out of scope.

Declaration

Constructors and destructors are declared like member procedures but with the Constructor keyword instead of Sub or Function, and without a name. Similarly, they are defined with only the name of the Type or Class they are declared in.

A Type or Class can have multiple constructors, but only one destructor.

Default constructors

Default constructors are constructors that either have no parameters, or all of their parameters have a default value. They are called when an object is defined but not initialized, or is created as part of an array, with the Dim, ReDim or New[] expression keywords. The first constructor declared in the example below is a default constructor.

Copy constructors

Copy constructors are constructors called when an object is created, or cloned, from another object of the same type (or an object that can be converted to that type). This happens explicitly when initializing an object with another object, or implicitly by passing an object to a procedure by value. Copy constructors are declared having one parameter: an object of the same type passed by reference.

Copy constructors are only called when creating and initializing object instances. Assignment to objects is handled by the Member Operator Let.

Calling constructors

Unlike other member procedures, constructors are generally not called directly from an object instance. Instead, a constructor is specified in a Dim statement either with an initializer or without one, or in a New Expression statement with or without arguments.

When specifying an initializer for an object, the name of the type followed by any arguments it requires is used.

Type foo
    '' Declare a default ctor, copy ctor and normal ctor
    Declare Constructor
    Declare Constructor (ByRef As foo)
    Declare Constructor (As Integer)

    '' Declare a destructor
    Declare Destructor

    ints As Integer Ptr
    numints As Integer
End Type

'' Define a constructor that creates 100 integers
Constructor foo
    ints = New Integer(100)
    numints = 100
End Constructor

'' Define a constructor that copies the integers from another object
Constructor foo (ByRef x As foo)
    ints = New Integer(x.numints)
    numints = x.numints
End Constructor

'' Define a constructor that creates some integers based on a parameter
Constructor foo (n As Integer)
    ints = New Integer(n)
    numints = n
End Constructor

'' Define a destructor that destroys those integers
Destructor foo
    Delete[] ints
End Destructor

Scope
    '' calls foo's default ctor
    Dim a As foo
    Dim x As foo Ptr = New foo

    '' calls foo's copy ctor
    Dim b As foo = a
    Dim y As foo Ptr = New foo(*x)

    '' calls foo's normal ctor
    Dim c As foo = foo(20)
    Dim z As foo Ptr = New foo(20)

    '' calls foo's dtor
    Delete x
    Delete y
    Delete z
End Scope '' <- a, b and c are destroyed here as well

Compiler-provided constructors and destructors

If no copy constructor is declared for a Type or Class, the compiler provides one. If no constructor has been declared, the compiler also provides a default constructor.

The compiler-provided default constructor initializes member data to default values, that is, numeric and pointer members are set to zero (0), and object members are default-constructed. The copy constructor that the compiler declares shallow-copies all member data from one type to another: numeric and pointer types are initialized with the corresponding data members in the object that is copied, and object members are copy-constructed from their corresponding object members. This means that dynamic resources, such as memory pointed to by a pointer data member, is not copied; only the address is copied. So if an object owns a resource, meaning it is responsible for its creation and destruction, then the compiler-generated copy constructor will not be sufficient.

If a destructor is not declared, the compiler generates one. This destructor calls object members' destructors and does nothing for numeric and pointer types. Again, if an object owns a dynamic resource, then the compiler-generated destructor will not be sufficient, as the resource will not be freed when the object is destroyed.

This is commonly referred to as the "Rule of 3": If an object needs a custom copy constructor, assignment operator or destructor, chances are it needs all three.

See also

  • Constructor, Destructor
  • Constructors, '=' Assignment-Operators, and Destructors (advanced, part #1)
  • Constructors, '=' Assignment-Operators, and Destructors (advanced, part #2)


Member Procedures
Procedures with full access to members of a Type or Class

Declaration and definition
Declaring and defining member procedures.
Usage
Calling member procedures.
The hidden parameter, This
Implicit access to the instance with which non-static member procedures are called.
Access rights
Referring to other members in member procedures.
Overloading
Declaring two or more member procedures with the same name.
Static member procedures
Differences from non-static member procedures.

The term 'member procedure' refers to both static and non-static member procedures, unless otherwise noted.

Declaration and definition

Member procedures are declared much like normal module-level procedures except that they are declared within, and defined outside, a Type or Class definition [1].

When defining member procedures, the procedure name is prefixed with the name of the Type or Class and the member access operator (Operator . (Member Access)). It is an error to define a member procedure without a matching declaration in the Type or Class definition.

The following example declares and defines a Sub and Function member procedure:

'' foo1.bi

Type foo
    Declare Sub f (As Integer)
    Declare Function g As Integer

    i As Integer
End Type

Sub foo.f (n As Integer)
    Print n
End Sub

Function foo.g As Integer
    Return 420
End Function

Usage

Member procedures are referred to just like member data, that is, their name is prefixed with the name of an object instance and the member access operator (Operator . (Member Access)) [2].

The following example, using the code from the last example, calls Sub and Function member procedures:

'' ... foo with non-static members as before ...
#include Once "foo1.bi"

Dim bar As foo
bar.f(bar.g())

The hidden parameter, This

Member procedures actually have an additional parameter than what they are declared with [3]. When they are called, using the name of an instance and Operator . (Member Access), a reference to that instance is passed along with any other arguments in the call, allowing the member procedure direct access to the instance.

The additional parameter added by the compiler is called This, and since it's a reference, any modifications to This are actually modifications to the instance that was passed to the member procedure when it was called. You can use This just like any other variable, ie., pass it to procedures taking a object of the same type, call other member procedures and access member data using Operator . (Member Access), etc.

Most of the time, however, using This explicitly is unnecessary; member procedures can refer to other members of the instance which they are passed directly by name, without having to qualify it with This and Operator . (Member Access). The only times when you need to qualify member names with This is when the member name is hidden, for example, by a parameter or local variable. In these situations, qualifying the member name is the only way to refer to these hidden member names.

Note:
To access duplicated symbols defined as global outside the Type, add one or preferably two dot(s) as prefix: .SomeSymbol or preferably ..SomeSymbol (or only ..SomeSymbol if inside a With..End With block).

The following example uses the This keyword to refer to member data whose name is hidden by a parameter and local variable:

Type foo
    Declare Sub f (i As Integer)
    Declare Sub g ()

    i As Integer = 420
End Type

Sub foo.f (i As Integer)
    '' A parameter hides T.i, so it needs to be qualified to be used:
    Print this.i
End Sub

Sub foo.g ()
    '' A local variable hides T.i, so it needs to be qualified to be used:
    Dim i As Integer
    Print this.i
End Sub

Access rights

Unlike normal module-level procedures, member procedures have full access rights to the members of the Type or Class they are declared in; they can refer to the public, protected and private members of a Type or Class.

Overloading

A member procedure can be declared to have the same name as another member procedure, provided the parameters are different, either in number or in type. This is referred to as overloading.

Only the parameters are used to determine if a procedure declaration is a valid overload. For example, a Type or Class could have static and non-static member procedures with the same name, or Sub and Function member procedures with the same name

Unlike a module-level procedure, which needs to specify the Overload clause in the declaration to allow overloading it, a member procedure is overloadable by default, and does not need the Overload clause.

Type T
    Declare Sub f

    '' Different number of parameters:
    Declare Sub f (As Integer)

    '' Different type of parameters:
    Declare Sub f (ByRef As String)

    '' Again, parameters are different:
    Declare Function f (As UByte) As Integer

    '' following three members would cause an error,
    '' number of parameters and/or types do not differ:

    '' Declare Function f As Integer
    '' Declare Function f (As UByte) As String
    '' Declare Static Function f (As UByte) As Integer

    '' ...
    somedata As Any Ptr
End Type

Static member procedures

Static member procedures are declared and defined much in the same way as non-static member procedures, with the Static keyword preceding the declaration and definition.

Member procedures defined using the Static keyword must be declared with the Static keyword in the Type or Class definition, or a compiler error will occur. Like non-static member procedures, it is an error to define a static member procedure without a matching declaration in the Type or Class definition.

Do not confuse this with procedure definitions that specify static storage for their variables and objects by appending the Static keyword to the procedure header. The Static keyword can be used in both contexts, however; static member procedures can be defined with static variable and object storage.

The following example declares two static member procedures, the first of which also has static variable and object storage. Note that the Static keyword is optional in the member procedure definition:

'' foo2.bi

Type foo
    Declare Static Sub f (As Integer)
    Declare Static Function g As Integer

    i As Integer
End Type

Static Sub foo.f (n As Integer) Static
    Print n
End Sub

Function foo.g As Integer
    Return 420
End Function

Static member procedures can be called like non-static member procedures, qualifying the name of the procedure with the name of an instance and the member access operator (Operator . (Member Access)).

They can also be called by qualifying the procedure name with the name of the Type or Class they were declared in and the member access operator (Operator . (Member Access)). In other words, an instance is not required in order to call static-member procedures.

The following example, using the code from the last example, uses both ways to call static member procedures:

'' ... foo with static members as before ...
#include Once "foo2.bi"

Dim bar As foo
bar.f(foo.g())

Unlike non-static member procedures, which are declared with an extra This parameter, static member procedures do not get passed an instance when called. Because of this, static member procedures can only refer to constants, enumerations, other static members (data or procedures), etc., without qualifying their names. Static member procedures can still refer to non-static members when qualified with an instance, for example: a parameter or local variable.

The following example refers to a non-static member from a static procedure:

Type foo
    Declare Static Sub f (ByRef As foo)

    i As Integer
End Type

Sub foo.f (ByRef self As foo)
    '' Ok, self is an instance of foo:
    Print self.i

    '' would cause error
    '' cannot access non-static members, no foo instance:
    '' Print i
End Sub

[1] In the future, member procedures may be able to be defined within the Type or Class definition.
[2] Static member procedures do not require an object instance in order to be called.
[3] Static member procedures do not have this extra parameter added by the compiler, and so cannot access the object instance from which it was called with.


Properties
Properties are a special mix of member variable and member procedure.

They provide a way to set or retrieve values of an object, through normal looking assignments or member accesses, but also let the object perform actions if it needs to update itself.

Basic properties
Declaring and using setter and getter properties.
Indexed properties
Properties with an additional parameter.

Basic properties

A property is declared similar to a member procedure, except that the Property keyword is used instead of Sub or Function. For example, let's consider a window class for a windowing system or GUI library.

Type Window
Private:
    As String title_
End Type

Dim As Window w

In order to set the window's title, a setter property can be added:

Type Window
    Declare Property title(ByRef s As String)
Private:
    As String title_
End Type

Property Window.title(ByRef s As String)
    this.title_ = s
End Property

Dim As Window w
w.title = "My Window"

It is very similar to a member Sub, as it takes a parameter and updates the object to the new state based on the parameter. However, the syntax for sending this parameter is a basic assignment, not a function call. By assigning the new value to the title property, the property procedure will automatically be called with the given new value, and can update the window to reflect the change. It is up to the object how to represent the property state internally.

By design, properties can only be assigned one value at a time, and as a result the property procedure can not have more than one parameter.

After setting the window title, it should also be possible to retrieve it. Here is how to add a getter property:

Type Window
    '' setter
    Declare Property title(ByRef s As String)
    '' getter
    Declare Property title() As String
Private:
    As String title_
End Type

'' setter
Property Window.title(ByRef s As String)
    this.title_ = s
End Property

'' getter
Property Window.title() As String
    Return this.title_
End Property

Dim As Window w
w.title = "My Window"
Print w.title

The getter is very similar to a Function. It is supposed to return the current value of the property, and it allows the current value to be calculated from other internal values, if needed. Note that both setter and getter use the same identifier, indicating they handle the same property.

Just like method overloading, it is possible to specify multiple setters, provided they have different parameter types:

Type Window
    Declare Property title(ByRef s As String)
    Declare Property title(ByVal i As Integer)
    Declare Property title() As String
Private:
    As String title_
End Type

Property Window.title(ByRef s As String)
    this.title_ = s
End Property

Property Window.title(ByVal i As Integer)
    this.title_ = "Number: " & i
End Property

Property Window.title() As String
    Return this.title_
End Property

Dim As Window w
w.title = "My Window"
Print w.title
w.title = 5
Print w.title

In comparison to this example of properties, here is similar code that does not use properties:

Type Window
    Declare Sub set_title(ByRef s As String)
    Declare Sub set_title(ByVal i As Integer)
    Declare Function get_title() As String
Private:
    As String title
End Type

Sub Window.set_title(ByRef s As String)
    this.title = s
End Sub

Sub Window.set_title(ByVal i As Integer)
    this.title = "Number: " & i
End Sub

Function Window.get_title() As String
    Return this.title
End Function

Dim As Window w
w.set_title("My Window")
Print w.get_title()
w.set_title(5)
Print w.get_title()

The code is basically the same, only the syntax is different. Properties are specifically designed to combine the setter/getter concept and the language's normal way of literally assigning and accessing values to a class' member variables. It is up to the programmers to decide which way they prefer.

Here is an example demonstrating a text user interface window class allowing to set position and title using properties:

Namespace tui
    Type Point
        Dim As Integer x, y
    End Type

    Type char
        Dim As UByte value
        Dim As UByte Color
    End Type

    Type Window
        '' public
        Declare Constructor _
            ( _
                x As Integer = 1, y As Integer = 1, _
                w As Integer = 20, h As Integer = 5, _
                title As ZString Ptr = 0 _
            )

        Declare Destructor

        Declare Sub show

        '' title property
        Declare Property title As String
        Declare Property title( new_title As String )

        '' position properties
        Declare Property x As Integer
        Declare Property x( new_x As Integer )

        Declare Property y As Integer
        Declare Property y( new_y As Integer )

    Private:
        Declare Sub redraw
        Declare Sub remove
        Declare Sub drawtitle

        Dim As String p_title
        Dim As Point Pos
        Dim As Point siz
    End Type

    Constructor Window _
        ( _
            x_ As Integer, y_ As Integer, _
            w_ As Integer, h_ As Integer, _
            title_ As ZString Ptr _
        )

        pos.x = x_
        pos.y = y_
        siz.x = w_
        siz.y = h_

        If( title_ = 0 ) Then
            title_ = @"untitled"
        End If

        p_title = *title_
    End Constructor

    Destructor Window
        Color 7, 0
        Cls
    End Destructor

    Property window.title As String
        title = p_title
    End Property

    Property window.title( new_title As String )
        p_title = new_title
        drawtitle
    End Property

    Property window.x As Integer
        Return pos.x
    End Property

    Property window.x( new_x As Integer )
        remove
        pos.x = new_x
        redraw
    End Property

    Property window.y As Integer
        Property = pos.y
    End Property

    Property window.y( new_y As Integer )
        remove
        pos.y = new_y
        redraw
    End Property

    Sub window.show
        redraw
    End Sub

    Sub window.drawtitle
        Locate pos.y, pos.x
        Color 15, 1
        Print Space( siz.x );
        Locate pos.y, pos.x + (siz.x \ 2) - (Len( p_title ) \ 2)
        Print p_title;
    End Sub

    Sub window.remove
        Color 0, 0
        Var sp = Space( siz.x )
        For i As Integer = pos.y To pos.y + siz.y - 1
            Locate i, pos.x
            Print sp;
        Next
    End Sub

    Sub window.redraw
        drawtitle
        Color 8, 7
        Var sp = Space( siz.x )
        For i As Integer = pos.y + 1 To pos.y + siz.y - 1
            Locate i, pos.x
            Print sp;
        Next
    End Sub
End Namespace

Dim win As tui.window = tui.window( 3, 5, 50, 15 )

win.show
Sleep 500

win.title = "Window 1"
Sleep 250
win.x = win.x + 10
Sleep 250

win.title = "Window 2"
Sleep 250
win.y = win.y - 2
Sleep 250

Locate 25, 1
Color 7, 0
Print "Press any key...";

Sleep

Note how updating the window's position or title automatically causes the window to be redrawn.

Indexed properties

Properties can have an additional parameter that is called an index (currently only one additional parameter is allowed). The index is specified in parentheses behind the property's name, as if the property was an array (with only one dimension). For example:

Type IntArray
    '' setters
    Declare Property value(index As Integer, v As Integer)
    Declare Property value(index As String, v As Integer)
    Declare Property value(index As Integer, v As String)
    Declare Property value(index As String, v As String)

    '' getters
    Declare Property value(index As Integer) As Integer
    Declare Property value(index As String) As Integer

Private:
    Dim As Integer data_(0 To 9)
End Type

Property IntArray.value(index As Integer) As Integer
    Return This.data_(index)
End Property

Property IntArray.value(index As String) As Integer
    Return This.data_(CInt(index))
End Property

Property IntArray.value(index As Integer, v As Integer)
    This.data_(index) = v
End Property

Property IntArray.value(index As String, v As Integer)
    This.data_(CInt(index)) = v
End Property

Property IntArray.value(index As Integer, v As String)
    This.data_(index) = CInt(v)
End Property

Property IntArray.value(index As String, v As String)
    This.data_(CInt(index)) = CInt(v)
End Property

Dim a As IntArray

a.value(0) = 1234
a.value("1") = 5678
a.value(2) = "-1234"
a.value("3") = "-5678"

Print a.value(0)
Print a.value("1")
Print a.value(2)
Print a.value("3")

Sleep

This simulates an integer array that can be assigned strings, and even be indexed with strings. See KeyPgProperty for another example.


Variable-length member data
Management of variable-length strings/arrays as members of a Type.

Preamble:

In FreeBASIC, Type data structures must ultimately be fixed-size, such that the compiler knows how much memory to allocate for objects of that Type.
Nevertheless, Types may contain variable-length string or array data members.

However, the string's/array's data will not be embedded in the Type directly. Instead, the Type will only contain a string/array descriptor structure, which FreeBASIC uses behind the scenes to manage the variable-length string/array data.
For sizing the structure of the array descriptor in the Type, a variable-length array data member must be always declared by using Any(S) in place of the array bounds, in order to fix the amount of dimensions based on the number of Anys specified. A variable-length array data member can also be pre-sized in its declaration by using syntax with ReDim.

Variable-length array fields are considered as pseudo-objects when they are declared in a Type (variable-length strings are real objects).
So the implicit copy constructor and the implicit let operator of the Type themselves support [re]sizing and copying such strings/arrays, or their erasing.

Implicit string/array sizing and copying by the compiler code

When the compiler build a default copy-constructor and a default copy-assignment operator for such a Type (having string/array members), it also includes all code for sizing the destination string/array and copying the data from the source string/array, if needed.

Example:
Type UDT
    Dim As String s
    Dim As Integer array(Any)
End Type

Dim As UDT u1, u2

u1.s = "FreeBASIC"
ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
    u1.array(I) = I
Next I

u2 = u1
Print u2.s
For I As Integer = LBound(u2.array) To UBound(u2.array)
    Print u2.array(I);
Next I
Print
Print

Dim As UDT u3 = u1
Print u3.s
For I As Integer = LBound(u3.array) To UBound(u3.array)
    Print u3.array(I);
Next I
Print

Sleep

Output:
FreeBASIC
 1 2 3 4 5 6 7 8 9

FreeBASIC
 1 2 3 4 5 6 7 8 9
Implicit string/array sizing and copying by the compiler code broken by an explicit copy-constructor and copy-assignment operator

If the user want to specify his own copy-constructor and copy-assignment operator (to initialize additional complex field members for example), the above automatic string/array sizing and copying by compiler code is broken.

Example:
Type UDT
    Dim As String s
    Dim As Integer array(Any)
    Declare Constructor ()
    Declare Constructor (ByRef u As UDT)
    Declare Operator Let (ByRef u As UDT)
    'user fields
End Type

Constructor UDT ()
    'code for user fields in constructor
End Constructor

Constructor UDT (ByRef u As UDT)
    'code for user fields in copy-constructor
End Constructor

Operator UDT.Let (ByRef u As UDT)
    'code for user fields in copy-assignement operator
End Operator

Dim As UDT u1, u2

u1.s = "FreeBASIC"
ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
    u1.array(I) = I
Next I

u2 = u1
Print u2.s
For I As Integer = LBound(u2.array) To UBound(u2.array)
    Print u2.array(I);
Next I
Print
Print

Dim As UDT u3 = u1
Print u3.s
For I As Integer = LBound(u3.array) To UBound(u3.array)
    Print u3.array(I);
Next I
Print

Sleep

Output (blank):
String/Array sizing and copying explicitly set in the user copy-constructor and copy-assignment operator

The variable-length array cannot be processed as a true object like a variable-length string, because for example there is no implicit assignment.
Referring to the above example, This.array() = u.array() is disallowed, while This.s = u.s is allowed.
The user must code explicitly the sizing and the copying of the array member (for the array data copy, a C run-time function memcpy() is used to optimize the execution time).

Example:
#include "crt/string.bi"  '' C run-time header for 'memcpy()'

Type UDT
    Dim As String s
    Dim As Integer array(Any)
    Declare Constructor ()
    Declare Constructor (ByRef u As UDT)
    Declare Operator Let (ByRef u As UDT)
    'user fields
End Type

Constructor UDT ()
    'code for user fields in constructor
End Constructor

Constructor UDT (ByRef u As UDT)
    This.s = u.s
    If UBound(u.array) >= LBound(u.array) Then  '' explicit array sizing and copying
        ReDim This.array(LBound(u.array) To UBound(u.array))
        memcpy(@This.array(LBound(This.array)), @u.array(LBound(u.array)), (UBound(u.array) - LBound(u.array) + 1) * SizeOf(@u.array(LBound(u.array))))
    End If
    'code for user fields in copy-constructor
End Constructor

Operator UDT.Let (ByRef u As UDT)
    If @This <> @u Then  '' not self-assignment
        This.s = u.s
        If UBound(u.array) >= LBound(u.array) Then  '' explicit array sizing and copying
            ReDim This.array(LBound(u.array) To UBound(u.array))
            memcpy(@This.array(LBound(This.array)), @u.array(LBound(u.array)), (UBound(u.array) - LBound(u.array) + 1) * SizeOf(@u.array(LBound(u.array))))
        End If
        'code for user fields in copy-assignement operator
    End If
End Operator

Dim As UDT u1, u2

u1.s = "FreeBASIC"
ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
    u1.array(I) = I
Next I

u2 = u1
Print u2.s
For I As Integer = LBound(u2.array) To UBound(u2.array)
    Print u2.array(I);
Next I
Print
Print

Dim As UDT u3 = u1
Print u3.s
For I As Integer = LBound(u3.array) To UBound(u3.array)
    Print u3.array(I);
Next I
Print

Sleep

Output:
FreeBASIC
 1 2 3 4 5 6 7 8 9

FreeBASIC
 1 2 3 4 5 6 7 8 9
Using an extra base Type containing the variable-length string and array

Another elegant possibility is to keep this sizing/copying, automatically coded by the compiler, but by simply calling it explicitly.
For this, an elegant solution for the member array is to no longer put it at the level of the Type itself, but rather in another specific Type which is inherited (seen from the outside, it is exactly the same). This is not necessary for the member string, but including also allows to save one code line each time.

Example:
Type UDT0
    Dim As String s
    Dim As Integer array(Any)
End Type

Type UDT Extends UDT0
    Declare Constructor ()
    Declare Constructor (ByRef u As UDT)
    Declare Operator Let (ByRef u As UDT)
    'user fields
End Type

Constructor UDT ()
    'code for user fields in constructor
End Constructor

Constructor UDT (ByRef u As UDT)
    Base(u)  '' inherited string copying plus array sizing and copying from Base implicit copy-constructor call
    'code for user fields in copy-constructor
End Constructor

Operator UDT.Let (ByRef u As UDT)
    Cast(UDT0, This) = u  '' inherited string copying plus array sizing and copying from Base implicit copy-assignement operator call
    'code for user fields in copy-assignement operator
End Operator

Dim As UDT u1, u2

u1.s = "FreeBASIC"
ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
    u1.array(I) = I
Next I

u2 = u1
Print u2.s
For I As Integer = LBound(u2.array) To UBound(u2.array)
    Print u2.array(I);
Next I
Print
Print

Dim As UDT u3 = u1
Print u3.s
For I As Integer = LBound(u3.array) To UBound(u3.array)
    Print u3.array(I);
Next I
Print

Sleep

Output:
FreeBASIC
 1 2 3 4 5 6 7 8 9

FreeBASIC
 1 2 3 4 5 6 7 8 9
See also

  • Constructors, '=' Assignment-Operators, and Destructors (advanced, part #1)
  • Constructors, '=' Assignment-Operators, and Destructors (advanced, part #2)


Member Access Rights and Encapsulation
Restricting member access to certain parts of code.

Member Access Rights

Overview
All members of a Type - including member data, procedures, constants, etc. - belong in one of three different classifications, each with its own rules dictating where in code they may be accessed, or referred to.
These rules are called access rights.
There are public, protected and private members, and they are declared in a Type definition following a Public, Protected or Private label, respectively.

By default, that is, without an access classification label, members of a Type are public.

Public members
Public members can be referred to from anywhere; they are accessible from, for example, member procedures or module-level code or procedures.

Protected members
Protected members can only be accessed from member procedures of the Type they are declared in, or member procedures of a derived Type. They are not accessible to outside code.

Private members
Private members can only be accessed from member procedures of the Type they are declared in. They are not accessible to outside code or member procedures from a derived Type.

Constructors and destructors
Constructors and destructors follow the same rules as any other member:
- When public, objects can be instantiated and destroyed from anywhere in code.
- When protected, objects can be instantiated and destroyed only from member procedures of their Type or a derived Type.
- Private constructors and destructors restrict object instantiation solely to member procedures of their Type.

Encapsulation

Overview
Encapsulation is the process of keeping the details about how an object is implemented hidden away from users of the object.
Instead, users of the object access the object through a public interface.
In this way, users are able to use the object without having to understand how it is implemented.

Encapsulation is implemented via access specifiers (Private, Protected or Public).
Typically, all member variables of the Type are made private (hiding the implementation details), and most member procedures are made public (exposing an interface for the user).
Although requiring users of the Type to use the public interface may seem more burdensome than providing public access to the member variables directly, doing so actually provides a large number of useful benefits that help encourage Type re-usability and maintainability.

Benefit of encapsulated Types
Protection:
Global access to variables is dangerous because you don’t have strict control over who has access to the global variable, or how they use it.
Only the public members of a Type suffers from the same problem, but just on a smaller scale.

Encapsulation allows the programmer of a Type to:
- Actively control the access to its internals (pointers, variables, ...), by: none / read only / write only / read & write.
- Secure operations, by denying certain destructive user actions (like pointer overwriting, deallocating, ...)

Abstraction:
With a fully encapsulated Type, you only need to know what member procedures are publicly available to use the Type, what arguments they take, and what values they return. It doesn’t matter how the Type was implemented internally.

For example, a Type holding a list of names could have been implemented using different data structures.
In order to use the Type, you don’t need to know (or care) which.
This dramatically reduces the complexity of your programs, and also reduces mistakes.

Hiding the internal implementation details:
- internal members declared as Private/Protected and user interface using methods and properties as getter/setter,
- in addition to define constructors, copy-constructor, destructor, assignment operators, ... ,
provides some abstraction.
More than any other reason, this is the key advantage of encapsulation.

Examples

  • In the example below, the data member hour, minute, and second are private while the member procedures set_Time(), get_Time() and increment_Time() are public:
- As all data is declared as private, the data is only accessible through the public procedures provided by the Type.
- This also allows programmers to validate changes to data members before making such a change. In this example, the set_Time() procedure would be written to check to valid values for time (hour between 0 and 23, minute and second between 0 and 59).
Type my_Time
    Public:
        Declare Sub set_Time (ByVal new_Hour As UByte, ByVal new_Minute As UByte, ByVal new_Second As UByte)
        Declare Sub get_Time (ByRef curr_Hour As UByte, ByRef curr_Minute As UByte, ByRef curr_Second As UByte)
        Declare Function get_Time () As String
        Declare Sub increment_Time ()
    Private:
        Dim As UByte Hour
        Dim As UByte Minute
        Dim As UByte Second
End Type

Example of use:
Type my_Time
    Public:
        Declare Sub set_Time (ByVal new_Hour As UByte, ByVal new_Minute As UByte, ByVal new_Second As UByte)
        Declare Sub get_Time (ByRef curr_Hour As UByte, ByRef curr_Minute As UByte, ByRef curr_Second As UByte)
        Declare Function get_Time () As String
        Declare Sub increment_Time ()
    Private:
        Dim As UByte Hour
        Dim As UByte Minute
        Dim As UByte Second
End Type

Sub my_Time.set_Time (ByVal new_Hour As UByte, ByVal new_Minute As UByte, ByVal new_Second As UByte)
    If new_Hour <= 23 And new_Minute <= 59 And New_Second <= 59 Then
        This.Hour = new_Hour
        This.Minute = new_Minute
        This.Second = new_Second
    End If
End Sub

Sub my_Time.get_Time (ByRef curr_Hour As UByte, ByRef curr_Minute As UByte, ByRef curr_Second As UByte)
    curr_Hour = This.Hour
    curr_Minute = This.Minute
    curr_Second = This.Second
End Sub

Function my_Time.get_Time () As String
    Return Right("0" & Str(This.Hour), 2) & ":" & Right("0" & Str(This.Minute), 2) & ":" & Right("0" & Str(This.second), 2)
End Function

Sub my_Time.increment_Time ()
    This.Second += 1
    If This.Second = 60 Then
        This.Second = 0
        This.Minute += 1
        If This.Minute = 60 Then
            This.Minute = 0
            This.Hour += 1
            If This.Hour = 24 Then
                This.Hour = 0
            End If
        End If
    End If
End Sub

Dim As my_Time my_T
Dim As UByte h, m, s
Input "Hour? ", h
Input "Minute? ", m
Input "Second? ", s
my_T.set_Time(h, m, s)

Print
Dim As Double Tr = Int(Timer)
Do
    If Tr <> Int(Timer) Then
        Tr = Int(Timer)
        my_T.increment_Time()
        Locate  , 1, 0
        Print my_T.get_Time;
    End If
    Sleep 100, 1
Loop Until Inkey <> ""
Print

  • For a more advanced example using OOP (with encapsulation + abstraction + inheritance + polymorphism), see the example (Graph type collection) in 'Inheritance Polymorphism'.

See also

  • Properties


Operator Overloading
Changing the way user defined types work with built-in operators.

Overview
Global Operators
Member Operators
Special Cases of Operators: '.' (Member access), '@' (Address of), '->' (Pointer to member access), and '*' (Value of)

Overview

Simply, operators are procedures, and their arguments are called operands. Operators that take one operand (Operator Not) are called unary operators, operators that take two operands (Operator +) are called binary operators and operators taking three operands (Operator Iif) are called ternary operators.

Most operators are not called like procedures. Instead, their operator symbol is placed next to their operands. For unary operators, their sole operand is placed to the right of the symbol. For binary operators, their operands - referred to as the left and right-hand side operands - are placed to the left and right of the operator symbol. FreeBASIC has one ternary operator, Operator Iif, and it is called like a procedure, with its operands comma-separated surrounded by parenthesis.
For example, the following code calls Operator Iif to determine if a pointer is valid. If it is, Operator * (Value Of) is called to dereference the pointer, and if not, Operator / (Divide) is called to find the value of twenty divided by four:
Dim i As Integer = 420
Dim p As Integer Ptr = @i

Dim result As Integer = IIf( p, *p, CInt( 20 / 4 ) )

Notice the call to Operator Iif is similar to a procedure call, while the calls to Operator * (Value Of) and Operator / (Divide) are not. In the example, p is the operand to Operator * (Value Of), and 20 and 4 are the left and right-hand side operands of Operator / (Divide), respectively.

All operators in FreeBASIC are predefined to take operands of standard data types, like Integer and Single, but they may also be overloaded for user-defined types; that is, they can be defined to accept operands that are objects as well. There are two types of operators that can be overloaded, global operators and member operators.

Global Operators

Global operators are those that are declared in module-level scope (globally). These are the operators - (Negate), Not (Bitwise Not), -> (Pointer To Member Access), * (Value Of), + (Add), - (Subtract), * (Multiply), / (Divide), \ (Integer Divide), & (Concatenate), Mod (Modulus), Shl (Shift Left), Shr (Shift Right), And (Bitwise And), Or (Bitwise Or), Xor (Bitwise Xor), Imp (Bitwise Imp), Eqv (Bitwise Eqv), ^ (Exponentiate), = (Equal), <> (Not Equal), < (Less Than), > (Greater Than), <= (Less Than Or Equal), >= (Greater Than Or Equal), Abs, Sgn, Fix, Frac, Int, Exp, Log, Sin, Asin, Cos, Acos, Tan, Atan, Len, and Sqr.

Declaring a custom global operator is similar to declaring a procedure. The Declare keyword is used with the Operator keyword. The operator symbol is placed next followed by the comma-separated list of parameters surrounded in parenthesis that will represent the operands passed to the operator. Unlike procedures, operators can be overloaded by default, so the Overload keyword is not necessary when declaring custom operators. At least one of the operator's parameters must be of user-defined type (after all, operators with built-in type parameters are already defined).

The following example declares the global operators - (Negate) and + (Multiply) to accept operands of a user-defined type:
Type Rational
    As Integer numerator, denominator
End Type

Operator - (ByRef rhs As Rational) As Rational
    Return Type(-rhs.numerator, rhs.denominator)
End Operator

Operator * (ByRef lhs As Rational, ByRef rhs As Rational) As Rational
    Return Type(lhs.numerator * rhs.numerator, _
        lhs.denominator * rhs.denominator)
End Operator

Dim As Rational r1 = (2, 3), r2 = (3, 4)
Dim As Rational r3 = -(r1 * r2)
Print r3.numerator & "/" & r3.denominator

Here the global operators are defined for type Rational, and are used in the initialization expression for r3. The output is -6/12.

Member Operators

Member operators are declared inside a Type or Class definition, like member procedures, and they are the cast and assignment operators Operator Cast (Cast), Operator @ (Address Of), Operator [] (Pointer Index), Operator New Overload, Operator Delete Overload, Operator For (Iteration), Operator Step (Iteration), Operator Next (Iteration), Let (Assign), += (Add And Assign), -= (Subtract And Assign), *= (Multiply And Assign), /= (Divide And Assign), \= (Integer Divide And Assign), ^= (Exponentiate And Assign), &= (Concat And Assign), Mod= (Modulus And Assign), Shl= (Shift Left And Assign), Shr= (Shift Right And Assign), And= (Conjunction And Assign), Or= (Inclusive Disjunction And Assign), Xor= (Exclusive Disjunction And Assign), Imp= (Implication And Assign) and Eqv= (Equivalence And Assign).

When declaring member operators, the Declare and Operator keywords are used followed by the operator symbol and its parameter list. Like member procedures, member operators are defined outside the Type or Class definition, and the symbol name is prefixed with the name of the Type or Class name.

The following example overloads the member operators Operator Cast (Cast) and *= (Multiply And Assign) for objects of a user-defined type:
Type Rational
    As Integer numerator, denominator

    Declare Operator Cast () As Double
    Declare Operator Cast () As String
    Declare Operator *= (ByRef rhs As Rational)
End Type

Operator Rational.cast () As Double
    Return numerator / denominator
End Operator

Operator Rational.cast () As String
    Return numerator & "/" & denominator
End Operator

Operator Rational.*= (ByRef rhs As Rational)
    numerator *= rhs.numerator
    denominator *= rhs.denominator
End Operator

Dim As Rational r1 = (2, 3), r2 = (3, 4)
r1 *= r2
Dim As Double d = r1
Print r1, d

Notice that the member operator Cast (Cast) is declared twice, once for the conversion to Double and once for the conversion to String. This is the only operator (or procedure) that can be declared multiple times when only the return type differs. The compiler decides which cast overload to call based on how the object is used (in the initialization of the Double d, Rational.Cast as double is called, and in the Print statement, Rational.Cast as string is used instead).

Special Cases of Operators: '.' (Member access), '@' (Address of), '->' (Pointer to member access), and '*' (Value of)

- Overloading Operator . (Member access)
The operator '.' (member access) cannot be overloaded.

- Overloading Operator @ (Address of)
The operator @ (Adress of) is used to access the address of a variable.
There is no many interest to overload this operator for an object, and moreover if we did, we could no longer access its address.

- Overloading Operator -> (Pointer to member access) and Operator * (Value of)
The operator -> (Pointer to member access) is used to access any member of an object (instance) via a pointer to this instance.
The operator * (Value of) is used to access to variable via a pointer to this variable.
Under normal circumstances, the operand of these operators must be a pointer:
Declare Operator -> ( ByRef lhs As T Ptr ) ByRef As U
Declare Operator * ( ByRef rhs As T Ptr ) ByRef As T

Overloading of these operators allows you to create a pointer wrapper class and let it behave like the pointer itself:
Declare Operator -> ( ByRef lhs As wrapperClass ) ByRef As U
Declare Operator * ( ByRef rhs As wrapperClass ) ByRef As U

The wrapper can be then used (to access a member) like:
wrapper->member
instead of:
wrapper.realPointer->member
and:
(*wrapper).member
instead of:
(*wrapper.realPointer).member

Clarifying the particular case of overloading the operator -> (pointer to member access):
The operator -> (pointer to member access) exhibits a different behavior from the other operators with respect to overloading:
- It doesn't return only the user datatype as indicated in the overloaded procedure header,
- but it returns this user datatype implicitly followed by the operator . (member access).

The operator -> (pointer to member access) is mainly used often in conjunction with the operator * (Value of) to implement "smart pointers".

- Using smart pointer
The use of smart pointers allows automatic management of dynamic references created by New (each reference is destroyed automatically when its smart pointer goes out of scope), without even making any copy of these references.

Reminder of what a smart pointer:
- A smart pointer is an object which behaves like a pointer but does more than a pointer.
- This object is flexible as a pointer and has the advantage of being an object (like constructor and destructor called automatically).
- Therefore, the destructor of the smart pointer will be automatically called when this object goes out of scope, and it will delete the user pointer.

As the smart pointer must behave like a pointer, it must support the same interface as a pointer does.
So it must support the following operations:
- Dereferencing (operator * (Value of))
- Indirection (operator -> (pointer to member access))

The operator * (Value of) and the operator -> (pointer to member access) must return references (by means of using Byref As ..... in the declaration of there return type).

Example of a smart pointer (to UDT) with an interface:
- public default-constructor
- public copy-constructor
- public destructor
- private UDT pointer and public operator cast (Cast) to access it in read only mode
- private operator let to disallow assignment not implemented here (to avoid copying the pointers values only)
- operator * (Value of) and operator -> (pointer to member access)
Type UDT
    Declare Constructor ()
    Declare Destructor ()
    Dim As String s = "object #0"
End Type

Constructor UDT ()
    Print "  UDT construction "; @This
End Constructor

Destructor UDT ()
    Print "  UDT destruction "; @This
End Destructor

Type SmartPointer
    Public:
        Declare Constructor ()                            '' to construct smart pointer (and UDT object)
        Declare Constructor (ByRef rhs As SmartPointer)   '' to copy construct smart pointer
        Declare Operator Cast () As UDT Ptr               '' to cast private UDT pointer (for read only)
        Declare Destructor ()                             '' to destroy smart pointer (and UDT object)
    Private:
        Dim As UDT Ptr p                                  '' private UDT pointer
        Declare Operator Let (ByRef rhs As SmartPointer)  '' to disallow assignment (to avoid copy of real pointers)
End Type

Constructor SmartPointer ()
    Print "SmartPointer construction "; @This
    This.p = New UDT
End Constructor

Constructor SmartPointer (ByRef rhs As SmartPointer)
    Print "SmartPointer copy-construction "; @This; " from "; @rhs
    This.p = New UDT
    *This.p = *rhs.p
End Constructor

Operator SmartPointer.Cast () As UDT Ptr
    Return This.p
End Operator

Destructor SmartPointer ()
    Print "SmartPointer destruction "; @This
    Delete This.p
End Destructor

Operator * (ByRef sp As SmartPointer) ByRef As UDT   '' overloaded operator '*'
    Print "SmartPointer operator '*'"
    Return *Cast(UDT Ptr, sp)                        ''    (returning byref)
End Operator                                         ''    to behave as pointer

Operator -> (ByRef sp As SmartPointer) ByRef As UDT  '' overloaded operator '->'
    Print "SmartPointer operator '->'"
    Return *Cast(UDT Ptr, sp)                        ''    (returning byref)
End Operator                                         ''    to behave as pointer

Scope
    Dim sp1 As SmartPointer
    Print "'" & sp1->s & "'"
    sp1->s = "object #1"
    Print "'" & sp1->s & "'"
    Print

    Dim sp2 As SmartPointer = sp1
    Print "'" & (*sp2).s & "'"
    (*sp2).s = "object #2"
    Print "'" & (*sp2).s & "'"
    Print

    Dim sp3 As SmartPointer = sp1
    Print "'" & sp3->s & "'"
    *sp3 = *sp2
    Print "'" & sp3->s & "'"
    sp3->s = "object #3"
    Print "'" & sp3->s & "'"
    Print
End Scope

Sleep

Example of output:
SmartPointer construction 1703576
  UDT construction 10693312
SmartPointer operator '->'
'object #0'
SmartPointer operator '->'
SmartPointer operator '->'
'object #1'

SmartPointer copy-construction 1703524 from 1703576
  UDT construction 10693384
SmartPointer operator '*'
'object #1'
SmartPointer operator '*'
SmartPointer operator '*'
'object #2'

SmartPointer copy-construction 1703472 from 1703576
  UDT construction 10693456
SmartPointer operator '->'
'object #1'
SmartPointer operator '*'
SmartPointer operator '*'
SmartPointer operator '->'
'object #2'
SmartPointer operator '->'
SmartPointer operator '->'
'object #3'

SmartPointer destruction 1703472
  UDT destruction 10693456
SmartPointer destruction 1703524
  UDT destruction 10693384
SmartPointer destruction 1703576
  UDT destruction 10693312
Example of an extended smart pointer type macro for any UDT (or any predefined type), with an extended interface:
- public constructor
- public reference counter in read only mode
- public destructor
- private UDT pointer and 2 public operators cast to access it in read only mode (numeric value and string value)
- private default-constructor to disallow self construction
- private copy-constructor to disallow cloning
- private operator let to disallow assignment
- operator * (Value of) and operator -> (pointer to member access)
#macro Define_SmartPointer (_UDTname_)

    Type SmartPointer_##_UDTname_
        Public:
            Declare Constructor (ByVal rhs As _UDTname_ Ptr)              '' to construct smart pointer
            '                                                                '' from _UDTname_ pointer,
            '                                                                '' with reference counter increment
            Declare Static Function returnCount () As Integer             '' to return reference counter value
            Declare Operator Cast () As _UDTname_ Ptr                     '' to cast private _UDTname_ pointer
            '                                                                '' to _UDTname_ pointer (read only)
            Declare Operator Cast () As String                            '' to cast private _UDTname_ pointer
            '                                                                '' to string (read only)
            Declare Destructor ()                                         '' to destroy smart pointer
            '                                                                '' and _UDTname_ object
            '                                                                '' with reference counter decrement
        Private:
            Dim As _UDTname_ Ptr p                                        '' private _UDTname_ pointer
            Static As Integer Count                                       '' private reference counter
            Declare Constructor ()                                        '' to disallow default-construction
            Declare Constructor (ByRef rhs As SmartPointer_##_UDTname_)   '' to disallow copy-construction
            Declare Operator Let (ByRef rhs As SmartPointer_##_UDTname_)  '' to disallow copy-assignment
    End Type
    Dim As Integer SmartPointer_##_UDTname_.Count = 0

    Constructor SmartPointer_##_UDTname_ (ByVal rhs As _UDTname_ Ptr)
        If rhs <> 0 Then
            This.p = rhs
            SmartPointer_##_UDTname_.count += 1
        End If
    End Constructor

    Static Function SmartPointer_##_UDTname_.returnCount () As Integer
        Return SmartPointer_##_UDTname_.count
    End Function

    Operator SmartPointer_##_UDTname_.Cast () As _UDTname_ Ptr
        Return This.p
    End Operator

    Operator SmartPointer_##_UDTname_.Cast () As String
        Return Str(This.p)
    End Operator

    Destructor SmartPointer_##_UDTname_ ()
        If This.p <> 0 Then
            Delete This.p
            SmartPointer_##_UDTname_.count -= 1
            This.p = 0
        End If
    End Destructor

    Operator * (ByRef sp As SmartPointer_##_UDTname_) ByRef As _UDTname_  '' operator '*' (return byref)
        '                                                                    '' to behave as pointer
        Return ByVal sp                                                   '' 'Return *sp' would induce an infinite loop
    End Operator

    Operator -> (ByRef sp As SmartPointer_##_UDTname_) ByRef As _UDTname_  '' operator '->' (return byref)
        '                                                                     '' to behave as pointer
        Return ByVal sp
    End Operator

#endmacro

'--------------------------------------------------------------------------------------------------------

' Example using all eight keywords of inheritance:
'   'Extends', 'Base.', 'Base()', 'Object', 'Is' operator, 'Virtual', 'Abstract', 'Override'

Type root Extends Object ' 'Extends' to activate RTTI by inheritance of predefined Object type
    Public:
        Declare Function ObjectHierarchy () As String
        Declare Function ObjectName () As String
        Declare Abstract Function ObjectRealType () As String  '' 'Abstract' declares function without local body
        '                                                         '' which must be overridden
        Declare Virtual Destructor ()                          '' 'Virtual' declares destructor
    Protected:
        Declare Constructor ()                                 '' to avoid default-construction from outside Types
        Declare Constructor (ByRef _name As String = "")       '' to avoid construction from outside Types
        Declare Constructor (ByRef rhs As root)                '' to avoid copy-construction from outside Types
        Declare Operator Let (ByRef rhs As root)               '' to avoid copy-assignment from outside Types
    Private:
        Dim Name As String
End Type                                                   '' derived type may be member data empty

Constructor root ()  '' only to avoid compile error (due to inheritance)
End Constructor

Constructor root (ByRef _name As String = "")              '' only to avoid compile error (due to inheritance)
    This.name = _name
    Print "root constructor:", This.name
End Constructor

Function root.ObjectHierarchy () As String
    Return "Object(forRTTI) <- root"
End Function

Function root.ObjectName () As String
    Return This.name
End Function

Virtual Destructor root ()
    Print "root destructor:", This.name
End Destructor

Operator root.Let (ByRef rhs As root)                      '' only to avoid compile error (due to onheritance)
End Operator

Type animal Extends root                                           '' 'Extends' to inherit of root
    Declare Constructor (ByRef _name As String = "")
    Declare Function ObjectHierarchy () As String
    Declare Virtual Function ObjectRealType () As String Override  '' 'Virtual' declares function with local
    '                                                              ''    body which can be overridden
    '                                                              '' 'Override' to check if the function is
    '                                                              ''    well an override
    Declare Virtual Destructor () Override                         '' 'Virtual' declares destructor with local body
    '                                                              '' 'Override' to check if the destructor is well an override
End Type

Constructor animal (ByRef _name As String = "")
    Base(_name)                                                    '' 'Base()' allows to call parent constructor
    Print "  animal constructor:", This.ObjectName()
End Constructor

Function animal.ObjectHierarchy () As String
    Return Base.ObjectHierarchy & " <- animal"                     '' 'Base.' allows to access to parent member function
End Function

Virtual Function animal.ObjectRealType () As String
    Return "animal"
End Function

Virtual Destructor animal ()
    Print "  animal destructor:", This.ObjectName()
End Destructor

Type dog Extends animal                                    '' 'Extends' to inherit of animal
    Declare Constructor (ByRef _name As String = "")
    Declare Function ObjectHierarchy () As String
    Declare Function ObjectRealType () As String Override  '' 'Override' to check if the function is well an
    '                                                      ''    override
    Declare Destructor () Override                         '' 'Override' to check if the destructor is well an override
End Type                                                   '' derived type may be member data empty

Constructor dog (ByRef _name As String = "")
    Base(_name)                                            '' 'Base()' allows to call parent constructor
    Print "    dog constructor:", This.ObjectName()
End Constructor

Function dog.ObjectHierarchy () As String
    Return Base.ObjectHierarchy & " <- dog"                '' 'Base.' allows to access to parent member function
End Function

Function dog.ObjectRealType () As String
    Return "dog"
End Function

Destructor dog ()
    Print "    dog destructor:", This.ObjectName()
End Destructor

Type cat Extends animal                                  '' 'Extends' to inherit of animal
    Declare Constructor (ByRef _name As String = "")
    Declare Function ObjectHierarchy () As String
    Declare Function ObjectRealType () As String Override  '' 'Override' to check if the function is well an
    '                                                      ''    override
    Declare Destructor () Override                         '' 'Override' to check if the destructor is well an override
End Type                                                   '' derived type may be member data empty

Constructor cat (ByRef _name As String = "")
    Base(_name)                                            '' 'Base()' allows to call parent constructor
    Print "    cat constructor:", This.ObjectName()
End Constructor

Function cat.ObjectHierarchy () As String
    Return Base.ObjectHierarchy & " <- cat"                '' 'Base.' allows to access to parent member function
End Function

Function cat.ObjectRealType () As String
    Return "cat"
End Function

Destructor cat ()
    Print "    cat destructor:", This.ObjectName()
End Destructor

Sub PrintInfo (ByVal p As root Ptr)                                       '' parameter is a 'root Ptr' or compatible (smart pointer)
    Print "  " & p->ObjectName, "  " & p->ObjectRealType, "           ";
    If *p Is dog Then                                                     '' 'Is' allows to check compatibility with type symbol
        Print  Cast(dog Ptr, p)->ObjectHierarchy
    ElseIf *p Is cat Then                                                 '' 'Is' allows to check compatibility with type symbol
        Print Cast(cat Ptr, p)->ObjectHierarchy
    ElseIf *p Is animal Then                                              '' 'Is' allows to check compatibility with type symbol
        Print Cast(animal Ptr, p)->ObjectHierarchy
    End If
End Sub

Define_SmartPointer(root)  '' smart pointer definition

Scope
    Print "reference counter value:"; SmartPointer_root.returnCount()
    Print
    Dim As SmartPointer_root sp(2) = {New animal("Mouse"), New dog("Buddy"), New cat("Tiger")}
    Print
    Print "reference counter value:"; SmartPointer_root.returnCount()
    For I As Integer = 0 To 2
        Print "  " & sp(I), sp(I)->ObjectName()
    Next I
    Print
    Print "Name:", "Object (real):         Hierarchy:"
    For I As Integer = 0 To 2
        #if __FB_VERSION__ = "1.10.0" Or __FB_VERSION__ = "1.10.1"
            PrintInfo(Cast(root Ptr, sp(I)))  '' bug workaround
        #else
            PrintInfo(sp(I))
        #endif
    Next I
    Print
End Scope
Print
Print "reference counter value:"; SmartPointer_root.returnCount()
Print

Sleep

Example of output:
reference counter value: 0

root constructor:           Mouse
  animal constructor:       Mouse
root constructor:           Buddy
  animal constructor:       Buddy
    dog constructor:        Buddy
root constructor:           Tiger
  animal constructor:       Tiger
    cat constructor:        Tiger

reference counter value: 3
  11145960    Mouse
  11151496    Buddy
  11151616    Tiger

Name:         Object (real):         Hierarchy:
  Mouse         animal                 Object(forRTTI) <- root <- animal
  Buddy         dog                    Object(forRTTI) <- root <- animal <- dog
  Tiger         cat                    Object(forRTTI) <- root <- animal <- cat

    cat destructor:         Tiger
  animal destructor:        Tiger
root destructor:            Tiger
    dog destructor:         Buddy
  animal destructor:        Buddy
root destructor:            Buddy
  animal destructor:        Mouse
root destructor:            Mouse

reference counter value: 0


Iterators
The overload Operators For, Next, and Step, allowing to construct User-Defined Types Iterators (instead of only intrinsic scalar types iterators) for a For...Next loop

Syntax (declaration)

{ Type | Class | Union } typename
' For...Next' statement with implicit step (1st version of operators)
Declare Operator For ( )
Declare Operator Next ( [ ByRef | ByVal ] cond As typename ) As Integer
Declare Operator Step ( )
' For...Next' statement with explicit step (2nd version of operators)
Declare Operator For ( [ ByRef | ByVal ] stp As typename )
Declare Operator Next ( [ ByRef | ByVal ] cond As typename, [ ByRef | ByVal ] stp As typename ) As Integer
Declare Operator Step ( [ ByRef | ByVal ] stp As typename )
End { Type | Class | Union }

Usage

For iterator [ As typename ] = start_value To end_value [ Step step_value ]
[ ...statements... ]
Next

The first version of operators is used if no step_value is given in the For...Next statement.
If a step_value is given, the second version is used and a step object (initialized with step-value) is passed through the stp parameter:
- to Operator For because eventual additional initialization may use it,
- to Operator Next because testing for iterating end may depend on it,
- to Operator Step to increment the iterator object.
Both versions of the operators can coexist (thanks to member overloading) in the same user-defined type (to be able to both use and not use the explicit increment in For...Next statements of the user code).

Parameters

(including arguments)
typename
name of the Type, Class, or Union
stp, step_value
a typename object used as an incremental value
iterator
a typename object used as an iterator
cond, end_value
a typename object used as a loop-terminating value
start_value
a typename object used to copy construct or assign to the iterator initially

Description

Operator For, Operator Next and Operator Step can be overloaded in user-defined type definitions to allow objects of that type to be used as iterators and step values in For...Next loops (instead of the pre-defined for intrinsic scalar types).

As all non-static member procedures, the 3 operators have passed a hidden This parameter that allows to access by reference to the iterator object (initialized to the start_value argument value from the For...Next statement).
The cond parameter of the Operator Next allows to access the end_value argument value from the For...Next statement.
If a step_value is given (as argument) in the For...Next statement, the stp parameter allows to access this value in the 3 operators.

Note: If no step_value is given in the For...Next statement (implicit step), the user-defined type must have a default constructor (implicit or explicit) or a conversion constructor. It is a bug at the moment because if the user defines a default constructor, the compiler does not even use it when initializing the For...Next loop!

Operator For
Operator For is called once immediately after copy constructing or assigning to the iterator object (with the start_value), constructing the end object (with the end_value), and constructing the step object (with step_value if defined in the For...Next statement).
Operator For allows to perform any additional initialization needed in preparation for the loop.

Operator Next
Operator Next is called every time the iterator object needs to be checked against the end value. This happens immediately after the call to the Operator For, and then immediately after any calls to the Operator Step.
Operator Next should return zero (0) if the loop should be terminated, or non-zero if the loop should continue iterating.
The first time Operator Next is called, no statements in the For...Next body have been executed yet.
Operator Next also allows to perform some processing before the execution of all statements in the For...Next body.

Operator Step
Operator Step is called to increment the iterator object immediately after all statements in the For...Next body are executed.

Advanced usage
The above description seems to imply that the 3 arguments start_value, end_value, and step_value must be of the same type as the iterator (this is the more obvious use), but it is not quite true:
- The start_value, end_value, and step_value arguments can be of any type (of different types among themselves and also of different types from the one of the iterator).
- The only constraint is that the iterator could be constructed (in case of local iterator) or assigned (in case of global iterator) from the start_value argument (because the iterator is implicitly constructed or assigned under the hood).
- Similarly the other arguments end_value, and step_value must be able to be converted into objects of the same type as the iterator.

Algorithm

For...Next loop algorithm around the 3 overload operators:
'                       FOR...NEXT loop
'                              V
'                              |
'            constructing/assigning iterator object
'       (This = start_value from For...Next statement)
'                              |
'                   constructing end object
'       (cond = end_value from For...Next statement)
'                              |
'                   if step_value is defined >---------------------.
'                            else                                  :
'                              v                                   v
'                              :                        constructing step object
'                              :               (stp = step_value from For...Next statement)
'                              :                                   :
'                              :<----------------------------------'
'                              |
'                    calling Operator For
'                              |
'     .----------------------->|
'     |                        |
'     |              calling Operator Next
'     |     (if end-condition verified: =0 returned) >-------------.
'     |               (else: <>0 returned)                         |
'     |                        v                                   |
'     |                        |                                   |
'     |            executing For...Next body                       |
'     |                        |                                   |
'     |              calling Operator Step                         |
'     |                        |                                   |
'     '------------------------'                                   |
'                                                                  |
'                                                                  V
Example

Type for iterating through screen resolutions, with implicit step-value:
Type screenResolution
    ' user interface
        Declare Constructor (ByVal colorBit As Long)
        Declare Property colorDepth () As Long
        Declare Property screenWidth () As Long
        Declare Property screenHeigth () As Long
    ' overload iteration operators when Step is not defined in For...Next statement
        Declare Operator For ()
        Declare Operator Next (ByRef iterateCondition As screenResolution) As Integer
        Declare Operator Step ()
    ' internal variables
        Dim As Long colorBit, resolutionWH
End Type

Constructor screenResolution (ByVal colorBit As Long)
    This.colorBit = colorBit
End Constructor

Property screenResolution.colorDepth () As Long
    Return This.colorBit
End Property

Property screenResolution.screenWidth () As Long
    Return HiWord(This.resolutionWH)
End Property

Property screenResolution.screenHeigth () As Long
    Return LoWord(This.resolutionWH)
End Property

Operator screenResolution.For ()
    This.resolutionWH = ScreenList(This.colorBit)
End Operator

Operator screenResolution.Next (ByRef iterateCondition As screenResolution) As Integer
    While This.resolutionWH = 0
        If This.colorBit < iterateCondition.colorBit Then
            This.colorBit += 1
            This.resolutionWH = ScreenList(This.colorBit)
        Else
            Exit While
        End If
    Wend
    Return (This.resolutionWH <> iterateCondition.resolutionWH)
End Operator

Operator screenResolution.Step ()
    This.resolutionWH = ScreenList()
End Operator

Print "Screen resolutions supported within [1 bpp , 64 bpp]:"
For iterator As screenResolution = screenResolution(1) To screenResolution(64)
    Print "    " & iterator.colorDepth & " bpp ",
    Print ":" & iterator.screenWidth & "x" & iterator.screenHeigth
Next iterator
Print "End of supported screen resolutions"

Sleep

Output example:
Screen resolutions supported within [1 bpp , 64 bpp]:
    24 bpp    :320x200
    24 bpp    :320x240
    24 bpp    :400x300
    24 bpp    :512x384
    24 bpp    :640x400
    24 bpp    :640x480
    24 bpp    :800x600
    24 bpp    :1024x768
    24 bpp    :1152x864
    24 bpp    :1280x600
    24 bpp    :1280x720
    24 bpp    :1280x768
    24 bpp    :1280x800
    24 bpp    :1280x960
    24 bpp    :1280x1024
    24 bpp    :1360x768
    24 bpp    :1366x768
    24 bpp    :1400x1050
    24 bpp    :1440x900
    24 bpp    :1600x900
    24 bpp    :1680x1050
    24 bpp    :1920x1080
    32 bpp    :320x200
    32 bpp    :320x240
    32 bpp    :400x300
    32 bpp    :512x384
    32 bpp    :640x400
    32 bpp    :640x480
    32 bpp    :800x600
    32 bpp    :1024x768
    32 bpp    :1152x864
    32 bpp    :1280x600
    32 bpp    :1280x720
    32 bpp    :1280x768
    32 bpp    :1280x800
    32 bpp    :1280x960
    32 bpp    :1280x1024
    32 bpp    :1360x768
    32 bpp    :1366x768
    32 bpp    :1400x1050
    32 bpp    :1440x900
    32 bpp    :1600x900
    32 bpp    :1680x1050
    32 bpp    :1920x1080
End of supported screen resolutions
Type for iterating through fractions, with explicit step-value used in the 3 operators:
(improved example compared to the one of Operator Step page)
Type fraction
    ' user interface
        Declare Constructor (ByVal n As Integer, ByVal d As Integer)
        Declare Operator Cast () As String
    ' overload iteration operators when Step is defined in For...Next statement
        Declare Operator For (ByRef iterateStep As fraction)
        Declare Operator Next (ByRef iterateCondition As fraction, ByRef iterateStep As fraction) As Integer
        Declare Operator Step (ByRef step_var As fraction)
    ' internal variables and cast operator
        As Integer num, den
        Declare Operator Cast () As Double
End Type

Constructor fraction (ByVal n As Integer, ByVal d As Integer)
    this.num = n
    this.den = d
End Constructor

Operator fraction.Cast () As String
    ' search for the highest common factor (a) between numerator and denominator
        Dim As Integer a = Abs(This.num), b = Abs(This.den)
        If a <> 0 Then
            While a <> b
                If a > b Then
                    a -= b
                Else
                    b -= a
                End If
            Wend
        Else
            a = 1
        End If
    ' reduce the fraction
        Return num \ a & "/" & den \ a
End Operator

Operator fraction.Cast () As Double
    Return This.num / This.den
End Operator

Operator fraction.For (ByRef iterateStep As fraction)
    ' search for the least common multiple (a) between the two denominators
        Dim As Integer a = Abs(This.den), b = Abs(iterateStep.den), c = a, d = b
        While a <> b
            If a > b Then
                b += d
            Else
                a += c
            End If
        Wend
    ' align at the same denominator the 2 fractions
        This.num *= a \ This.den
        This.den = a
        iterateStep.num *= a \ iterateStep.den
        iterateStep.den = a
End Operator

Operator fraction.Next (ByRef iterateCondition As fraction, ByRef iterateStep As fraction) As Integer
    If iterateStep.num < 0 Or iterateStep.den < 0 Then
        Return This >= iterateCondition
    Else
        Return This <= iterateCondition
    End If
End Operator

Operator fraction.Step (ByRef iterateStep As fraction)
    This.num += iterateStep.num
End Operator

Print "iteration from 1/8 to 1/2 by step of 1/12:"
For iterator As fraction = fraction(1, 8) To fraction(1, 2) Step fraction(1, 12)
    Print "    " & iterator;
Next
Print
Print
Print "iteration from 7/10 to -8/5 by step of -8/15:"
For iterator As fraction = fraction(7, 10) To fraction(-8, 5) Step fraction(-8, 15)
    Print "    " & iterator;
Next
Print

Sleep

Output:
iteration from 1/8 to 1/2 by step of 1/12:
    1/8    5/24    7/24    3/8    11/24

iteration from 7/10 to -8/5 by step of -8/15:
    7/10    1/6    -11/30    -9/10    -43/30
See also

  • For...Next
  • Operator


New and Delete
The different operators New (Implicit/Overload/Expression/Placement) and Delete (Implicit/Overload/Statement), and all their array-versions New[] and Delete[]

Definition

There may be confusion in the user mind between the different operators New and Delete (despite the documentation that distinguishes them from each other through some different pages):
    • 'Operator New Implicit' (inaccessible by user):
- It is a static function that only allocates memory (it is not very different from Allocate).
    • Operator New Overload:
- It is a member operator (static function) that can overload the 'Operator New Implicit' only for user-defined types.
- So the user can define its own dynamic memory allocation process part (the following process part for implicit data construction can not be modified).
    • 'Operator Delete Implicit' (inaccessible by user):
- It is a static sub that only frees the memory (it is not very different from Deallocate).
    • Operator Delete Overload:
- It is a member operator (static sub) that can overload the 'Operator Delete Implicit' only for user-defined types.
- So the user can define its own memory deallocation process part (the previous process part for implicit data destruction can not be modified).
    • Operator New Expression:
- It starts by using the 'Operator New Implicit/Overload' (the implicit, or the overload if exists) to allocate memory.
- Then it invokes the constructor for the right type of object. If that object contains any other objects (either embedded or as base types) those constructors as invoked as well.
- So the final result is memory allocated and object constructed.
- This operator applies to pre-defined types (except fixed-length strings) as well as user-defined types.
    • Operator Delete Statement:
- It starts by invoking the destructor for the right type of object. If that object contains any other objects (either embedded or as base types) those destructors as invoked as well.
- Then it uses the 'Operator Delete Implicit/Overload' (the implicit, or the overload if exists) to deallocate memory.
- So the final result is object destroyed and memory freed.
- This operator applies to pre-defined types (except fixed-length strings) as well as user-defined types.
    • Operator Placement New:
- It only constructs object at a specified memory address (already allocated). This operator applies to pre-defined types (except fixed-length strings) as well as user-defined types.

Similar definition for 'Operator New[] Implicit/Overload/Expression', 'Operator Delete[] Implicit/Overload/Statement' and 'Operator Placement New[]', which are only the (one-dimensional) array-versions of the previous operators (there is construction/destruction loop on the array elements).

Instances created with 'Operator New Overload/Expression' must be freed with 'Operator Delete Overload/Statement'.
Instance array created with 'Operator New[] Overload/Expression' must be freed with 'Operator Delete[] Overload/Statement', the array-version of 'Operator Delete Overload/Statement'.
User can not mix and match the different versions of the operators.

Instances constructed at a specified memory address with 'Operator Placement New' must never induce a symmetric freeing with any 'Operator Delete' on the address (because the memory has been allocated in another way than by the 'Operator Placement New').
Instance array constructed at a specified memory address with 'Operator Placement New[]' must never induce a symmetric freeing with any 'Operator Delete[]' on the address (because the memory has been allocated in another way than by the 'Operator Placement New[]').
For such UDT instances, the proper way to just destroy each instance if necessary, is to call only the destructor if one exists (implicitly or explicitly), with syntax as for a member method by using member access operator.

Note: Any operators 'New[]' or 'Delete[]' (the array versions for statement/expression/overload operators) and even the overload operator 'Delete' are not directly compatible with inheritance polymorphism (sub-type polymorphism), because the use of a sub-type pointer (instead of the real type) mainly fails for accessing the other elements (except the first).

Algorithm (applied to user-defined types)

Operator New/New[] Expression:
'       OPERATOR NEW/NEW[] EXPRESSION
'                     V
'                     |
'              call if there is >----------------------------.
'                   else                                     :
'                     v                                      v
'       (Operator New/New[] Implicit)          (Operator New/New[] Overload)
'                     :                                      :
'          BASIC MEMORY ALLOCATION            USER BODY for memory allocation
'                     :                                      :
'                     :<-------------------------------------'
'                     |
'                     |<-------------------------------------.
'                     |                                      :
'        DATA FIELDS INITIALIZATION                          :
'        OBJECT FIELDS CONSTRUCTION                          :
'           (VPTR INITIALIZATION)                            :
'                     |                                      :
'              call if there is >-----------.                :
'                   else                    :                :
'                     v                     v                :
'                     :            (User Constructor)        :
'                     :                     :                :
'                     :                 USER BODY            :
'                     :                     :                :
'                     :<--------------------'                :
'                     |                                      :
'        loop if array-version NEW[] >-----------------------'
'                   else
'                     v
'                     |
'                     V

Operator Delete/Delete[] Statement:
'     OPERATOR DELETE/DELETE[] STATEMENT
'                     V
'                     |
'                     |<-------------------------------------.
'                     |                                      :
'          (VPTR REINITIALIZATION)                           :
'                     |                                      :
'              call if there is >-----------.                :
'                   else                    :                :
'                     v                     v                :
'                     :             (User Destructor)        :
'                     :                     :                :
'                     :                 USER BODY            :
'                     :                     :                :
'                     :<--------------------'                :
'                     |                                      :
'         OBJECT FIELDS DESTRUCTION                          :
'                     |                                      :
'       loop if array-version DELETE[] >---------------------'
'                   else
'                     v
'                     |
'              call if there is >----------------------------.
'                   else                                     :
'                     v                                      v
'    (Operator Delete/Delete[] Implicit)    (Operator Delete/Delete[] Overload)
'                     :                                      :
'         BASIC MEMORY DEALLOCATION          USER BODY for memory deallocation
'                     :                                      :
'                     :<-------------------------------------'
'                     |
'                     V

Operator Placement New/New[]:
'        OPERATOR PLACEMENT NEW/NEW[]
'                     V
'                     |
'                     |<-------------------------------------.
'                     |                                      :
'        DATA FIELDS INITIALIZATION                          :
'        OBJECT FIELDS CONSTRUCTION                          :
'           (VPTR INITIALIZATION)                            :
'                     |                                      :
'              call if there is >-----------.                :
'                   else                    :                :
'                     v                     v                :
'                     :             (User Constructor)       :
'                     :                     :                :
'                     :                 USER BODY            :
'                     :                     :                :
'                     :<--------------------'                :
'                     |                                      :
'        loop if array-version NEW[] >-----------------------'
'                   else
'                     v
'                     |
'                     V
Example

Example that uses the operators New (Overload/Expression/Placement) and Delete (Overload/Statement), and all their array-versions New[] and Delete[]:
(all the 10 operators New and Delete accessible by the user):
Declare Sub printArray (ByRef label As String = "", array() As String)

Type UDT
    Declare Constructor ()
    Declare Destructor ()
    Declare Operator New (ByVal size As UInteger) As Any Ptr    ' Operator New Overload
    Declare Operator New[] (ByVal size As UInteger) As Any Ptr  ' Operator New[] Overload
    Declare Operator Delete (ByVal buf As Any Ptr)              ' Operator Delete Overload
    Declare Operator Delete[] (ByVal buf As Any Ptr)            ' Operator Delete[] Overload
    Dim As String array (1 To 4)
End Type

Constructor UDT ()
    Static As Integer n
    Print "    Constructor"
    printArray("        init: @" & @This & " (descriptors) -> ", This.array())
    For i As Integer = LBound(This.array) To UBound(This.array)
        This.array(i) = Chr(Asc("a") + n + i - LBound(This.array))
    Next i
    printArray(" => ", This.array())
    Print
    n += UBound(This.array)- LBound(This.array) + 1
End Constructor

Destructor UDT ()
    Print "    Destructor"
    printArray("        erase: @" & @This & " (descriptors) -> ", This.array())
    Erase This.array
    printArray(" => ", This.array())
    Print
End Destructor

Operator UDT.New (ByVal size As UInteger) As Any Ptr
    Print "    Operator New Overload"
    Dim As Any Ptr p = Allocate(size)                   ' Memory allocation (with passed size)
    Print "        memory allocation: ";
    Print size & " Bytes from @" & p
    Return p                                            ' Returning memory pointer
End Operator

Operator UDT.New[] (ByVal size As UInteger) As Any Ptr
    Print "    Operator New[] Overload"
    Dim As Any Ptr p = Allocate(size)                   ' Memory allocation (with passed size)
    Print "        memory allocation: ";
    Print size & " Bytes from @" & p
    Return p                                            ' Returning memory pointer
End Operator

Operator UDT.Delete (ByVal buf As Any Ptr)
    Print "    Operator Delete Overload"
    Deallocate(buf)                                     ' Memory deallocation (with passed pointer)
    Print "        memory deallocation: ";
    Print "for @" & buf
End Operator

Operator UDT.Delete[] (ByVal buf As Any Ptr)
    Print "    Operator Delete[] Overload"
    Deallocate(buf)                                     ' Memory deallocation (with passed pointer)
    Print "        memory deallocation: ";
    Print "for @" & buf
End Operator

Print "Operator New Expression"
Dim As UDT Ptr pu1 = New UDT         ' Operator New Expression
Print "Operator Delete Statement"
Delete pu1                           ' Operator Delete Statement
Sleep

Print
Print "Operator New[] Expression"
Dim As UDT Ptr pu2 = New UDT[2]      ' Operator New[] Expression
Print "Operator Delete[] Statement"
Delete[] pu2                         ' Operator Delete[] Statement
Sleep

Dim As Byte buffer(1 To 256)
Dim As Any Ptr p = @buffer(1)

Print
Print "Operator Placement New"
Dim As UDT Ptr pu3 = New(p) UDT      ' Operator Placement New
Print "User call of Destructor"
pu3->Destructor()                    ' User Call of Destructor
Sleep

Print
Print "Operator Placement New[]"
Dim As UDT Ptr pu4 = New(p) UDT[2]   ' Operator Placement New[]
For i As Integer = 0 To 1
    Print "User Call of Destructor"
    pu4[i].Destructor()              ' User Call of Destructor
Next i
Sleep

Sub printArray (ByRef label As String = "", array() As String)
    Print label & "{";
    For i As Integer = LBound(array) To UBound(array)
        Print """" & array(i) & """";
        If i < UBound(array) Then
            Print ",";
        End If
    Next I
    Print "}";
End Sub

Output example (for a 64-bit system):
Operator New Expression
    Operator New Overload
        memory allocation: 96 Bytes from @1728352
    Constructor
        init: @1728352 (descriptors) -> {"","","",""} => {"a","b","c","d"}
Operator Delete Statement
    Destructor
        erase: @1728352 (descriptors) -> {"a","b","c","d"} => {"","","",""}
    Operator Delete Overload
        memory deallocation: for @1728352

Operator New[] Expression
    Operator New[] Overload
        memory allocation: 200 Bytes from @1728352
    Constructor
        init: @1728360 (descriptors) -> {"","","",""} => {"e","f","g","h"}
    Constructor
        init: @1728456 (descriptors) -> {"","","",""} => {"i","j","k","l"}
Operator Delete[] Statement
    Destructor
        erase: @1728456 (descriptors) -> {"i","j","k","l"} => {"","","",""}
    Destructor
        erase: @1728360 (descriptors) -> {"e","f","g","h"} => {"","","",""}
    Operator Delete[] Overload
        memory deallocation: for @1728352

Operator Placement New
    Constructor
        init: @1375248 (descriptors) -> {"","","",""} => {"m","n","o","p"}
User call of Destructor
    Destructor
        erase: @1375248 (descriptors) -> {"m","n","o","p"} => {"","","",""}

Operator Placement New[]
    Constructor
        init: @1375248 (descriptors) -> {"","","",""} => {"q","r","s","t"}
    Constructor
        init: @1375344 (descriptors) -> {"","","",""} => {"u","v","w","x"}
User Call of Destructor
    Destructor
        erase: @1375248 (descriptors) -> {"q","r","s","t"} => {"","","",""}
User Call of Destructor
    Destructor
        erase: @1375344 (descriptors) -> {"u","v","w","x"} => {"","","",""}
See also

  • Operator
  • Constructor
  • Destructor
  • Use Implicit / Overload New([]) and Delete([]) Operators with Inheritance Polymorphism


Types as Objects
An example of the overloadable operators and member procedures

Description

When a Type structure is used to define a user object, many procedure kinds can also be added to the data field skeleton to simplify its use by any user:
- Sub, Function and Properties members.
- Constructors and Destructor.
- Overload Operators (inside and outside Type definition).

The following example is just a syntactic template for their declarations and definitions:
'' Sample Type showing available methods and operators
'' Practically this is a pointless example, as the only
'' data member is an Integer.  It serves only as a
'' guide to syntax.
''
'' There are many other combinations that can be
'' used in passed parameters.  For simplicity
'' This example only uses byref and type T
'' where ever possible.

'' The type 'DataType' is included to show where
'' any data type might be used
Type DataType As Integer

'' The type 'UDT' is included to show where only
'' a UDT data type can be used
Type UDT
  value As DataType
End Type

'' Our main type
Type T
  value As DataType
  value_array( 0 ) As DataType

  '' let, cast, combined assignment operators,
  '' constructors, and the destructor, must be
  '' declared inside the type.
  ''
  '' Parameters can be passed Byval or Byref
  '' in most (All? - verify this).
  ''
  '' All procs can be overloaded with different
  '' types as parameters.  In many cases this is not
  '' necessary as the TYPE can be coerced and
  '' converted depending on the CAST methods
  '' it exposes.  The compiler will to its best
  '' to evaluate statements and expressions if
  '' there is enough information to complete
  '' the operation.
  ''
  '' For example,
  '' Even though operator += may not be overloaded
  '' but operator let and operator + are, the
  '' compiler will convert the T += datatype
  '' to T = T + datatype.

  '' Nonstatic members must be declared inside the
  '' type.
  ''
  '' All Nonstatic members are implicitly
  '' passed a hidden **this** parameter having
  '' the same type as the TYPE in which they are
  '' declared.
  ''
  '' Nonstatic member overloaded operators do not
  '' return a type.  All operations are done on the
  '' hidden this parameter.
  ''
  '' Properties: Can be value properties or single
  '' indexed value properties
  '' GET/SET methods must be each delcared if used.

  '' Nonstatic Member Declarations:

  '' Memory Allocation/Deallocation

  Declare Operator New ( ByVal size As UInteger ) As Any Ptr
  Declare Operator New[] ( ByVal size As UInteger ) As Any Ptr
  Declare Operator Delete ( ByVal buf As Any Ptr )
  Declare Operator Delete[] ( ByVal buf As Any Ptr )

  '' Assignment

  Declare Operator Let ( ByRef rhs As T )
  Declare Operator Let ( ByRef rhs As DataType )

  '' Cast can be overloaded to return multiple types

  Declare Operator Cast () As String
  Declare Operator Cast () As DataType

  '' Combined assignment

  Declare Operator += ( ByRef rhs As T )
  Declare Operator += ( ByRef rhs As DataType )
  Declare Operator -= ( ByRef rhs As DataType )
  Declare Operator *= ( ByRef rhs As DataType )
  Declare Operator /= ( ByRef rhs As DataType )
  Declare Operator \= ( ByRef rhs As DataType )
  Declare Operator Mod= ( ByRef rhs As DataType )
  Declare Operator Shl= ( ByRef rhs As DataType )
  Declare Operator Shr= ( ByRef rhs As DataType )
  Declare Operator And= ( ByRef rhs As DataType )
  Declare Operator Or= ( ByRef rhs As DataType )
  Declare Operator Xor= ( ByRef rhs As DataType )
  Declare Operator Imp= ( ByRef rhs As DataType )
  Declare Operator Eqv= ( ByRef rhs As DataType )
  Declare Operator ^= ( ByRef rhs As DataType )
  Declare Operator &= ( ByRef rhs As DataType )

  '' Address of

  Declare Operator @ () As DataType Ptr

  '' Constructors can be overloaded

  Declare Constructor ()
  Declare Constructor ( ByRef rhs As T )
  Declare Constructor ( ByRef rhs As DataType )

  '' There can be only one destructor

  Declare Destructor ()

  '' Nonstatic member functions and subs
  '' overloaded procs must have different parameters

  Declare Function f ( ) As DataType
  Declare Function f ( ByRef arg1 As DataType ) As DataType

  Declare Sub s ( )
  Declare Sub s ( ByRef arg1 As T )
  Declare Sub s ( ByRef arg1 As DataType )

  '' Properties

  Declare Property p () As DataType
  Declare Property p ( ByRef new_value As DataType )

  Declare Property pidx ( ByVal index As DataType ) As DataType
  Declare Property pidx ( ByVal index As DataType, ByRef new_value As DataType )

  '' Iterator

  Declare Operator For ()
  Declare Operator Step ()
  Declare Operator Next ( ByRef cond As T ) As Integer

  Declare Operator For ( ByRef stp As T )
  Declare Operator Step ( ByRef stp As T )
  Declare Operator Next ( ByRef cond As T, ByRef stp As T ) As Integer

End Type

'' These must be global procedures
'' Globals are not prefixed with the the TYPE name

'' At least one parameter must be of Type 'T'
'' For simplicity, type 'T' is always given first for binary ops
'' in this example

Declare Operator - ( ByRef rhs As T ) As DataType
Declare Operator Not ( ByRef rhs As T ) As DataType

Declare Operator -> ( ByRef rhs As T ) As UDT
Declare Operator * ( ByRef rhs As T ) As DataType

Declare Operator + ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator - ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator * ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator / ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator \ ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator Mod ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator Shl ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator Shr ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator And ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator Or ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator Xor ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator Imp ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator Eqv ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator ^ ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator = ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator <> ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator < ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator > ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator <= ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator >= ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
Declare Operator & ( ByRef lhs As T, ByRef rhs As DataType ) As DataType

Declare Operator Abs ( ByRef arg As UDT ) As Double
Declare Operator Fix ( ByRef arg As UDT ) As Double
Declare Operator Frac ( ByRef arg As UDT ) As Double
Declare Operator Int ( ByRef arg As UDT ) As Double
Declare Operator Sgn ( ByRef arg As UDT ) As Double

'' Global procedures (subs and funcs) can also accept the TYPE
'' as a parameter or return it as a value, as could be done
'' in previous versions of FreeBASIC.
'' No example given. See function or sub in the manual.

'' All TYPE members are defined outside the TYPE

'' Nonstatic members must be prefixed with type name
'' in this case 'T'

'' Name resolution in a NAMESPACE is same as other
'' subs/funcs.  Use USING or prefix the namespace name

Operator T.new ( ByVal size As UInteger ) As Any Ptr
    Operator = Allocate( size )
End Operator

Operator T.new[] ( ByVal size As UInteger ) As Any Ptr
    Operator = Allocate( size )
End Operator

Operator T.delete ( ByVal buf As Any Ptr )
    Deallocate buf
End Operator

Operator T.delete[] ( ByVal buf As Any Ptr )
    Deallocate buf
End Operator

Operator T.let ( ByRef rhs As T )
  value = rhs.value
End Operator

Operator T.let ( ByRef rhs As DataType )
  value = rhs
End Operator

Operator T.cast ( ) As String
  Return Str( value )
End Operator

Operator T.cast ( ) As DataType
  Return value
End Operator

Operator T.+= ( ByRef rhs As T )
  value += rhs.value
End Operator

Operator T.+= ( ByRef rhs As DataType )
  value += rhs
End Operator

Operator T.-= ( ByRef rhs As DataType )
  value -= rhs
End Operator

Operator T.*= ( ByRef rhs As DataType )
  value *= rhs
End Operator

Operator T./= ( ByRef rhs As DataType )
  value /= rhs
End Operator

Operator T.\= ( ByRef rhs As DataType )
  value \= rhs
End Operator

Operator T.mod= ( ByRef rhs As DataType )
  value Mod= rhs
End Operator

Operator T.shl= ( ByRef rhs As DataType )
  value Shl= rhs
End Operator

Operator T.shr= ( ByRef rhs As DataType )
  value Shr= rhs
End Operator

Operator T.and= ( ByRef rhs As DataType )
  value And= rhs
End Operator

Operator T.or= ( ByRef rhs As DataType )
  value Or= rhs
End Operator

Operator T.xor= ( ByRef rhs As DataType )
  value Xor= rhs
End Operator

Operator T.imp= ( ByRef rhs As DataType )
  value Imp= rhs
End Operator

Operator T.eqv= ( ByRef rhs As DataType )
  value Eqv= rhs
End Operator

Operator T.^= ( ByRef rhs As DataType )
  value ^= rhs
End Operator

Operator T.&= ( ByRef rhs As DataType )
  Dim tmp As String
  tmp &= Str( rhs )
End Operator

Operator T.@ () As DataType Ptr
  Return( Cast( DataType Ptr, @This ))
End Operator

'' Constructors:

Constructor T ()
  '' default constructor
  value = 0
End Constructor

Constructor T ( ByRef rhs As T )
  '' copy constructor
  value = rhs.value
End Constructor

Constructor T ( ByRef rhs As DataType )
  '' custom constructor
  value = rhs
End Constructor

'' There can be only one destructor

Destructor T ()
  '' clean-up, none in this example
End Destructor

'' Globals must specify all arguments and return type

Operator - ( ByRef rhs As T ) As DataType
  Return (-rhs.value)
End Operator

Operator Not ( ByRef rhs As T ) As DataType
  Return (Not rhs.value)
End Operator

Operator -> ( ByRef rhs As T ) As UDT
  Return Type(4)
End Operator

Operator * ( ByRef rhs As T ) As DataType
  Return 5
End Operator

Operator + ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value + rhs)
End Operator

Operator - ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value - rhs)
End Operator

Operator * ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value * rhs)
End Operator

Operator / ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value / rhs)
End Operator

Operator \ ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value \ rhs)
End Operator

Operator Mod ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value Mod rhs)
End Operator

Operator Shl ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value Shl rhs)
End Operator

Operator Shr ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value Shr rhs)
End Operator

Operator And ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value And rhs)
End Operator

Operator Or ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value Or rhs)
End Operator

Operator Xor ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value Xor rhs)
End Operator

Operator Imp ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value Imp rhs)
End Operator

Operator Eqv ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value Eqv rhs)
End Operator

Operator ^ ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value ^ rhs)
End Operator

Operator = ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value = rhs)
End Operator

Operator <> ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value <> rhs)
End Operator

Operator < ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value < rhs)
End Operator

Operator > ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value > rhs)
End Operator

Operator <= ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value <= rhs)
End Operator

Operator >= ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return (lhs.value >= rhs)
End Operator

Operator & ( ByRef lhs As T, ByRef rhs As DataType ) As DataType
  Return Val(lhs.value & rhs)
End Operator

Operator Abs ( ByRef arg As UDT ) As Double
    Return Abs(arg.value)
End Operator

Operator Fix ( ByRef arg As UDT ) As Double
    Return Fix(arg.value)
End Operator

Operator Frac ( ByRef arg As UDT ) As Double
    Return Frac(arg.value)
End Operator

Operator Int ( ByRef arg As UDT ) As Double
    Return Int(arg.value)
End Operator

Operator Sgn ( ByRef arg As UDT ) As Double
    Return Sgn(arg.value)
End Operator

'' Nonstatic member methods

Function T.f ( ) As DataType
  Dim x As DataType
  Return x
End Function

Function T.f ( ByRef arg1 As DataType ) As DataType
  arg1 = this.value
  Return value
End Function

Sub T.s ( )
  '' refer to the type using

  '' with block
  With This
    .value = 1
  End With

  '' field access
  this.value = 2

  '' directly
  value = 3

End Sub

Sub T.s ( ByRef arg1 As T )
  value = arg1.value
End Sub

Sub T.s ( ByRef arg1 As DataType )
  value = arg1
End Sub

Property T.p () As DataType
  '' GET property
  Return value
End Property

Property T.p ( ByRef new_value As DataType )
  '' SET property
  value = new_value
End Property

Property T.pidx ( ByVal index As DataType ) As DataType
  '' GET indexed property
  Return value_array( index )
End Property

Property T.pidx ( ByVal index As DataType, ByRef new_value As DataType )
  '' SET indexed property
  value_array( index ) = new_value
End Property

Operator T.for ()
End Operator

Operator T.step ()
End Operator

Operator T.next ( ByRef cond As T ) As Integer
  Return 0
End Operator

Operator T.for ( ByRef stp As T )
End Operator

Operator T.step ( ByRef stp As T )
End Operator

Operator T.next ( ByRef cond As T, ByRef stp As T ) As Integer
  Return 0
End Operator

'' new, delete, delete[]

'' Allocate object
Dim X As T Ptr = New T

'' Deallocate object
Delete X

'' Allocate object vector
Dim Xlist As T Ptr = New T[10]

'' Deallocate object vector
Delete[] Xlist

See also

  • Type


Composition, Aggregation, Inheritance
Properly choice between Composition, Aggregation, and Inheritance, for User Defined Types.

Preamble:

Complex type structures can be built through composition, aggregation, and inheritance.
Composition or aggregation (specialized form of association) is the process of creating more complex types from simple ones, while inheritance is the process of creating more complex type by acquiring the attributes and behaviors of existing ones.

There is a very famous design principle which says “Favor composition over inheritance“, or also "Don’t use inheritance just to get code reuse".
One other reason to favor composition with FreeBASIC (as with many other languages) is it does not support multiple inheritance.

Definition of composition, aggregation, inheritance

The right question to ask himself for choosing the type of relationship to use is to determine if this relationship is rather "HAS-A" or "USES-A" or "IS-A" throughout the lifetime of the application (see example below).

When user needs to use property and behavior of a type without modifying it inside its type, then association is a better option.
Whereas if needed to use and modify property and behavior of a type inside the user type, it is best to use inheritance.

Definition of each relationship
Composition (strong association) establishes a "HAS-A" relationship between objects:
- In composition, the component object exists solely for the use of its composite object.
- If the composite object is destroyed, the component object is destroyed as well:
> because the type contains the object itself.
- Syntax of the member field to add in the type for object's composition (or similar syntax):
objectname As objecttypename [= initializer]

Aggregation (weak association) establishes a “USES-A” relationship between objects:
- In aggregation, the component object can have an existence independent of its use in the aggregate object.
- Destroying the aggregate does not destroy the component:
> because the type contains only a pointer to the object.
- Syntax of the member field to add in the type for object's aggregation (or similar syntax):
objectptrname As objecttypename Ptr [= initializer]

Inheritance establishes an “IS-A” relationship between instantiable types:
- The derived-type has the same features and functionality as its base-type, with some extensions.
- Syntax of the type's declaration header for object's inheritance:
Type typename [Alias alternatename] Extends objecttypename [Field = alignment]

Advantages of object association (composition/aggregation) over inheritance

In most cases "HAS-A" or "USES-A" relationship is more semantically correct than "IS-A" relationship.

Aggregation is more flexible than inheritance.
Implementation of type can be changed at run-time by changing the component object, but this cannot be do with inheritance (behavior of base type cannot be changed at run-time).

A design based on object association usually will have less types.

It is possible to implement a pseudo "multiple inheritance" (in languages which do not support it) by composing multiple objects into one.
There is no conflict between procedures/properties names, which might occur with inheritance.

Downsides of object association (composition/aggregation) compared to inheritance

When using aggregation, the behavior of the system may be harder to understand just by looking at the source code, since it's more dynamic and more interaction between types happens in run-time, rather than compile time.

Association approach might require more code and time effort.
A design based on object association usually will have more objects.

Example

Simple illustrative example that mixes composition, aggregation, and inheritance:
' Between the different types "Driver", "Person", "Driver_license" and "Vehicle", the respective relationships are:
'    - A driver “IS-A” person (driver is a person): => "INHERITANCE".
'    - A driver “HAS-A” driver's license (driver license only existing for the driver): => "COMPOSITION".
'    - A driver “USES-A” vehicle (vehicle lifetime independent of the driver life): => "AGGREGATION".

Type Person
    Public:
        Dim As String full_name
        Declare Constructor (ByRef _full_name As String)
    Protected:  '' to forbid at compile time the default-construction attempt of a Person instance
        Declare Constructor ()
End Type
Constructor Person (ByRef _full_name As String)
    This.full_name = _full_name
End Constructor

Type Driver_license
    Public:
        Dim As Integer number
End Type

Type Vehicle
    Public:
        Dim As String registration
        Declare Constructor (ByRef _registration As String)
End Type
Constructor Vehicle (ByRef _registration As String)
    This.registration = _registration
End Constructor

Type Driver Extends Person        '' inheritance
    Public:
        Dim As Driver_license dl  '' composition
        Dim As Vehicle Ptr pv     '' aggregation
        Declare Constructor (ByRef _full_name As String, ByRef _dl As Driver_license)
End Type
Constructor Driver (ByRef _full_name As String, ByRef _dl As Driver_license)
    Base(_full_name)
    This.dl = _dl
End Constructor

Dim As Driver d1 = Driver("User fxm", Type<Driver_license>(123456789))

Dim As Vehicle Ptr pv1 = New Vehicle("ABCDEFGHI")
d1.pv = pv1

Print "Person full name      : " & d1.full_name
Print "Driver license number : " & d1.dl.number
Print "Vehicle registration  : " & d1.pv->registration

Delete pv1
d1.pv = 0

Sleep

Output:
Person full name      : User fxm
Driver license number : 123456789
Vehicle registration  : ABCDEFGHI
See also

  • Type (Udt)
  • Extends
  • Inheritance Polymorphism
  • OBJECT built-in and RTTI info


Inheritance Polymorphism
The Inheritance Polymorphism is the ability of calling from the base-type the member procedures of derived-types without worrying about the real type of the processed objects.

Preamble:

Inheritance polymorphism (sub-type polymorphism) is the concept of providing a single interface to entities that can have different types.
More precisely, a same interface is implemented by member procedures having the same identifier in each type belonging to the same inheritance hierarchy.

Thanks to the 'abstract'/'virtual' procedures, one can write a code using only the base-type that will automatically call the derived-type procedures.
It is then possible to call the procedure of an object without worrying about its intrinsic type.

By using the same procedure name for several different types, the polymorphism allows a much more generic programming (abstraction).
The coder does not have to know, when calling a base procedure, the precise type of object on which the procedure will apply. He just needs to know that this type will implement the procedure.

For example a procedure 'moving()' will perform the appropriate movement according to the real derived-type of the instance referenced at the time of the call. This will allow the program to say 'instance.moving()' without having to worry about the real derived-type of 'instance'.

Inheritance polymorphism operating

The ability to redefine a procedure in a derived-type inheriting from a base-type is called specialization.
It is then possible to call the procedure of an object without worrying about its intrinsic type: it is the inheritance polymorphism.

This makes it possible to abstract the details of the specialized types of an object family, by masking them by a common interface which is the base-type.

Designation of objects using pointers or references of base-type
Considering a collection of objects whose instantiate types are derived-types from a base-type, then all these objects can be manipulated in an uniform way by considering them as objects of the base-type.
Better, certain behaviors can be specialized according to the instantiate type of each object. In other words, the use of distinct objects of the same inheritance hierarchy is homogeneous even if the behavior of these objects remains specific.

Thus, a base-type pointer or reference, pointing to an instance of a derived-type, can be used to manipulate such an object.

Overriding the abstract/virtual procedures in the base-type by specialized procedures in derived-types
To can declare abstract/virtual procedures in a type, this type must 'Extends' (directly or indirectly) the built-in 'Object' type.

A derived-type can override an abstract/virtual procedure declared in its base-type, by declaring a procedure with the same identifier and signature, meaning same number and type of parameters, same calling convention, and if any, same return type (or a return of a derived-type for return by reference or by pointer):
- Normally a base-type reference/pointer can access only a procedure in the same type or in a type upper in hierarchy (static binding at compile-time), even if this reference/pointer refers to an object of instantiate type derived from the base-type.
- But when the base-type procedure is abstract/virtual, this tells the running program to resolve the override procedure the most derived relating to the real object type (dynamic binding at run-time).

Restriction:
Polymorphism is not directly compatible with:
- any operators 'New[]' or 'Delete[]' (the array versions for statement/expression/overload operators) because the use of a sub-type pointer (instead of the real type) fails for accessing the other elements (except the first),
- even the overload operator 'Delete' is not directly compatible because it can not be declared virtual (being static).

Instead of having to call such an operator 'Delete([])' statement on derived-type pointer, the safest way is to simply call (on base-type pointer) an overridden user virtual member procedure that will automatically launch the operator 'Delete([])' statement at derived-type level.

Inheritance polymorphism learning, through example: 'Graph type collection'

In the below proposed example, the polymorphic part is broken down to better bring out all the elements necessary for the mechanics of polymorphism.

The generic base-type chosen is any 'Graphic Form' defined by two graphic points and a color (abstraction).
The specialized derived-types are a 'Graphic Line', a 'Graphic Box', and a 'Graphic Circle' (all defined by two graphic points and a color):
- The 'Graphic Line' connects the point 1 and the point 2.
- The 'Graphic Box' has as opposite vertices the point 1 (at the top and on the left) and the point 2 (in bottom and on the right).
- The 'Graphic Circle' has as center the point 1 and go through point 2.

The abstract procedure declared in the generic base-type, and which must be defined in each specialized derived-type, is the graphic drawing of the specialized form in a graphic window.
The two graphic points and the color being generic data, they so induce three generic data fields in the generic base-type, included by composition.
A 'graphic point' type is also defined with encapsulation of the x/y coordinate values (declared private), in order to control their validity (depending on the defined graphic screen size) by means of properties (but public these ones).

Notations:
- Generic base-type name: 'GraphicForm2P'
- Specialized derived-type names: 'GraphicLine2P', 'GraphicBox2P', 'GraphicCircle2P'
- Virtual procedure name: 'drawGraphicForm2P()'
- Additional type name (include by composition within the generic type): 'GraphicPoint'

  • 'GraphicPoint' type declaration (additional type for composition within generic base-type):
- The two coordinates ('_x' and '_y') are private as well as the two static internal functions ('xValid' and 'yValid') to return the validity of each coordinate passed as argument, compared to the graphic window size.
- For each coordinate, there are two public properties ('x' or 'y') as user interface: a getter, and a setter which tests the validity of the given value.
- The public non default constructor calls also the setters to initialize the two coordinates.
Type GraphicPoint
    Public:  '' user interface
        Declare Constructor ()
        Declare Constructor (Byval x0 As Integer = 0, Byval y0 As Integer = 0)
        Declare Property x () As Integer          '' x-coordinate getter
        Declare Property x (Byval x0 As Integer)  '' x-coordinate setter (control if inside open graphic window)
        Declare Property y () As Integer          '' y-coordinate getter
        Declare Property y (Byval y0 As Integer)  '' y-coordinate setter (control if inside open graphic window)
    Private:  '' hidden members
        Dim As Integer _x, _y
        Declare Static Function xValid (Byval x0 As Integer) As Integer  '' x-coordinate inside open graphic window?
        Declare Static Function yValid (Byval y0 As Integer) As Integer  '' y-coordinate inside open graphic window?
End Type
  • 'GraphicForm2P' type declaration (generic base-type):
- Two public graphic point variables ('pt1' and 'pt2') and a public color variable ('col') are included within the base type by composition.
- A public abstract procedure ('drawGraphicForm2P()') is declared (but without any body defining it).
- Although the base type is non-instantiable, a protected constructor is still defined to initialize the data fields, but called from each derived type constructor only.
- A virtual destructor (with an empty body) is declared, for getting compatibility with a derived type declaring its own destructor (not the case here). This base type destructor is public to be able to be called on a base type pointer or reference.
Type GraphicForm2P Extends Object  '' abstract graphic form defined by two points
    Public:  '' user interface
        Dim As GraphicPoint pt1, pt2
        Dim As Integer col
        Declare Abstract Sub drawGraphicForm2P ()  '' request procedure implementation for instantiable derived type
        Declare Virtual Destructor ()              '' for polymorphic compatibility with any derived type
    Protected:  '' hidden members
        Declare Constructor ()
        Declare Constructor (Byref p1 As GraphicPoint = Type(0, 0), Byref p2 As GraphicPoint = Type(0, 0), Byval col0 As Integer = 0)
End Type
  • 'GraphicLine2P', 'GraphicBox2P', 'GraphicCircle2P' type declarations (specialized derived-types):
- For each derived type, the same public procedure ('drawGraphicForm2P()') is declared, but its body is specialized for each derived type.
- For each derived type, a public constructor is declared and defined to initialize the base data fields (by calling the base constructor). No destructor because nothing specific to destroy from this derived type.
Type GraphicLine2P Extends GraphicForm2P  '' graphic line from point 1 to point 2
    Public:  '' user interface
        Declare Constructor (Byref p1 As GraphicPoint = Type(0, 0), Byref p2 As GraphicPoint = Type(0, 0), Byval col0 As Integer = 0)
        Declare Sub drawGraphicForm2P () Override  '' overridden procedure
End Type

Type GraphicBox2P Extends GraphicForm2P  '' graphic box from point 1 to point 2
    Public:  '' user interface
        Declare Constructor (Byref p1 As GraphicPoint = Type(0, 0), Byref p2 As GraphicPoint = Type(0, 0), Byval col0 As Integer = 0)
        Declare Sub drawGraphicForm2P () Override  '' overridden procedure
End Type

Type GraphicCircle2P Extends GraphicForm2P  '' graph circle centered on point1 and passing by point 2
    Public:  '' user interface
        Declare Constructor (Byref p1 As GraphicPoint = Type(0, 0), Byref p2 As GraphicPoint = Type(0, 0), Byval col0 As Integer = 0)
        Declare Sub drawGraphicForm2P () Override  '' overridden procedure
End Type
  • Full code of example:
- Three graphic points (used by the six forms) are constructed.
- To be able to trigger polymorphism, a base type-pointer array ('pgf') is declared then initialized with instances of different derived types, in order to constitute a collection of objects from different types (but all having a common base type).
- So, the same compiled code line, put in a loop, processes all instances from different types ('pgf(I)->drawGraphicForm2P()' or 'Delete pgf(I)'), because the polymorphism mechanic allows to call each specialized procedure at run-time.
Type GraphicPoint
    Public:  '' user interface
        Declare Constructor ()
        Declare Constructor (ByVal x0 As Integer = 0, ByVal y0 As Integer = 0)
        Declare Property x () As Integer          '' x-coordinate getter
        Declare Property x (ByVal x0 As Integer)  '' x-coordinate setter (control if inside open graphic window)
        Declare Property y () As Integer          '' y-coordinate getter
        Declare Property y (ByVal y0 As Integer)  '' y-coordinate setter (control if inside open graphic window)
    Private:  '' hidden members
        Dim As Integer _x, _y
        Declare Static Function xValid (ByVal x0 As Integer) As Integer  '' x-coordinate inside open graphic window?
        Declare Static Function yValid (ByVal y0 As Integer) As Integer  '' y-coordinate inside open graphic window?
End Type

Constructor GraphicPoint ()
End Constructor

Constructor GraphicPoint (ByVal x0 As Integer = 0, ByVal y0 As Integer = 0)
    This.x = x0
    This.y = y0
End Constructor

Property GraphicPoint.x () As Integer
    Return This._x
End Property

Property GraphicPoint.x (ByVal x0 As Integer)
    If GraphicPoint.xValid(x0) Then This._x = x0
End Property

Property GraphicPoint.y () As Integer
    Return This._y
End Property

Property GraphicPoint.y (ByVal y0 As Integer)
    If GraphicPoint.yValid(y0) Then This._y = y0
End Property

Static Function GraphicPoint.xValid (ByVal x0 As Integer) As Integer
    If ScreenPtr = 0 Then Return 0  '' no open graphic window
    Dim As Long w
    ScreenInfo(w)
    If x0 >= 0 And x0 <= w - 1 Then Return -1 Else Return 0
End Function

Static Function GraphicPoint.yValid (ByVal y0 As Integer) As Integer
    If ScreenPtr = 0 Then Return 0  '' no open graphic window
    Dim As Long h
    ScreenInfo( , h)
    If y0 >= 0 And y0 <= h - 1 Then Return -1 Else Return 0
End Function

Type GraphicForm2P Extends Object  '' abstract graphic form defined by two points
    Public:  '' user interface
        Dim As GraphicPoint pt1, pt2
        Dim As Integer col
        Declare Abstract Sub drawGraphicForm2P ()  '' request procedure implementation for instantiable derived type
        Declare Virtual Destructor ()              '' for polymorphic compatibility with any derived type
    Protected:  '' hidden members
        Declare Constructor ()
        Declare Constructor (ByRef p1 As GraphicPoint = Type(0, 0), ByRef p2 As GraphicPoint = Type(0, 0), ByVal col0 As Integer = 0)
End Type

Virtual Destructor GraphicForm2P ()
End Destructor

Constructor GraphicForm2P ()  '' implementation not absolutely necessary
End Constructor

Constructor GraphicForm2P (ByRef p1 As GraphicPoint = Type(0, 0), ByRef p2 As GraphicPoint = Type(0, 0), ByVal col0 As Integer = 0)
    This.pt1 = p1
    This.pt2 = p2
    This.col = col0
End Constructor

Type GraphicLine2P Extends GraphicForm2P  '' graphic line from point 1 to point 2
    Public:  '' user interface
        Declare Constructor (ByRef p1 As GraphicPoint = Type(0, 0), ByRef p2 As GraphicPoint = Type(0, 0), ByVal col0 As Integer = 0)
        Declare Sub drawGraphicForm2P () Override  '' overridden procedure
End Type

Constructor GraphicLine2P (ByRef p1 As GraphicPoint = Type(0, 0), ByRef p2 As GraphicPoint = Type(0, 0), ByVal col0 As Integer = 0)
    Base(p1, p2, col0)  '' call the base type constructor
End Constructor

Sub GraphicLine2P.drawGraphicForm2P ()
    If ScreenPtr <> 0 Then  '' open graphic window
        Line (This.pt1.x, This.pt1.y)-(This.pt2.x, This.pt2.y), This.col
    End If
End Sub

Type GraphicBox2P Extends GraphicForm2P  '' graphic box from point 1 to point 2
    Public:  '' user interface
        Declare Constructor (ByRef p1 As GraphicPoint = Type(0, 0), ByRef p2 As GraphicPoint = Type(0, 0), ByVal col0 As Integer = 0)
        Declare Sub drawGraphicForm2P () Override  '' overridden procedure
End Type

Constructor GraphicBox2P (ByRef p1 As GraphicPoint = Type(0, 0), ByRef p2 As GraphicPoint = Type(0, 0), ByVal col0 As Integer = 0)
    Base(p1, p2, col0)  '' call the base type constructor
End Constructor

Sub GraphicBox2P.drawGraphicForm2P ()
    If ScreenPtr <> 0 Then  '' open graphic window
        Line (This.pt1.x, This.pt1.y)-(This.pt2.x, This.pt2.y), This.col, B
    End If
End Sub

Type GraphicCircle2P Extends GraphicForm2P  '' graph circle centered on point1 and passing by point 2
    Public:  '' user interface
        Declare Constructor (ByRef p1 As GraphicPoint = Type(0, 0), ByRef p2 As GraphicPoint = Type(0, 0), ByVal col0 As Integer = 0)
        Declare Sub drawGraphicForm2P () Override  '' overridden procedure
End Type

Constructor GraphicCircle2P (ByRef p1 As GraphicPoint = Type(0, 0), ByRef p2 As GraphicPoint = Type(0, 0), ByVal col0 As Integer = 0)
    Base(p1, p2, col0)  '' call the base type constructor
End Constructor

Sub GraphicCircle2P.drawGraphicForm2P ()
    If ScreenPtr <> 0 Then  '' open graphic window
        Dim As Integer r = Sqr((This.pt2.x - This.pt1.x) * (This.pt2.x - This.pt1.x) + (This.pt2.y - This.pt1.y) * (This.pt2.y - This.pt1.y))
        Circle (This.pt1.x, This.pt1.y), r, This.col
    End If
End Sub

Screen 12  '' open graphic window

Dim As GraphicPoint p1 = GraphicPoint(320, 240)  '' to construct graphic point 1
Dim As GraphicPoint p2 = GraphicPoint(500, 350)  '' to construct graphic point 2
Dim As GraphicPoint p3 = GraphicPoint(280, 170)  '' to construct graphic point 2

'' array of base type pointer referring to instances of different derived types
Dim As GraphicForm2P Ptr pgf (...) = {New GraphicLine2P(p1, p2, 14), New GraphicBox2P(p1, p2, 13), New GraphicCircle2P(p1, p2, 12), _
                                      New GraphicLine2P(p1, p3, 11), New GraphicBox2P(p1, p3, 10), New GraphicCircle2P(p1, p3, 09)}

For I As Integer = LBound(pgf) To UBound(pgf)
    pgf(I)->drawGraphicForm2P()  '' accessing dedicated overridden procedure by polymorphism
Next I

For I As Integer = LBound(pgf) To UBound(pgf)
    Delete pgf(I)  '' accessing dedicated overridden destructor (if necessary) by polymorphism
Next I

Sleep

See the graphics output by running this code.

See also

  • Type (Udt), Extends, Object, Operator Is (Rtti)
  • Virtual, Abstract, Override
  • Pointer, Reference
  • Composition, Aggregation, Inheritance
  • OBJECT built-in and RTTI info
  • Use Implicit / Overload New([]) and Delete([]) Operators with Inheritance Polymorphism


Constructors, '=' Assignment-Operators, and Destructors (advanced, part #1)
Proper use of Constructors, '=' Assignment-Operators, and Destructors, which are the special member procedures for constructing/initializing, assigning, and destroying objects (part #1).

Preamble:

Some member procedures, which have a predefined name in the Type, have a specific role that is constructing, assigning, and destroying objects. These are the constructors, the '=' assignment-operators, and the destructor.
Of these, some are special because a version will be automatically built by the compiler if they are needed and not explicitly defined by the user. These are the default-constructor, the copy-constructor, the copy-assignment operator, and the destructor.

It is therefore often necessary that user defines its own explicit versions in order to execute actions that must take place during all lifetime of an object:
- For example, if the object contains dynamically allocated variables, it is necessary to reserve them memory when the object is created.
- For object assignment, the memory must often be reallocated.
- At the destruction of the object, it is necessary to free the allocated memory.

Once the user defines any explicit constructor or explicit destructor, the compiler no longer automatically defines the implicit default constructor or the implicit destructor.
In particular, if the user only defines an explicit constructor taking parameters, it will no longer be possible to simply construct an object, without providing the parameters to this constructor, unless, of course, the user defines also an explicit default constructor (the one which does not take parameters).

Type inheritance and virtuality must also be taken into account for defining these special member procedures.

As any member, these special member procedures may have any accessibility, public, protected or private (but a private access does not have much interest, and even can totally prevent any direct or derived object construction).

Table of Contents
1. Constructors and destructors
2. '=' Assignment-operators
3. Processing variable-length arrays as members of Type

1. Constructors and destructors

A constructor is called automatically when instantiating the object. The destructor is called automatically when it is destroyed.
This destruction occurs when outputting the current scope block for auto storage kind objects.

For dynamically allocated objects, the constructor and destructor are automatically called by expressions that use the 'New', 'New[]', and 'Delete', 'Delete[]' operators. That is why it is recommended to use them instead of the '[C|Re]]Allocate' and 'Deallocate' functions to dynamically create objects.
Also, do not use 'Delete' or 'Delete[]' on 'Any Ptr' pointers, because the compiler must determine which destructor to call with the type of pointer.

Definition of constructors and destructors
The constructor is called after the memory allocation of the object and the destructor is called before this memory is freed. The management of the dynamic allocation of memory with the Types is thus simplified.
In the case of member object fields, the order of construction is that of their declarations, and the order of destruction is the reverse. It is in this order that the constructors and destructors of each member object field are called.

The explicit constructors may have parameters. They can be overloaded, but not the explicit destructors. This is because in general one knows the context in which an object is created, but one cannot know the context in which it is destroyed: there can only be one destructor.
Constructors that do not take a parameter or with all parameters having a default value, automatically replace the default constructors defined by the compiler if there were no explicitly defined constructors in the Types. This means that these constructors will be called automatically by the default constructors of the derived Types.

Example - Constructors and destructor ('ZstringChain' Type):
Type ZstringChain                                '' implement a zstring chain
    Dim As ZString Ptr pz                        '' define a pointer to the chain
    Declare Constructor ()                       '' declare the explicit default constructor
    Declare Constructor (ByVal size As Integer)  '' declare the explicit constructor with as parameter the chain size
    Declare Destructor ()                        '' declare the explicit destructor
End Type

Constructor ZstringChain ()
    This.pz = 0  '' reset the chain pointer
End Constructor

Constructor ZstringChain (ByVal size As Integer)
    This.pz = CAllocate(size + 1, SizeOf(ZString))  '' allocate memory for the chain
End Constructor

Destructor ZstringChain ()
    If This.pz <> 0 Then
        Deallocate This.pz  '' free the allocated memory if necessary
    This.pz = 0         '' reset the chain pointer
    End If
End Destructor

Dim As ZstringChain zc1  '' instantiate a non initialized chain : useless

Dim As ZstringChain zc2 = ZstringChain(9)  '' instantiate a szstring chain of 9 useful characters
'                                          '' shortcut: Dim As ZstringChain zc2 = 9
*zc2.pz = "FreeBASIC"                      '' fill up the chain with 9 characters
Print "zc2 chain:"
Print "'" & *zc2.pz & "'"                  '' print the chain

Sleep

Output:
zc2 chain:
'FreeBASIC'
The constructors will sometimes have to perform more complicated tasks than those given in this example. In general, they can do all the feasible operations in a normal member procedure, except using uninitialized data of course.
In particular, the data of inherited objects are not initialized as long as the constructors of the base-Types are not called. For this reason, the constructors of the base-Types must always be called before running the constructor of the Type being instantiated.
If the constructors of the base-Types are not explicitly called, the compiler will call, by default, the constructors of the base-Types that do not take a parameter or whose parameters have a default value (and, if no such a constructor is explicitly defined in the base-Types, it will call the implicit default constructors of these Types).

Constructors
A constructor is a kind of member procedure that initializes an instance of its Type. A constructor has the same name as the Type and no return value. A constructor can have any number of parameters and a Type may have any number of overloaded constructors.

If not any constructor is explicitly defined, the compiler will generate (if needed) a default-constructor that takes no parameters. The user can cancel this behavior by declaring and defining explicitly its own default-constructor.
If the user defines his own constructor(s), implicit default-initialization operations are always done prior to the user's constructor(s) code execution.

Order of Construction:
- The Type constructor is called.
- The base Types and their member constructors are called in the order of declaration.
- If the Type has virtual member procedures (including these inherited from its Base), the virtual-pointer (vptr) is set to point to the virtual-table (vtbl) of the Type.
- The body of the Type constructor procedure is executed.

Default-constructor:
The default-constructor has no parameters.
It follows slightly different rules:
- The default-constructor is one of the special member functions.
- If no constructors are explicitly declared in a Type, the compiler provides a default-constructor.
- If any non default-constructors are declared, the compiler does not provide a default-constructor and the user is forced to declare one if necessary.

Conversion-constructors:
If a Type has a constructor with a single parameter, or if at least all other parameters have a default value, the type of the first argument can be implicitly converted to the type of the Type by the compiler.
In such expressions, the term 'variable' may be considered as a short-cut of 'typename(variable)'.

Such conversions can be useful in some cases, but more often they can lead to poor readability (in these cases, it is better to explicitly call the matching constructor).

Example - Implicit conversion:
Type UDT Extends Object
    Declare Constructor ()
    Declare Constructor (ByVal i0 As Integer)
    Declare Destructor ()
    Dim As Integer i
End Type

Constructor UDT ()
    Print "   => UDT.default-constructor", @This
End Constructor

Constructor UDT (ByVal i0 As Integer)
    Print "   => UDT.conversion-constructor", @This
    This.i = i0
End Constructor

Function RtnI (ByRef u As UDT) As Integer
    Return u.i
End Function

Destructor UDT ()
    Print "   => UDT.destructor", , @This
End Destructor

Scope
    Print "Construction: 'Dim As UDT u'"
    Dim As UDT u
    Print
    Print "Assignment: 'u = 123'"
    Print "      " & RtnI(123)            ''  RtnI(123): implicite conversion using the conversion-constructor,
    '                                     ''             short_cut of RtnI(UDT(123))
    Print
    Print "Going out scope: 'End Scope'"
End Scope

Sleep

Output example:
Construction: 'Dim As UDT u'
   => UDT.default-constructor             1703588

Assignment: 'u = 123'
   => UDT.conversion-constructor          1703580
      123
   => UDT.destructor                      1703580

Going out scope: 'End Scope'
   => UDT.destructor                      1703588
But such conversions can even lead to subtle but serious errors in the code.

Example - Subtle/Serious errors in the code:
Type point2D
    Declare Constructor (ByVal x0 As Integer = 0, ByVal y0 As Integer = 0)
    Declare Operator Cast () As String
    Dim As Integer x, y
End Type

Constructor point2D (ByVal x0 As Integer = 0, ByVal y0 As Integer = 0)
    This.x = x0
    This.y = y0
End Constructor

Operator point2D.cast () As String
    Return "(" & This.x & ", " & This.y & ")"
End Operator

Operator + (ByRef v0 As point2D, ByRef v1 As point2D) As point2D
    Return Type(v0.x + v1.x, v0.y + v1.y)
End Operator

Operator * (ByRef v0 As point2D, ByRef v1 As point2D) As Integer
    Return v0.x * v1.x + v0.y * v1.y
End Operator

Print "Construction of v1: 'Dim As point2D v1 = point2D(2, 3)'"
Dim As point2D v1 = point2D(2, 3)
Print "  => " & v1
Print
Print "Addition to v1: 'v1 + 4'"
Print "  => " & v1 + 4                  ''  4: implicite conversion using the conversion-constructor,
'                                       ''             short_cut of point2D(4, ) or point2D(4)
Print
Print "Multiplication of v1: 'v1 * 5'"
Print "  => " & v1 * 5                  ''  5: implicite conversion using the conversion-constructor,
'                                       ''             short_cut of point2D(5, ) or point2D(5)
Sleep

Output example:
Construction of v1: 'Dim As point2D v1 = point2D(2, 3)'
  => (2, 3)

Addition to v1: 'v1 + 4'
  => (6, 3)

Multiplication of v1: 'v1 * 5'
  => 10
In this case, it is dangerous to declare a multi-parameter constructor with optional parameters, because the compiler can interpret one of its forms as a conversion-constructor.

A workaround: define a default constructor, and the multi-parameter constructor but without optional parameters (or at least one non-optional parameter without considering the first one).

Copy-constructor:
The copy-constructor is a special member procedure that takes as input a reference to an object of the same type, and constructs a new object by copying it.
If the user does not declare a copy-constructor, the compiler generates (if needed) a copy-constructor for him. The declaration of a copy-assignment operator (see above) does not remove the generation by the compiler of a copy-constructor.

It will sometimes be necessary to create a copy constructor. The purpose of this kind of constructor is to initialize an object when instantiating it from another object.
Any Type has if needed an implicit copy constructor automatically generated by the compiler, whose sole purpose is to copy the fields of the object to be copied one by one into the fields of the object to be instantiated.
However, this implicit copy constructor will not always be enough, and the user will sometimes have to provide one explicitly.

This will be particularly the case when some data objects have been allocated dynamically (only member pointers in the Type for object aggregation). A shallow copy of the fields of one object in another would only copy the pointers, not the data pointed. Thus, changing this data for one object would result in the modification of the other object's data, which would probably not be the desired effect.

If the user implements a copy-constructor, it is recommend to also implement a copy-assignment operator so that the meaning of the code is clear.

In addition to the explicit syntax for calling the copy-constructor, this one is also called when an object is passed by value to a procedure, and when a function returns an object by value by using the 'Return' keyword.

A copy constructor can not take into account its argument (the object to clone) by value, because "byval arg" itself generally calls the copy-constructor, and this would induce an infinite loop.

For the 'ZstringChain' Type defined above, the user needs a copy constructor:
Type ZstringChain                                   '' implement a zstring chain
    Dim As ZString Ptr pz                           '' define a pointer to the chain
    Declare Constructor ()                          '' declare the explicit default constructor
    Declare Constructor (ByVal size As Integer)     '' declare the explicit constructor with as parameter the chain size
    Declare Constructor (ByRef zc As ZstringChain)  '' declare the explicit copy constructor
    Declare Destructor ()                           '' declare the explicit destructor
End Type

Constructor ZstringChain ()
    This.pz = 0  '' reset the chain pointer
End Constructor

Constructor ZstringChain (ByVal size As Integer)
    This.pz = CAllocate(size + 1, SizeOf(ZString))  '' allocate memory for the chain
End Constructor

Constructor ZstringChain (ByRef zc As ZstringChain)
    This.pz = CAllocate(Len(*zc.pz) + 1, SizeOf(ZString))  '' allocate memory for the new chain
    *This.pz = *zc.pz                                      '' initialize the new chain
End Constructor

Destructor ZstringChain ()
    If This.pz <> 0 Then
        Deallocate This.pz  '' free the allocated memory if necessary
        This.pz = 0         '' reset the chain pointer
    End If
End Destructor

Dim As ZstringChain zc1  '' instantiate a non initialized chain : useless

Dim As ZstringChain zc2 = ZstringChain(9)           '' instantiate a szstring chain of 9 useful characters
'                                                   '' shortcut: Dim As ZstringChain zc2 = 9
*zc2.pz = "FreeBASIC"                               '' fill up the chain with 9 characters
Print "zc2 chain:"
Print "'" & *zc2.pz & "'"                           '' print the chain
Print
Dim As ZstringChain zc3 = zc2                       '' instantiate a new szstring chain by copy construction
Print "zc3 chain (zc3 copy constructed from zc2):"
Print "'" & *zc3.pz & "'"                           '' print the chain
Print
*zc3.pz = "modified"                                '' modify the new chain
Print "zc3 chain (modified):"
Print "'" & *zc3.pz & "'"                           '' print the new chain
Print
Print "zc2 chain:"
Print "'" & *zc2.pz & "'"                           '' print the copied chain (not modified)

Sleep

Output:
zc2 chain:
'FreeBASIC'

zc3 chain (zc3 copy constructed from zc2):
'FreeBASIC'

zc3 chain (modified):
'modified'

zc2 chain:
'FreeBASIC'
Destructor
The destructor function is the inverse of constructor function. It is called when an objects is destroyed.
The destructor is commonly used to "clean up" when an object is no longer necessary. The destructor cannot have parameters.

If none destructor is explicitly defined, the compiler will generate (if needed) a destructor. The user can override this behavior by declaring and defining explicitly its own destructor.
If the user defines his own destructor, implicit destruction operations are always done posterior to the user's destruction code execution.

The destructor is called when one of the following events occurs:
- An object created using the New operator is explicitly destroyed using the Delete operator.
- A local object with block scope goes out of scope.
- A program terminates and global or static objects exist.

If a base Type or data member has an accessible destructor, and if the Type does not declare a destructor, the compiler generates one.

Order of destruction:
- The Type destructor is called
- If the Type has virtual member procedures (including these inherited from its Base), the virtual-pointer (vptr) is set to point to the virtual-table (vtbl) of the Type.
- The body of the Type destructor procedure is executed.
- Destructors for base Types are called in the reverse order of declaration.

Constructors and destructors when inheritance
How to call constructors and destructors of base Types when instantiating and destroying a derived Type instance?
The compiler cannot know which constructor to call among the different overloaded constructors potentially present. To call another constructor of a base Type than the constructor taking no parameter, use the keyword 'Base ()' specifying the parameters to pass, and this, only authorized on the first code line of the calling constructor.

On the other hand, it is useless to specify the destructor to call, since this one is unique. The user must not call the destructors of the base Types themselves, the compiler does it on its own by chaining the destructors.

Example - Explicit call of the base Type constructor ('Child' Type extends 'Parent' Type):
Type Parent  '' declare the parent type
    Dim As Integer I
    Declare Constructor ()
    Declare Constructor (ByVal i0 As Integer)
    Declare Destructor ()
End Type

Constructor Parent ()  '' define parent Type constructor
    Print "Parent.Constructor()"
End Constructor

Constructor Parent (ByVal i0 As Integer)  '' define parent Type constructor
    This.I = i0
    Print "Parent.Constructor(Byval As Integer)"
End Constructor

Destructor Parent ()  '' define parent Type destructor
    Print "Parent.Destructor()"
End Destructor

Type Child Extends Parent  '' declare the child Type
    Declare Constructor ()
    Declare Destructor ()
End Type

Constructor Child ()  '' define child Type default constructor
    Base(123)         '' authorize only on the first code line of the constructor body
    Print "  Child.Constructor()"
End Constructor

Destructor Child ()  '' define child Type destructor
    Print "  Child.Destructor()"
End Destructor

Scope
    Dim As Child c
    Print
End Scope

Sleep

Output:
Parent.Constructor(Byval As Integer)
  Child.Constructor()

  Child.Destructor()
Parent.Destructor()
If it was not specified that the constructor to be called for the base Type was the constructor taking an Integer parameter, the compiler would have called the default constructor of the base Type (in the above example, one can put in comment the line 'Base(123)' and execute again to see this different behavior).

If the Type derives from several base Types (multiple-level inheritance), each derived Type constructor can explicitly call only one constructor, the one of its direct base Type, thus the all constituting a chaining of the base constructors.

When using inheritance polymorphism (sub-type polymorphism), the object are manipulated through base Type pointers or references:
- If at least one derived Type has an explicit destructor defined, all its base destructors must be virtual so that the destruction can start at this most derived Type and works its way down to the last base Type.
- To do this, it may be necessary to add virtual destructors with an empty body anywhere an explicit destruction was not yet required, in order to supersede each non-virtual implicit destructor built by the compiler.

Note:
- When a derived Type has a base Type where a default constructor or copy constructor or destructor is defined (implicitly or explicitly), the compiler defines a default constructor or copy constructor or destructor for that derived Type.
- The built-in 'Object' Type having a default-constructor and a copy-constructor both defined implicitly, so all Types deriving (directly or indirectly) from 'Object' have at least implicitly a default constructor and copy constructor.

Virtual procedures calls in constructors and destructors
It is usually safe to call any member procedure from within a constructor or destructor because the object is completely set up (virtual tables are initialized and so on) prior to the execution of the first line of user code. However, it is potentially unsafe for a member procedure to call a virtual member procedure for an abstract base Type during construction or destruction.

Be careful when calling virtual procedures in constructors or destructor, because the procedures that are called in the base constructors or destructor is the base Type versions, not the derived Type versions. When such a virtual procedure is called, the procedure invoked is the procedure defined for the constructor's or destructor's own Type (or inherited from its Bases).

Back to top

2. '=' Assignment-operators

Among all '=' assignment-operators, there is a specific one that takes as input a reference to an object of the same type, and makes a copy of it (without creating a new object).
It is the copy-assignment operator.

Copy-assignment operator
If the user does not declare a copy-assignment operator, the compiler generates (if needed) a copy-assignment operator for him. The declaration of a copy-constructor (see above) does not remove the generation by the compiler of a copy-assignment operator.

A copy-assignment operator must be defined if the implicit copy is not sufficient.
This happens in cases when the object manages dynamically allocated memory or other resources which need to be specially copied (for example if a member pointer points to dynamically allocated memory, the implicit '=' assignment operator will simply copy the pointer value instead of allocate memory and then perform the copy of data).

If the user implements a copy-assignment operator, it is recommend to also implement a copy-constructor so that the meaning of the code is clear.

In addition to the explicit syntax for calling the copy-assignment operator, this one is also called when a function returns an object by value by using the 'Function =' keyword.

A copy-assignment operator can not take into account its argument (the object to clone) by value, because there are cases where "byval arg" is coded as default-construction + copy-assignment (when for example the UDT has no copy-constructor but has an object field with a default-constructor or a Base with default-constructor), and this would induce an infinite loop.

For the 'ZstringChain' Type defined above, the user needs also a copy-assignment operator (see the 'rule of three' in 'advanced #2' page):
Type ZstringChain                                    '' implement a zstring chain
    Dim As ZString Ptr pz                            '' define a pointer to the chain
    Declare Constructor ()                           '' declare the explicit default constructor
    Declare Constructor (ByVal size As Integer)      '' declare the explicit constructor with as parameter the chain size
    Declare Constructor (ByRef zc As ZstringChain)   '' declare the explicit copy constructor
    Declare Operator Let (ByRef zc As ZstringChain)  '' declare the explicit copy assignment operator
    Declare Destructor ()                            '' declare the explicit destructor
End Type

Constructor ZstringChain ()
    This.pz = 0  '' reset the chain pointer
End Constructor

Constructor ZstringChain (ByVal size As Integer)
    This.pz = CAllocate(size + 1, SizeOf(ZString))  '' allocate memory for the chain
End Constructor

Constructor ZstringChain (ByRef zc As ZstringChain)
    This.pz = CAllocate(Len(*zc.pz) + 1, SizeOf(ZString))  '' allocate memory for the new chain
    *This.pz = *zc.pz                                      '' initialize the new chain
End Constructor

Operator ZstringChain.Let (ByRef zc As ZstringChain)
    If @zc <> @This Then                                       '' avoid self assignment destroying the chain
        If This.pz <> 0 Then
            Deallocate This.pz                                 '' free the allocated memory if necessary
        End If
        This.pz = CAllocate(Len(*zc.pz) + 1, SizeOf(ZString))  '' allocate memory for the new chain
        *This.pz = *zc.pz                                      '' initialize the new chain
    End If
End Operator

Destructor ZstringChain ()
    If This.pz <> 0 Then
        Deallocate This.pz  '' free the allocated memory if necessary
        This.pz = 0         '' reset the chain pointer
    End If
End Destructor

Dim As ZstringChain zc1  '' instantiate a non initialized chain : useless

Dim As ZstringChain zc2 = ZstringChain(9)           '' instantiate a szstring chain of 9 useful characters
'                                                   '' shortcut: Dim As ZstringChain zc2 = 9
*zc2.pz = "FreeBASIC"                               '' fill up the chain with 9 characters
Print "zc2 chain:"
Print "'" & *zc2.pz & "'"                           '' print the chain
Print
Dim As ZstringChain zc3 = zc2                       '' instantiate a new szstring chain by copy construction
Print "zc3 chain (zc3 copy constructed from zc2):"
Print "'" & *zc3.pz & "'"                           '' print the chain
Print
*zc3.pz = "modified"                                '' modify the new chain
Print "zc3 chain (modified):"
Print "'" & *zc3.pz & "'"                           '' print the new chain
Print
Print "zc2 chain:"
Print "'" & *zc2.pz & "'"                           '' print the copied chain (not modified)
Print
zc3 = zc2
Print "zc3 chain (zc3 copy assigned from zc2):"
Print "'" & *zc3.pz & "'"                           '' print the new chain
Print
*zc3.pz = "changed"                                 '' modify the new chain
Print "zc3 chain (changed):"
Print "'" & *zc3.pz & "'"                           '' print the new chain
Print
Print "zc2 chain:"
Print "'" & *zc2.pz & "'"                           '' print the copied chain (not modified)

Sleep

Output:
zc2 chain:
'FreeBASIC'

zc3 chain (zc3 copy constructed from zc2):
'FreeBASIC'

zc3 chain (modified):
'modified'

zc2 chain:
'FreeBASIC'

zc3 chain (zc3 copy assigned from zc2):
'FreeBASIC'

zc3 chain (changed):
'changed'

zc2 chain:
'FreeBASIC'
Back to top

3. Processing variable-length arrays as members of Type

A variable-length array is not a pseudo-object like a variable-length string, because there is no default copy-constructor and copy-assignment operators as for a variable-length string.

But when a variable-length array is declared in a Type, the compiler build a default copy-constructor and a default copy-assignment operator for the Type. It includes all code for sizing the destination array and copying the data from the source array, if needed:
Example for automatic array sizing and copying by the compiler:
Type UDT
    Dim As Integer array(Any)
End Type

Dim As UDT u1, u2

ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
    u1.array(I) = I
Next I

u2 = u1
For I As Integer = LBound(u2.array) To UBound(u2.array)
    Print u2.array(I);
Next I
Print

Dim As UDT u3 = u1
For I As Integer = LBound(u3.array) To UBound(u3.array)
    Print u3.array(I);
Next I
Print

Sleep

Output:
 1 2 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9
If the user want to specify his own copy-constructor and copy-assignment operator (to process additional complex field members for example), the above automatic array sizing and copying by the compiler is broken:
Example for automatic array sizing and copying by the compiler broken by an explicit copy-constructor and copy-assignment operator:
Type UDT
  Dim As Integer array(Any)
  'user fields
  Declare Constructor ()
  Declare Constructor (ByRef u As UDT)
  Declare Operator Let (ByRef u As UDT)
End Type

Constructor UDT ()
  'code for user fields in constructor
End Constructor

Constructor UDT (ByRef u As UDT)
  'code for user fields in copy-constructor
End Constructor

Operator UDT.Let (ByRef u As UDT)
  'code for user fields in copy-assignement operator
End Operator

Dim As UDT u1, u2

ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
  u1.array(I) = I
Next I

u2 = u1
For I As Integer = LBound(u2.array) To UBound(u2.array)
  Print u2.array(I);
Next I
Print

Dim As UDT u3 = u1
For I As Integer = LBound(u3.array) To UBound(u3.array)
  Print u3.array(I);
Next I
Print

Sleep

Output (none):
The elementary variable-length array member cannot be copied as a pseudo-object like a variable-length string member, because there is not implicit assignment (referring to the above example, 'This.array = u.array' is disallowed).
The user must code explicitly the sizing and the copying of the array member:
Example for array sizing and copying explicitly set in the user copy-constructor and copy-assignment operator:
#include "crt/string.bi"

Type UDT
    Dim As Integer array(Any)
    'user fields
    Declare Constructor ()
    Declare Constructor (ByRef u As UDT)
    Declare Operator Let (ByRef u As UDT)
End Type

Constructor UDT ()
    'code for user fields in constructor
End Constructor

Constructor UDT (ByRef u As UDT)
    'code for user fields in copy-constructor
    If UBound(u.array) >= LBound(u.array) Then  '' explicit array sizing and copying
        ReDim This.array(LBound(u.array) To UBound(u.array))
        memcpy(@This.array(LBound(This.array)), @u.array(LBound(u.array)), (UBound(u.array) - LBound(u.array) + 1) * SizeOf(@u.array(LBound(u.array))))
    End If
End Constructor

Operator UDT.Let (ByRef u As UDT)
    'code for user fields in copy-assignement operator
    If @This <> @u And UBound(u.array) >= LBound(u.array) Then  '' explicit array sizing and copying
        ReDim This.array(LBound(u.array) To UBound(u.array))
        memcpy(@This.array(LBound(This.array)), @u.array(LBound(u.array)), (UBound(u.array) - LBound(u.array) + 1) * SizeOf(@u.array(LBound(u.array))))
    End If
End Operator

Dim As UDT u1, u2

ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
    u1.array(I) = I
Next I

u2 = u1
For I As Integer = LBound(u2.array) To UBound(u2.array)
    Print u2.array(I);
Next I
Print

Dim As UDT u3 = u1
For I As Integer = LBound(u3.array) To UBound(u3.array)
    Print u3.array(I);
Next I
Print

Sleep

Output:
 1 2 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9
Another elegant possibility is to keep this sizing/copying automatically coded by the compiler, but by simply calling it explicitly. For this, an obvious solution for the member array is to no longer put it at the level of the Type itself, but rather in another specific Type, but inherited (seen from the outside, it is exactly the same):
Example for using a base Type including the dynamic array member:
Type UDT0
    Dim As Integer array(Any)
End Type

Type UDT Extends UDT0
    'user fields
    Declare Constructor ()
    Declare Constructor (ByRef u As UDT)
    Declare Operator Let (ByRef u As UDT)
End Type

Constructor UDT ()
    'code for user fields in constructor
End Constructor

Constructor UDT (ByRef u As UDT)
    'code for user fields in copy-constructor
    Base(u)  '' inherited array sizing and copying from Base copy-constructor
End Constructor

Operator UDT.Let (ByRef u As UDT)
    'code for user fields in copy-assignement operator
    Cast(UDT0, This) = u  '' inherited array sizing and copying from Base copy-assignement operator
End Operator

Dim As UDT u1, u2

ReDim u1.array(1 To 9)
For I As Integer = LBound(u1.array) To UBound(u1.array)
    u1.array(I) = I
Next I

u2 = u1
For I As Integer = LBound(u2.array) To UBound(u2.array)
    Print u2.array(I);
Next I
Print

Dim As UDT u3 = u1
For I As Integer = LBound(u3.array) To UBound(u3.array)
    Print u3.array(I);
Next I
Print

Sleep

Output:
 1 2 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9
Back to top

See also

  • Constructor, Destructor
  • Operator =[>] (Assignment), Operator Let (Assignment)
  • Constructors and Destructors (basics)
  • Constructors, '=' Assignment-Operators, and Destructors (advanced, part #2)


Constructors, '=' Assignment-Operators, and Destructors (advanced, part #2)
Proper use of Constructors, '=' Assignment-Operators, and Destructors, which are the special member procedures for constructing/initializing, assigning, and destroying objects (part #2).

Table of Contents
1. Compiler interaction (with default-constructor, copy-constructor, and copy-assignment operator)
2. Rules of good manners (for constructors, copy-constructors, copy-assignment operators, and destructors)
3. Member access rights impact (on declaration of constructors, copy-constructors, copy-assignment operators, and destructors)

1. Compiler interaction (with default-constructor, copy-constructor, and copy-assignment operator)

During assignments or copy-constructions, the compiler interacts with the different calls of copy-assignment operators, default-constructors and copy-constructors, in order to optimize the copy for resulting objects, making the best use of the member procedures provided by the user (maybe non-exhaustively).

  • For an assignment ('object2 = object1')
Algorithm:
'   If (Type has a copy-assignment operator) Then
'   |  => the copy-assignment operator of Type is called
'   Else
'   |  If (Type has a Base with default-constructor) AND (Type has a Base with copy-assignment operator) Then
'   |  |  => the copy-assignment operator of Base is called (for Base fields)
'   |  |  => a shallow-copy is done on only Type fields
'   |  Else
'   |  |  => a full shallow-copy is done on all fields
'   |  End If
'   End If
  • For a copy-construction ('Dim As typename object2 = object1')
Algorithm:
'   If (Type has a Base with default-constructor) Then
'   |  => the default-constructor of Base is called
'   End If
'   If (Type has a copy-constructor) Then
'   |  => the copy-constructor of Type is called
'   Else
'   |  => the Type object is implicitly default-constructed (even if exists an explicit Type default-constructor)
'   |  If (Type has a copy-assignment operator) Then
'   |  |  If (Type has an object field with a default-constructor) OR (Type has a Base with default-constructor) Then
'   |  |  |  => the copy-assignment operator of Type is called
'   |  |  Else
'   |  |  |  => a full shallow-copy is done on all fields
'   |  |  End If
'   |  Else
'   |  |  If (Type has a Base with default-constructor) AND (Type has a Base with copy-assignment operator) Then
'   |  |  |  => the copy-assignment operator of Base is called (for Base fields)
'   |  |  |  => a shallow-copy is done on only Type fields
'   |  |  Else
'   |  |  |  => a full shallow-copy is done on all fields
'   |  |  End If
'   |  End If
'   End If
Notes:
A Type has a default-constructor if one among the following propositions is verified:
- It has an explicit default-constructor
- One at least of its field has an initializer.
- One at least of its members is an object having a default-constructor itself.
- Its Base (if exists) has a default-constructor (the built-in 'Object' has a default-constructor).

Under certain conditions, the explicit copy-assignment operator may be called for a copy-construction (if there is not an explicit copy-constructor).
But inversely, the explicit copy-constructor will never be called for an assignment (if there is not an explicit copy-assignment operator) regardless of the conditions.

  • Example to highlight/validate this interaction with default-constructor, copy-constructor, and copy-assignment operator, during assignments or copy-constructions
By commenting or not, as you want, independently each of the first 7 code lines ('#define UDT...'), you can test all conditions of the above algorithms:
#define UDTbase_copy_assignment_operator
#define UDTbase_default_constructor
#define UDTbase_copy_constructor

#define UDTderived_copy_assignment_operator
#define UDTderived_default_constructor
#define UDTderived_copy_constructor
#define UDTderived_object_field_with_constructor

Type UDTbase
    #ifdef UDTbase_copy_assignment_operator
    Declare Operator Let (ByRef u1 As UDTbase)
    #endif
    #ifdef UDTbase_copy_constructor
    Declare Constructor ()
    Declare Constructor (ByRef u1 As UDTbase)
    #endif
    #ifdef UDTbase_default_constructor
    #ifndef UDTbase_copy_constructor
    Declare Constructor ()
    #endif
    #endif
    Declare Destructor ()
    Dim As Integer i1
End Type

#ifdef UDTbase_copy_assignment_operator
Operator UDTbase.Let (ByRef u1 As UDTbase)
    Print "   => UDTbase.copy-assignment", @This & " from " & @u1
    This.i1 = u1.i1
End Operator
#endif
#ifdef UDTbase_copy_constructor
Constructor UDTbase ()
    Print "   => UDTbase.default-constructor", @This
End Constructor
Constructor UDTbase (ByRef u1 As UDTbase)
    Print "   => UDTbase.copy-constructor", @This & " from " & @u1
    This.i1 = u1.i1
End Constructor
#endif
#ifdef UDTbase_default_constructor
#ifndef UDTbase_copy_constructor
Constructor UDTbase ()
    Print "   => UDTbase.default-constructor", @This
End Constructor
#endif
#endif
Destructor UDTbase ()
    Print "   => UDTbase.destructor", , @This
End Destructor

Type UDTderived Extends UDTbase
    #ifdef UDTderived_copy_assignment_operator
    Declare Operator Let (ByRef u2 As UDTderived)
    #endif
    #ifdef UDTderived_copy_constructor
    Declare Constructor ()
    Declare Constructor (ByRef u2 As UDTderived)
    #endif
    #ifdef UDTderived_default_constructor
    #ifndef UDTderived_copy_constructor
    Declare Constructor ()
    #endif
    #endif
    Declare Destructor ()
    Dim As Integer i2
    #ifdef UDTderived_object_field_with_constructor
    Dim As String s2
    #endif
End Type

#ifdef UDTderived_copy_assignment_operator
Operator UDTderived.Let (ByRef u2 As UDTderived)
    Print "   => UDTderived.copy-assignment", @This & " from " & @u2
    This.i2 = u2.i2
    This.i1 = u2.i1
End Operator
#endif
#ifdef UDTderived_copy_constructor
Constructor UDTderived ()
    Print "   => UDTderived.default-constructor", @This
End Constructor
Constructor UDTderived (ByRef u2 As UDTderived)
    Print "   => UDTderived.copy-constructor", @This & " from " & @u2
    This.i2 = u2.i2
    This.i1 = u2.i1
End Constructor
#endif
#ifdef UDTderived_default_constructor
#ifndef UDTderived_copy_constructor
Constructor UDTderived ()
    Print "   => UDTderived.default-constructor", @This
End Constructor
#endif
#endif
Destructor UDTderived ()
    Print "   => UDTderived.destructor", , @This
End Destructor

Scope
    Print "Construction: 'Dim As UDTderived a, b : a.i1 = 1 : a.i2 = 2'"
    Dim As UDTderived a, b : a.i1 = 1 : a.i2 = 2
    Print "      " & a.i1
    Print "      " & a.i2
    Print
    Print "Assignment: 'b = a'"
    b = a
    Print "      " & b.i1
    Print "      " & b.i2
    Print
    Print "Copy-construction: 'Dim As UDTderived c = a'"
    Dim As UDTderived c = a
    Print "      " & c.i1
    Print "      " & c.i2
    Print
    Print "Going out scope: 'End Scope'"
End Scope

Sleep

Output example:
Construction: 'Dim As UDTderived a, b : a.i1 = 1 : a.i2 = 2'
   => UDTbase.default-constructor         1703576
   => UDTderived.default-constructor      1703576
   => UDTbase.default-constructor         1703556
   => UDTderived.default-constructor      1703556
      1
      2

Assignment: 'b = a'
   => UDTderived.copy-assignment          1703556 from 1703576
      1
      2

Copy-construction: 'Dim As UDTderived c = a'
   => UDTbase.default-constructor         1703488
   => UDTderived.copy-constructor         1703488 from 1703576
      1
      2

Going out scope: 'End Scope'
   => UDTderived.destructor               1703488
   => UDTbase.destructor                  1703488
   => UDTderived.destructor               1703556
   => UDTbase.destructor                  1703556
   => UDTderived.destructor               1703576
   => UDTbase.destructor                  1703576
Back to top

2. Rules of good manners (for constructors, copy-constructors, copy-assignment operators, and destructors)

Reminder of behaviors impacting constructors, copy-constructors, copy-assignment operators, and destructors:
- Defining an explicit default-constructor replaces the implicit default-constructor built by the compiler.
- Defining an explicit constructor other than the one default suppresses the implicit default-constructor built by the compiler. In this precise case, there is no default-constructor at all!
- The implicit copy-constructor (or copy-assignment operator, or destructor) built by the compiler can be replaced by an explicit copy-constructor (or copy-assignment operator, or destructor) defined by the user.
- But (as opposed to the default-constructor), there is always a copy-constructor (or a copy-assignment operator or a destructor), either an implicit built by the compiler or an explicit defined by the user.
- When there is object composition, the composed object Type must have an implicit or explicit constructor matching with the declaration of the compound object.
- When there is Type inheritance, the inherited Type must have a default implicit or explicit constructor (unless the inheriting Type has a constant copy-constructor, explicitly defined by user), and all this even if no object is constructed (compiler test on the only inheritance structure). This behavior appears to be specific to FreeBASIC.

From all the above, one can deduce 'golden rules' that avoid most of compilation errors and run-time bugs.

Golden rules for a code safer (at compile-time and run-time):
- If the user explicitly defines any constructor, he is very strongly advised to also define explicitly the default-constructor as well.
- If the user needs to explicitly define (with a non empty body) a copy-constructor or a copy-assignment operator or a destructor, it is better to define the 3 simultaneously (the known 'rule of three'), plus the default-constructor (rule above also applied).

From all the above and more specific cases (with inheritance), one can propose one 'maximizing rule' that allows a very safer operating.

Maximizing rule for a very safer code (at compile-time and run-time), but sometimes maximizing the real constraints:
- If the user needs to explicitly define any form of constructor procedure (including any form of copy-constructor) or any form of let-operator procedure or a destructor procedure, it is strongly recommended to define together the default-constructor and the standard copy-constructor and the standard let-operator and the destructor.
(these 4 explicit procedures are explicitly defined to always overload correctly the corresponding implicit operations from compiler)

  • Example of applying rules of good manners
UDT with a string member, to compare to UDT with a string pointer member where the 4 explicit procedures above must be defined (otherwise program hangs):
'=== UDT with a string member =====================

Type UDTstr
   Dim As String s
End Type

'--------------------------------------------------

Dim As UDTstr us1
us1.s = "UDTstr"
Dim As UDTstr us2
us2 = us1
Dim As UDTstr us3 = us2
Print us1.s,
us1.s = ""
Print us2.s,
us2.s = ""
Print us3.s

'=== UDT with a string ptr member =================

Type UDTptr2str
   Dim As String Ptr ps
   Declare Constructor ()
   Declare Destructor ()
   Declare Operator Let (ByRef ups As UDTptr2str)
   Declare Constructor (ByRef ups As UDTptr2str)
End Type

Constructor UDTptr2str ()
   This.ps = New String
End Constructor

Destructor UDTptr2str ()
   Delete This.ps
End Destructor

Operator UDTptr2str.Let (ByRef ups As UDTptr2str)
   *This.ps = *ups.ps
End Operator

Constructor UDTptr2str (ByRef ups As UDTptr2str)
   Constructor()  '' calling the default constructor
   This = ups     '' calling the assignment operator
End Constructor

'--------------------------------------------------

Dim As UDTptr2str up1
*up1.ps = "UDTptr2str"
Dim As UDTptr2str up2
up2 = up1
Dim As UDTptr2str up3 = up2
Print *up1.ps,
*up1.ps = ""
Print *up2.ps,
*up2.ps = ""
Print *up3.ps

'==================================================

Sleep

Output example:
UDTstr        UDTstr        UDTstr
UDTptr2str    UDTptr2str    UDTptr2str
Back to top

3. Member access rights impact (on declaration of constructors, copy-constructors, copy-assignment operators, and destructors)

Access rights can be applied when declaring such member procedures, to prohibit the user from performing certain specific commands (from the outside of Type).

The default constructor, copy-constructor, copy-assignment operator, and destructor are the only member procedures which can have an implicit version built by the compiler.
So if one want to forbid their accesses by the user from the outside of the Type, it is necessary to overload these by explicit versions with restricted access rights (not Public) when declaring. In addition, such member procedures may have no implementation (no body defining) if they are never actually called in the program.

  • Example - Inheritance structure where any base object construction must be forbidden:
- In order to forbid any construction of base object from the outside of Types, the default-constructor and the copy-constructor of base Type must be explicitly declared (to overload their implicit versions) with restricted access rights.
- The base Type default-constructor cannot be declared as Private, because it must be accessible from the derived Type (to construct a derived object), thus it is declared as Protected. It must have an implementation
- The base Type copy-constructor can be declared as Private because it is never called in this example. So it may have no implementation.
Type Parent
    Public:
        Dim As Integer I
    Protected:
        Declare Constructor ()
    Private:
        Declare Constructor (ByRef p As Parent)
End Type

Constructor Parent
End Constructor

Type Child Extends Parent
    Public:
        Dim As Integer J
End Type

Dim As Child c1
Dim As Child C2 = c1
c2 = c1

'Dim As Parent p1                        '' forbidden
'Dim As Parent p2 = c1                   '' forbidden
'Dim As Parent Ptr pp1 = New Parent      '' forbidden
'Dim As Parent Ptr pp2 = New Parent(c1)  '' forbidden

Sleep

  • Example - Singleton structure (at most one object can exist at any time):
- The singleton construction must only be done by calling the static procedure 'Singleton.create()'.
- So, the default-constructor and the copy-constructor must be explicitly declared (to overload their implicit versions) with restricted access rights as Private, in order to forbid any object user creation by 'Dim' or 'New'. Only the copy-constructor may have no implementation because it will never be called (the default-constructor is called from inside the Type by 'New').
- The singleton destruction must only be done by calling the static procedure 'Singleton.suppress()'.
- So, the destructor must be explicitly declared (to overload its implicit version) with restricted access rights as Private, in order to forbid any object user destruction by 'Delete' (the destructor must have implementation because it is called from inside the Type by 'Delete').
Type Singleton
    Public:
        Declare Static Function create () As Singleton Ptr
        Declare Static Sub suppress ()
        Dim i As Integer
    Private:
        Static As Singleton Ptr ref
        Declare Constructor ()
        Declare Constructor (ByRef rhs As Singleton)
        Declare Destructor ()
End Type

Dim As Singleton Ptr Singleton.ref = 0

Static Function Singleton.create () As Singleton Ptr
    If Singleton.ref = 0 Then
        Singleton.ref = New Singleton
        Return Singleton.ref
    Else
        Return 0
    End If
End Function

Static Sub Singleton.suppress ()
    If Singleton.ref > 0 Then
        Delete Singleton.ref
        Singleton.ref = 0
    End If
End Sub

Constructor Singleton ()
End Constructor

Destructor Singleton ()
End Destructor

Dim As Singleton Ptr ps1 = Singleton.create()
ps1->i = 1234
Print ps1, ps1->i

Dim As Singleton Ptr ps2 = Singleton.create()
Print ps2

Singleton.suppress()

Dim As Singleton Ptr ps3 = Singleton.create()
Print ps3, ps3->i

Singleton.suppress()

'Delete ps3                                      '' forbidden
'Dim As Singleton s1                             '' forbidden
'Dim As Singleton s2 = *ps3                      '' forbidden
'Dim As Singleton Ptr ps4 = New Singleton        '' forbiden
'Dim As Singleton Ptr ps5 = New Singleton(*ps3)  '' forbidden

Sleep

Output example:
5122656        1234
0
5122656        0
Back to top

See also

  • Constructor, Destructor
  • Operator =[>] (Assignment), Operator Let (Assignment)
  • Constructors and Destructors (basics)
  • Constructors, '=' Assignment-Operators, and Destructors (advanced, part #1)


Differences between Expressions and Statements
To introduce the expressions and statements (syntactic entities) in FreeBASIC.

Preamble:

Simply put, an expression represents a value and a statement represents an operation.
However, in fact, some special expressions may be composed and represent multiple values, and some statements may be composed of several sub operations/statements.
By context, some statements can also be considered expressions.

Statements can contain other constructs such as expressions, keywords, operators, and other elements.
Expressions are parts of statements that return values. Thus, expressions can be used whenever a value is expected.

The following description does not make very precise definitions for expressions and statements. It is difficult to achieve this.
Not all types of expressions and statements are covered here, but only simple examples are highlighted.

Expressions

An expression is a collection of one or more terms that can perform a mathematical or logical operation. To be precise, an expression must have at least one operand but may not have any operator.
The terms are usually either literals or variables or functions that are combined with operators to evaluate for example to a string or numeric result or boolean or user defined type.
A value in itself is a simple expression, just like a variable. Evaluating a variable gives the value to which the variable refers.

Expressions can be used to perform calculations, manipulate variables, or concatenate strings.
Expressions are evaluated according to precedence order. Use parentheses to override the default precedence order.

Statements

A statement is a basic unit of execution of a program. A statement is a complete instruction in FreeBASIC programs.
It may contain keywords, operators, variables, literal values, constants and expressions.

Statements could be categorized as:
- Declaration statements: these are the statements where the user names a variable, constant, or procedure, and can also initialize it.
- Executable statements: these are the statements which initiate actions. These statements can call a procedure, loop or branch through blocks of code or assign values or expression to a variable or constant. In the last case, it is called an expression statement. The most common expression statements are assignments and function calls.

A statements normally takes up one line with no terminator character, but:
- several statements can also be stacked on a same line by using the separator character, a colon (:) to delimit them,
- or a statement may also be continued to the next line using the continuation character, an underscore (_) to extend the statement from one line to the next.

Basic examples of expressions / statements

Some basic examples just to illustrate the definitions above.

Basic examples of expressions
- Arithmetic expression:
10
this is an expression that is evaluated to the numeric value 10.
10 + 13
this is another expression that is evaluated to produce the numeric value 23.

- String expression:
"hello"
evaluates to the string "hello".
"hello" & " " & "world"
evaluates to the string "hello world".

- Combined expression:
Len("hello" & " " & "world")
Len function evaluating the length of the string expression.

- Logical expression:
10 > 9
evaluates to boolean value true (more precisely produces the integer value -1 here).
10 < 20
evaluates to boolean value false (more precisely produces the integer value 0 here).
true
evaluates to boolean value true.
a=20 And b=30
evaluates to true (-1) or false (0) based on the values of a and b.

Basic examples of statements
- Statement:
Print 10 + 13
Print "hello" & " " & "world"
where the arguments of the two Print statements are expressions listed above.

- Assignment statement:
average = 55 + 33
where the right-hand-side is an expression.

- Declaration statement with initializer:
Dim As Integer I = J + 3
where the right-hand-side of the initializer is an expression.

- Conditional statement:
If (expression) Then
statement 1
Else
statement 2
End If
if the expression following the If statement evaluates to a truth value, statement 1 is executed else statement 2 is executed.

See also

  • Constant Expressions
  • Assignments
  • Operator list
  • Operator Precedence
  • Control Flow Statements


Constant Expressions
Constant term defines an expression that can be evaluated at compile time.

Preamble:

A constant expression is an expression that contains only constants (this means that it does not contain any variables at the evaluation time) with some operators/functions allowed, and possibly parentheses to override the operator precedence order.
A constant expression can be evaluated during compilation rather than at run time, and can be used in any place that a constant can occur.

Constant expressions are mainly required:
- as arguments for the preprocessor directives and intrinsic defines macros,
- as initializers for the Enum/Type structures and global/static variables,
- as data constants for the Data Statements.

List of operators/functions allowed in constant expressions

fbc is not a scripting language and does not have a full feature macro processor on the front end.
Constant folding is an optimization in fbc that takes constant expressions, evaluates the constant expression at compile time and produces a single constant result.
For constant folding to work, a compile time implementation of the operators/functions must be written.

Only the following operators/functions with constant arguments can be used to constitute constant expressions:
Bit, BitReset, BitSet, HiByte, HiWord, LoByte, LoWord
+, -, *, /, \, ^, Mod, Shl, Shr
=, <>, <=, >=, <, >
And, Eqv, Imp, Or, Not, Xor
Abs, Acos, Asin, Atan2, Atn, Cos, Exp, Fix, Frac, Int, Log, Sgn, Sin, Sqr, Tan
Asc, Chr, CVD, CVI, CVL, CVLongInt, CVS, CVShort, Len, SizeOf
Cast, CBool, CByte, CDbl, CInt, CLng, CLngInt, CShort, CSign, CSng, CUByte, CUInt, CULng, CULngInt, CUnsg, CUShort
more obviously all predefined symbols

Basic example of using constant expressions

Example illustrating three different use-cases:
#define pi 4 * Atn(1)

Dim Shared As Double d = Sqr(2)

Type pt
    Dim As Integer x = 300 * Cos(pi / 6)
    Dim As Integer y = 300 * Sin(pi / 6)
End Type

Dim As pt p

Print pi        ''  3.14159...
Print d         ''  1.41421...
Print p.x, p.y  ''  260           150

Sleep

See also

  • Differences between Expressions and Statements
  • Variable Initializers


Assignments
Assignments typically allow a variable to hold different values at different times during its life-span and scope.

Single assignment statement

An assignment statement consists of a variable (simple or with indices, or even a byref function result) and an expression (constructed from variables, constants, operators, and parentheses):
variable = expression
Executing an assignment statement evaluates the expression on the right-hand side and assigns it to the variable on the left-hand side.

For an assignment operation, it is necessary that the value of the expression is well-defined (it is a valid 'rvalue') and that the variable represents a modifiable entity (it is a valid modifiable (non-const) 'lvalue').
For an assignment to be well formed, the type of the expression on the right-hand side should be compatible with the type of the variable on the left-hand side (in other words, it must be possible to cast the expression to the type of the variable).

An example of a simple assignment is as follows:
x = 0
For the above example, the variable 'x' must be declared as being of numeric type.
After the assignment statement executes, the variable 'x' will have the value zero (either as an integer or a floating-point value, depending on its type).

In an assignment statement:
- The expression is evaluated in the current state of the program.
- The variable is assigned the computed value, replacing the prior value of that variable.
Because the right-hand side is evaluated first, it is possible to increment a variable by writing:
x = x + 1

Compound assignment statement

Arithmetic operators may be used in compound arithmetic and assignment operations.

For example, consider the following example of compound addition and assignment:
x += 7
The compound arithmetic and assignment statement above is equivalent to the following long form:
x = x + 7

More generally, the compound form (with operator op):
x op= y
is equivalent to:
x = x op y

The compound statement is legal whenever the long form is legal. This requires that the operation 'x op y' must itself be well formed and that the result of the operation be assignable to 'x'.

Assignment versus equality

The use of the equals sign '=' as an assignment operator has been frequently criticized, due to the conflict with equal as comparison for equality. This results both in confusion by novices in writing code, and confusion even by experienced programmers in reading code.
Beginning programmers sometimes confuse assignment with the relational operator for equality, as '=' means equality in mathematics, and is used for assignment in many languages. But assignment alters the value of a variable, while equality testing tests whether two expressions have the same value.

In FreeBASIC, a single equals sign '=' is generally used for both the assignment operator and the equality relational operator, with context determining which is meant.
For this purpose (and for solving some cases of ambiguity of the parser), the alternative symbol '=>' can be used for assignments in place of '=' (same as already used commonly for the initializers inside declarations).
Note: the '=>' symbol has been chosen against '<=' (already the operator 'Less Than Or Equal') and ':=' (':' used as statement separator).

Statements can even mix assignment and equality operators.
For example:
x = a = b
is parsed as:
x = ( a = b )
The equality expression 'a = b' above returns '-1' or '0', or 'true' or 'false', depending on the types of the variables 'a' and 'b', but the values of these variables are not modified. Only the equality expression result is assigned to 'x'.
In the above assignment statement, using parentheses around the equality expression allows to highlight the global behavior.
The alternative symbol '=>' can also be used:
x => a = b
('=>' can not be also used as symbol for equality operator)

Example

Example illustrating the different cases:
Dim As Integer x, y, z

x = 5          ''        (or 'x => 5')
Print x        ''  5     (assignment expression is a constant)

y = x + 4      ''        (or 'y => x + 4')
Print y        ''  9     (assignment expression is the sum of a variable and a constant)

y = y + 3      ''        (or 'y => y + 3')
Print y        ''  12    (value of x is incremented by 3)

z = 3          ''        (or 'z => 3')
z *= x         ''        (or 'z *=> x')
Print z        ''  15    (value of z is multiplied by value of x)

If x = y Then  ''        (value of x is not modified)
  Print x
Else
  Print x, y   ''  5     12
End If

x = y = z      ''        (or 'x => y = z')    (value of y is not modified)
Print x, y, z  ''  0     12    15

See also

  • Operator =[>] (Assignment)
  • Operator = (Equal)
  • Operator List
  • Operator Precedence


Control Flow Statements
Statements that direct the flow of execution.

Description

Control flow statements control program execution from one statement to the next; they determine what statements get executed and when, based on some kind of condition. The condition is always some expression that evaluates to true or false. Most control flow statements check for some kind of condition, and direct code flow accordingly, that is, they do or do not execute a block of code (except for the transferring control flow statements and Do..Loop, which has an optional condition). Additionally, all control flow statements can be nested, that is, they can have other control flow statements within the statement block.

Control flow statements come in three flavors: transferring, branching and looping. Transferring control flow statements transfer execution to different parts of code. Branching control flow statements execute certain statements blocks based on a condition, while looping control flow statements execute code repeatedly while or until a condition is met.

Transferring Statements

These statements are used for either unconditional or conditional, temporary or permanent transfer of execution. The "ON" variants conditionally select a point of transfer from a list of text labels. Execution may be transferred between different scopes provided that the branching does not cross any local array, variable length string or object definition.

Goto
Unconditionally transfers execution to another point in code defined by a text label. Execution resumes with the first statement after the label.

GoSub
Unconditionally and temporarily transfers execution to another point in code, defined by a text label. Execution resumes with the first statement after the label. Execution is then brought back to its original location with the Return keyword. Yes, GoSub statements can be nested, that is, multiple GoSub statements can be executed before the first corresponding Return, but there must always be a corresponding Return throughout the course of an application.

On Goto
Transfers execution to one of a number of points in code defined by text labels, based on the value of an expression.

On Gosub
Temporarily transfers execution to one of a number of points in code defined by text labels, based on the value of an expression.

Branching Statements

These statements are used for executing one of a number of statement blocks.

If..End If
Executes a block of statements if an expression evaluates to true (the condition). If and only if the expression evaluates to false, another statement block can be executed if yet another expression evaluates to true using the ElseIf keyword. If and only if all of those expressions evaluate to false, a statement block can be execute using the Else keyword.

Select..End Select
Executes one of a number of statement blocks. This branching statement tries to meet a condition of an expression and one of a number of case expressions. The case expressions are checked in the order in which they are given, and the first case expression that is met has its associated statement block executed. Like If..End If, a default case can be defined when no other case expression meets the condition, and, as with the looping control flow statements, a case's statement block can be prematurely broken out of with the Exit keyword.

Looping Statements

These statements are used for executing a block of statements repeatedly. Within a statement block, the loop can be prematurely re-executed using the Continue keyword, or broken out of using the Exit keyword. Whether the loop is terminated by the condition or with the Exit keyword, execution always begins at the first statement after the block.

While..Wend
Executes a block of statements while some expression evaluates to true (the condition). The expression is evaluated and checked before the block of statements is executed.

For..Next
Like While..Wend, but more suited to loop a certain number of times. This loop initializes a so-called iterator with an initial value that is checked against a test expression. If the iterator compares less than or equal to the test expression (the condition), the block of statements is executed and the iterator gets incremented. The loop can also be setup so that the iterator gets decremented after every loop, in which case it is compared greater than or equal to the test expression. Iterators can be numeric data types like Integer or Double, or user-defined types. User-defined types must implement Operator For.

Do..Loop
The most versatile of the looping control flow statements, this loop can execute a block of statements while or until an expression evaluates to true (the condition). It can also delay the checking of the expression until after the block has executed the first time, useful when a block of statements needs to be executed at least once. Finally, this loop can have no condition at all, and merely loop indefinitely.


Procedures Overview
Overview of the different FB procedure types.

Procedures are blocks of code that can be executed, or called, from anywhere in a program, any number of times. The code that is executed is called the procedure body. There are two types of procedures in FreeBASIC: procedures that don't return a value and procedures that do.

Subs

Subs are procedures that don't return values. They are declared using the Declare keyword, and defined using the Sub keyword. Declaring a procedure introduces its name so that it can be called, and a procedure definition lists the statements of code that will be executed when called. A sub is called simply by using its name somewhere in the program.

' introduces the sub 'MyProcedure'
Declare Sub MyProcedure

' calls the procedure 'MyProcedure'
MyProcedure

' defines the procedure body for 'MyProcedure'
Sub MyProcedure
    Print "the body of MyProcedure"
End Sub

will produce the output:

the body of MyProcedure

Notice that only the declaration is needed to call the procedure. The procedure can be defined later in code, or even in a different source file altogether.

Functions

Functions are procedures that return a value back to the point in code in which they are called. You can think of a function call as evaluating to some expression, just like a variable or object. They are declared using the Declare keyword, and defined using the Function keyword. The type of value that functions return is specified at the end of the declaration.

' introduces and defines a procedure that returns an integer value
Function MyProcedure As Integer
    Return 10
End Function

' calls the procedure, and stores its return value in a variable
Dim i As Integer = MyProcedure
Print i

will produce the output:

10
Since a definition is a declaration, a procedure can be called after it has been defined, as well.

It is a common convention when calling a procedure to place parenthesis '()' after the procedure name, to signify a procedure call. FreeBASIC does not require this, however.

See also

  • Passing Arguments to Procedures
  • Returning Values
  • Declare
  • Sub
  • Function


Passing Arguments to Procedures
Passing information to procedures.

Declaring parameters

Procedures can get passed information in the form of variables and objects when they are called. In the context of a procedure call, these variables and objects are called arguments. These arguments are then represented as so-called parameters inside the procedure body. Parameters can be used just like any other variable or object.

To specify that a procedure should get passed arguments when called, declare the procedure with a parameter list. A parameter list is a list of one or more names and types that the procedure will use when referring to the arguments that are passed to it. Parameter lists are surrounded by parenthesis.

Sub Procedure (s As String, n As Integer)
    Print "The parameters have the values: " & s & " and " & n
End Sub

Procedure "abc", 123

will produce the following output:

The parameters have the values: abc and 123
There are two ways to pass arguments to procedures: by value and by reference. By default, arguments are passed by value unless otherwise specified.

Passing arguments by value

Arguments that are passed by value are not actually passed to procedures; a copy of the argument is made and passed instead. This allows the procedure to modify the copy, and the original variable or object remains unchanged.

When passing objects to procedures by value, the copy is made by calling the copy constructor of the Type or Class.

To specify that an argument should be passed by value, precede the parameter name in the procedure declaration with the ByVal keyword:

Sub Procedure (ByVal param As Integer)
    param *= 2
    Print "The parameter 'param' = " & param
End Sub

Dim arg As Integer = 10
Print "The variable 'arg' before the call = " & arg
Procedure(arg)
Print "The variable 'arg' after the call = " & arg

will produce the following output:

The variable 'arg' before the call = 10
The parameter 'param' = 20
The variable 'arg' after the call = 10
Notice how parenthesis surround the arguments - in this case only one, arg - in the procedure call. These parenthesis are optional, but are a common convention to signify a procedure call.

Passing arguments by reference

Unlike arguments that are passed by value, arguments that are passed to procedures by reference really do get passed; no copy is made. This allows the procedure to modify the original variable or object that was passed to it.

A reference is like an alias for a variable or object. Whenever you refer to a reference, you're referring to the actual variable or object that it aliases. In other words, you can think of a reference parameter of a procedure as the argument that is passed to it; any changes made to the reference parameter are actually changes to the argument it represents.

To specify that an argument should be passed by reference, precede the parameter name in the procedure declaration with the ByRef keyword:

Sub Procedure (ByRef param As Integer)
    param *= 2
    Print "The parameter 'param' = " & param
End Sub

Dim arg As Integer = 10
Print "The variable 'arg' before the call = " & arg
Procedure(arg)
Print "The variable 'arg' after the call = " & arg

will produce the following output:

The variable 'arg' before the call = 10
The parameter 'param' = 20
The variable 'arg' after the call = 20

Manually passing pointers to by-reference parameters

By specifying the Byval keyword in front of an argument to a ByRef parameter, an address (usually stored in a pointer) can be passed directly as-is, forcing the Byref parameter to reference the same memory location which the address pointed to.

Sub f( ByRef i As Integer )
    i = 456
End Sub

Dim i As Integer = 123
Dim pi As Integer Ptr = @i

Print i
f( ByVal pi )
Print i

See also

  • Procedures Overview
  • Returning Values
  • Declare
  • Sub
  • Function
  • ByVal
  • ByRef
  • ... (Ellipsis)


Returning Values

Returning Values

Refers to the ability of a Function procedure to have a value when the function finishes such that the value can be used in an expression or assigned to a variable.

The value of a function can be returned in three ways:
- Return keyword followed by a value exits the function immediately, and returns that value to the caller.
- Functions can also return values by assigning the Function keyword or the function_identifier to the desired return value (but Function keyword or function_identifier does not allow to evaluate the current assigned value). The latter two methods do not cause the function to exit, however.
'' Using the name of the function to set the return value and continue executing the function:
Function myfunc1() As Integer
   myfunc1 = 1
End Function

'' Using the keyword 'Function' to set the return value and continue executing the function:
Function myfunc2() As Integer
   Function = 2
End Function

'' Using the keyword 'Return' to set the return value and immediately exit the function:
Function myfunc3() As Integer
   Return 3
End Function

'' This program demonstrates a function returning a value.

Declare Function myFunction () As Integer

Dim a As Integer

'Here we take what myFunction returns and add 10.
a = myFunction() + 10

'knowing that myFunction returns 10, we get 10+10=20 and will print 20.
Print a

Function myFunction () As Integer
  'Here we tell myFunction to return 10.
  Function = 10
End Function

Return keyword mixed with Function= keyword or function_identifier= or Exit Function keyword in a same function is unsupported when returning objects with constructors.

Note:
When returning by value a UDT instance, Return on the one hand, and Function= or function_identifier= on the other hand, do not treat the instance in the same way (in order to optimize execution time):
- When using Return, the only executed 'Return u' directly calls the copy-constructor.
- When using Function= or function_identifier=, the constructor is always called once at the beginning of the function body, and then each executed 'Function= u' or 'function_identifier= u' only calls the let-operator.

Returning References

Function results can also be returned by reference, rather than by value. The semantics are quite different.

When assigning a Byref function result through a Function = variable or Return variable statement, the function does not copy and return the variable's value. Instead, it returns a reference to that variable. The caller of the function can modify the variable through the reference returned from the function, without having to use pointers manually. This is very much like ByRef parameters.

For more information, refer to: Byref (Function Results)

Manually returning pointers as-is from Byref functions

By specifying the Byval keyword in front of the result variable in the Function = variable or Return variable statements, an address (usually stored in a pointer) can be passed directly as-is, forcing the Byref function result to reference the same memory location which the address pointed to. For example:

Dim Shared i As Integer = 123

Function f( ) ByRef As Integer
    Dim pi As Integer Ptr = @i

    Function = ByVal pi

    '' or, with RETURN it would look like this:
    Return ByVal pi
End Function

Print i, f( )

See also

  • Function
  • Byref (Function Results)


Procedure Scopes
The Scope (visibility) of a Procedure through the different modules of a program.

Preamble:

A procedure is a "subroutine" (sub) or a "function" that can be called by code outside the procedure (or internal code in the case of a recursion).
A procedure consists of a sequence of instructions that form the body of the procedure.
It is possible to pass values or variables to a procedure, and a function may return a value or a reference.

Description

Scopes of procedures in modules follows simple rules:
    • Private scope:
procedure visible only in its own module (where it is defined).
    • Public scope:
procedure visible from all modules constituting a compiled program (including static libraries).
    • Export scope:
when defined in a DLL (dynamically linked library), procedure visible from an external program that has loaded it (statically or dynamically).

Syntax

[ Public | Private ] { Sub | Function } proc_name ( argumentlist ) [ [ ByRef ] As datatype ] Export
or
[ Public ] { Sub | Function } proc_name ( argumentlist ) [ [ ByRef ] As datatype ] Export

Usage

Private, Public and Export access controls are used in procedure definitions only (forbidden at declaration line level).

By default, a procedure is Public except if the Option Private statement (in the module) modifies the default state.
That is why both Private and Public access controls are useful, depending on the default state.
Export access control is incompatible with Private procedures (implicitly or explicitly defined like this).

Among the compiled modules, two procedures with the same identifier, but defined inside different modules, may exist if both are Private.

The compiler removes the Private procedures that are not called, but this does not currently work for Private procedures that are only called by other Private procedures that are not called themselves, because the first one appears as being called.

See also

  • Modularizing
  • Procedures
  • Variable and Procedure Linkage


Calling Conventions
Specifying how procedures are called.

Calling conventions determine how calling code interacts with procedures when called. They specify rules about how parameters are pushed onto the call stack, how values are returned and when the call stack is cleaned up. This information is useful when interacting with code written in other languages, particularly assembly language. In some cases, calling conventions also apply some kind of name decoration to procedure names.

FreeBASIC supports 3 calling conventions: stdcall, cdecl and pascal, specified with stdcall, cdecl and pascal, respectively. Calling convention can be specified in either a procedure declaration or definition immediately following the procedure name. The declaration of a procedure must have the same calling convention as the definition.

In all calling conventions, integral procedure return values are returned in the EAX(, EDX) register(s), and floating-point return values are stored in the ST(0) register (the top of the floating-point stack). User-defined type (UDT) values are returned in the EAX(, EDX) register(s) if eight (8) bytes or smaller, otherwise they are returned in memory by having their address pushed onto the call stack after any parameters.

stdcall

In the stdcall convention, procedure parameters are pushed onto the call stack prior to the procedure call (which will push the return address just above parameters) in the reverse order they are declared, that is, from right to left. The procedure is in charge of popping any parameters from the call stack (commonly by appending a constant to the RET instruction, signifying the number of bytes to release).

stdcall is the default calling convention on Windows, and for procedures within Extern "Windows" and Extern "Windows-Ms" blocks. It is also the default convention used in the Windows API.

Platform Differences

  • In DOS and Windows platforms, the procedure name is decorated with an "@N" suffix, where N is the total size, in bytes, of any parameters passed.

cdecl

In the cdecl convention, procedure parameters are pushed onto the call stack prior to the procedure call, in the reverse order they are declared, that is, from right to left. The calling code is in charge of popping parameters from the call stack.

cdecl is the default calling convention on Linux, the *BSDs, and DOS, and for procedures within Extern "C" and Extern "C++" blocks. It is also the default convention used by most C and C++ compilers.

pascal

In the pascal convention, procedure parameters are pushed onto the call stack, in the order they are declared, that is, from left to right. The procedure is in charge of popping any parameters from the call stack.

pascal is the default convention used by Pascal and the Microsoft QuickBASIC series of compilers.

The following table summarizes the differences between the various calling conventions:

Calling convention | Parameters are pushed onto the call stack from | Parameters are popped off the call stack by
stdcall | right to left | the procedure
cdecl | right to left | the calling code
pascal | left to right | the procedure

Platform Differences

  • In DOS and Windows platforms, all calling conventions decorate procedure names with an underscore ("_") prefix.
  • The default calling convention changes depending on the platform. For Windows it is stdcall; while on Linux, the *BSDs, and DOS, it is cdecl.

See also

  • Declare, Sub, Function
  • stdcall, cdecl, pascal
  • Extern..End Extern


Recursion
Recursive procedures (subroutines or functions)

Preamble:

Iteration and recursion are two very useful ways to program, especially to perform a certain number of times a certain script, and thus allow optimization of the code. If iteration is relatively easy to understand, recursion is a concept not necessarily obvious at the beginning.
When speaking of a recursive procedure (subroutine or function), we refer to a syntactic characteristic: the procedure, in its own definition, refers to itself (it calls itself).
But when talking about recursive process, linear or tree, we are interested in the process flow, not in the syntax of the procedure's writing.
Thus, a procedure can have a recursive definition but correspond to an iterative process.

Some treatments are naturally implemented as a recursive algorithm (although this is not always the most optimal solution).
The main problem of the recursive approach is that it consumes potentially a lot of space on the execution stack: from a certain level of "depth" of recursion, the space allocated for the execution stack of the thread is exhausted, and causes an error of type "stack overflow".
Repeatedly calling the same procedure can also make the execution slower, although this may make the code easier.
To increase the speed of execution, simple recursive algorithms can be recreated in little more complicated iterative algorithms using loops that execute much faster.

What is the use of recursion if it increases the execution time and memory space compared to an iterative solution?
There are still cases where it is not possible to do otherwise, where iterative translation does not exist or, where it exists, is much heavier to implement (requiring for example a dynamic storage capacity to substitute for the execution stack).

Recursion beside iteration

Iteration and recursion both repeatedly execute the instruction set:
    • Iteration occurs when a loop executes repeatedly until the control condition becomes false.
    • Recursion occurs when an instruction in a procedure calls the procedure itself repeatedly.
The main difference between iteration and recursion is that iteration is a process applied to a set of instructions to execute repeatedly, while recursion is a process always applied to a procedure.

Definition of iteration
Iteration is a process of repeatedly executing a set of instructions until the iteration condition becomes false.
The iteration block includes the initialization, the comparison, the execution of the instructions to be iterated and finally the update of the control variable.
Once the control variable is updated, it is compared again and the process is repeated until the condition in the iteration is false.
Iteration blocks are For loop, While loop, ...

The iteration block does not use the execution stack to store the variables at each cycle. Therefore, the execution of the iteration block is faster than the recursion block. In addition, iteration does not have the overhead of repeated procedure calls that also make its execution faster than a recursion.
The iteration is complete when the control condition becomes false.

Simple example with a iterative function which returns the factorial of the integer:
'' The code body of the iterative function is defined by using the iterative definition of the factorial function:
''    Case (n = 0) : factorial(0) = 1
''    Case (n > 0) : factorial(n) = (1) * ..... * (n - 2) * (n - 1) * (n)
''    The first line allows to determine the cumulative variable initialization: 'result = 1'
''    The second line allows to determine the statement syntax which accumulates: 'result = result * I'

Function iterativeFactorial (ByVal n As Integer) As Integer
    Dim As Integer result = 1  '' variable initialization
    For I As Integer = 1 To n  '' iteration loop
        result = result * I    '' iterative accumulation
    Next I
    Return result
End Function

Print iterativeFactorial(6)

Sleep

Definition of recursion
FreeBASIC allows a procedure to call itself in its code. This means that the procedure definition has a procedure call to itself. The set of local variables and parameters used by the procedure are newly created each time the procedure is called and are stored at the top of the execution stack. But every time a procedure calls itself, it does not create a new copy of that procedure. The recursive procedure does not significantly reduce the size of the code and does not even improve the memory usage, but it does a little bit compared to iteration.

To end recursion, a condition must be tested to force the return of the procedure without giving a recursive call to itself. The absence of a test of a condition in the definition of a recursive procedure would leave the procedure in infinite recursion once called.

Note: When the parameters of a recursive procedure are passed by reference, take care to work with local variables when the code body needs to modify their values.

Simple example with a recursive function which returns the factorial of the integer:
'' The code body of the recursive function is defined by using the recursive definition of the factorial function:
''    Case (n = 0) : factorial(0) = 1
''    Case (n > 0) : factorial(n) = n * factorial(n-1)
''    The first line allows to determine the end condition: 'If (n = 0) Then Return 1'
''    The second line allows to determine the statement syntax which calls the function itself: 'Return n * factorial(n - 1)'

Function recursiveFactorial (ByVal n As Integer) As Integer
    If (n = 0) Then                           '' end condition
        Return 1
    Else                                      '' recursion loop
        Return n * recursiveFactorial(n - 1)  '' recursive call
    End If
End Function

Print recursiveFactorial(6)

Sleep

Recursion structure

Different types of recursion structure can be found:
    • Tail recursion.
    • Non-tail but final recursion.
    • Non-tail and non-final recursion.
    • Mutual recursion.
    • Nested recursion.

Tail recursion
The recursive procedure is a tail recursive procedure if the only recursive call is at the end of the recursion and is therefore not followed by any other statement:
      • for a recursive subroutine, the only recursive call is at the end of the recursion,
      • for a recursive function, the only recursive call is at the end of the recursion and consists in taking into account the return of the function without any other additional operation on it.
A tail recursive procedure is easy to transform into an iterative procedure.

Example with the simple "factorial" recursive function (already presented above):
      • This function has a non-tail recursive form because even though the recursive call is at the end of function, this recursive call is not the last instruction of the function because one has to multiplied again by 'n' when 'recursiveFactorial(n - 1)' is got.
      • This calculation is done when popping context from execution stack.

It is quite easy to transform this function so that the recursion is a tail recursion.
To achieve this, it is necessary to add a new parameter to the function: the 'result' parameter which will serve as accumulator:
Function tailRecursiveFactorial (ByVal n As Integer, ByVal result As Integer = 1) As Integer
    If (n = 0) Then                                       '' end condition
        Return result
    Else                                                  '' recursion loop
        Return tailRecursiveFactorial(n - 1, result * n)  '' tail recursive call
    End If
End Function

Print tailRecursiveFactorial(6)

Sleep

This time, the calculation is done when pushing context on execution stack.

Similar transformation steps for the simple "reverse string" recursive function following:
Function recursiveReverse (ByVal s As String) As String
    If (s = "") Then                                     '' end condition
        Return s
    Else                                                 '' recursion loop
        Return recursiveReverse(Mid(s, 2)) & Left(s, 1)  '' recursive call
    End If
End Function

Print recursiveReverse("9876543210")

Sleep

Function tailRecursiveReverse (ByVal s As String, ByVal cumul As String = "") As String
    If (s = "") Then                                                '' end condition
        Return cumul
    Else                                                            '' recursion loop
        Return tailRecursiveReverse(Mid(s, 2), Left(s, 1) & cumul)  '' tail recursive call
    End If
End Function

Print tailRecursiveReverse("9876543210")

Sleep

Note: As the "&" operator (string concatenation) is not a symmetric operator ('(a & b) <> (b & a)', while '(x * y) = (y * x)' like previously), the two operand order must to be reversed when pushing context on execution stack instead of before when popping context from execution stack.

Non-tail but final recursion
A non-tail recursive procedure is final when the recursive call(s) is(are) placed at the end of executed code (no executable instruction line after and between for several recursive calls).

First example, computation of the combination coefficients nCp (binomial coefficients calculation) and display of the Pascal's triangle:
Function recursiveCombination (ByVal n As UInteger, ByVal p As UInteger) As LongInt
    If p = 0 Or p = n Then
        Return 1
    Else
        Return recursiveCombination(n - 1, p) + recursiveCombination(n - 1, p - 1)
    End If
End Function

Dim As UInteger n = 10
For I As UInteger = 0 To n
    For J As UInteger = 0 To I
        Locate , 6 * J + 3 * (n - I) + 3
        Print recursiveCombination(I, J);
    Next J
    Print
Next I

Sleep

Second example, recursive drawing of circles:
Sub recursiveCircle (ByVal x As Integer, ByVal y As Integer, ByVal r As Integer)
    Circle (x, y), r
    If r > 16 Then
        recursiveCircle(x + r / 2, y, r / 2)
        recursiveCircle(x - r / 2, y, r / 2)
        recursiveCircle(x, y + r / 2, r / 2)
        recursiveCircle(x, y - r / 2, r / 2)
    End If
End Sub

Screen 12

Locate 2, 2
recursiveCircle(160, 160, 150)

Sleep

Third example, quick sort algorithm:
Dim Shared As UByte t(99)

Sub recursiveQuicksort (ByVal L As Integer, ByVal R As Integer)
    Dim As Integer pivot = L, I = L, J = R
    Do
        If t(I) >= t(J) Then
            Swap t(I), t(J)
            pivot = L + R - pivot
        End If
        If pivot = L Then
            J = J - 1
        Else
            I = I + 1
        End If
    Loop Until I = J
    If L < I - 1 Then
        recursiveQuicksort(L, I - 1)
    End If
    If R > J + 1 Then
        recursiveQuicksort(J + 1, R)
    End If
End Sub

Randomize
For I As Integer = LBound(t) To UBound(t)
    t(i) = Int(Rnd * 256)
Next I

Print "raw memory:"
For K As Integer = LBound(t) To UBound(t)
    Print Using "####"; t(K);
Next K
Print

recursiveQuicksort(LBound(t), UBound(t))

Print "sorted memory:"
For K As Integer = LBound(t) To UBound(t)
    Print Using "####"; t(K);
Next K
Print

Sleep

Non-tail and non-final recursion
A non-tail recursive procedure is also not final when the recursive call(s) is(are) not all placed at the end of executed code (an executable instruction line at least after or between for several recursive calls).

Example, tower of Hanoi algorithm:
'' For this example, the two recursive calls are at the end of executed code block,
''   but separated by an instruction line and there is an order constraint.

Sub recursiveHanoi (ByVal n As Integer, ByVal departure As String, ByVal middle As String, ByVal arrival As String)
    If n > 0 Then
        recursiveHanoi(n - 1, departure, arrival, middle)
        Print "  move one disk from " & departure & " to " & arrival
        recursiveHanoi(n -1 , middle, departure, arrival)
    End If
End Sub

recursiveHanoi(3, "A", "B", "C")

Sleep

Mutual recursion
Two functions are said to be mutually recursive if the first calls the second, and in turn the second calls the first.

Example using mutual recursive, 'even()' and 'odd()' recursive functions:
Declare Function recursiveIsEven(ByVal n As Integer) As Boolean
Declare Function recursiveIsOdd(ByVal n As Integer) As Boolean

Function recursiveIsEven(ByVal n As Integer) As Boolean
    If n = 0 Then
        Return True
    Else
        Return recursiveIsOdd(n - 1)
    End If
End Function

Function recursiveIsOdd(ByVal n As Integer) As Boolean
     If n = 0 Then
        Return False
    Else
        Return recursiveIsEven(n - 1)
    End If
End Function

Print recursiveIsEven(16), recursiveIsOdd(16)
Print recursiveIsEven(17), recursiveIsOdd(17)

Sleep

Nested recursion
A recursive function is said nested if an argument passed to the function refers to the function itself.

Example using nested recursive function, 'Ackermann()' recursive function:
Function recursiveAckermann (ByVal m As Integer, ByVal n As Integer) As Integer
    If m = 0 Then
        Return n + 1
    Else
        If n = 0 Then
            Return recursiveAckermann(m - 1, 1)
        Else
            Return recursiveAckermann(m - 1, recursiveAckermann(m, n - 1))
        End If
    End If
End Function

Print recursiveAckermann(3, 0), recursiveAckermann(3, 1), recursiveAckermann(3, 2), recursiveAckermann(3, 3), recursiveAckermann(3, 4)

Sleep

See also

  • Replace Recursion with Iteration


Callback
Callback procedures

Description

From Wikipedia: A callback, also known as a "call-after" function, is any executable code that is passed as an argument to other code; that other code is expected to call back (execute) the argument at a given time. This execution may be immediate as in a synchronous (or blocking) callback, or it might happen at a later time as in an asynchronous (or deferred) callback.

Example

Example where a mathematical function is an argument in plot function call:
(the invocation is implemented by calling the 'PlotF()' procedure, and the callback function is the 'Linear()', 'Sinusoidal()', or 'Quadratic()' function)
Type MathFunction As Function( ByVal x As Double ) As Double

Function Linear( ByVal x As Double ) As Double
    Return x
End Function

Function Quadratic( ByVal x As Double ) As Double
    Return x * x
End Function

Function Sinusoidal( ByVal x As Double ) As Double
    Return Sin(x)
End Function

Sub PlotF( ByVal f As MathFunction )
    PSet( -15, f(-15) )
    For x As Double = -15 To 15 Step 0.1
        Line -( x, f(x) )
    Next
End Sub

Screen 19
Window (-15,-10)-(15,10)

PlotF( @Linear )
PlotF( @Sinusoidal )
PlotF( @Quadratic )

Sleep

The three callback processes are synchronous because each callback function is completely executed before 'PlotF()' returns to the caller code.

Similar plotting example (as above) but with a asynchronous callback. Here an artificial delay is added. A more realistic case would be if the data to be plotted is retrieved form a web-server with a response time larger than acceptable for the main loop of our program (prevent blocking behavior):
#include "fbthread.bi"

Type MathFunction As Function( ByVal x As Double ) As Double

Sub ThreadPlot( ByVal p As Any Ptr )
    Sleep 1500, 1  '' sleep added only to check the asynchronous way of the callback
    Dim f As MathFunction = p
    Window (-15,-10)-(15,10)
    PSet( -15, f(-15) )
    For x As Double = -15 To 15 Step 0.1
        Line -( x, f(x) )
    Next
End Sub

Function PlotF( ByVal f As MathFunction ) As String
    Print "Plotting requested"
    ThreadDetach( ThreadCreate( @ThreadPlot, f ) )
    Return "Plotting request taken into account"
End Function

Function Linear( ByVal x As Double ) As Double
    Return x
End Function

Function Quadratic( ByVal x As Double ) As Double
    Return x * x
End Function

Function Sinusoidal( ByVal x As Double ) As Double
    Return Sin(x)
End Function

Screen 19

Print PlotF( @Linear )
Print PlotF( @Sinusoidal )
Print PlotF( @Quadratic )

'' following code added only to check the asynchronous way of callbacks
Print "Main program continues ";
For I As Integer = 1 To 15
    Print ".";
    Sleep 200, 1
Next I
Print
Print "Main program finished"

Sleep

The three callback processes are asynchronous because the 'PlotF()' function returns immediately, and each callback function is executed in parallel with the caller's next code, from a 'ThreadPlot()' thread created (previously) by 'PlotF()' for each of them.

Example of a callback function for comparing values in a array to be sorted by the qsort algorithm:
(the invocation is implemented by calling the 'qsort()' procedure, and the callback function is the 'CmpVarLenStr()' function)
#include "crt/stdlib.bi"

Function CmpVarLenStr cdecl( ByVal p1 As Any Ptr, ByVal p2 As Any Ptr ) As Long  '' compare 2 var-len strings
    Dim As String Ptr ps1 = p1
    Dim As String Ptr ps2 = p2
    If *ps1 < *ps2 Then
        Return -1
    ElseIf *ps1 > *ps2 Then
        Return 1
    Else
        Return 0
    End If
End Function

Sub PrintList( array() As String)  '' print a var-len string list
    For I As Integer = LBound(array) To UBound(array)
        Print array(I)
    Next I
    Print
End Sub

Dim forename(1 To 12) As String = {"Madison", "Emily", "Hailey", "Sarah", "Kaitlyn", "Hannah", _
                                   "Jacob", "Christopher", "Nicholas", "Michael", "Matthew", "Joshua" }

Print "LIST OF UNSORTED FORENAMES:"
PrintList( forename() )

qsort( @forename(LBound(forename)), UBound(forename) - LBound(forename) + 1, SizeOf(String), @CmpVarLenStr )

Print "LIST OF SORTED FORENAMES:"
PrintList( forename() )

Sleep

The callback process is synchronous because the callback function is completely executed (several times), (successively) called by 'qsort()', before 'qsort()' returns to the caller code.

See also

  • Sub Pointer
  • Function Pointer
  • Threads
  • OBJECT built-in and RTTI info


Constructors and Destructors
The module Constructors / Destructors executed before / after module-level code.

Preamble:

Do not confuse the module constructor (destructor) with the UDT constructor (destructor) that runs at the creation (destruction) of a UDT instance.

The module constructors (destructors) allow to specify the execution of procedures at the beginning (at the end) of a program.

Description

The constructor (destructor) keyword is used to force execution of the procedure prior (posterior) to that of module-level code.
Procedures defined as module constructors or destructors may be used the same way as ordinary procedures (they may be also called from within module-level code).
A module may define multiple constructor and destructors procedures.

In a single module, depending on the build and run-time environment of the target system:
- module constructors (destructors) may execute in the order in which they are defined, or reverse order,
- module constructors (destructors) may execute before or after global static variables having constructors (destructors),
- module constructors (destructors) may execute before or after other module constructors (destructors) having priority attribute,
- module constructors (destructors) with priority attribute may execute before or after global static variables having constructors (destructors).

The constructors and destructors of a module are always executed (like its main code), even if it is compiled as secondary module or static library, or even loaded as dynamic library (dll).

Syntax

[Public | Private] Sub procedure_name [Alias "external_identifier"] [()] {Constructor | Destructor} [priority] [Static]
{ procedure body }
End Sub

Usage

The constructor (destructor) keyword is used in Sub definitions only (forbidden at declaration line level).
Subs defined as constructors (destructors) may be used in the same way as ordinary Subs (they may be called from within module-level code).
The Sub must have an empty parameter list.

The priority attribute, an integer between 101 and 65535, can be used to force constructors (destructors) to be executed in a certain order, relative to other constructors (destructors) also having priority attribute.
The value of priority has no specific meaning, only the relationship of the number with other constructor (destructor) priorities.
101 is the highest (lowest) priority and is executed first (last), relative to other constructors (destructors) also having priority attribute.

Public static member Subs (having an empty parameter list) of UDT can be defined as a module constructors (destructors), by adding the constructor (destructor) keyword in the Sub definitions.

Accessing global static objects having constructors (destructors) from module constructors (destructors) should be avoided due to variations in execution order on different build systems.

See also

  • UDT Constructors and Destructors


Pointers to Procedures
Pointers that point to procedures

Just as pointers can be made to point to an Integer or Single type, pointers can also point to procedures, that is, they can store the address of a procedure.

Declaration

To declare a pointer to procedure, use the Sub or Function keywords, followed by any parameters and return value type:
' declares a pointer to sub procedure that takes no arguments
Dim pointerToProcedure As Sub

Procedure pointers store procedure addresses, which are retrieved using Operator @ (Address of) or the Procptr Operator:
'' pfunc.bi

Function Add (a As Integer, b As Integer) As Integer
    Return a + b
End Function

Dim pFunc As Function (As Integer, As Integer) As Integer = @Add

Calling a procedure pointer

The interesting thing about procedure pointers is that they can be called just like a procedure:
'' .. Add and pFunc as before ..
#include Once "pfunc.bi"

Print "3 + 4 = " & pFunc(3, 4)

For a calling example of subroutine pointer, see the Operator @ (Address Of) page.

Note: When calling a procedure through a procedure pointer, parentheses surrounding the argument list (even empty) are mandatory to resolve ambiguity with a simple access to the pointer value.

Passing procedure pointers to procedures

Passing procedure pointers to other procedures is similar as well:
'' .. Add and pFunc as before ..
#include Once "pfunc.bi"

Function DoOperation (a As Integer, b As Integer, operation As Function (As Integer, As Integer) As Integer) As Integer
    Return operation(a, b)
End Function

Print "3 + 4 = " & DoOperation(3, 4, @Add)

Because procedure pointer declarations can be lengthy, it often helps to create a type alias for the procedure pointer, in an effort to make clearer code:
'' .. Add and pFunc as before ..
#include Once "pfunc.bi"

Type operation As Function (As Integer, As Integer) As Integer

Function DoOperation (a As Integer, b As Integer, op As operation) As Integer
    Return op(a, b)
End Function

Print "3 + 4 = " & DoOperation(3, 4, @Add)

Pointers to procedure pointers

Because the syntax of a procedure pointer does not allow declaration of a pointer to procedure pointer when the procedure is a function (because ptr applies on return type and not on procedure), a type alias is used. Notice how it is necessary to surround a dereferenced pointer to procedure pointer by parenthesis when calling the procedure. This is because the function-call operator '()' has higher precedence than Operator * (Value of):
Function Halve (ByVal i As Integer) As Integer
    Return i / 2
End Function

Function Triple (ByVal i As Integer) As Integer
    Return i * 3
End Function

Type operation As Function (ByVal As Integer) As Integer

' an array of procedure pointers, NULL indicates the
' end of the array
Dim operations(20) As operation = _
{ @Halve, @Triple, 0 }

Dim i As Integer = 280

' apply all of the operations to a variable by iterating through the array
' with a pointer to procedure pointer
Dim op As operation Ptr = @operations(0)
While (*op <> 0)
    ' call the procedure that is pointed to, note the extra parenthesis
    i = (*op)(i)
    op += 1
Wend

Print "Value of 'i' after all operations performed: " & i

Pointers to member procedures

Before fbc 1.10.0, method pointers are not implemented yet, but it is possible to work-around that by using a static wrapper:
/''
 ' This example shows, before fbc 1.10.0, how you can simulate getting a class method pointer,
 ' until support is properly implemented in the compiler.
 '
 ' When this is supported (since fbc 1.10.0), you will only need to remove the static wrapper
 ' function presented here, to maintain compatibility.
 '/

Type T
    Declare Function test(ByVal number As Integer) As Integer
    Declare Static Function test(ByRef This As T, ByVal number As Integer) As Integer
    Dim As Integer i = 420
End Type

Function T.test(ByVal number As Integer) As Integer
    Return i + number
End Function

Function T.test(ByRef This As T, ByVal number As Integer) As Integer
    Return this.test(number)
End Function

Dim p As Function(ByRef As T, ByVal As Integer) As Integer
p = @T.test

Dim As T obj

Print p(obj, 69) '' prints 489

Since fbc 1.10.0, method pointers are implemented via updating the 'Procptr' operator:
/''
 ' This example shows, since fbc 1.10.0, how to directly obtain a class method pointer,
 '/

Type T
    Declare Function test(ByVal number As Integer) As Integer
    Dim As Integer i = 420
End Type

Function T.test(ByVal number As Integer) As Integer
    Return i + number
End Function

Dim p As Function(ByRef As T, ByVal As Integer) As Integer
p = ProcPtr(T.test)  '' or 'p = @T.test'

Dim As T obj

Print p(obj, 69) '' prints 489

Typing rule for procedure pointer declaration

The procedure pointer declaration allows to assign to the pointer:
    • not only a procedure with the same parameters types, and if any, the same result type,
    • but also a procedure with contravariant parameters (by reference or by pointer) or/and a covariant result (by reference or by pointer).

Assigning to a function pointer a fonction with a contravariant parameter and a covariant result, both by pointer:
'Example of assigning to a function pointer a function with:
'   - a contravariant parameter by pointer,
'   - and a covariant result by pointer.

Type A
    Dim As Integer I
    Declare Constructor ()
    Declare Destructor ()
End Type
Constructor A ()
    Print "    A instance constructed", @This
End Constructor
Destructor A ()
    Print "    A instance destroyed", @This
End Destructor

Type B Extends A
    Dim As Integer J
    Declare Constructor ()
    Declare Constructor (ByRef a0 As A)
    Declare Destructor ()
End Type
Constructor B ()
    Print "    B instance constructed", @This
End Constructor
Constructor B (ByRef a0 As A)
    Cast(A, This) = a0
    Print "    B instance constructed", @This
End Constructor
Destructor B ()
    Print "    B instance destroyed", @This
End Destructor

Function f (ByVal pa0 As A Ptr) As B Ptr
    Return New B(*pa0)
End Function

Scope
    Dim As Function (ByVal As B Ptr) As A Ptr pf = @f
    Print "'Scope : Dim As B b0':"
    Dim As B b0
    Print
    Print "'Dim As A Ptr pab = pf(@b0)':"
    Dim As A Ptr pab = pf(@b0)
    Print
    Print "'Delete CPtr(B Ptr, pab)':"
    Delete CPtr(B Ptr, pab)
    Print
    Print "'End Scope':"
End Scope

Sleep

Assigning to a function pointer a function with a contravariant parameter and a covariant result, both by reference:
'Example of assigning to a function pointer a function with:
'   - a contravariant parameter by reference,
'   - and a covariant result by reference.

Type A Extends Object
    Dim As Integer I
    Declare Constructor ()
    Declare Virtual Destructor ()
End Type
Constructor A ()
    Print "    A instance constructed", @This
End Constructor
Destructor A ()
    Print "    A instance destroyed", @This
End Destructor

Type B Extends A
    Dim As Integer J
    Declare Constructor ()
    Declare Constructor (ByRef a0 As A)
    Declare Virtual Destructor ()
End Type
Constructor B ()
    Print "    B instance constructed", @This
End Constructor
Constructor B (ByRef a0 As A)
    Cast(A, This) = a0
    Print "    B instance constructed", @This
End Constructor
Destructor B ()
    Print "    B instance destroyed", @This
End Destructor

Function f (ByRef a0 As A) ByRef As B
    Return *New B(a0)
End Function

Scope
    Dim As Function (ByRef As B) ByRef As A pf = @f
    Print "'Scope : Dim As B b0':"
    Dim As B b0
    Print
    Print "'Dim Byref As A rab = pf(b0)':"
    Dim ByRef As A rab = pf(b0)
    Print
    Print "'Delete @rab':"
    Delete @rab
    Print
    Print "'End Scope':"
End Scope

Sleep

See also

  • Sub
  • Function
  • Pointer
  • Operator @ (Address Of)
  • Procptr Operator


Variadic Arguments
Allows a procedure to accept a variable number of arguments.

Preamble:

Normal procedures take a fixed number of arguments. When such a procedure is defined, the data-type of each argument is specified.
Every call to this procedure should supply the expected number of arguments, with types that can be converted to the specified ones.

Variadic arguments allow to define procedures which accept a variable number of arguments.
Support for variadic procedures differs widely among programming languages.

Variadic procedures can expose type-safety problems because the language support for variadic procedures is not type-safe:
- it allows to extract any number of arguments from the stack or elsewhere,
- and this while defining their types from entered user parameters.
If the variable arguments are all of same type or compatible with the same type, a sized array can be passed instead, but this requires slightly more work at the caller level.

Table of Contents
1. Syntax for using variadic procedures
2. ' FB's va_* ' support's keywords family (deprecated va_* support but kept for backwards compatibility on some targets)
3. ' C's va_* ' support's keywords family (recommended va_* support for all new coding)

1. Syntax for using variadic procedures

The ellipsis "..." (three dots), as last parameter, is used in procedure declarations (and definitions) to indicate a variable length argument list:
- A first fixed parameter (at least) must always be specified:
The fixed parameters(s) can provide information about how many variadic arguments there are, by any mechanism not imposed on the user.
Otherwise a terminal argument can be added in the variable length argument list, but this reserves a special argument value forbidden to the useful variadic arguments.
(if one choose to pass the variable arguments all by pointer, in this case an obvious terminal argument is the null pointer)
- The procedure must be called with the C calling convention cdecl.
- Two va_* support's keywords families exist to retrieve the variable arguments in the variadic procedure body.
- A variadic procedure can never be overloaded:
so this also excludes its use in UDT for constructors and properties,
but otherwise it can be declared abstract or virtual or even overriding.
- For variadic procedure members, the implicit passed This parameter is not taken into account as first fixed parameter (at least one explicit fixed parameter must always be specified).

Declaration syntax
Declare { Sub | Function } proc_name cdecl ( param_list, ... ) { | [ ByRef ] As return_type }
param_list
Parenthesized comma-separated list of fixed parameters.
return_type
The return type of a Function.
proc_name
The name or symbol of the procedure.

Calling syntax
There is nothing special to do for calling a variadic procedure.
The procedure is simply called by writing the arguments for the fixed parameters, followed by the additional variable arguments, as usual.

Only numeric types and pointers are supported as variable arguments:
- All bytes and shorts passed on variable arguments are implicitly converted to integers.
- All singles passed on variable arguments are implicitly converted to doubles.
- Strings (or WStrings) can be directly passed by user, in which case a ZString Ptr (or a WString Ptr) to the string (or the wstring) data is taken into account as internal passed argument.

Retrieving variable arguments
Variable arguments may use the stack, or registers, as per the normal conventions for the compiler:
- For some compilers in the past, while registers were used for normal procedures, stack was (always) used for variadic procedures.
- The gcc compiler family all seem to agree that the call signatures for variadic procedures are exactly the same as properly specified procedures. This means that some of the variable arguments may be in registers and some may be on the stack if there are too many.

The va_* support's keywords create an interface to retrieve these variable arguments in procedure body:
- The first FreeBASIC va_* support's keywords family developed have been the FB's va_* support's keywords family:
These FB's va_* support's keywords are very x86-specific and only work with the gas for x86 GAS assembly backend, because using a pointer to the argument stack.
These FB's va_* support's keywords don't work on all targets (like 64-bit) because arguments can be passed to procedures in cpu registers.
These FB's va_* support's keywords are now deprecated but kept for backwards compatibility on some targets.
- More recently, a FreeBASIC update added a new support's keywords family for variadic procedure argument lists:
This is the C's va_* support's keywords family compatible for all target platforms.
These C's va_* support's keywords are now recommended for all new coding.

Back to top

2. ' FB's va_* ' support's keywords family

Deprecated va_* support but kept for backwards compatibility on some targets.

There are 3 FB's va_* support's keywords (va_first, va_arg, va_next):
pointer_variable = va_first( )
va_first provides an untyped pointer value that points to the first variable argument passed to a procedure.

variable = va_arg( argument_list, datatype )
va_arg returns the current argument in the list, argument_list, with an expected data type of datatype.
(before first va_arg use, argument_list must be initialized with the command va_first)

argument_pointer = va_next( argument_list, datatype )
va_next provides a datatype pointer value that points to the next argument within the list argument_list, datatype being the type of the current argument being stepped over.

Note:
pointer_variable = va_first( )
computes the address in the stack following the address of the last passed fixed parameter.

variable = va_arg( argument_list, datatype )
is equivalent to:
variable = *CPtr( datatype Ptr, CPtr( Any Ptr, argument_list ) )

argument_pointer = va_next( argument_list, datatype )
is equivalent to:
argument_pointer = CPtr( datatype Ptr, CPtr( Any Ptr, argument_list ) ) + 1

Examples:
' Variadic function:
'    The first(fixed) parameter provides the number of elements.
'    The variable arguments are all strings except the last one which must be a float.
'    The user's string arguments are in fact passed under the hood through zstring pointers.

Function concatenation cdecl (ByVal count As Integer, ...) As String
    Dim arg As Any Ptr
    Dim s As String

    arg = va_first()

    For i As Integer = 1 To count
        If i < count Then
            s &= *va_arg(arg, ZString Ptr)
            arg = va_next(arg, ZString Ptr)
        Else
            s &= va_arg(arg, Double)
        End If
    Next

    Return s
End Function

Print concatenation(6, "Free", "BASIC", " ", "version", " ", 0.13)

Sleep

' Output: FreeBASIC version 0.13

' Variadic sub:
'    The first(fixed) parameter provides the number of elements.
'    The variable arguments are all UDT Ptr.

Type UDT
    Dim As String s
    Dim As Single d
End Type

Sub printUDT cdecl (ByVal count As Integer, ...)
    Dim arg As Any Ptr
    Dim pu As UDT Ptr

    arg = va_first()

    For i As Integer = 1 To count
        pu = va_arg(arg, UDT Ptr)
        Print pu->s, pu->d
        arg = va_next(arg, UDT Ptr)
    Next
End Sub

Dim As UDT u1, u2, u3
u1.s = "4*Atn(1)" : u1.d = 4 * Atn(1)
u2.s = "Sqr(2)"   : u2.d = Sqr(2)
u3.s = "Log(10)"  : u3.d = Log(10)

printUDT(3, @u1, @u2, @u3)

Sleep

' Output:
' 4*Atn(1)       3.141593
' Sqr(2)         1.414214
' Log(10)        2.302585

' Variadic sub:
'    The first (fixed) parameter is the type of the variable arguments (Integer Ptr or Double Ptr).
'    One terminal argument (added after the useful arguments) must be the null pointer.

Sub printNumericList cdecl (ByRef argtype As String, ...)
    Dim As Integer datatype = 0
    If UCase(argtype) = "INTEGER PTR" Then
        datatype = 1
        Print "List of integers : ";
    ElseIf UCase(argtype) = "DOUBLE PTR" Then
        datatype = 2
        Print "List of doubles : ";
    Else
        Print "Invalid argument type"
        Exit Sub
    End If

    Dim arg As Any Ptr
    Dim pn As Any Ptr

    arg = va_first()

    Do
        pn = va_arg(arg, Any Ptr)
        If pn = 0 Then Exit Do
        Print ,
        Select Case As Const datatype
        Case 1
            Print *CPtr(Integer Ptr, pn);
        Case 2
            Print *CPtr(Double Ptr, pn);
        End Select
        arg = va_next(arg, Any Ptr)
    Loop

    Print
End Sub

printNumericList("Integer Ptr", @Type<Integer>(123), @Type<Integer>(456), @Type<Integer>(789), CPtr(Integer Ptr, 0))
printNumericList("Double Ptr", @Type<Double>(1.2), @Type<Double>(3.4), @Type<Double>(5.6), @Type<Double>(7.8), CPtr(Double Ptr, 0))

Sleep

' Output:
' List of integers :           123           456           789
' List of doubles :            1.2           3.4           5.6           7.8

Back to top

3. ' C's va_* ' support's keywords family

Recommended va_* support for all new coding.

There are 5 C's va_* support's keywords (Cva_List, Cva_Start, Cva_Copy, Cva_Arg, Cva_End):
Dim variable As Cva_List
Cva_List is a built in data type for working with the variable length argument list in a variadic procedure.

Cva_Start( argument_list, last_param )
Cva_Start "constructs" the argument_list object declared of Cva_List data-type, settling on the last_param declared in the procedures parameter list before the Ellipsis "...".

Cva_Copy( dst_list, src_list )
Cva_Copy "copy-constructs" the dst_list object declared of Cva_List data-type, from an already constructed src_list object.

variable = Cva_Arg( argument_list, datatype )
Cva_Arg returns the current argument in the argument_list, with an expected data-type of datatype.
(Cva_Arg automatically increments argument_list to the next argument within the list after obtaining the value of the current argument)

Cva_End( argument_list )
Cva_End "destructs" the argument_list object of Cva_List data-type (previously "constructed" with Cva_Start or Cva_Copy).

The implementation and code emitted for Cva_List, Cva_Start, Cva_Copy, Cva_Arg, Cva_End varies depending on target, backend, and architecture.

Note:
The exact type and size of Cva_List depends on -target, -arch, -gen command line options:
In -gen gas, Cva_List is a pointer.
In -gen gcc, Cva_List is a pointer, a struct, or a struct array. The Cva_List type is replaced by "__builtin_va_list" in gcc.
On 32-bit targets, gas backend: type Cva_List as any alias "char" ptr
On 32-bit targets, gcc backend: type Cva_List as any alias "__builtin_va_list" ptr
On Windows 64-bit, gcc backend: type Cva_List as any alias "__builtin_va_list" ptr
On Linux x86_64, gcc backend: type Cva_List as __va_list_tag alias "__builtin_va_list[]"
On arm64, gcc backend: type Cva_List as __va_list alias "__builtin_va_list"

Cva_Start (or Cva_Copy) is like a constructor (or a copy-constructor) for the variadic argument_list object and must finally have a matching call to Cva_End, which is like a destructor.
After Cva_End for argument_list has been called, argument_list can be reused and reinitialized with another call to Cva_Start (or Cva_Copy).
The Cva_Start (or Cva_Copy) and Cva_End calls must both be called in pairs in the same procedure (for cross platform compatibility).

Examples:
' Variadic function:
'    The first(fixed) parameter provides the number of elements.
'    The variable arguments are all strings except the last one which must be a float.
'    The user's string arguments are in fact passed under the hood through zstring pointers.

Function concatenation cdecl (ByVal count As Integer, ...) As String
    Dim s As String
    Dim args As Cva_List

    Cva_Start(args, count)

    For i As Integer = 1 To count
        If i < count Then
            s &= *Cva_Arg(args, ZString Ptr)
        Else
            s &= Cva_Arg(args, Double)
        End If
    Next

    Cva_End(args)

    Return s
End Function

Print concatenation(6, "Free", "BASIC", " ", "version", " ", 1.07)

Sleep

' Output: FreeBASIC version 1.07

' Variadic sub:
'    The first(fixed) parameter provides the number of elements.
'    The variable arguments are all UDT Ptr.

Type UDT
    Dim As String s
    Dim As Single d
End Type

Sub printUDT cdecl (ByVal count As Integer, ...)
    Dim args As Cva_List
    Dim pu As UDT Ptr

    Cva_Start(args, count)

    For i As Integer = 1 To count
        pu = Cva_Arg(args, UDT Ptr)
        Print pu->s, pu->d
    Next

    Cva_End(args)
End Sub

Dim As UDT u1, u2, u3
u1.s = "4*Atn(1)" : u1.d = 4 * Atn(1)
u2.s = "Sqr(2)"   : u2.d = Sqr(2)
u3.s = "Log(10)"  : u3.d = Log(10)

printUDT(3, @u1, @u2, @u3)

Sleep

' Output:
' 4*Atn(1)       3.141593
' Sqr(2)         1.414214
' Log(10)        2.302585

' Variadic sub:
'    The first (fixed) parameter is the type of the variable arguments (Integer Ptr or Double Ptr).
'    One terminal argument (added after the useful arguments) must be the null pointer.

Sub printNumericList cdecl (ByRef argtype As String, ...)
    Dim As Integer datatype = 0
    If UCase(argtype) = "INTEGER PTR" Then
        datatype = 1
        Print "List of integers : ";
    ElseIf UCase(argtype) = "DOUBLE PTR" Then
        datatype = 2
        Print "List of doubles : ";
    Else
        Print "Invalid argument type"
        Exit Sub
    End If

    Dim args As Cva_List
    Dim pn As Any Ptr

    Cva_Start(args, argtype)

    Do
        pn = Cva_Arg(args, Any Ptr)
        If pn = 0 Then Exit Do
        Print ,
        Select Case As Const datatype
        Case 1
            Print *CPtr(Integer Ptr, pn);
        Case 2
            Print *CPtr(Double Ptr, pn);
        End Select
    Loop

    Print

    Cva_End(args)
End Sub

printNumericList("Integer Ptr", @Type<Integer>(123), @Type<Integer>(456), @Type<Integer>(789), CPtr(Integer Ptr, 0))
printNumericList("Double Ptr", @Type<Double>(1.2), @Type<Double>(3.4), @Type<Double>(5.6), @Type<Double>(7.8), CPtr(Double Ptr, 0))

Sleep

' Output:
' List of integers :           123           456           789
' List of doubles :            1.2           3.4           5.6           7.8

Back to top

See also

  • ... (Ellipsis)
  • va_first
  • va_arg
  • va_next
  • Cva_List
  • Cva_Start
  • Cva_Copy
  • Cva_Arg
  • Cva_End


Graphics Mode Refresh and Anti-Flickering
The Anti-Flikering coding methods for Graphics Mode Refresh (after clearing screen or viewport).

Preamble:

Refreshing (redrawing without precaution) a window in graphics mode can result in annoying flickering (this is due to the display of unwanted intermediate images picked up during the user refreshing phase).
The phenomenon is obviously intensified if the entire window is first erased before being completely updated, and this repeatedly.

This page presents the basic FreeBASIC coding techniques to fight against this phenomenon.

Main principles for anti-flickering

If one takes into account the only graphical user task (regardless of CPU resources needed for the OS), there are two main methods to avoid flickering:
- In first priority, use of a block '[Screenlock...Screenunlock]' to encapsulate the graphical instructions for refreshing.
But the documentation enlightens a warning to its use:
It is strongly recommended that the lock on a page be held for as short a time as possible. Only screen drawing should occur while the screen is locked, input/output and waiting must be avoided. In Win32 and Linux the screen is locked by stopping the thread that processes also the OS' events. If the screen is kept locked for a long time the event queue could overflow and make the system unstable.
- In a second priority, if the lock time is too long, use the principle of double video paging.

The instruction 'Screensync' is a reminiscence of the old QuickBASIC where there was only this type of instruction ('wait &h3DA, 8') to improve flickering.
It is empirical because it only allows to synchronize the drawing relating to the fixed dead time between two frames.
To be used occasionally with very little time drawing.

There is usually no interest to mix together these 2 (or 3) methods.

Then, in the display loop (if exists), user must provide enough CPU resources to OS (smoothed with 'Sleep' instruction at the end of the loop). Otherwise, it will take itself out of user control, resulting in a jerky display.

Note: Using 'Screensync' provides CPU resources to OS (dead time between end of graphic drawing and end of frame tracing), but by a non controllable way (because linked to the frame period).

Anti-flickering methods learning, through small example

A small program to enlighten (and compare the different efficiencies) the 4 following different methods from 2 to 5 (after the no method 1) to animate a drawing/printing to a graphic screen by a loop with clearing screen:
Method 1: Draw/Print loop to screen with raw coding (no method)
Algorithm:
'    SCREEN 19, , 2 'to enable double-paging
'    SCREENSET 0, 0 'to cancel double-paging
'
'  ┌─► CLS          'to clear the page
'  │   Drawing      'to draw on the page
'  │   Printing     'to print on the page
'  └── Temporizing  'to avoid hogging the CPU
Method 2: Draw/Print loop to screen with synchronizing
Algorithm:
'    SCREEN 19, , 2 'to enable double-paging
'    SCREENSET 0, 0 'to cancel double-paging
'
'  ┌─► SCREENSYNC   'to synchronize between two frames
'  │   CLS          'to clear the page
'  │   Drawing      'to draw on the page
'  │   Printing     'to print on the page
'  └── Temporizing  'to avoid hogging the CPU
Method 3: Draw/Print loop to screen with locking
Algorithm:
'    SCREEN 19, , 2 'to enable double-paging
'    SCREENSET 0, 0 'to cancel double-paging
'
'  ┌─► SCREENLOCK   'to lock the page's frame buffer
'  │   CLS          'to clear the page
'  │   Drawing      'to draw on the page
'  │   Printing     'to print on the page
'  │   SCREENUNLOCK 'to unlock the page's frame buffer
'  └── Temporizing  'to avoid hogging the CPU
Method 4: Draw/Print loop to screen with double buffering
Algorithm:
'    SCREEN 19, , 2 'to enable double-paging
'    SCREENSET 1, 0 'to activate double-paging
'
'  ┌─► CLS          'to clear the work page
'  │   Drawing      'to draw on the work page
'  │   Printing     'to print on the work page
'  │   SCREENCOPY   'to copy the work page into the visible page
'  └── Temporizing  'to avoid hogging the CPU
Note:
Double buffering and page flipping (below) are functionally equivalent (but not under the hood) if the work page is entirely refreshed at each iteration as here.

Method 5: Draw/Print loop to screen with page flipping
Algorithm:
'    SCREEN 19, , 2     'to enable double-paging
'    SCREENSET 1, 0     'to activate double-paging
'    p0=0 : p1=1        'to initialize flipping
'
'  ┌─► CLS              'to clear the work page
'  │   Drawing          'to draw on the work page
'  │   Printing         'to print on the work page
'  │   SCREENSET p0, p1 'to set the work page to the p0 value, and the visible page to the p1 value
'  │   SWAP p0, p1      'to exchange the values of p0 and p1
'  └── Temporizing      'to avoid hogging the CPU
Note:
Page flipping and double buffering (above) are functionally equivalent (but not under the hood) if the work page is entirely refreshed at each iteration as here.

Complete program listing
Code:
Declare Sub Draw_circle_recursion (ByVal x As Integer, ByVal y As Integer, ByVal r As Integer, ByVal rmin As Integer)

Dim I As Integer = 0
Dim Inc As Integer
Dim Key As String
Dim Code As Integer = 1
Dim Tempo As Integer = 3
Dim T As Single = Tempo
Dim p0 As Integer = 0
Dim p1 As Integer = 1

Screen 19, , 2

Do
    If Code = 4 Or Code = 5 Then
        ScreenSet 1, 0
    Else
        ScreenSet 0, 0
    End If
    Do
        Select Case Code
        Case 2
            ScreenSync
        Case 3
            ScreenLock
        End Select
        Cls
        Draw_circle_recursion(10 + I, 300, 9 + I * I / 29 / 29, 10)
        Locate 1, 1
        Select Case Code
        Case 1
            Print "1. Draw/Print loop to screen with raw coding:"
            Print
            Print "   SCREEN 19, , 2 'to enable double-paging"
            Print "   SCREENSET 0, 0 'to cancel double-paging"
            Print
            Print " " & Chr(218) & Chr(196) & Chr(16) & " CLS"
            Print " " & Chr(179) & " " & " " & " Drawing"
            Print " " & Chr(179) & " " & " " & " Printing"
            Print " " & Chr(192) & Chr(196) & Chr(196) & " Temporizing"; T; " ms";
        Case 2
            Print "2. Draw/Print loop to screen with synchronizing:"
            Print
            Print "   SCREEN 19, , 2 'to enable double-paging"
            Print "   SCREENSET 0, 0 'to cancel double-paging"
            Print
            Print " " & Chr(218) & Chr(196) & Chr(16) & " SCREENSYNC"
            Print " " & Chr(179) & " " & " " & " CLS"
            Print " " & Chr(179) & " " & " " & " Drawing"
            Print " " & Chr(179) & " " & " " & " Printing"
            Print " " & Chr(192) & Chr(196) & Chr(196) & " Temporizing"; T; " ms";
        Case 3
            Print "3. Draw/Print loop to screen with locking:"
            Print
            Print "   SCREEN 19, , 2 'to enable double-paging"
            Print "   SCREENSET 0, 0 'to cancel double-paging"
            Print
            Print " " & Chr(218) & Chr(196) & Chr(16) & " SCREENLOCK"
            Print " " & Chr(179) & " " & " " & " CLS"
            Print " " & Chr(179) & " " & " " & " Drawing"
            Print " " & Chr(179) & " " & " " & " Printing"
            Print " " & Chr(179) & " " & " " & " SCREENUNLOCK"
            Print " " & Chr(192) & Chr(196) & Chr(196) & " Temporizing"; T; " ms";
        Case 4
            Print "4. Draw/Print loop to screen with double buffering:"
            Print
            Print "   SCREEN 19, , 2 'to enable double-paging"
            Print "   SCREENSET 1, 0 'to activate double-paging"
            Print
            Print " " & Chr(218) & Chr(196) & Chr(16) & " CLS"
            Print " " & Chr(179) & " " & " " & " Drawing"
            Print " " & Chr(179) & " " & " " & " Printing"
            Print " " & Chr(179) & " " & " " & " SCREENCOPY"
            Print " " & Chr(192) & Chr(196) & Chr(196) & " Temporizing"; T; " ms";
        Case 5
            Print "5. Draw/Print loop to screen with page flipping:"
            Print
            Print "   SCREEN 19, , 2 'to enable double-paging"
            Print "   SCREENSET 1, 0 'to activate double-paging"
            Print "   p0=0 : p1=1    'to initialize flipping"
            Print
            Print " " & Chr(218) & Chr(196) & Chr(16) & " CLS"
            Print " " & Chr(179) & " " & " " & " Drawing"
            Print " " & Chr(179) & " " & " " & " Printing"
            Print " " & Chr(179) & " " & " " & " SCREENSET p0, p1"
            Print " " & Chr(179) & " " & " " & " SWAP p0, p1"
            Print " " & Chr(192) & Chr(196) & Chr(196) & " Temporizing"; T; " ms";
        End Select
        Locate 30, 1
        Print "<1>: Draw/Print with raw coding"
        Print "<2>: Draw/Print with synchronizing"
        Print "<3>: Draw/Print with locking"
        Print "<4>: Draw/Print with double buffering"
        Print "<5>: Draw/Print with page flipping"
        Print "<+/->: Tempo setting (+/-)"
        Print
        Print "<Escape> or click [X]: Quit";
        Select Case Code
        Case 3
            ScreenUnlock
        Case 4
            ScreenCopy
        Case 5
            ScreenSet p0, p1
            Swap p0, p1
        End Select
        If I = 0 Then
            Inc = +1
        ElseIf I = 480 Then
            Inc = -1
        End If
        I = I + Inc
        Key = Inkey
        If Key = "+" And Tempo < 10 Then
            Tempo = Tempo + 1
        ElseIf Key = "-" And Tempo > 0 Then
            Tempo = Tempo - 1
        End If
        If Tempo > 0 Then
            T = Tempo
        Else
            T = 0.5
        End If
        Static As Integer K
        K += 1
        If K >= 25 / T Then
            Sleep 25
            K = 0
        End If
    Loop While Key <> "1" And Key <> "2" And Key <> "3" And Key <> "4" And Key <> "5" And Key <> Chr(27) And Key <> Chr(255) & "k"
    Code = Val(Key)
Loop Until Key = Chr(27) Or Key = Chr(255) & "k"

Sub Draw_circle_recursion ( ByVal x As Integer, ByVal y As Integer, ByVal r As Integer, ByVal rmin As Integer )
    Circle (x, y), r, r Shr 1
    If r > rmin Then
        Draw_circle_recursion(x + r Shr 1, y, r Shr 1, rmin)
        Draw_circle_recursion(x - r Shr 1, y, r Shr 1, rmin)
        Draw_circle_recursion(x, y + r Shr 1, r Shr 1, rmin)
        Draw_circle_recursion(x, y - r Shr 1, r Shr 1, rmin)
        Draw_circle_recursion(x + r Shr 1, y + r Shr 1, r Shr 2, rmin)
        Draw_circle_recursion(x - r Shr 1, y + r Shr 1, r Shr 2, rmin)
        Draw_circle_recursion(x + r Shr 1, y - r Shr 1, r Shr 2, rmin)
        Draw_circle_recursion(x - r Shr 1, y - r Shr 1, r Shr 2, rmin)
    End If
End Sub

Note on temporizing in the loop (for compatibility with any PC):
The true temporizing value applied is always 25 ms ('Sleep 25'), but only for one loop on N (with 'N = 25 / tempo', and 'tempo' the wanted value in ms between 0.5 and 10).

See also

  • ScreenSync
  • ScreenLock, ScreenUnlock
  • ScreenSet, ScreenCopy
  • Lite regulation function to be integrated into user loop for FPS control
  • Fine-grain procedure for waiting and in-loop procedure for fine-regulating FPS


Graphics internal formats

The graphics library stores pixels in a small set of internal formats and then converts to the platform backend as needed. Backend code should treat the internal format as a contract, not as a suggestion, because SCREEN modes, SCREENSET, image buffers, palette handling, and driver updates all depend on it.

What to watch

  • Preserve byte order and pitch calculations.
  • Keep palette modes separate from true-color modes.
  • Avoid assuming that the visible window size equals the framebuffer size.
  • Test mode changes, SCREENSET pages, fullscreen/maximized/windowed cases, and mouse coordinate scaling on the backend being changed.

Backend rule

If a backend needs a native surface format that differs from the FreeBASIC format, do the conversion at the backend boundary. Do not let backend-specific pixel assumptions leak back into shared graphics code.


BLOAD and BSAVE Graphics File Formats

BLOAD and BSAVE understand two families of graphics files:

  • FreeBASIC/QuickBASIC BSAVE memory blocks.
  • Windows/OS2 bitmap files, normally using the .bmp extension.

The BSAVE block format is useful when a program wants to store raw screen or image memory exactly as FreeBASIC sees it. BMP is useful when the file should be opened by other graphics tools.

Format selection

File marker or name | Meaning
&hFD | QuickBASIC BSAVEd block. BLOAD can read this.
&hFE | FreeBASIC BSAVEd block. BSAVE writes this format unless the file name ends in .bmp.
&h42, ASC("B") | Possible BMP file. BLOAD seeks back to the start and validates the full BMP header.
.bmp extension | BSAVE writes a BMP file instead of a FreeBASIC BSAVE block. The extension check is case-insensitive.

FreeBASIC BSAVE block

A FreeBASIC BSAVE block starts with an &hFE marker followed by a 32-bit little-endian byte count.

Offset  Size  Description
0       1     &hFE, FreeBASIC BSAVE block marker
1       4     Data size in bytes, little-endian unsigned 32-bit value
5       ...   Raw image or memory bytes
?       ...   Optional palette data when saving an indexed screen

BSAVE writes this format for any output file whose name does not end in .bmp.

When saving from the current graphics screen, BSAVE writes at most the current screen pitch multiplied by the screen height. When the current graphics mode has an indexed palette, BSAVE also writes palette entries after the raw pixel data.

When saving from an explicit source pointer, BSAVE writes the requested number of bytes from that address. In that case the caller is responsible for choosing a sensible size.

QuickBASIC BSAVE block

BLOAD can also read the older QuickBASIC block marker, &hFD. That format stores a 16-bit little-endian byte count after four bytes that FreeBASIC ignores.

Offset  Size  Description
0       1     &hFD, QuickBASIC BSAVE block marker
1       4     Ignored by FreeBASIC
5       2     Data size in bytes, little-endian unsigned 16-bit value
7       ...   Raw image or memory bytes
?       ...   Optional palette data

FreeBASIC BSAVE does not write the QuickBASIC &hFD form. It writes the newer &hFE form instead.

BLOAD with BSAVE blocks

BLoad filename [, dest [, pal]]

If dest is omitted, BLOAD copies the raw bytes into the current graphics screen. If the file contains more bytes than the screen can hold, the load is truncated to the screen size.

If dest is supplied, BLOAD copies the raw bytes to that address instead. For BSAVE blocks, this is a straight memory load. The destination must point at enough writable memory for the data being loaded.

Palette data is only useful for indexed graphics modes. If pal is omitted while loading to the screen, FreeBASIC loads the palette into the current graphics device palette when palette data is present.

BMP files

BLOAD treats a file beginning with &h42 as a possible BMP file, but the full BMP header must still be valid. The loader understands common uncompressed BMP headers, including the old OS/2 core header and Windows bitmap info headers.

The BMP loader handles palette BMPs and 16-bit, 24-bit, and 32-bit BMP data. One-bit and four-bit indexed BMP data are expanded while loading. Top-down BMP files, identified by a negative height, are accepted for uncompressed and bitfield BMP data.

RLE-compressed BMP files should not be relied on. Use uncompressed BMP files for portable BLOAD/BSAVE work.

BSAVE as BMP

BSave( filename, source [,[ size ][,{ pal | pal, bitsperpixel }]] )

If the file name ends in .bmp, BSAVE writes a Windows BMP file instead of a FreeBASIC BSAVE block.

  • 8-bit indexed graphics can be saved as 8-bit BMP, or as 24-bit BMP when a higher output depth is requested.
  • 16-bit graphics are saved as 24-bit BMP.
  • 32-bit graphics are saved as 32-bit BMP by default, or as 24-bit BMP if 24 bits per pixel is requested.
  • Rows are padded as required by the BMP format.
  • Palette output is written for 8-bit BMP files.

Choosing a format

Use a BSAVE block when both the writer and reader are FreeBASIC or QuickBASIC-style programs and you want a simple raw memory dump. This is fast and small, but it is tied to the screen mode, image layout, pitch, and palette assumptions of the program that reads it.

Use BMP when the file should be inspected, edited, or generated by normal image tools. BMP is larger, but it carries width, height, bit depth, palette, and row layout information in the file header.

Common mistakes

  • Loading a BSAVE block into the wrong screen mode can produce scrambled output because the raw bytes no longer match the destination layout.
  • Loading to a pointer does not allocate memory. Allocate the image or buffer first.
  • A file starting with the letter B is not automatically a valid BMP. BLOAD checks the BMP header after seeing the first byte.
  • A .bmp file saved by BSAVE is not the same format as a FreeBASIC BSAVE block. The extension selects the BMP writer.

See also

  • BLOAD
  • BSAVE
  • Graphics Library


Lite regulation function to be integrated into user loop for FPS control
Controlling FPS (Frames Per Second) by using an in-loop lite-regulating procedure.

Preamble:

The main objective of this regulation function is to minimize the CPU load which is added to that of the user's own code.
Therefore, all dead times used by this function to control the user FPS (Frames per second) are generated using exclusively the SLEEP keyword (no CPU consuming waiting loops).

But the exclusive use of the SLEEP keyword induces a limitation on the maximum accessible FPS value because the delay generated by SLEEP cannot drop below a limit value depending on the OS cycle period.
A workaround is integrated into this regulation function to provide from values greater than this FPS limit value no longer a true FPS but an apparent FPS which seems to match the requested FPS.

In addition, as the SLEEP keyword does not have a good accuracy on the generated delay, the FPS obtained fluctuates around the requested value.
SLEEP also produces a temporal bias which varies over time, but whose average is corrected by the regulation function.

1. Principles to overcome the penalizing behavior of SLEEP keyword

To implement the best regulation but based only on the SLEEP keyword, some bad behaviors as described above must be overcome or minimized.

Principle for measuring the minimum delay accessible with SLEEP
To evaluate the minimum delay 'tos' accessible with SLEEP, the duration of a sequence of ten 'Sleep 1, 1' is measured using the keyword TIMER and the time found is divided by ten.
But this minimum value 'tos' is not directly usable because a margin must be added to it to preserve an almost linear regulation operation.
The minimum usable value is thus fixed at '3 * tos / 2'.

This calibration phase is executed automatically on the first call to the regulation function, or if the time measured is much different than the time ordered, or on request from the user thanks to an optional parameter of the function, or on opportunity measurements when the high limit of regulation is reached (when a heel value of delay with 'Sleep tos / 2, 1' is used).

Principle for accessing an apparent FPS higher than the maximum true FPS accessible with SLEEP
When for a required FPS value, the 't' value to be applied in 'Sleep t, 1' becomes less than '3 * tos / 2', an 'image skipping' feature is started to provide an apparent FPS which matches to the required FPS.

The basic principle of 'image skipping' is to remove all delays between these images (the regulation function returns immediately) so that the persistence of these images is low compared to that which is followed by a true delay.
The smaller the image tracing time, the less these skipped images are visible.

The temporal principle for skipping 'n - 1' images over 'n' images is to generate only one delay after viewing the last image, with a value such that the total period of the sequence (of 'n' images) is equal to 'n' times the period corresponding to the required FPS.
The number of images to skip is stopped as soon as the only delay to generate for the sequence becomes greater than or equal to '3 * tos / 2'.
When 'n - 1' images are skipped over 'n' images, the apparent FPS is equal to the inverse of the full display period of the 'n' images but multiplied by 'n'.

This functionality of scrolling skipped images can be inhibited by the user thanks to an optional parameter of the function.

Conversely, the user can reinforce this functionality by downright removing the skipped images by testing an optional parameter of the function which marks these skipped images.
For this to work, the user code must very well separate the part of the code that concerns only the graphical drawing (the only part that should not be executed when the 'skipped image' tag is true) from the part of the code that concerns only the progress of the movement (which must always be executed). Otherwise, if these two parts of code are skipped at the same time (or even only a small portion of the progress of the movement), the graphical drawing will on the contrary be slowed down (although the returned FPS value says otherwise).

This operating configuration ('image skipping' +'skipped image' user removing) is therefore the most efficient (from the point of view of apparent FPS) but imposes complete separation rules between two tasks (tracing only and calculating only) in the user animation code loop.

On the other hand, the 'image skipping' configuration alone (with 'skipped image' scrolled by default) does not require any coding rules for the user.

In case of too high requested FPS and to avoid having an image that is too jerky in the case of image skipping applied, the actual refresh rate of the screen cannot go below 10 Hz in case of skipped images (the apparent FPS can be very much higher), and the number of consecutive skipped images + 1 cannot exceed 20.
These two limits when 'image skipping' is active induce an apparent displacement quasi-fluid for an initial image moving by 1 pixel every time (a displacement of 20 pixels at a frequency of 10 Hz appears to the user as a fairly smooth movement, not too jerky).

Principle for compensating for the average temporal bias produced by SLEEP
The principle is to measure (using the TIMER keyword) the real delay provided by 'Sleep t, 1' in relation with 't'.

If the time measured is much different than the time ordered, this probably means that there is an error compared to the resolution measured on initialization, and the function then will automatically restart at the next call a new calibration phase (with ten 'Sleep 1, 1').
Example: requested time = 4 µs, but measured time = 16 µs, probably because the resolution was changed (lowered) while program running.

If the time measured is only a little different from the time ordered, the differences are averaged to try to correct the average bias of the SLEEP keyword.

2. Description of the function body to include

The 'regulateLite()' function is described here.
The code is not thread safe because it uses 'Static' variables.

The function is well suited to regulate the FPS of an image refresh by inserting a delay into the loop, without adding a notable CPU load to that of the user's own code.

Note: Like any waiting feature, it is strongly discouraged to use 'regulateLite()' when the screen is locked, so no call from inside a [ScreenLock...ScreenUnlock] block.

The function has a mandatory first parameter 'MyFps' through which the user passes their required FPS.

For debugging purposes, the function returns the FPS (true or apparent) value it applied. If the user does not wish to use this debug data, then they can call the function as a Sub quite simply.
If this returned FPS value is much lower than that required, this means that it becomes difficult to reach the FPS setpoint. Otherwise, the fluctuation of the FPS returned is mainly due the use of the only SLEEP keyword to generate the delay in the loop.

Otherwise, the function proposes 3 optional parameters:
- 'Restart' ('False' by default):
This input parameter allows the user to force a new calibration phase, because for example he knows that the resolution of the OS cycle has just changed.
Set this parameter to 'True' in the call, then to 'False' on the next call.
- 'SkipImage' ('True' by default):
This input parameter allows the user to inhibit the automatic 'image skipping' feature. Whatever happens, there will be no skipped images, but perhaps to the detriment of the FPS obtained which may not follow the requested FPS.
Set this parameter to 'False' to disable the 'image skipping' feature (can be reset to 'True' later).
- 'ImageSkipped' ('False' by default bur default value irrelevant) :
This output parameter is set to 'True' at each image skipped by the function (if the 'image skipping' feature is activated).
The user can test it to know if the image is skipped or not ("image skipping" feature working in scrolling mode).
He can also use this parameter to downright not draw this skipped image at all, allowing for example even higher FPS ("image skipping" feature working in removing mode).

File to be included: "regulateLite.bi"
' regulateLite.bi

Function regulateLite(ByVal MyFps As ULong, ByVal SkipImage As Boolean = True, ByVal Restart As Boolean = False, ByRef ImageSkipped As Boolean = False) As ULong
    '' 'MyFps' : requested FPS value, in frames per second
    '' 'SkipImage' : optional parameter to activate the image skipping (True by default)
    '' 'Restart' : optional parameter to force the resolution acquisition, to reset to False on the next call (False by default)
    '' 'ImageSkipped' : optional parameter to inform the user that the image has been skipped (if image skipping is activated)
    '' function return : applied FPS value (true or apparent), in frames per second
    Static As Single tos
    Static As Single bias
    Static As Long count
    Static As Single sum
    ' initialization calibration
    If tos = 0 Or Restart = True Then
        Dim As Double t = Timer
        For I As Integer = 1 To 10
            Sleep 1, 1
        Next I
        Dim As Double tt = Timer
        #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
        If tt < t Then t -= 24 * 60 * 60
        #endif
        tos = (tt - t) / 10 * 1000
        bias = 0
        count = 0
        sum = 0
    End If
    Static As Double t1
    Static As Long N = 1
    Static As ULong fps
    Static As Single tf
    ' delay generation
    Dim As Double t2 = Timer
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
    If t2 < t1 Then t1 -= 24 * 60 * 60
    #endif
    Dim As Double t3 = t2
    Dim As Single dt = (N * tf - (t2 - t1)) * 1000 - bias
    If (dt >= 3 * tos / 2) Or (SkipImage = False) Or (N >= 20) Or (fps / N <= 10) Then
        If dt <= tos Then dt = tos / 2
        Sleep dt, 1
        t2 = Timer
        #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
        If t2 < t1 Then t1 -= 24 * 60 * 60 : t3 -= 24 * 60 * 60
        #endif
        fps = N / (t2 - t1)
        tf = 1 / MyFps
        t1 = t2
        ' automatic test and regulation
        Dim As Single delta = (t2 - t3) * 1000 - (dt + bias)
        If Abs(delta) > 3 * tos Then
            tos = 0
        Else
            bias += 0.1 * Sgn(delta)
        End If
        ' automatic calibation
        If dt < tos Then
            If count = 100 Then
                tos = sum / 100 * 1000
                bias = 0
                sum = 0
                count = 0
            Else
                sum += (t2 - t3)
                count += 1
            End If
        End If
        ImageSkipped = False
        N = 1
    Else
        ImageSkipped = True
        N += 1
    End If
    Return fps
End Function

Maximum performance estimation
Simple modeling of the user loop and its environment:
- tt : execution time of the tracing task
- tc : execution time of the calculation task (+ other process)
- tm : time of the minimum delay generated by 'regulateLite()' (depending on the OS cycle period) = 16 ms (for Windows in normal resolution)
- fm = minimum frequency of the true FPS authorized by 'regulateLite()' in case of 'image skipping' = 10 Hz (fixed in the code)
- siM = maximum number of skipped images + 1 = 20 (fixed in the code)

This estimate neglects the execution time of 'regulateLite()' except obviously the delay ('tm') it generates.

Without 'image skipping':
FPSmax = 1 / (tt + tc + tm)

With 'image skipping' alone (skipped images scrolled):
k0 = Int((1 / fm - tm) / (tt + tc))
k = Iif(k0 < siM, k0, siM)
apparent FPSmax = Cint(1 / (k * (tt + tc) + tm) * k)
('k' value corresponds to the number of 'skipped images' + 1 : 'k - 1' skipped images over 'k')

With 'image skipping' + skipped images removed by user:
k0 = Int((1 / fm - tm - tt) / tc)
k = Iif(k0 < siM, k0, siM)
apparent FPSmax = Cint(1 / (k * tc + tt + tm) * k)
('k' value corresponds to the number of 'skipped images' + 1 : 'k - 1' skipped images over 'k')

To highlight the maximum accessible FPS values with 'regulateLite()', depending on the duration of the 2 tasks in the user loop (tracing task and calculation task (+ other process)), a complete table of estimated values is below provided (duration of the 2 tasks ranging from 1 ms to 9 ms each in steps of 1 ms, Windows in normal resolution):
'' Constant tm = 0.016 : time of the minimum delay generated by 'regulateLite()' (depending on the OS cycle period)
'' Constant fm = 10    : minimum frequence of the true FPS authorized by 'regulateLite()' in case of 'image skipping'
'' Constant siM = 20   : maximum number of skipped images + 1

'' Variable tt : execution time of the tracing task
'' Variable tc : execution time of the calculation task (+ other process)

'' For each couple (tt,tc) of values, 3 FPS max are estimated, and for each the quota of 'skipped images':
''    - FPS without 'image skipping'                                         , number of 'skipped images' (always '0')
''    - Apparent FPS with 'image skipping' ('skipped images' only scrolled)  , quota of 'skipped images' (only scrolled)
''    - Apparent FPS with 'image skipping' + 'skipped images' removed by user, quota of 'skipped images' removed by user

''           | tt = 1 ms | tt = 2 ms | tt = 3 ms | tt = 4 ms | tt = 5 ms | tt = 6 ms | tt = 7 ms | tt = 8 ms | tt = 9 ms |
'' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
''           |  56 0     |  53 0     |  50 0     |  48 0     |  45 0     |  43 0     |  42 0     |  40 0     |  38 0     |
'' tc = 1 ms | 357 19/20 | 263 19/20 | 208 19/20 | 167 15/16 | 140 13/14 | 120 11/12 | 104 9/10  |  93 8/9   |  83 7/8   |
''           | 541 19/20 | 526 19/20 | 513 19/20 | 500 19/20 | 488 19/20 | 476 19/20 | 465 19/20 | 455 19/20 | 444 19/20 |
'' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
''           |  53 0     |  50 0     |  48 0     |  45 0     |  43 0     |  42 0     |  40 0     |  38 0     |  37 0     |
'' tc = 2 ms | 263 19/20 | 208 19/20 | 167 15/16 | 140 13/14 | 120 11/12 | 104 9/10  |  93 8/9   |  83 7/8   |  75 6/7   |
''           | 351 19/20 | 345 19/20 | 339 19/20 | 333 19/20 | 328 19/20 | 323 19/20 | 317 19/20 | 312 19/20 | 308 19/20 |
'' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
''           |  50 0     |  48 0     |  45 0     |  43 0     |  42 0     |  40 0     |  38 0     |  37 0     |  36 0     |
'' tc = 3 ms | 208 19/20 | 167 15/16 | 140 13/14 | 120 11/12 | 104 9/10  |  93 8/9   |  83 6/7   |  75 6/7   |  70 6/7   |
''           | 260 19/20 | 256 19/20 | 253 19/20 | 250 19/20 | 247 19/20 | 244 19/20 | 241 19/20 | 238 19/20 | 235 19/20 |
'' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
''           |  48 0     |  45 0     |  43 0     |  42 0     |  40 0     |  38 0     |  37 0     |  36 0     |  34 0     |
'' tc = 4 ms | 167 15/16 | 140 13/14 | 120 11/12 | 104 9/10  |  93 8/9   |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |
''           | 206 19/20 | 204 19/20 | 202 19/20 | 200 19/20 | 196 18/19 | 194 18/19 | 192 18/19 | 190 18/19 | 186 17/18 |
'' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
''           |  45 0     |  43 0     |  42 0     |  40 0     |  38 0     |  37 0     |  36 0     |  34 0     |  33 0     |
'' tc = 5 ms | 140 13/14 | 120 11/12 | 104 9/10  |  93 8/9   |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |  60 5/6   |
''           | 165 15/16 | 163 15/16 | 162 15/16 | 160 15/16 | 156 14/15 | 155 14/15 | 153 14/15 | 152 14/15 | 150 14/15 |
'' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
''           |  43 0     |  42 0     |  40 0     |  38 0     |  37 0     |  36 0     |  34 0     |  33 0     |  32 0     |
'' tc = 6 ms | 120 11/12 | 104 9/10  |  93 8/9   |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |  60 5/6   |  55 4/5   |
''           | 137 12/13 | 135 12/13 | 134 12/13 | 133 12/13 | 131 12/13 | 130 12/13 | 126 11/12 | 125 11/12 | 124 11/12 |
'' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
''           |  42 0     |  40 0     |  38 0     |  37 0     |  36 0     |  34 0     |  33 0     |  32 0     |  31 0     |
'' tc = 7 ms | 104 9/10  |  98 8/9   |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |  60 5/6   |  55 4/5   |  52 4/5   |
''           | 117 10/11 | 116 10/11 | 115 10/11 | 113 10/11 | 112 10/11 | 111 10/11 | 110 10/11 | 106 9/10  | 105 9/10  |
'' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
''           |  40 0     |  38 0     |  37 0     |  36 0     |  34 0     |  33 0     |  32 0     |  31 0     |  30 0     |
'' tc = 8 ms |  93 8/9   |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |  60 5/6   |  55 4/5   |  52 4/5   |  48 3/4   |
''           | 103 9/10  | 102 9/10  | 101 9/10  | 100 9/10  |  97 8/9   |  96 8/9   |  95 8/9   |  94 8/9   |  93 8/9   |
'' ----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|
''           |  38 0     |  37 0     |  36 0     |  34 0     |  33 0     |  32 0     |  31 0     |  30 0     |  29 0     |
'' tc = 9 ms |  83 7/8   |  75 6/7   |  70 6/7   |  64 5/6   |  55 5/6   |  55 4/5   |  52 4/5   |  48 3/4   |  45 3/4   |
''           |  92 8/9   |  91 8/9   |  90 8/9   |  87 7/8   |  86 7/8   |  85 7/8   |  84 7/8   |  83 7/8   |  82 7/8   |
'' -----------------------------------------------------------------------------------------------------------------------

3. Examples of test code

4 code examples for testing the behavior of the 'regulateLite()' function.

Test code for simple use of 'regulateLite()'
The 'regulateLite()' function is called as a sub (ignoring the return value) and only the mandatory parameter ('MyFps') is used.
=> "image skipping" feature working in scrolling mode.

The dynamic part of the display consists of the value of the requested FPS and a graphic progress bar to judge the quality of the regulation.

The static part of the display is the list of available commands:
- <+/-> for increase/decrease FPS
- <escape> to Quit

Test code 1:
#include "regulateLite.bi"

Screen 12
Dim As ULong FPS = 100
Do
    Static As ULongInt l
    ScreenLock
    Cls
    Color 15
    Print Using "Requested FPS : ###"; FPS
    Print
    Print
    Print
    Color 14
    Print "<+>      : Increase FPS"
    Print "<->      : Decrease FPS"
    Print
    Print "<Escape> : Quit"
    Line (0, 32)-(639, 48), 7, B
    Line (0, 32)-(l, 48), 7, BF
    ScreenUnlock
    l = (l + 1) Mod 640
    Dim As String s = Inkey
    Select Case s
    Case "+"
        If FPS < 200 Then FPS += 1
    Case "-"
        If FPS > 10 Then FPS -= 1
    Case Chr(27)
        Exit Do
    End Select
    regulateLite(FPS)
Loop

Test code for improved use of 'regulateLite()'
Compared to 'Code for simple test', this code calls 'regulateLite()' as a function and displays the applied FPS (return value).
Additionally, this code uses the optional 'ImageSkipped' parameter to not display images tagged 'ImageSkipped=True' at all, and also specify the quantum of images displayed.
=> "image skipping" feature working in removing mode.

Test code 2:
#include "regulateLite.bi"

Screen 12
Dim As ULong FPS = 100
Do
    Static As ULongInt l
    Static As ULong MyFPS
    Static As Boolean ImageSkipped
    Static As Long nis
    Static As Long tnis
    If ImageSkipped = False Then
        ScreenLock
        Cls
        Color 15
        Print Using "Requested FPS   : ###"; FPS
        Print
        Color 11
        Print Using "Applied FPS        : ###"; MyFPS
        Print "   Image displayed : 1/" & tnis + 1
        Print
        Print
        Print
        Color 14
        Print "<+>      : Increase FPS"
        Print "<->      : Decrease FPS"
        Print
        Print "<Escape> : Quit"
        Line (0, 80)-(639, 96), 7, B
        Line (0, 80)-(l, 96), 7, BF
        ScreenUnlock
    End If
    l = (l + 1) Mod 640
    Dim As String s = Inkey
    Select Case s
    Case "+"
        If FPS < 200 Then FPS += 1
    Case "-"
        If FPS > 10 Then FPS -= 1
    Case Chr(27)
        Exit Do
    End Select
    MyFPS = regulateLite(FPS, , , ImageSkipped)
    If ImageSkipped = True Then
        nis += 1
    Else
        tnis = nis
        nis = 0
    End If
Loop

Test code for intensive use of 'regulateLite()'
Compared to 'Code for improved test', this code additionally uses the other two optional parameters 'SkipImage' and 'Restart'.
This code is used to test all the combinations of the parameters of the function as well as the type of display of the skipped images (either scrolling of the skipped images, or removing of the skipped images).
("image skipping" feature activated and pre-selected in scrolling mode)

For the Windows platform only, this code allows to change the OS cycle resolution (normal or high) and commands a new calibration phase at each change of resolution.

Added commands:
- <T/F> for True/False for image skipping
- <S/R> for Scroll/Remove image skipped
- <C> for Calibration phase
- <N/H> for Normal/High resolution (for Windows platform only)

Test code 3:
#include "regulateLite.bi"

#if defined(__FB_WIN32__)
Declare Function _setTimer Lib "winmm" Alias "timeBeginPeriod"(ByVal As ULong = 1) As Long
Declare Function _resetTimer Lib "winmm" Alias "timeEndPeriod"(ByVal As ULong = 1) As Long
#endif

Screen 12, , 2
ScreenSet 1, 0

Dim As ULongInt MyFps = 100

Dim As String res = "N"
Dim As Boolean SkipImage = True
Dim As Boolean Restart = False
Dim As Boolean RemoveImageSkipped = False

Do
    Static As ULongInt l
    Static As Double dt
    Static As ULong fps
    Static As Double t
    Static As ULong averageFps
    Static As Double sumFps
    Static As Double averageDelay
    Static As Double sumDelay
    Static As Long N
    Static As Boolean ImageSkipped
    Static As Long ist = 0
    Static As Long mist = 0
    Dim As Double t1
    Dim As Double t2
    If (RemoveImageSkipped = False) Or (ImageSkipped = False) Then
        t = Timer
        Cls
        Print
        Color 15
        Select Case res
        Case "N"
            Print "                      NORMAL RESOLUTION"
        Case "H"
            Print "                      HIGH RESOLUTION (for Windows only)"
        End Select
        Print
        Print " Procedure : regulateLite( "; MyFPS & " [, " & SkipImage & " ])";
        If SkipImage = True Then
            Select Case RemoveImageSkipped
            Case True
                Print "      Images skipped : Removing"
            Case False
                Print "      Images skipped : Scrolling"
            End Select
        Else
                Print "     No image skipping"
        End If
        Print
        Color 11
        If mist = 0 Then
            Print Using " Applied true FPS     : ###         (average : ###)"; fps; averageFps
            Print Using "    Applied delay     : ###.### ms  (average : ###.### ms)"; dt; averageDelay;
        Else
            Print Using " Applied apparent FPS : ###         (average : ###)"; fps; averageFps
            Print Using "    Applied delay     : ###.### ms  (average : ###.### ms)"; dt; averageDelay;
        End If
        If SkipImage = True Then
            Print "  (not skipped image)"
        Else
            Print
        End If
        If SkipImage = True Then
            Select Case RemoveImageSkipped
            Case True
                Print "    Images removed    :  " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")
            Case False
                Print "    Images scrolled   :  " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")
            End Select
        Else
                Print "    No image skipped"
        End If
        Print
        Print
        Print
        Color 14
        #if defined(__FB_WIN32__)
        Print " <n> or <N> : Normal resolution"
        Print " <h> or <H> : High resolutiion"
        Print
        #endif
        Print " <+>        : Increase FPS"
        Print " <->        : Decrease FPS"
        Print
        Print " Optional parameter :"
        Print "    <t> or <T> : True for image skipping"
        If SkipImage = True Then
            Print "        <r> or <R> : Remove image skipped"
            Print "        <s> or <S> : Scroll image skipped"
        End If
        Print "    <f> or <F> : False for image skipping"
        Print "    <c> or <C> : Calibration phase"
        Print
        Print " <escape>   : Quit"
        Line (8, 144)-(631, 160), 7, B
        Line (8, 144)-(8 + l, 160), 7, BF
        Do
        #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
            t2 = Timer
            If t2 < t Then t -= 24 * 60 * 60
        Loop Until t2 >= t + 0.002
        #else
        Loop Until Timer >= t + 0.002
        #endif
        ScreenCopy
    End If
    l = (l + 1) Mod 624
    Dim As String s = UCase(Inkey)
    Select Case s
    Case "+"
        If MyFPS < 500 Then MyFPS += 1
    Case "-"
        If MyFPS > 10 Then MyFPS -= 1
    Case "T"
        SkipImage = True
    Case "F"
        SkipImage = False
    #if defined(__FB_WIN32__)
    Case "N"
        If res = "H" Then
            Restart = True
            res = "N"
        End If
    Case "H"
        If res = "N" Then
            Restart = True
            res = "H"
        End If
    #endif
    Case "C"
        Restart = True
    Case "R"
        If SkipImage = True Then RemoveImageSkipped = True
    Case "S"
        If SkipImage = True Then RemoveImageSkipped = False
    Case Chr(27)
        Exit Do
    End Select
    sumFps += fps
    sumDelay += dt
    N += 1
    If N >= fps / 2 Then
        averageFps = sumFps / N
        averageDelay = sumDelay / N
        N = 0
        sumFps = 0
        sumDelay = 0
    End If
    #if defined(__FB_WIN32__)
    If res = "H" Then
        _setTimer()
    End If
    #endif
    t1 = Timer
    fps = regulateLite(MyFPS, SkipImage, Restart, ImageSkipped)
    If ImageSkipped = False Then
        t2 = Timer
        #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
        If t2 < t1 Then t1 -= 24 * 60 * 60
        #endif
        dt = (t2 - t1) * 1000
    End If
    #if defined(__FB_WIN32__)
    If res = "H" Then
        _resetTimer()
    End If
    #endif
    Restart = False
    If ImageSkipped = True Then
        ist += 1
    Else
        mist = ist
        ist = 0
    End If
Loop

Test code for maximum performance emulation of 'regulateLite()'
Consider following notations:
- tt : execution time of the user tracing task
- tc : execution time of the user calculation task (+ other process)

An emulation code is written with two TIMER waiting loops (for 'tt' and 'tc'), and calling the real 'regulateLite()', which allows to estimate the magnitude order of the maximum accessible performance.
This emulation allows to validate the order of magnitude of the results from the 'Maximum performance estimation' paragraph above.

Use the runtime commands to adjust the 'tt' and 'tc' values and test the 3 configurations using the optional parameters.
This test code works with the resolution from the OS cycle.

Test code 4:
Dim As Integer tt = 10  ' (in milliseconds)
Dim As Integer tc =  5  ' (in milliseconds)

#include "regulateLite.bi"

Screen 12, , 2
ScreenSet 1, 0

Dim As ULongInt MyFps = 9999

Dim As String res = "N"
Dim As Boolean SkipImage = False
Dim As Boolean RemoveImageSkipped = False

Do
    Static As ULong fps
    Static As ULong averageFps
    Static As Double sumFps
    Static As Long N
    Static As Boolean ImageSkipped
    Static As Long ist = 0
    Static As Long mist = 0
    Static As ULongInt l
    Dim As Double t1
    Dim As Double t2
    If (RemoveImageSkipped = False) Or (ImageSkipped = False) Then
        t1 = Timer
        Cls
        Print
        Color 15
        Print "          MAXIMUM PERFORMANCE EMULATION of 'regulateLite()' regulation"
        Print
        Print
        Print Using " tt = ## ms   (execution time of the tracing task)"; tt
        Print Using " tc = ## ms   (execution time of the calculation task + other process)"; tc
        Print
        Print " Procedure : regulateLite( "; MyFPS & " [, " & SkipImage & " ])";
        If SkipImage = True Then
            Select Case RemoveImageSkipped
            Case True
                Print "      Images skipped : Removing"
            Case False
                Print "      Images skipped : Scrolling"
            End Select
        Else
                Print "     No image skipping"
        End If
        Print
        Print
        Color 11
        If mist = 0 Then
            Print Using " Applied true FPS max     :####         (average :####)"; fps; averageFps
        Else
            Print Using " Applied apparent FPS max :####         (average :####)"; fps; averageFps
        End If
        If SkipImage = True Then
            Select Case RemoveImageSkipped
            Case True
                Print "    Images removed        :  " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")
            Case False
                Print "    Images scrolled       :  " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")
            End Select
        Else
                Print "    No image skipped"
        End If
        Print
        Print
        Color 14
        Print " <+>        : Increment tt"
        Print " <->        : Decrement tt"
        Print
        Print " <i> or <I> : Increment tc"
        Print " <d> or <D> : Decrement tc"
        Print
        Print " Optional parameter :"
        Print "    <t> or <T> : True for image skipping"
        If SkipImage = True Then
            Print "        <r> or <R> : Remove image skipped"
            Print "        <s> or <S> : Scroll image skipped"
        End If
        Print "    <f> or <F> : False for image skipping"
        Print
        Print " <escape>   : Quit"
        Line (0, 197)-(639, 199), 3, B
        Line (0, 198)-(l, 198), 11
        Do
        #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
            t2 = Timer
            If t2 < t Then t -= 24 * 60 * 60
        Loop Until t2 >= t1 + tt / 1000
        #else
        Loop Until Timer >= t1 + tt / 1000
        #endif
        ScreenCopy
    End If
    t1 = Timer
    Dim As String s = UCase(Inkey)
    Select Case s
    Case "+"
        If tt < 30 Then tt += 1
    Case "-"
        If tt > 1 Then tt -= 1
    Case "I"
        If tc < 30 Then tc += 1
    Case "D"
        If tc > 1 Then tc -= 1
    Case "T"
        SkipImage = True
    Case "F"
        SkipImage = False
    Case "R"
        If SkipImage = True Then RemoveImageSkipped = True
    Case "S"
        If SkipImage = True Then RemoveImageSkipped = False
    Case Chr(27)
        Exit Do
    End Select
    sumFps += fps
    N += 1
    If N >= fps / 2 Then
        averageFps = sumFps / N
        N = 0
        sumFps = 0
    End If
    If ImageSkipped = True Then
        ist += 1
    Else
        mist = ist
        ist = 0
    End If
    l = (l + 1) Mod 640
    Do
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
        t2 = Timer
        If t2 < t Then t -= 24 * 60 * 60
    Loop Until t2 >= t1 + tc / 1000
    #else
    Loop Until Timer >= t1 + tc / 1000
    #endif
    fps = regulateLite(MyFPS, SkipImage, , ImageSkipped)
Loop

Note: As the applied FPS is at maximum limit, 'regulateLite()' periodically refreshes the value of tm (calibration at runtime), so the FPS values found can shift at each new calibration (slow rate).

4. Examples of graphic animation code

These two graphic animations highlight the interest of the image skipping feature of 'regulateLite()', with scrolling and above all removing of skipped images.

Graphic animation 1
This graphic animation (heavy CPU load) comes from dodicat (https://www.freebasic.net/forum/viewtopic.php?p=184009#p184009).
Some initializations and code lines may have been modified from the author's original in order to better highlight the regulation with 'regulateLite()' and its different configurations of use.

This code allows to modify:
- the value of the requested FPS (and it visualizes the applied FPS),
- the image skipping activation (false or true),
- and in case of image skipping activated, the mode for skipping images (scrolling or removing).
(the removing skipped images adds only three lines to the user code)
The full status of the image skipping feature is also visualized.

Graphic animation code 1:
'' Graphic animation from dodicat (https://www.freebasic.net/forum/viewtopic.php?p=184009#p184009)

#include "regulateLite.bi"

Type vector3d
    As Single x,y,z
End Type
'assignment macro
#define vct Type<vector3d>

 'macros
    #define map(a,b,x,c,d) ((d)-(c))*((x)-(a))/((b)-(a))+(c)
    #macro combsort(array,begin,finish,dot)
    Scope
        Var size=(finish),switch=0,j=0
        Dim As Single void=size
        Do
            void=void/1.3: If void<1 Then void=1
            switch=0
            For i As Integer =(begin) To size-void
                j=i+void
                If array(i)dot<array(j)dot Then
                    Swap array(i),array(j): switch=1
                End If
            Next
        Loop Until  switch =0 And void=1
    End Scope
    #endmacro

    Operator -(ByRef v1 As vector3d,ByRef v2 As vector3d) As vector3d
        Return Type<vector3d>(v1.x-v2.x,v1.y-v2.y,v1.z-v2.z)
    End Operator

    Operator + (ByRef v1 As vector3d,ByRef v2 As vector3d) As vector3d
    Return Type<vector3d>(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z)
    End Operator

    Function length(ByRef v1 As vector3d) As Single
        Return Sqr(v1.x*v1.x+v1.y*v1.y+v1.z*v1.z)
    End Function

    Function rotate3d(ByVal pivot As vector3d,ByVal pt As vector3d,ByVal Angle As vector3d, ByVal scale As vector3d=Type<vector3d>(1,1,1)) As vector3d
        #define cr 0.0174532925199433
        Angle=Type<vector3d>(Angle.x*cr,Angle.y*cr,Angle.z*cr)
        #macro Rotate(a1,a2,b1,b2,d)
        temp=Type<vector3d>((a1)*Cos(Angle.d)+(a2)*Sin(Angle.d),(b1)*Cos(Angle.d)+(b2)*Sin(Angle.d))
        #endmacro
        Dim As vector3d p=Type<vector3d>(pt.x-pivot.x,pt.y-pivot.y,pt.z-pivot.z)
        Dim As vector3d rot,temp
        Rotate(p.y,-p.z,p.z,p.y,x)'X
        rot.y=temp.x:rot.z=temp.y
        p.y = rot.y:p.z = rot.z
        Rotate(p.z,-p.x,p.x,p.z,y)'Y
        rot.z=temp.x:rot.x=temp.y
        p.x=rot.x
        Rotate(p.x,-p.y,p.y,p.x,z)'Z
        rot.x=temp.x:rot.y=temp.y
        Return Type<vector3d>((scale.x*rot.x+pivot.x),(scale.y*rot.y+pivot.y),(scale.z*rot.z+pivot.z))
    End Function

    Function apply_perspective(ByRef p As vector3d,ByRef eyepoint As vector3d) As vector3d
        Dim As Single   w=1+(p.z/eyepoint.z)
        If w=0 Then w=1e-20
        Return Type<vector3d>((p.x-eyepoint.x)/w+eyepoint.x,(p.y-eyepoint.y)/w+eyepoint.y,(p.z-eyepoint.z)/w+eyepoint.z)
    End Function
    '====================== End of rotator and perspective getter ======================================
    Dim Shared As Integer xres,yres
    ScreenRes 800, 640, 8
    Width 800 \ 8, 640 \ 16
    ScreenInfo xres,yres

    'two main arrays
    Dim Shared As vector3d rotated()
    ReDim Shared As vector3d array()
    'extra subs to regulate speed
    'four shapes
    Function create1(ByVal number As Integer) As Integer
        ReDim array(0)
        Dim As Integer count,stepper=10
        For x As Integer=xres/2-number To xres/2+number Step stepper
            For y As Integer=yres/2-number To yres/2+number Step stepper
                For z As Integer=-number To number Step stepper
                    count=count+1
                    ReDim Preserve array(1 To count)
                    array(UBound(array))=vct(x,y,z)
                Next z
            Next y
        Next x
        ReDim rotated(LBound(array) To UBound(array))
        Return 0
    End Function

    'variables
    Dim As vector3d centre=vct(xres/2,yres/2,0)
    Dim As vector3d eyepoint=vct(xres/2,yres/2,600)
    Dim As vector3d angle
    Dim As Long fps=150,rfps
    Dim As Boolean skipping=False,remove=False,skipped
    Dim As Long ist,mist
    Dim As ULong averageFps
    Dim As Double sumFps
    Dim As Long N
    Dim As vector3d disp=vct(xres/2,yres/2,0)
    Dim As Integer k,flag=1,border=.2*xres
    Dim As Single sx=1,kx=2,ky=1.9,kz=1.5
    create1(90)
    Do
        angle=angle+vct(.2,2,.1)
        With angle
            If .x>=360 Then .x-=360
            If .y>=360 Then .y-=360
            If .x>=360 Then .z-=360
        End With
        disp=disp+vct(kx,ky,0)
        If disp.x<border Then kx=-kx
        If disp.x>xres-border Then kx=-kx
        If disp.y<border Then ky=-ky
        If disp.y>yres-border Then ky=-ky
        If (remove = False) Or (skipped = False) Then
            ScreenLock
            Cls

            For n As Integer=1 To UBound(rotated)
                rotated(n)=rotate3d(centre,(array(n)),angle,vct(sx,sx,sx))
            Next n

            combsort(rotated,1,UBound(rotated),.z)

            For n As Integer=1 To UBound(rotated)
                Var dist=length(rotated(n)-centre)
                rotated(n)=apply_perspective(rotated(n),eyepoint)
                rotated(n)=rotated(n)+(disp-centre)

                Var col=map(0,200,dist,1,15)
                Var radius=map(-400,400,rotated(n).z,10,1)
                Circle (rotated(n).x,rotated(n).y),radius,col,,,,f
            Next n

            Draw String (16,16),"Requested FPS = " & Right("  " & fps, 3)
            Draw String (16,32),"Applied FPS   = " & Right("  " & rfps, 3) & "   (average = " & Right("  " & averageFps, 3) & ")"
            Draw String (16,48),"Status : " & _
                IIf(skipping = True, "Image skipping activation = true, with " & _
                IIf(remove = True, "removing images skipped = " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0"), _
                "scrolling images skipped = " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")), _
                "Image skipping activation = false")
            Draw String (16,80),"<+> : Increase FPS"
            Draw String (16,96),"<-> : Decrease FPS"
            Draw String (16,128),"<t> or <T> : True for image skipping activation"
            If Skipping = True Then
                Draw String (16,144),"   <s> or <S> : Scroll image skipped"
                Draw String (16,160),"   <r> or <R> : Remove image skipped"
                Draw String (16,176),"<f> or <F> : False for image skipping activation"
                Draw String (16,208),"<escape> : Quit"
            Else
                Draw String (16,144),"<f> or <F> : False for image skipping activation"
                Draw String (16,176),"<escape> : Quit"
            End If
            Draw String (544,608),"Graphic animation from dodicat"
            ScreenUnlock
        End If
        rfps = regulateLite(fps,skipping, ,skipped)
        If skipped = True Then
            ist += 1
        Else
            mist = ist
            ist = 0
        End If
        sumFps += rfps
        N += 1
        If N >= rfps / 2 Then
            averageFps = sumFps / N
            N = 0
            sumFps = 0
        End If
        Dim As String s = UCase(Inkey)
        Select Case s
        Case "+"
            If fps < 700 Then fps += 1
        Case "-"
            If fps > 10 Then fps -= 1
        Case "T"
            skipping = True
        Case "F"
            skipping = False
        Case "S"
            If skipping = True Then remove = False
        Case "R"
            If skipping = True Then remove = True
        Case Chr(27)
            Exit Do
        End Select
    Loop

Graphic animation 2
This graphic animation (heavy CPU load) comes from dodicat (https://www.freebasic.net/forum/viewtopic.php?p=195122#p195122).
Some initializations and code lines may have been modified from the author's original in order to better highlight the regulation with 'regulateLite()' and its different configurations of use.

This code allows to modify:
- the value of the requested FPS (and it visualizes the applied FPS),
- the image skipping activation (false or true),
- and in case of image skipping activated, the mode for skipping images (scrolling or removing).
(the removing skipped images adds only three lines to the user code)
The full status of the image skipping feature is also visualized.

Graphic animation code 2:
'' Graphic animation from dodicat (https://www.freebasic.net/forum/viewtopic.php?p=195122#p195122)

#include "regulateLite.bi"

Sub Thing(ByVal w As Integer=700, _
    ByVal h As Integer=600, _
    ByVal posx As Integer=400, _
    ByVal posy As Integer=300, _
    ByVal morph As Single=24, _
    ByVal aspect As Single=3, _
    ByVal grade As Single=6, _
    ByVal col As UInteger=RGBA(255,255,255,0))

    Dim As Single XStep = 1
    Dim As Single YStep = 1
    Dim As Single b=w*w
    Dim As Single y,m,n
    For x As Single = 0 To w Step XStep
        Dim As Single s=x*x
        Dim As Single p=Sqr(b-s)
        For i As Single = -P To P Step grade*YStep
            Dim As Single r = Sqr(s+i*i)/w
            Dim As Single Q = (R - 1) * Sin(morph*r)
            y=i/aspect+q*h
            If i = -p Then m=y:n=y
            If y > m Then m = y
            If y < n Then n = y
            If m=y OrElse n=y Then
                PSet(x/2+posx,-Y/2+posy),col/i
                PSet(-x/2+posx,-Y/2+posy),col/i
            End If
        Next
    Next
End Sub

'===========================================================

ScreenRes 800, 640, 32, 2
Width 800 \ 8, 640 \ 16
Color,RGB(0,0,50)
ScreenSet 1,0

Dim As Single Morph=0,k=1
Dim As Single aspect,counter,pi2=8*Atn(1)
Dim As Long fps=80,rfps
Dim As Boolean skipping=False,remove=False,skipped
Dim As Long ist,mist
Dim As ULong averageFps
Dim As Double sumFps
Dim As Long N

Do
    counter+=.1
    If counter>=pi2 Then counter=0
    aspect=3+Sin(counter)
    Morph+=.1*k
    If Morph>35 Then k=-k
    If Morph<-35 Then k=-k
    If (remove = False) Or (skipped = False) Then
        Cls
        Thing(800,500,400,300,Morph,aspect)
        Draw String (16,16),"Requested FPS = " & Right("  " & fps, 3)
        Draw String (16,32),"Applied FPS   = " & Right("  " & rfps, 3) & "   (average = " & Right("  " & averageFps, 3) & ")"
        Draw String (16,48),"Status : " & _
            IIf(skipping = True, "Image skipping activation = true, with " & _
            IIf(remove = True, "removing images skipped = " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0"), _
            "scrolling images skipped = " & IIf(mist > 0, Str(mist) & "/" & Str(mist + 1), "0")), _
            "Image skipping activation = false")
        Draw String (16,80),"<+> : Increase FPS"
        Draw String (16,96),"<-> : Decrease FPS"
        Draw String (16,128),"<t> or <T> : True for image skipping activation"
        If Skipping = True Then
            Draw String (16,144),"   <s> or <S> : Scroll image skipped"
            Draw String (16,160),"   <r> or <R> : Remove image skipped"
            Draw String (16,176),"<f> or <F> : False for image skipping activation"
            Draw String (16,208),"<escape> : Quit"
        Else
            Draw String (16,144),"<f> or <F> : False for image skipping activation"
            Draw String (16,176),"<escape> : Quit"
        End If
        Draw String (544,608),"Graphic animation from dodicat"
        Flip
    End If
    rfps = regulateLite(fps,skipping, ,skipped)
    If skipped = True Then
        ist += 1
    Else
        mist = ist
        ist = 0
    End If
    sumFps += rfps
    N += 1
    If N >= rfps / 2 Then
        averageFps = sumFps / N
        N = 0
        sumFps = 0
    End If
    Dim As String s = UCase(Inkey)
    Select Case s
    Case "+"
        If fps < 600 Then fps += 1
    Case "-"
        If fps > 10 Then fps -= 1
    Case "T"
        skipping = True
    Case "F"
        skipping = False
    Case "S"
        If skipping = True Then remove = False
    Case "R"
        If skipping = True Then remove = True
    Case Chr(27)
        Exit Do
    End Select
Loop

One can notice that for these 2 graphic animations, the mode of operation with 'image skipping' activated plus 'skipped image' removed by the user makes it possible to reach higher FPS, because in these two cases the user code has well separated the drawing part of the new image (the only part that should not be executed when the 'skipped image' tag is true) from the calculation part of the parameters for the new image (which must always be executed).

See also

  • Sleep
  • Timer
  • Graphics Mode Refresh and Anti-Flickering
  • Fine-grain procedure for waiting and in-loop procedure for fine-regulating FPS


Fine-grain procedure for waiting and in-loop procedure for fine-regulating FPS
- Improving SLEEP feature by using a fine-grain waiting procedure.
- Controlling FPS (Frames Per Second) by using an in-loop fine-regulating procedure.

Preamble:

The proper use of the SLEEP and TIMER keywords in user procedures requires a good knowledge of their limits and behaviors.

SLEEP keyword
Generally the SLEEP feature has not a good accuracy with respect to the delay requested and does not allow to produce very low waiting times.

The accuracy of SLEEP is variable depending on the OS cycle time:
Windows NT/2K/XP: 15 ms, 9x/Me: 50 ms, Linux: 10ms, DOS: 55 ms.
And using for the delay values lower than these accuracy values does not allow SLEEP to produce the corresponding wait values (always higher wait, and of the order of these values) except for the delay value '0'.

TIMER keyword
The accuracy of the time returned by TIMER is much greater than that generally of the SLEEP keyword to generate a wait:
for modern processors, sub-microsecond accuracy is expected.

On some platforms (except Windows and Linux), the return value of TIMER is reset at midnight, so if the start and end time are on either side of the reset point, this could cause unexpected behavior in some programs if this event is ignored.

1. Principles to overcome the penalizing behaviors of SLEEP and TIMER keywords

To implement effective time management procedures based on SLEEP and TIMER keywords, some bad behaviors as described above must be overcome.

Principle for producing a waiting feature with accurate delay
Using SLEEP does not hog the CPU, but provides poor accuracy and is not compatible with very small delays.
Using a loop while testing the TIMER value produces a good accuracy but hogs the CPU.

The principle is to chain:
- a large-grain delay using SLEEP for the first part of the requested delay,
- then a fine-grain delay for the rest of the time using a loop that tests the TIMER value.
A time threshold makes it possible to switch between these two types of operation.

A typical setting for this threshold corresponds to a value of 2 times the OS cycle period.
This value corresponds to a tradeoff between CPU load and delay accuracy.

Principle for compensating a possible reset of the TIMER return value
The detection of a possible reset (at midnight) of the TIMER between two calls is carried out by testing the sign of the difference between the two returned values from the TIMER.
If the second value is smaller than the first (due to a reset of the TIMER), a negative temporal compensation is applied on the first, corresponding to a day expressed in seconds.

2. Procedures body (without declarations) description

3 procedures are described here:
- 'delay()' procedure => to generate the wait for an accurate time (floating value expressed in ms)
- 'regulate()'procedure => to finely control and regulate a loop frequency (number of loops per second in integer value)
- 'framerate()' procedure => tool to measure the instantaneous loop frequency (number of loops per second in integer value)

The 2 first procedures are well suited to finely regulate and/or control the FPS (Frames Per Second) of an image refresh.
'delay()' is more for one-shot use every now and then, while 'regulate()' is more suited for inserting a fine delay into a loop to generally adjust its FPS.

'regulate()' and 'framerate()' are not thread safe because they each use a 'Static' variable.

Note: Like any waiting feature, it is strongly discouraged to use 'delay()' or 'regulate()' when the screen is locked, so no call from inside a [ScreenLock...ScreenUnlock] block.

'delay()' procedure body (improving SLEEP feature by using a fine-grain waiting)
The 'delay()' sub is useful for generating accurate wait functionality, more precise than that provided by the SLEEP keyword, and also compatible with very short wait times.
If the requested wait 'amount' is greater than the 'threshold' (+ 0.5), a first part of the requested waiting time ('amount' - 'threshold') is executed thanks to the SLEEP keyword (not hogging the CPU), and the remaining time is generated by testing in loop the accurate values of TIMER up to the target value.
An eventual resetting of the TIMER is also tested to compensate for it if it occurs.
Sub delay(ByVal amount As Single, ByVal threshold As ULong)
    '' 'amount'  : requested temporisation to apply, in milliseconds
    '' 'thresold' : fixing threshold for fine-grain temporisation (by waiting loop), in milliseconds
    Dim As Double t1 = Timer
    Dim As Double t2
    Dim As Double t3 = t1 + amount / 1000
    If amount > threshold + 0.5 Then Sleep amount - threshold, 1
    Do
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
        t2 = Timer
        If t2 < t1 Then t1 -= 24 * 60 * 60 : t3 -= 24 * 60 * 60
    Loop Until t2 >= t3
    #else
    Loop Until Timer >= t3
    #endif
End Sub

When the 'delay()' sub above is called from the "delay_regulate_framerate.bi" file to be included (see its definition below in paragraph 3.), a declaration added ahead the body of the sub defines its second parameter ('threshold') as an optional parameter and sets its default value.

'regulate()' procedure body (fine-controlling FPS by using an in-loop regulating)
The 'regulate()' function is built around the 'delay()' sub, but where the waiting time to be applied (if any remains) is deducted from the requested frame period (the inverse of the FPS) and the time already elapsed since the last call.
For debugging purposes, the 'regulate()' function returns the delay it applied (the one added to the initial loop). If the user does not wish to use this debug data, then they can call the function as a Sub quite simply.
If this delay value returned is very small, this means that it becomes difficult to reach the FPS setpoint. Otherwise, the fluctuation of the delay returned (assuming that the regulation is very accurate) represents the fluctuation of the user code executed in the loop from frame to frame.
Function regulate(ByVal MyFps As ULong, ByVal threshold As ULong) As Single
    '' 'MyFps' : requested FPS value, in frames per second
    '' function return : applied delay (for debug), in milliseconds
    '' 'thresold' : fixing threshold for fine-grain temporisation (by waiting loop), in milliseconds
    Static As Double t1
    Dim As Single tf = 1 / MyFps
    Dim As Double t2 = Timer
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
    If t2 < t1 Then t1 -= 24 * 60 * 60
    #endif
    Dim As Single dt = (tf - (t2 - t1)) * 1000
    delay(dt, threshold)
    t1 = Timer
    Return dt
End Function

When the 'regulate()' function above is called from the "delay_regulate_framerate.bi" file to be included (see its definition below in paragraph 3.), a declaration added ahead the body of the function defines its second parameter ('threshold') as an optional parameter and sets its default value.

'framerate()' procedure body (tool for FPS measuring)
For debugging purposes, an instantaneous measurement of FPS (by measuring the frame time and calculating the inverse) can be provided by calling from anywhere in the loop the following very simple tooling function 'framerate()'.
If 'framerate()' is placed right next to 'regulate()', this measures the intrinsic precision of the regulation.
If 'framerate()' is placed elsewhere in the program loop, it may be additionally impacted by program execution fluctuations from frame to frame.
Function framerate() As ULong
    '' function return : measured FPS value (for debug), in frames per second
    Static As Double t1
    Dim As Double t2 = Timer
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
    If t2 < t1 Then t1 -= 24 * 60 * 60
    #endif
    Dim As ULong tf = 1 / (t2 - t1)
    t1 = t2
    Return tf
End Function

Unlike 'delay()' and 'regulate()', the 'framerate()' function above can be used even when the screen is locked, so called even from inside a [ScreenLock...ScreenUnlock] block.

3. Complete source code to be included containing all the declarations and the procedure bodies

A header contains the declarations for the bodies of the 3 procedures described above.
For 'delay()' and 'regulate()', these allow to declare their second parameter as optional and to set the default values according to the platform used (Windows, Linux, DOS, or other).

File to be included: "delay_regulate_framerate.bi"
'  delay_regulate_framerate.bi

#if defined(__FB_WIN32__)
Declare Sub delay(ByVal amount As Single, ByVal threshold As ULong = 2 * 16)
Declare Function regulate(ByVal MyFps As ULong, ByVal threshold As ULong = 2 * 16) As Single
Declare Function _setTimer Lib "winmm" Alias "timeBeginPeriod"(ByVal As ULong = 1) As Long
Declare Function _resetTimer Lib "winmm" Alias "timeEndPeriod"(ByVal As ULong = 1) As Long
Declare Sub delayHR(ByVal amount As Single, ByVal threshold As ULong = 2 * 1)
Declare Function regulateHR(ByVal MyFps As ULong, ByVal threshold As ULong = 2 * 1) As Single
Sub delayHR(ByVal amount As Single, ByVal threshold As ULong)
    '' 'amount'  : requested temporisation to apply, in milliseconds
    '' 'thresold' : fixing threshold for fine-grain temporisation (by waiting loop), in milliseconds
    Dim As Double t1 = Timer
    Dim As Double t2
    Dim As Double t3 = t1 + amount / 1000
    If amount > threshold + 0.5 Then
        _setTimer()
        Sleep amount - threshold, 1
        _resetTimer()
    End If
    Do
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
        t2 = Timer
        If t2 < t1 Then t1 -= 24 * 60 * 60 : t3 -= 24 * 60 * 60
    Loop Until t2 >= t3
    #else
    Loop Until Timer >= t3
    #endif
End Sub
Function regulateHR(ByVal MyFps As ULong, ByVal threshold As ULong) As Single
    '' 'MyFps' : requested FPS value, in frames per second
    '' function return : applied delay (for debug), in milliseconds
    '' 'thresold' : fixing threshold for fine-grain temporisation (by waiting loop), in milliseconds
    Static As Double t1
    Dim As Single tf = 1 / MyFps
    Dim As Double t2 = Timer
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
    If t2 < t1 Then t1 -= 24 * 60 * 60
    #endif
    Dim As Single dt = (tf - (t2 - t1)) * 1000
    delayHR(dt, threshold)
    t1 = Timer
    Return dt
End Function
#elseif defined(__FB_LINUX__)
Declare Sub delay(ByVal amount As Single, ByVal threshold As ULong = 2 * 10)
Declare Function regulate(ByVal MyFps As ULong, ByVal threshold As ULong = 2 * 10) As Single
#elseif defined(__FB_DOS__)
Declare Sub delay(ByVal amount As Single, ByVal threshold As ULong = 2 * 55)
Declare Function regulate(ByVal MyFps As ULong, ByVal threshold As ULong = 2 * 55) As Single
#else
Declare Sub delay(ByVal amount As Single, ByVal threshold As ULong = 2 * 16)
Declare Function regulate(ByVal MyFps As ULong, ByVal ULong As Single = 2 * 16) As Single
#endif

Declare Function framerate() As ULong

'------------------------------------------------------------------------------

Sub delay(ByVal amount As Single, ByVal threshold As ULong)
    '' 'amount'  : requested temporisation to apply, in milliseconds
    '' 'thresold' : fixing threshold for fine-grain temporisation (by waiting loop), in milliseconds
    Dim As Double t1 = Timer
    Dim As Double t2
    Dim As Double t3 = t1 + amount / 1000
    If amount > threshold + 0.5 Then Sleep amount - threshold, 1
    Do
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
        t2 = Timer
        If t2 < t1 Then t1 -= 24 * 60 * 60 : t3 -= 24 * 60 * 60
    Loop Until t2 >= t3
    #else
    Loop Until Timer >= t3
    #endif
End Sub

Function regulate(ByVal MyFps As ULong, ByVal threshold As ULong) As Single
    '' 'MyFps' : requested FPS value, in frames per second
    '' function return : applied delay (for debug), in milliseconds
    '' 'thresold' : fixing threshold for fine-grain temporisation (by waiting loop), in milliseconds
    Static As Double t1
    Dim As Single tf = 1 / MyFps
    Dim As Double t2 = Timer
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
    If t2 < t1 Then t1 -= 24 * 60 * 60
    #endif
    Dim As Single dt = (tf - (t2 - t1)) * 1000
    delay(dt, threshold)
    t1 = Timer
    Return dt
End Function

Function framerate() As ULong
    '' function return : measured FPS value (for debug), in frames per second
    Static As Double t1
    Dim As Double t2 = Timer
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
    If t2 < t1 Then t1 -= 24 * 60 * 60
    #endif
    Dim As ULong tf = 1 / (t2 - t1)
    t1 = t2
    Return tf
End Function

This code ('delay_regulate_framerate.bi' file) should be included at the top of the user source program so that they can call the 3 procedures:
#include "delay_regulate_framerate.bi"

4. Examples of use

Two examples are available here:
- first example using the 'delay()' sub,
- second example using the 'regulate()' function (and also the 'framerate()' function as debug tool).

Example using 'delay()' sub
An example generating, thanks to 'delay()', 4 small waits of decreasing values:
100 ms, 10 ms, 1 ms, 0.1 ms
#include "delay_regulate_framerate.bi"

Dim As Double t
Dim As Single t0 = 100

For N As Integer = 1 To 4
    Print "Requested delay :"; t0; " ms"
    For I As Integer = 1 To 4
        t = Timer
        delay(t0)
        Print Using"  Measured delay : ###.### ms"; (Timer - t) * 1000
    Next I
    Print
    t0 /= 10
Next N

Sleep

The measured results seem fairly accurate throughout the full range of use.

Example using 'regulate()' function and 'framerate()' tooling function
An example controlling, thanks to 'regulate()', the FPS (Frames Per Second) refreshing of a graphic image:
FPS range available from 10 to 100 in steps of 1
#include "delay_regulate_framerate.bi"

Screen 12
Dim As ULong FPS = 60
Do
    Static As ULongInt l
    Static As Single dt
    ScreenLock
    Cls
    Color 11
    Print Using "Requested FPS : ###"; FPS
    Print
    Print Using "Applied delay : ###.### ms"; dt
    Print Using "Measured FPS  : ###"; framerate()
    Print
    Print
    Print
    Color 14
    Print "<+>      : Increase FPS"
    Print "<->      : Decrease FPS"
    Print "<Escape> : Quit"
    Line (0, 80)-(639, 96), 7, B
    Line (0, 80)-(l, 96), 7, BF
    ScreenUnlock
    l = (l + 1) Mod 640
    Dim As String s = Inkey
    Select Case s
    Case "+"
        If FPS < 100 Then FPS += 1
    Case "-"
        If FPS > 10 Then FPS -= 1
    Case Chr(27)
        Exit Do
    End Select
    dt = regulate(FPS)
Loop

The measured FPS is almost stable and tracks the requested FPS well.
To monitor the regulation, the delay added in the loop by 'regulate()' is also visualized.

5. Adjusting the commutation threshold (large-grain/fine-grain) for 'delay()' and 'regulate()' procedures depending on the real OS cycle period

If the user wants to refine or modify the default value of the large-grain/fine-grain commutation threshold for the 'delay()' or 'regulate()' procedures, just call it by explicitly specifying the second parameter (integer value given in ms).
But a typical setting for this threshold corresponds to a value of 2 times the OS cycle period (default values set in the file to be included: "delay_regulate_framerate.bi"):
- 2 times the OS cycle period is the recommended value => tradeoff between CPU load and delay accuracy.
- A higher threshold would promote the delay accuracy (increasing), but to the detriment of CPU load (increasing).
- A lower threshold would promote the CPU load (decreasing), but to the detriment of delay accuracy (decreasing).
- A downright null threshold would induce no CPU load as long as the required delay is greater than 0.5 ms. Only for a necessary required delay < 0.5 ms, the CPU load would not be minimized, but this small value already means that it will become difficult to reach the FPS setpoint.

Default values set in the header of the 'delay_regulate_framerate.bi' file to be included, according to the platform used:
- Windows (basic resolution OS cycle period = 16 ms) => threshold = 2 * 16 ms
- Windows (high resolution OS cycle period = 1 ms) => threshold = 2 * 1 ms (only used by 'delayHR() and 'regulateHR(), see paragraph 6 below).
- Linux (OS cycle period = 10 ms) => threshold = 2 * 10 ms.
- DOS (OS cycle period = 55 ms) => threshold = 2 * 55 ms.
- Other (OS cycle period per default = 16 ms) => threshold = 2 * 16 ms by default.
If the actual resolution of the OS cycle period is better than the default one above (depending on platform), the user can call 'delay()' and 'regulate()' by explicitly specifying the second parameter (two times this actual resolution value given in ms).

6. Windows platform

For the Windows platform only, the user can force temporarily the high resolution of the OS cycle period to 1 ms (instead of 16 ms) by calling 'delayHR()' or 'regulateHR()' instead.
The correct threshold (2 * 1 ms) is also temporarily applied.
This mainly reduces the CPU load because the threshold can be low.

'delayHR()' procedure included in the "delay_regulate_framerate.bi" file:
Sub delayHR(ByVal amount As Single, ByVal threshold As ULong)
    '' 'amount'  : requested temporisation to apply, in milliseconds
    '' 'thresold' : fixing threshold for fine-grain temporisation (by waiting loop), in milliseconds
    Dim As Double t1 = Timer
    Dim As Double t2
    Dim As Double t3 = t1 + amount / 1000
    If amount > threshold + 0.5 Then
        _setTimer()
        Sleep amount - threshold, 1
        _resetTimer()
    End If
    Do
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
        t2 = Timer
        If t2 < t1 Then t1 -= 24 * 60 * 60 : t3 -= 24 * 60 * 60
    Loop Until t2 >= t3
    #else
    Loop Until Timer >= t3
    #endif
End Sub

'regulateHR()' procedure included in the "delay_regulate_framerate.bi" file:
Function regulateHR(ByVal MyFps As ULong, ByVal threshold As ULong) As Single
    '' 'MyFps' : requested FPS value, in frames per second
    '' function return : applied delay (for debug), in milliseconds
    '' 'thresold' : fixing threshold for fine-grain temporisation (by waiting loop), in milliseconds
    Static As Double t1
    Dim As Single tf = 1 / MyFps
    Dim As Double t2 = Timer
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
    If t2 < t1 Then t1 -= 24 * 60 * 60
    #endif
    Dim As Single dt = (tf - (t2 - t1)) * 1000
    delayHR(dt, threshold)
    t1 = Timer
    Return dt
End Function

'regulateHR()' is not thread safe because it uses a 'Static' variable.

In any cases, a call to 'delay()' and 'regulate()' always runs in the default resolution, but with a threshold adapted to the basic resolution (2 * 16 ms).
So if the OS cycle period is already in high resolution (1 ms), it is necessary to use 'delayHR()' and 'regulateHR()' to also apply the correct corresponding threshold, otherwise this high resolution combined with a much too high threshold will not reduce CPU load compared to basic resolution (on the other hand the delay accuracy will be good).
Preferably use 'delay()' and 'regulate()' only when the OS cycle period is in basic resolution (16 ms).

Note:
For the Windows platform, the "delay_regulate_framerate.bi" file is only supported from Windows 2000 (because it refers to high resolution for OS cycle period).
For earlier Windows systems, remove all references to high resolution from the "delay_regulate_framerate.bi" file (last part of the '#if defined(__FB_WIN32__)'):
- remove declarations of '-setTimer', '_resetTimer', 'delayHR' and 'regulateHR',
- delete the body of the 'delayHR' and 'regulateHR' procedures.

7. Enhanced example of use

This example is improved compared to the second example of the paragraph 4.

It allows in addition to:
- Switch between the normal resolution and the high resolution of the OS cycle period for the Windows platform only.
- Modify the value of the optional parameter 'threshold'.
#include "delay_regulate_framerate.bi"

Screen 12, , 2
ScreenSet 1, 0

Dim As ULongInt MyFps = 100

Dim As String res = "N"
Dim As ULong thresholdNR = 32
Dim As ULong thresholdHR = 2

Do
    Static As ULongInt l
    Static As Double dt
    Static As ULong fps
    Static As Double t
    Static As ULong averageFps
    Static As Double sumFps
    Static As Double averageDelay
    Static As Double sumDelay
    Static As Long N
    Static As ULong fpsE
    Dim As Double t1
    Dim As Double t2
    t = Timer
    Cls
    Print
    Color 15
    Select Case res
    Case "N"
        Print "                      NORMAL RESOLUTION"
    Case "H"
        Print "                      HIGH RESOLUTION (for Windows only)"
    End Select
    Print
    Select Case res
    Case "N"
        Print " Procedure : regulate( " & MyFPS & " [, " & thresholdNR & " ])"
    Case "H"
        Print " Procedure : regulateHR( " & MyFPS & " [, " & thresholdHR & " ])"
    End Select
    Print
    Color 11
    Print Using " Measured FPS  : ###          (average : ###)"; fpsE; averageFps
    Print Using " Applied delay : ###.### ms   (average : ###.### ms)"; dt; averageDelay
    Print
    Print
    Print
    Color 14
    #if defined(__FB_WIN32__)
    Print " <n> or <N> : Normal resolution"
    Print " <h> or <H> : High resolutiion"
    Print
    #endif
    Print " <+>        : Increase FPS"
    Print " <->        : Decrease FPS"
    Print
    Print " Optional parameter :"
    Select Case res
    Case "N"
        Print "    <i> or <I> : Increase NR threshold"
        Print "    <d> or <D> : Decrease NR threasold"
        Draw String (320, 280), "(optimal value : 32)"
    Case "H"
        Print "    <i> or <I> : Increase HR threshold"
        Print "    <d> or <D> : Decrease HR threasold"
        Draw String (320, 280), "(optimal value : 2)"
    End Select
    Print
    Print " <escape>   : Quit"
    Line (8, 128)-(631, 144), 7, B
    Line (8, 128)-(8 + l, 144), 7, BF
    Do
    #if Not defined(__FB_WIN32__) And Not defined(__FB_LINUX__)
        t2 = Timer
        If t2 < t Then t -= 24 * 60 * 60
    Loop Until t2 >= t + 0.002
    #else
    Loop Until Timer >= t + 0.002
    #endif
    ScreenCopy
    l = (l + 1) Mod 624
    Dim As String s = UCase(Inkey)
    Select Case s
    Case "+"
        If MyFPS < 500 Then MyFPS += 1
    Case "-"
        If MyFPS > 10 Then MyFPS -= 1
    #if defined(__FB_WIN32__)
    Case "N"
        If res = "H" Then
            res = "N"
        End If
    Case "H"
        If res = "N" Then
            res = "H"
        End If
    #endif
    Case "I"
        Select Case res
        Case "N"
            If thresholdNR < 64 Then thresholdNR += 16
        Case "H"
            If thresholdHR < 4 Then thresholdHR += 1
        End Select
    Case "D"
        Select Case res
        Case "N"
            If thresholdNR > 0 Then thresholdNR -= 16
        Case "H"
            If thresholdHR > 0 Then thresholdHR -= 1
        End Select
    Case Chr(27)
        Exit Do
    End Select
    sumFps += fpsE
    sumDelay += dt
    N += 1
    If N >= MyFps / 2 Then
        averageFps = sumFps / N
        averageDelay = sumDelay / N
        N = 0
        sumFps = 0
        sumDelay = 0
    End If
    Select Case res
    Case "N"
        dt = regulate(MyFps, thresholdNR)
    #if defined(__FB_WIN32__)
    Case "H"
        dt = regulateHR(MyFps, thresholdHR)
    #endif
    End Select
    fpsE = framerate()
Loop

See also

  • Sleep
  • Timer
  • Graphics Mode Refresh and Anti-Flickering
  • Lite regulation function to be integrated into user loop for FPS control


Multi-Threading
The Multi-threading programming by using the built-in support provided by FreeBASIC.

Preamble:

Multi-threading programming allows to spawn concurrent process flow.
It is most effective on multi-processor or multi-core systems where the process flow can be scheduled to run on another processor/core thus gaining speed through parallel or distributed processing.

While most effective on a multi-processor or multi-core system, gains are also found on single-processor or single-core systems which exploit latency in I/O and other system functions that may halt process execution.
One thread may execute while another is waiting for I/O or some other system latency.

Threads require less overhead than spawning a new process because the system does not initialize a new system virtual memory space and environment for the process.
All threads within a process share the same address space.

Definitions

A multi-threaded process contains two or more parts that can run concurrently.
Each part of such a program is called a thread, and each thread defines a separate path of execution.

Difference between a process and a thread:
    • Process:
Intuitively, a process is nothing more than the program placed in memory or running with all the run-time environment (or all the resources) associated with it.
In other words, when your program is located on the hard disk of your machine it is always a program or a simple application, but once executed (or placed in memory) a set of resources (amount of memory space occupied, used processor registers and its status, the owner of the process, the permitted operations, ...) is assigned to it and it simply changes its name. It becomes a process.

So to simplify, process rhymes with program running.

    • Thread:
A thread is a portion or part of the process and the smallest sequential unit of instructions processed independently by the scheduler of the operating system.
It is simply an execution or processing unit composed of a set of instructions contained in the process. See it as the smallest task or operation within the process that is manageable by the scheduler.

A thread shares information such as a data segment, a code segment, ..., with its peer threads spawned by the same base-thread (see below), while it contains its own registers, stack, ....

Obviously, a process can be composed of one or more threads, everything will depend on the programmer.
In the case of a single-threaded process we speak of single-threading, in the opposite case of multi-threading.

Threads are the popular way to improve program through parallelism.

Multi-threaded programming

Single-threaded program executes one line of code at a time, then move onto the next line in sequential order (except for branches, function calls etc.).
This is generally the default behavior when programmer writes code.

There are a few main reasons to create threads, for example:
- You need to perform a task which takes a long time to complete but don’t want to make the user wait for it to finish. This is called task parallelisms. The purpose of creating threads is to ensure the application maintains responsiveness in the user interface, rather than to actually make the task run faster. Importantly, the task must be able to run largely independently of the implicit main thread for this design pattern to be useful.
- You have a complex task which can gain a performance advantage by being split up into chunks. Here you create several threads, each dedicated to one piece of the task. When all the pieces have completed, the main thread aggregates the sub-results into a final result. This pattern is called the parallel aggregation pattern. For this to work, portions of the total result of the task or calculation must be able to be calculated independently – the second part of the result cannot rely on the first part etc.

Multi-threaded program is executing from two or more locations in the program at the same time (or at least, with the illusion of running at the same time):
    • Sequencing overview:
When a program starts up, one implicit thread already begins running immediately.
This is usually called the "main" thread of the program, because it is the one that is executed when a program begins:
- The main thread is the thread from which programmer may spawn other “child” threads (which in turn may spawn other "sub-child" threads).
- Main thread and other threads run concurrently.
- Generally, a "parent" thread needs to wait for the result of a child thread, and so waits for the relevant thread to finish.
- Often, the main thread must be the last thread to finish execution because it performs various shutdown actions (as a "child" thread must also do so with respect to its eventual "sub-child" threads spawned).
- But other than that, the implicit main thread can also compete with all other threads explicitly spawned by programmer.

    • Threading on multi-processor or multi-core systems:
If the system has a multi-processor or multi-core capacity, creating a new thread may cause the new thread to be executed on an unused or least busy processor/core. If your application is multi-threaded, you can be mostly assured that it will automatically take advantage of multiple processors/cores.
In contrast, on a single processor/core, the threads will be time-sliced by the operating system in the same way processes are in a multi-tasking environment and all run on the single processor/core, so there is no effective performance gain.

Note:
- Creating for example 4 threads will not necessarily lead to a 4-fold performance increase. Depending on the algorithms in use, the amount of co-ordination between threads, overhead from accessing memory shared between threads and other factors, the speedup will be sub-linear.
- Algorithms which are well-designed for sequential (single thread) use are not necessarily optimized for multi-threaded use and may need to be significantly redesigned.

    • Thread safety:
Sometimes running several parts of the program at once can lead to unexpected behavior or even cause memory corruption and crashes if precautions are not taken into account.
If the same procedure can be executed in several threads simultaneously and independently without affecting each other, and additionally is designed to cooperate with other threads when accessing or changing their variables etc., the procedure is said to be thread safe.

Creating thread safe code is essentially the biggest problem and stumbling block for programmers getting to grips with multi-threaded programming. Even experienced programmers can come up across subtle and dangerous bugs which are very difficult to understand and debug.

    • Synchronization objects:
Synchronization objects allow threads to communicate with each other by signalling events of any kind. For example, indicating that a resource is being accessed and that other threads wanting to use the resource should wait to avoid a problems.
The most commonly used synchronization objects are "mutual exclusion" and "conditional variables" used in critical sections of a multi-threaded program, the code blocks that has to be executed as atomic actions (no concurrence with other threads executing similar actions).

Multi-threading is quite dangerous, the mistakes here are very costly. When a problem is found, it’s either hard or impossible to debug, but it’s not the worst.

The worst is when the code has a mistake, but it works correctly.
This can happen because once threads are used, the execution flow is not deterministic anymore.
How long does it take to create a new thread?
How many concurrent threads can there be?
How is the CPU time distributed among the threads?

It is advised to always set a 'Sleep x, 1' tempo in each 'For' loop of each thread (including the main thread), in order to release time-slice allowing the other threads to execute as well.

All these factors affect the overall execution. Writing multi-threaded code is about being prepared to anything, and that’s making it both dangerous and exciting.

Multi-threaded built-in support

The thread-safe runtime library is automatically used if the FreeBASIC built-in threading functions are used (so the '-mt' compiler option for the linker, is only needed if programmer wants to use his own threading routines).

The following pages of this section ("Multi-Threading") explain:
    • On the 'Threads' page:
the built-in procedures that create, and detach/wait-for the threads.
    • On the 'Mutual Exclusion' page:
the built-in procedures that create, lock/unlock, and destroy the mutexes.
    • On the 'Conditional Variables' page:
the built-in procedures that create, wait-for/signal, and destroy conditional variables.
    • On the 'Critical Sections' page:
the use of these built-in procedures in code blocks for handling the concurrency with other threads.
    • On the 'Critical Sections FAQ' page:
the "Critical Sections" related questions in multi-threading".

See also

  • Threads
  • Mutual Exclusion
  • Conditional Variables
  • Critical Sections
  • Critical Sections FAQ


Threads
The built-in procedures that create, and detach/wait-for the Threads.

Preamble:

When a program starts executing, it has one implicit thread running (this is already a full-fledged thread).
This "main" thread executes the main function of the program.
This same program can explicitly launch additional threads that will run in a competitive manner (both between them and with the main thread).

All threads (including the main thread) share the same memory, and thus can access the same global variables, same heap memory, same set of file descriptors, etc.
All these threads execute in parallel (i.e. using time slices, or if the system has several processors/cores, then really in parallel).

Creating a thread

There are two methods to create a thread:
- a "classic" method ThreadCreate that starts a specific user-defined subroutine type (which has obligatorily one single parameter, an 'Any Ptr' type pointer) in a separate execution thread, this first method being 100% safe,
- a "specific" method ThreadCall that should have start any user-defined subroutine type (which may have almost any number and any type of parameters) in a separate execution thread, but for the moment this second method is bugged.

Classic method (100% safe) - ThreadCreate
- Syntax:
Declare Function ThreadCreate ( ByVal procptr As Sub ( ByVal userdata As Any Ptr ), ByVal param As Any Ptr = 0, ByVal stack_size As Integer = 0 ) As Any Ptr
- Usage:
threadid = ThreadCreate ( procptr [, [ param ] [, stack_size ] ] )
- Parameters:
procptr
A pointer to the Sub intended to work as a thread. The sub must have the following signature (same parameters, same calling convention) to be compatible to procptr:
Declare Sub myThread ( ByVal userdata As Any Ptr )
userdata
The Any Ptr parameter of the Sub intended to work as a thread. FreeBASIC expects this parameter to be present, it must not be omitted!
param
Any Ptr argument that will be passed to the thread Sub pointed to by procptr through its userdata parameter. For example, this can be a pointer to a structure or an array containing various information for the thread sub to work with. If param is not given, 0 (zero) will be passed to the thread sub's userdata parameter instead.
stack_size
Optional number of bytes to reserve for this thread's stack.
- Return value:
The Any Ptr handle (threadid) to the thread created, or the null pointer (0) on failure.

Note:
- The userdata parameter can be unused in the body of the myThread sub, but declaring it as an Any Ptr parameter is always mandatory in the header. In this case, the corresponding param parameter can then be omitted when calling ThreadCreate, or else a needless argument can still be passed ('0' is commonly used because this value is directly compatible with any pointer). See the 1st and 2nd example.
- In the case where data must be passed to myThread, the Any Ptr param can be used to reference them, usually requiring a type conversion (implicit or explicit) into Any Ptr before passing it to ThreadCreate, and a reverse type conversion from Any Ptr in the body of myThread before using it. See the 3rd example.

Specific method (bugged) - ThreadCall
- Syntax:
Declare Function ThreadCall subname ( [paramlist] ) As Any Ptr
- Usage:
threadid = ThreadCall subname ( [paramlist] )
- Parameters:
subname
The name of a subroutine
paramlist
A list of parameters to pass to the subroutine, as with a normal sub call.
- Return value:
The Any Ptr handle (threadid) to the thread created, or the null pointer (0) on failure.

Warning:
Presently when ThreadCall involves to pass parameters to the thread, there is no guarantee that the corresponding data are still maintained after the end of the ThreadCall statement and this until the thread is really launched. That can cause bad behavior.
Therefore it is more advisable for the moment to use ThreadCreate (100% safe) instead.

Description
Several different threads can be created from the same Sub, with different passed arguments allowing to define the behavior of each.

There may be a long time between the end of the ThreadCreate/ThreadCall statement execution and the effective launch of the thread. So some statements following the ThreadCreate/ThreadCall statement can be executed before the actual launch of the thread.
Conversely, the thread body can start executing even before ThreadCreate/ThreadCall returns.

There is no guarantee about the order in which different threads execute, and no assumptions can be made about the order in which multiple created threads actually start executing (except in Linux).

By default, a thread is always created in the "joinable" state, ie its handle is accessible by its 'threadid' identifier.
If a thread ends in this state (joinable), the resources that were assigned to it will not be released automatically (but only at the main thread termination).

So a good habit is to always use one and only one of the following two methods for a thread to finish properly (see the paragraph below):
- either waiting for the thread end,
- otherwise detaching the thread (the thread becomes no longer joinable).

Each running thread can be identified by its handle which is unique among all running threads.
When a new thread is created, a handle to the thread is returned by the creation function.
When the thread runs code, ThreadSelf (from fbc version 1.08) allows to also return the handle of the thread (the implicit main thread also has its own unique handle).
ThreadSelf may be used to code some sort of TLS (Thread Local Storage) from the unique handle of each thread (including the implicit main thread). Therefore, a same global variable name may be defined, but with a stored value specific to the thread that accesses it. This allows generic procedures to be coded, but with parameters depending on the thread which executes them.

Waiting for a thread end, otherwise detaching a thread

There are two methods to induce a proper thread termination:
- either a first method ThreadWait where another thread waits for this thread to finish,
- otherwise a second method ThreadDetach where another thread detaches this thread and continues.

First method - ThreadWait
- Syntax:
Declare Sub ThreadWait ( ByVal threadid As Any Ptr )
- Usage:
ThreadWait ( threadid )
- Parameters:
threadid
Any Ptr handle of a thread created by ThreadCreate or ThreadCall
- Note:
In other language (as C++), the 'wait()' suffix is called 'join()'.

Second method - ThreadDetach
- Syntax:
Declare Sub ThreadDetach ( ByVal threadid As Any Ptr )
- Usage:
#include "fbthread.bi"
ThreadDetach ( threadid )
- Parameters:
threadid
Any Ptr handle of a thread created by ThreadCreate or ThreadCall

Description
After creating it, the programmer must make sure that the thread is either waited for (joined) otherwise detached, and this from another thread (including the main thread).

ThreadWait waits for a thread to complete its execution, and then release the resources associated with the thread handle. ThreadWait does not return until the thread (designated by the identifier) ends.
During the wait, the caller does not consume CPU time.
ThreadWait does not force the thread to end. If a thread requires a signal to force an end, a mechanism such as a shared flag must be used.

ThreadDetach releases resources associated with the thread handle. The thread handle will be destroyed by ThreadDetach and can no longer be used.
Unlike ThreadWait, ThreadDetach does not wait for the end of the thread, and its execution continues independently. All allocated resources will be freed once the thread is complete.

After ThreadWait or ThreadDetach is applied, the thread can no longer be joined, so the handle identifier value must not be used again in any of these commands.

Generally, before finishing, a 'parent' thread is waiting for the 'child' thread to finish.
But if the programmer chooses not to wait until the end of the thread (and necessarily detaches it only), then he must make sure that the data accessed by that thread is valid until the thread has finished with it. Otherwise, one may encounter a situation where the 'parent' thread holds pointers/references to local variables and the 'child' thread hasn't finished when the 'parent' thread finishes (the variables being destroyed because becoming out of scope).

Example

  • Small example proving multi-threading capacity:
- In a non multi-threading code, we cannot do anything else while waiting for the "Line Input" instruction to return.
- Multi-threading makes this possible.
Sub thread(ByVal p As Any Ptr)
    Do
        Static As String t
        Draw String (0, 16), t, 0
        t = Time
        Draw String (0, 16), t
        Do
            If *Cast(String Ptr, p) = "quit" Then Exit Sub
            Sleep 100, 1
        Loop Until Time <> t  ' to limit flickering
    Loop
End Sub

Dim As String s
Dim As Any Ptr pThread

Screen 12
Print "Time is updated by thread even while 'Line Input' is waiting for characters:";
Locate 4, 1
Print "Enter any character line to test, or 'quit' to finish:"
pThread = ThreadCreate(@thread, @s)
Do
    Locate 5, 1
    Print Space(Len(s));
    Locate 5, 1
    Line Input s
Loop Until s = "quit"
ThreadWait(pThread)

  • The 'Main' thread displays ten "M" characters while the 'Child' thread simultaneously displays ten "C" characters.
A 'Sleep x, 1' tempo is put in the 'For' loop of each thread (main thread and child thread) to release time-slice allowing the other thread to execute as well.
The tempos are set so that the execution time of the child thread 'For' loop is greater than the one of the main thread 'For' loop.

- Using ThreadCreate ..... ThreadWait:
Declare Sub thread (ByVal userdata As Any Ptr)

Dim As Any Ptr threadID  '' declaration of an 'Any Ptr' thread-ID of the child thread

Print """M"": from 'Main' thread"
Print """C"": from 'Child' thread"
Print

threadID = ThreadCreate(@thread)  '' creation of the child thread from the main thread

For I As Integer = 1 To 10  '' 'For' loop of the main thread
    Print "M";
    Sleep 150, 1
Next I

ThreadWait(threadID)  '' waiting for the child thread termination
Print
Print "'Child' thread finished"

Sleep

Sub thread (ByVal userdata As Any Ptr)  '' sub executed by the child thread
    For I As Integer = 1 To 10          '' 'For' loop of the child thread
        Print "C";
        Sleep 350, 1
    Next I
End Sub

Output example:
"M": from 'Main' thread
"C": from 'Child' thread

MCMMCMMCMMCMMMCCCCCC
'Child' thread finished
- Using ThreadCreate + ThreadDetach ..... (a global end-flag is added at the end of the child thread):
#include "fbthread.bi"

Declare Sub thread (ByVal userdata As Any Ptr)

Dim As Any Ptr threadID          '' declaration of an 'Any Ptr' thread-ID of the child thread
Dim Shared As Boolean threadEnd  '' declaration of a global 'Boolean' thread-End flag for the child thread

Print """M"": from 'Main' thread"
Print """C"": from 'Child' thread"
Print

threadID = ThreadCreate(@thread)  '' creation of the child thread from the main thread
ThreadDetach(threadID)            '' detaching the child thread

For I As Integer = 1 To 10  '' 'For' loop of the main thread
    Print "M";
    Sleep 150, 1
Next I

While threadEnd = False  '' waiting for the thread-End flag = 'True' from the child thread
Wend
Print
Print "'Child' thread finishing or finished"

Sleep

Sub thread (ByVal userdata As Any Ptr)  '' sub executed by the child thread
    For I As Integer = 1 To 10          '' 'For' loop of the child thread
        Print "C";
        Sleep 350, 1
    Next I
    threadEnd = True                    '' set the thrend-End flag to 'True'
End Sub

Output example:
"M": from 'Main' thread
"C": from 'Child' thread

MCMMCMMCMMCMMMCCCCCC
'Child' thread finishing or finished
  • A UDT for a multi-timer feature:
- using internally a joinable thread (ThreadCreate ..... ThreadWait) to sequence each timer,
- and call-backing externally a detached thread (ThreadCreate + ThreadDetach .....) as event for user.
The user event being triggered by a detached-thread callback from the timer loop, the requested time-out is only biased by the execution time of ThreadCreate + ThreadDetach (small time about constant) and not by a ThreadWait waiting-for:
'    Only 4 member procedures in public access (the first 3 returning 'true' if success, 'false' else):
'        - Function 'Set' to parametrize the considered timer (time-out in ms, pointer to user thread)
'        - Function 'Start' to start the considered timer
'        - Function 'Stop' to stop the considered timer (then, the considered timer may be re-Set and re-Start)
'        - Property 'Counter' to get the occurrence number of the timer
'    Plus an 'Any Ptr' in public access:
'        - Pointer field 'userdata' to point to any user data structure (optional usage)
'
'    Remark:
'        - Pointer to the considered timer instance is provided to the user thread procedure
'          in order to be able to factorize the treatment per timers group,
'          and to address the right user data structure if used (see example for usage).
'
'    In private access:
'        - 4 internal variables (time-out value, pointer to user thread, handle to timer thread, counter of occurence)
'        - Static timer thread

#include "fbthread.bi"
Type UDT_timer_thread
    Public:
        Declare Function Set (ByVal time_out As UInteger, _
                              ByVal timer_procedure As Sub(ByVal param As Any Ptr)) _
                              As Boolean
        Declare Function Start () As Boolean
        Declare Function Stop () As Boolean
        Declare Property Counter () As UInteger
        Dim As Any Ptr userdata
    Private:
        Dim As UInteger tempo
        Dim As Sub(ByVal param As Any Ptr) routine
        Dim As Any Ptr handle
        Dim As UInteger count
        Declare Static Sub thread (ByVal param As Any Ptr)
End Type

Function UDT_timer_thread.Set (ByVal time_out As UInteger, _
                              ByVal timer_procedure As Sub(ByVal param As Any Ptr)) _
                              As Boolean
    If timer_procedure > 0 And This.handle = 0 Then
        This.tempo = time_out
        This.routine = timer_procedure
        This.count = 0
        Function = True
    Else
        Function = False
    End If
End Function

Function UDT_timer_thread.Start () As Boolean
    If This.handle = 0 And This.routine > 0 Then
        This.handle = ThreadCreate(@UDT_timer_thread.thread, @This)
        Function = True
    Else
        Function = False
    End If
End Function

Function UDT_timer_thread.Stop () As Boolean
    If This.handle > 0 Then
        Dim p As Any Ptr = 0
        Swap p, This.handle
        ThreadWait(p)
        Function = True
    Else
        Function = False
    End If
End Function

Property UDT_timer_thread.Counter () As UInteger
    Return This.count
End Property

Static Sub UDT_timer_thread.thread (ByVal param As Any Ptr)
    Dim As UDT_timer_thread Ptr pu = param
    While pu->handle > 0
        Sleep pu->tempo, 1
        pu->count += 1
        If pu->routine > 0 Then
            Dim As Any Ptr p = ThreadCreate(Cast(Any Ptr, pu->routine), param)
            ThreadDetach(p)
        End If
    Wend
End Sub

'---------------------------------------------------------------------------------------------------

Dim As UInteger tempo1 = 950
Dim As UInteger tempo2 = 380
Dim As UDT_timer_thread timer1
    timer1.userdata = New String("        callback from timer #1 (" & tempo1 & "ms)")
Dim As UDT_timer_thread timer2
    timer2.userdata = New String("        callback from timer #2 (" & tempo2 & "ms)")

Sub User_thread (ByVal param As Any Ptr)
    Dim As UDT_timer_thread Ptr pu = param
    Dim As String Ptr ps = pu->userdata
    Print *ps & ", occurrence: " & pu->Counter
End Sub

Print "Beginning of test"
If timer1.Set(tempo1, @User_thread) Then
    Print "    timer #1 set OK"
    If timer1.Start Then
        Print "        timer #1 start OK"
    End If
End If
If timer2.Set(tempo2, @User_thread) Then
    Print "    timer #2 set OK"
    If timer2.Start Then
        Print "        timer #2 start OK"
    End If
End If
Print "    Then, any key to stop the timers"

Sleep

If timer1.stop Then
    Print "    timer #1 stop OK"
End If
If timer2.stop Then
    Print "    timer #2 stop OK"
End If
Sleep 500, 1
Print "End of test"
Delete Cast(String Ptr, timer1.userdata)
Delete Cast(String Ptr, timer2.userdata)

Sleep

Output example:
Beginning of test
    timer #1 set OK
        timer #1 start OK
    timer #2 set OK
        timer #2 start OK
    Then, any key to stop the timers
        callback from timer #2 (380ms), occurrence: 1
        callback from timer #2 (380ms), occurrence: 2
        callback from timer #1 (950ms), occurrence: 1
        callback from timer #2 (380ms), occurrence: 3
        callback from timer #2 (380ms), occurrence: 4
        callback from timer #1 (950ms), occurrence: 2
        callback from timer #2 (380ms), occurrence: 5
        callback from timer #2 (380ms), occurrence: 6
        callback from timer #2 (380ms), occurrence: 7
        callback from timer #1 (950ms), occurrence: 3
        callback from timer #2 (380ms), occurrence: 8
        callback from timer #2 (380ms), occurrence: 9
        callback from timer #1 (950ms), occurrence: 4
        callback from timer #2 (380ms), occurrence: 10
        callback from timer #2 (380ms), occurrence: 11
        callback from timer #2 (380ms), occurrence: 12
    timer #1 stop OK
        callback from timer #1 (950ms), occurrence: 5
    timer #2 stop OK
        callback from timer #2 (380ms), occurrence: 13
End of test
See also

  • ThreadCreate, ThreadCall
  • ThreadSelf
  • ThreadWait, ThreadDetach
  • Multi-Threading Overview
  • Mutual Exclusion
  • Conditional Variables
  • Critical Sections
  • Critical Sections FAQ


Mutual Exclusion
The built-in procedures that deal with mutexes for Mutual Exclusion (create, lock/unlock, and destroy the mutexes).

Preamble:

Mutual exclusion is the method of serializing access to shared resources. If programmer do not want a thread to be accessing a shared resource that is already in the process of being accessed by another thread, he can use a mutex.

Logically a mutex is a lock with only one key:
- If a thread wishes to access a shared resource, the thread must first gain the lock.
- Once it has the lock it may do what it wants with the shared resource without concerns of other threads accessing the shared resource because other threads will have to wait.
- Once the thread finishes using the shared resource, it unlocks the mutex, which allows other threads to access the shared resource.

A mutex is a lock that guarantees three things:
- Atomicity - Locking a mutex is an atomic operation, meaning that the operating system (or threads library) assures you that if you locked a mutex, no other thread succeeded in locking this mutex at the same time.
- Singularity - If a thread managed to lock a mutex, it is assured that no other thread will be able to lock the thread until the original thread releases the lock.
- Non-Busy Wait - If a thread attempts to lock a thread that was locked by a second thread, the first thread will be suspended (and will not consume any CPU resources) until the lock is freed by the second thread. At this time, the first thread will wake up and continue execution, having the mutex locked by it.

This is a protocol that serializes access to a shared resource.
Note that such a protocol must be enforced for resource a mutex is protecting across all threads that may touch the resource being protected (including the implicit main thread).

Mutex capability can be fully used even with a detached thread (only its handler is no longer accessible by its identifier).

Creating / Destructing a mutex

MutexCreate creates a mutex, returning a handle identifier which is to be referred to when destroying the mutex.
Mutexes created with MutexCreate should be destroyed when no longer needed or before the end of the program with MutexDestroy.

Create
- Syntax:
Declare Function MutexCreate ( ) As Any Ptr
- Usage:
mutexid = MutexCreate
- Return value:
The Any Ptr handle (mutexid) to the mutex created, or the null pointer (0) on failure.

Destroy
- Syntax:
Declare Sub MutexDestroy ( ByVal mutexid As Any Ptr )
- Usage:
MutexDestroy( mutexid )
- Parameter:
mutexid
The Any Ptr handle of the mutex to be destroyed.

Description
The call to MutexCreate must be executed before creating any thread using it (and before its use in the thread that creates it).
The call to MutexDestroy must be executed after any threads using the mutex are no longer in use (and after its last use in the thread that destroys it).

Locking / Unlocking a mutex

MutexLock/MutexUnlock allow to lock/unlock a mutex by referring to its handle identifier get at its creation.

Lock
- Syntax:
Declare Sub MutexLock ( ByVal mutexid As Any Ptr )
- Usage:
MutexLock( mutexid )
- Parameter:
mutexid
The Any Ptr handle of the mutex to be locked.

Unlock
- Syntax:
Declare Sub MutexUnlock ( ByVal mutexid As Any Ptr )
- Usage:
MutexUnlock( mutexid )
- Parameter:
mutexid
The Any Ptr handle of the mutex to be unlocked.

Description
The code between the lock and unlock calls to the mutex, is referred to as a critical section.
Minimizing time spent in the critical section allows for greater concurrency because it potentially reduces the amount of time other threads must wait to gain the lock.
Therefore, it is important for a thread programmer to minimize critical sections if possible.

Pseudo-code section

By applying all proper above rules:
'  Principle of mutual exclusion between 2 threads
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                                         Other Thread
'      MUTEXLOCK(mutexID) <----------------.    .---> MUTEXLOCK(mutexID)
'      Do_something_with_exclusion    .--- | ---'     Do_something_with_exclusion
'      MUTEXUNLOCK(mutexID) ----------'    '--------- MUTEXUNLOCK(mutexID)
Example

The first example on the previous page (Threads) is modified so that each thread no longer displays a single character ("M" or "C") but now a sequence of three characters ("[M]" from the main thread, "(C)" for the child thread).
The tempo in each thread loop has been cut into three chunks to help interleave the display between threads.

  • Using this example as is:
Declare Sub thread (ByVal userdata As Any Ptr)

Dim As Any Ptr threadID  '' declaration of an 'Any Ptr' thread-ID of the child thread

Print """[M]"": from 'Main' thread"
Print """(C)"": from 'Child' thread"
Print

threadID = ThreadCreate(@thread)  '' creation of the child thread from the main thread

For I As Integer = 1 To 10  '' 'For' loop of the main thread
    Print "[";
    Sleep 50, 1
    Print "M";
    Sleep 50, 1
    Print "]";
    Sleep 50, 1
Next I

ThreadWait(threadID)  '' waiting for the child thread termination
Print
Print "'Child' thread finished"

Sleep

Sub thread (ByVal userdata As Any Ptr)  '' sub executed by the child thread
    For I As Integer = 1 To 10          '' 'For' loop of the child thread
        Print "(";
        Sleep 50, 1
        Print "C";
        Sleep 50, 1
        Print ")";
        Sleep 250, 1
    Next I
End Sub

Output example:
"[M]": from 'Main' thread
"(C)": from 'Child' thread

[(CM])[M][(MC])[M][(MC])[M][(MC])[M][M(]C)[M](C)(C)(C)(C)(C)
'Child' thread finished
The display highlights an interlace in the sequence (of three characters) outputted from each thread.
In each thread, the code section displaying the three-character sequence should not be interrupted by the display of the other thread.
These two sections of code must therefore be considered as critical sections to be protected by a block [Mutexlock ... Mutexunlock].

  • Using mutual exclusion with a mutex:
'  Principle of mutual exclusion
'      Main thread                      XOR            Child thread
'  .....                                           .....
'  MUTEXLOCK(mutID)                                MUTEXLOCK(mutID)
'      Do_something_with_exclusion                     Do_something_with_exclusion
'  MUTEXUNLOCK(mutID)                              MUTEXUNLOCK(mutID)
'  .....                                           .....

Declare Sub thread (ByVal userdata As Any Ptr)

Dim As Any Ptr threadID      '' declaration of an 'Any Ptr' thread-ID of the child thread
Dim Shared As Any Ptr mutID  '' declaration of a global 'Any Ptr' mutex-ID
    mutID = MutexCreate      '' creation of the mutex

Print """[M]"": from 'Main' thread"
Print """(C)"": from 'Child' thread"
Print

threadID = ThreadCreate(@thread)  '' creation of the child thread from the main thread

For I As Integer = 1 To 10  '' 'For' loop of the main thread
    MutexLock(mutID)        '' set mutex locked at the beginning of the exclusive section
    Print "[";
    Sleep 50, 1
    Print "M";
    Sleep 50, 1
    Print "]";
    MutexUnlock(mutID)      '' set mutex unlocked at the end of the exclusive section
    Sleep 50, 1
Next I

ThreadWait(threadID)  '' waiting for the child thread termination
Print
Print "'Child' thread finished"

MutexDestroy(mutID)  '' destruction of the mutex

Sleep

Sub thread (ByVal userdata As Any Ptr)  '' sub executed by the child thread
    For I As Integer = 1 To 10          '' 'For' loop of the child thread
        MutexLock(mutID)                '' set mutex locked at the beginning of the exclusive section
        Print "(";
        Sleep 50, 1
        Print "C";
        Sleep 50, 1
        Print ")";
        MutexUnlock(mutID)              '' set mutex unlocked at the end of the exclusive section
        Sleep 250, 1
    Next I
End Sub

Output example:
"[M]": from 'Main' thread
"(C)": from 'Child' thread

[M](C)[M][M](C)[M][M](C)[M][M](C)[M][M](C)[M](C)(C)(C)(C)(C)
'Child' thread finished
So, display becomes coherent compared to each three-character sequence.

See also

  • MutexCreate, MutexDestroy
  • MutexLock, MutexUnlock
  • Multi-Threading Overview
  • Threads
  • Conditional Variables
  • Critical Sections
  • Critical Sections FAQ


Conditional Variables
The built-in procedures that create, wait-for/signal, and destroy Conditional Variables.

Preamble:

A condition variable is a mechanism that allows threads to wait (without wasting CPU cycles) for some even to occur.
Several threads may wait on a condition variable, until some other thread signals this condition variable (thus sending a notification).
At this time, one of the threads waiting on this condition variable wakes up, and can act on the event. It is possible to also wake up all threads waiting on this condition variable by using a broadcast method on this variable.

A condition variable does not provide locking. Thus, a mutex must be used along with the condition variable, to provide the necessary locking when accessing this condition variable.

Conditional variable capability (and also mutex capability) can be fully used even with a detached thread (only its handler is no longer accessible by its identifier).

Creating / Destructing a conditional variable

CondCreate creates a condition variable, returning a handle identifier which is to be referred to when destroying the condition variable.
Condition variables created with CondCreate should be destroyed when no longer needed or before the end of the program with CondDestroy (to avoid leaking resources in the OS).

Create
- Syntax:
Declare Function CondCreate ( ) As Any Ptr
- Usage:
conditionalid = CondCreate
- Return value:
The Any Ptr handle (conditionalid) to the conditional variable created, or the null pointer (0) on failure.

Destroy
- Syntax:
Declare Sub CondDestroy ( ByVal conditionalid As Any Ptr )
- Usage:
CondDestroy ( conditionalid )
- Parameter:
conditionalid
The Any Ptr handle of the conditional variable to be destroyed.

Description
CondDestroy destroys a condition variable, freeing the resources it might hold.
No threads must be waiting on the condition variable on entrance to CondDestroy.

Waiting-for/Signaling a conditional variable

The condition variable mechanism allows threads to suspend execution and relinquish the processor until some condition is true.

CondWait stops execution of the current thread until some condition becomes true.
CondSignal allows to restart one thread waiting on the conditional, while CondBroadcast allows to restart all threads waiting on the conditional.

Wait-for
- Syntax:
Declare Sub CondWait ( ByVal conditionalid As Any Ptr, ByVal mutexid As Any Ptr )
- Usage:
CondWait ( conditionalid, mutexid )
- Parameters:
conditionalid
The handle identifier of a conditional variable.
mutexid
The handle identifier of the mutex associated with this conditional variable, which must be locked when testing the condition and calling CondWait.

Signal
- Syntax:
Declare Sub CondSignal ( ByVal conditionalid As Any Ptr )
or
Declare Sub CondBroadcast ( ByVal conditionalid As Any Ptr )
- Usage:
CondSignal ( conditionalid )
or
CondBroadcast ( conditionalid )
- Parameter:
conditionalid
The Any Ptr handle of the conditional variable to be signaled.

Description
Once the conditional variable is created with CondCreate and the threads are started, one of more of them (including the implicit main thread executing main program) can be set in waiting for the conditional state by CondWait.
They will be stopped until another thread signals by CondSignal that one among the waiting threads can restart.
CondBroadcast can be used to restart all threads waiting for the conditional.

A condition variable must always be associated with a mutex to avoid a race condition created by one thread preparing to wait and another thread which may signal the condition before the first thread actually waits on it resulting in a deadlock. The thread will be perpetually waiting for a signal that is never sent. Any mutex can be used, there is no explicit link between the mutex and the condition variable.

When calling CondWait, mutex should already be locked (using the same mutex as one used with CondSignal or CondBroadcast).
The detailed sequence is the following:
- An atomic unlock of the mutex is applied before entering in waiting on the conditional variable in order to release other eventual threads using this mutex (this is why CondWait takes as arguments both the mutex and condition variable).
- The thread execution is suspended and does not consume any CPU time until the condition variable is signaled.
- When the condition variable becomes signaled, mutex is atomically re-locked.
- The thread execution can resume after the CondWait statement, but is suspended because of the locked mutex owned by the signal-caller.
- So, the signal-caller is then responsible for unlocking mutex in order that the called-thread completes the CondWait subroutine and that execution after the CondWait call can then really resume.

CondSignal restarts one thread waiting. It should be called after mutex is locked (using the same mutex as one used with CondWait):
- If no threads are waiting on the conditional, nothing happens (the signal is lost forever).
- if several are waiting, only one is restarted:
. It might be that a condition variable that has several threads waiting on it is signaled many times, and yet one of the threads waiting on it never awakened.
. This is because it is not known which of the waiting threads is awakened when the variable is signaled.
. It might be that the awakened thread quickly comes back to waiting on the condition variables, and gets awakened again when the variable is signaled again, and so on (no wake-up priority based on history is assured).
. It is up to the programmer to make sure this situation does not occur if it implies bad behavior.

When using CondBroadcast, this does not mean all threads are running together:
- Each of them tries to lock the mutex again before returning from their wait function.
- And thus they will start running one by one, each one locking the mutex, doing their work, and freeing the mutex before the next thread gets its chance to run.

Note:
- It is a good habit to use CondWait in a protected way against eventual spurious wake-ups.
- For that, CondWait is put within a loop for checking that a Boolean predicate is indeed true (predicate set true by another thread just before executing CondSignal or CondBroadcast) when the thread has finished waiting:
signal-caller:
predicate = True
Condsignal(conditionalid)
waiting-called:
While predicate <> True
Condwait(conditionalid, mutexid)
Wend
predicate = False
- The loop can terminate only when the predicate is true.
- On the other hand, if the predicate is already true before the thread reaches the loop, CondWait is downright skipped (allowing to take into account a case of CondSignal or CondBroadcast that would have been lost otherwise, because prematurely executed in a second thread before the first thread is really waiting for this).

Pseudo-codes for detailed coding by applying all proper above rules:
      • Pseudo-code sub-section for a thread:
- Pseudo-code for the "waiting-for" critical sub-section (with the links to/from critical section items of other thread):
'  Principle of mutual exclusion + waiting-for, for a thread sub-section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                                         Other Thread
'      MUTEXLOCK(mutexID) <-------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      .......
'      While booleanT <> True <---------------------- from booleanT = True
'          ( atomic_mutex_unlock(mutexID) ) --------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
'          CONDWAIT(conditionalID, mutexID) <-------- from CONDSIGNAL(conditionalID)
'          ( atomic_mutex_re-lock(mutexID) ) <------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      Wend
'      booleanT = False
'      .......
'      MUTEXUNLOCK(mutexID) ------------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
- Pseudo-code for the "signaling" critical sub-section (with the links to/from the critical section items of other thread):
'  Principle of mutual exclusion + signaling, for a thread sub-section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                              Other Thread
'      MUTEXLOCK(mutexID) <--------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      .......
'      booleanOT = True -----------------> to While booleanOT <> True
'      CONDSIGNAL(conditionalID) --------> to CONDWAIT(conditionalID, mutexID)
'      .......
'      MUTEXUNLOCK(mutexID) -------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
      • Pseudo-code section for a thread:
- Pseudo-code for the "signaling, then waiting-for" critical section (with the links to/from the critical section items of other thread):
'  Principle (1) of mutual exclusion + mutual synchronization, for a thread section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                                        Other Thread
'      MUTEXLOCK(mutexID) <------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      Do_something_1_with_exclusion
'      booleanOT = True ---------------------------> to While booleanOT <> True
'      CONDSIGNAL(conditionalID) ------------------> to CONDWAIT(conditionalID, mutexID)
'      While booleanT <> True <--------------------- from booleanT = True
'          ( atomic_mutex_unlock(mutexID) ) -------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
'          CONDWAIT(conditionalID, mutexID) <------- from CONDSIGNAL(conditionalID)
'          ( atomic_mutex_re-lock(mutexID) ) <------ from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      Wend
'      booleanT = False
'      Do_something_2_with_exclusion
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
- Pseudo-code for the "waiting-for, then signaling" critical section (with the links to/from critical section items of other thread):
'  Principle (2) of mutual exclusion + mutual synchronization, for a thread section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                                         Other Thread
'      MUTEXLOCK(mutexID) <-------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      Do_something_1_with_exclusion
'      While booleanT <> True <---------------------- from booleanT = True
'          ( atomic_mutex_unlock(mutexID) ) --------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
'          CONDWAIT(conditionalID, mutexID) <-------- from CONDSIGNAL(conditionalID)
'          ( atomic_mutex_re-lock(mutexID) ) <------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      Wend
'      booleanT = False
'      Do_something_2_with_exclusion
'      booleanOT = True ----------------------------> to While booleanOT <> True
'      CONDSIGNAL(conditionalID) -------------------> to CONDWAIT(conditionalID, mutexID)
'      MUTEXUNLOCK(mutexID) ------------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
      • Pseudo-code section example for each of 2 threads:
- Pseudo-code for the "signaling, then waiting-for" critical section of a main thread and for "waiting-for, then signaling" critical section of a child thread (with the links to/from critical section items of both threads):
'  Principle (example) of mutual exclusion + mutual synchronization sections, between 2 threads,
'  using the principle (1) for the main thread, and the principle (2) for the child thread
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Main Thread                                                                          Child Thread
'      MUTEXLOCK(mutexID) <-----------------------------.     .-------------.-------------> MUTEXLOCK(mutexID)
'      Do_something_1_with_exclusion                    |     |             |               Do_something_1_with_exclusion
'      boolC = True ----------------------------------- | --- | ----------- | ------------> While boolC <> True
'      CONDSIGNAL(conditionalID) ---------------------- | --- | ----------- | ---.    .-------- ( atomic_mutex_unlock(mutexID) )
'      While boolM <> True <--------------------------- | --- | ---------.  |    '--- | ------> CONDWAIT(conditionalID, mutexID)
'          ( atomic_mutex_unlock(mutexID) ) ------.     |     |          |  '-------- | ------> ( atomic_mutex_re-lock(mutexID) )
'          CONDWAIT(conditionalID, mutexID) <---- | --- | --- | ------.  |            |     Wend
'          ( atomic_mutex_re-lock(mutexID) ) <--- | ----'---- | ---.  |  |            |     boolC = False
'      Wend                                       |           |    |  |  |            |     Do_something_2_with_exclusion
'      boolM = False                              |           |    |  |  '----------- | --- boolM = True
'      Do_something_2_with_exclusion              |           |    |  '-------------- | --- CONDSIGNAL(conditionalID)
'      MUTEXUNLOCK(mutexID) ----------------------'-----------'    '------------------'---- MUTEXUNLOCK(mutexID)
Example

The second example on the previous page (Mutual Exclusion) is modified by using a conditional variable to exhibit a synchronization example.
So, the two sections of code, previously protected by a mutual exclusion block [Mutexlock ... Mutexunlock], are now synchronized in order that each thread displays one after the other its character sequence ("[M]" or "(C)").

In this example, the two threads critical sections have their sub-sections in an reverse order:
- main thread critical section: at first signaling, then waiting for,
- child thread critical section: at first waiting for, then signaling.
Therefore the main thread will always be the first to display its sequence:
'  Principle of mutual exclusion + mutual synchronization between 2 threads with loop
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Main Thread                                                                  Child Thread
'  .....                                                                          .....
'  Loop                                                                           Loop
'      MUTEXLOCK(mutID) <-----------------------------.     .-------------.---------> MUTEXLOCK(mutID)
'      Do_something_with_exclusion              .---- | --- | ----------- | --------> While boolC <> True
'      boolC = True ----------------------------'     |     |             |     .-------- ( atomic_mutex_unlock(mutID) )
'      CONDSIGNAL(condID) --------------------------- | --- | ----------- | --- | ------> CONDWAIT(condID, mutID)
'      While boolM <> True <------------------------- | --- | ---------.  '---- | ------> ( atomic_mutex_re-lock(mutID) )
'          ( atomic_mutex_unlock(mutID) ) ------.     |     |          |        |     Wend
'          CONDWAIT(condID, mutID) <----------- | --- | --- | ------.  |        |     boolC = False
'          ( atomic_mutex_re-lock(mutID) ) <--- | ----'---- | ---.  |  |        |     Do_something_with_exclusion
'      Wend                                     |           |    |  |  '------- | --- boolM = True
'      boolM = False                            |           |    |  '---------- | --- CONDSIGNAL(condID)
'      MUTEXUNLOCK(mutID) ----------------------'-----------'    '--------------'---- MUTEXUNLOCK(mutID)
'  End Loop                                                                       End Loop
'  .....                                                                          .....
Declare Sub thread (ByVal userdata As Any Ptr)

Dim As Any Ptr threadID             '' declaration of an 'Any Ptr' thread-ID of the child thread
Dim Shared As Any Ptr mutID         '' declaration of a global 'Any Ptr' mutex-ID
    mutID = MutexCreate             '' creation of the mutex
Dim Shared As Boolean boolM, boolC  '' declaration of 2 global 'Boolean' boolM and boolC as predicates
Dim Shared As Any Ptr condID        '' declaration of a global 'Any Ptr' conditional-ID
    condID = CondCreate             '' creation of the conditional

Print """[M]"": from 'Main' thread"
Print """(C)"": from 'Child' thread"
Print

threadID = ThreadCreate(@thread)  '' creation of the child thread from the main thread

For I As Integer = 1 To 10       '' 'For' loop of the main thread
    MutexLock(mutID)             '' set mutex locked at the beginning of the exclusive section
    Print "[";
    Sleep 50, 1
    Print "M";
    Sleep 50, 1
    Print "]";
    boolC = True                 '' set to 'True' the predicate for the child thread
    CondSignal(condID)           '' signal to the child thread
    While boolM <> True          '' test predicate from the child thread
        CondWait(condID, mutID)  '' wait for signal from the child thread
    Wend
    boolM = False                '' reset the predicate from the child thread
    MutexUnlock(mutID)           '' set mutex unlocked at the end of the exclusive section
    Sleep 50, 1
Next I

ThreadWait(threadID)  '' waiting for the child thread termination
Print
Print "'Child' thread finished"

MutexDestroy(mutID)  '' destruction of the mutex
CondDestroy(condID)  '' destruction of the conditional

Sleep

Sub thread (ByVal userdata As Any Ptr)  '' sub executed by the child thread
    For I As Integer = 1 To 10          '' 'For' loop of the child thread
        MutexLock(mutID)                '' set mutex locked at the beginning of the exclusive section
        While boolC <> True             '' test predicate from the main thread
            CondWait(condID, mutID)     '' wait for signal from the main thread
        Wend
        boolC = False                   '' reset the predicate from the main thread
        Print "(";
        Sleep 50, 1
        Print "C";
        Sleep 50, 1
        Print ")";
        boolM = True                    '' set to 'True' the predicate for the main thread
        CondSignal(condID)              '' signal to the child thread
        MutexUnlock(mutID)              '' set mutex unlocked at the end of the exclusive section
        Sleep 250, 1
    Next I
End Sub

Output:
"[M]": from 'Main' thread
"(C)": from 'Child' thread

[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)[M](C)
'Child' thread finished
See also

  • CondCreate, CondDestroy,
  • CondWait, CondSignal, CondBroadcast
  • Multi-Threading Overview
  • Threads
  • Mutual Exclusion
  • Critical Sections
  • Critical Sections FAQ


Critical Sections
The proper use of the built-in procedures in Critical Sections for handling the concurrency with other threads.

Preamble:

A critical section is a part of a multi-threading program that has to be executed as atomic actions (no concurrence with other threads executing similar actions):
- It is a piece of a program that requires mutual exclusion of access.
- Typically, the critical section accesses a shared resource, such as a data structure, a peripheral device, or a network connection, that does not allow multiple concurrent accesses.

When a program starts up, one thread already begins running immediately. This is usually called the "main" thread of the program, because it is the one that is executed when a program begins:
- It is the thread from which user may spawn other "child" threads (which in turn may spawn other "sub-child" threads).
- Often, it must be the last thread to finish execution because it performs various shutdown actions (as a "child" thread must also do so with respect to its eventual "sub-child" threads spawned).
- But other than that, it can also compete (with its own critical sections) with all other threads explicitly spawned by user.

Basic algorithms

Using the built-in procedures provided by FreeBASIC, the method to ensure exclusive use of a critical section may be designed in a algorithm either asynchronous or synchronous, which applies to the threads.

  • Basic algorithm for an asynchronous method using a mutex:
By getting the mutex locking, the thread can take the exclusive control to access the shared resource.
When shared resource access is ended, the thread unlocks the mutex.

Algorithm:
'   Mutexlock
'   |
'   Critical section of code
'   |
'   Mutexunlock
  • Basic algorithm for a synchronous method using a condwait then a condsignal/condbroadcast (and mutex):
The thread waits for a Boolean predicate is indeed true and also a condition signal (condwait) before executing its critical section.
When shared resource access is ended, the thread sets another Boolean predicate then sends a condition signal to other thread(s) (condsignal or condbroadcast).

Algorithm:
'   Mutexlock
'   |
'   While my_predicate <> True
'   |   Condwait
'   Wend
'   my_predicate = False
'   |
'   Critical section of code
'   |
'   other_predicate = True
'   Condsignal or Condbroadcast
'   |
'   Mutexunlock
Similar algorithm with waiting-for, then signaling are put in reverse order:
'   Mutexlock
'   |
'   Critical section of code
'   |
'   other_predicate = True
'   Condsignal or Condbroadcast
'   |
'   While my_predicate <> True
'   |   Condwait
'   Wend
'   my_predicate = False
'   |
'   Mutexunlock
Examples

In the two following examples, the shared resource is the input/output display device:
- Print its counter for each of 6 user threads (and read the flag 'quit').
- Catching a key-press (any one) for the main thread (and if yes, set the flag 'quit' to 'true').

The outputting procedure ('Sub Counter()') has voluntarily a tempo between cursor positioning and printing, and also a repositioning of text cursor at middle of line before ending, in order to thoroughly check that there is no overlap between the critical sections executions (at opposite, one can see the result by removing some code dedicated to mutual exclusion processing).
A different tempo value is set at the end of each thread loop (from smaller to bigger).
A structure (UDT) groups all variables necessary to the threads. A pointer to each UDT instance is passed to each thread at its creation phase (with threadcreate).

  • Asynchronous method example using one mutex for all threads:
' User thread algorithm (same principle for the main thread):
'
'   Do
'   |
'   |   Mutexlock
'   |   |
'   |   Critical section of code
'   |   |
'   |   Mutexunlock
'   |   |
'   |   Sleep my_tempo, 1
'   |
'   Loop Until quit = true
'
' There is no any advantage or disadvantage between threads for running their critical sections.
Type UDT
    Dim As Integer number
    Dim As Integer tempo
    Dim As Any Ptr pThread
    Dim As ULongInt count
    Static As Any Ptr pMutex
    Static As Integer numberMax
    Static As Integer quit
End Type
Dim As Any Ptr UDT.pMutex
Dim As Integer UDT.numberMax
Dim As Integer UDT.quit

Sub Counter (ByVal pt As UDT Ptr)
    With *pt
        Locate .number, .number, 0
        Sleep 5, 1
        .count += 1
        Print .count;
        Locate .number, 30 + .number, 0
    End With
End Sub

Sub Thread (ByVal p As Any Ptr)
    Dim As Integer myquit
    Dim As UDT Ptr pUDT = p
    With *pUDT
        Do
            MutexLock(.pMutex)
            Counter(pUDT)
            myquit = .quit
            MutexUnlock(.pMutex)
            Sleep .tempo, 1
        Loop Until myquit = 1
    End With
End Sub

UDT.numberMax = 6
Dim As UDT u(0 To UDT.numberMax)
For I As Integer = 0 To UDT.numberMax
    u(I).number = i
    u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
Next I
UDT.pMutex = MutexCreate

Dim As Single t = Timer
For I As Integer = 1 To UDT.numberMax
    u(I).pThread = ThreadCreate(@Thread, @u(I))
Next I

Dim As String s
Do
    MutexLock(UDT.pMutex)
    s = Inkey
    If s <> "" Then
        UDT.quit = 1
    End If
    MutexUnlock(UDT.pMutex)
    Sleep u(0).tempo, 1
Loop Until s <> ""

For I As Integer = 1 To UDT.numberMax
    ThreadWait(u(I).pThread)
Next I
t = Timer - t

MutexDestroy(UDT.pMutex)
Dim As ULongInt c
For I As Integer = 1 To UDT.numberMax
    c += u(I).count
Next I
Locate UDT.numberMax+2, 1
Print CULngInt(c / t) & " increments per second"

Sleep

Output example:
'   159
'    127
'     105
'      85
'       78
'        71
'
'   63 increments per second
Different result values because asynchronous counting (decreasing count values because increasing tempo values).

  • Synchronous method example using a condwait then a condbroadcast (and one mutex) for all threads:
' User thread algorithm (same principle for the main thread):
'
'   Do
'   |
'   |   Mutexlock
'   |   |
'   |   While thread_priority_number <> my_number
'   |   |   Condwait
'   |   Wend
'   |   |
'   |   Critical section of code
'   |   |
'   |   thread_priority_number = next thread_priority_number
'   |   Condbroadcast
'   |   |
'   |   Mutexunlock
'   |   |
'   |   Sleep my_tempo, 1
'   |
'   Loop Until quit = true
'
' The critical sections of the threads are run synchronously one after the other, with a predefined order.
Type UDT
    Dim As Integer number
    Dim As Integer tempo
    Dim As Any Ptr pThread
    Dim As ULongInt count
    Static As Integer threadPriorityNumber
    Static As Any Ptr pMutex
    Static As Any Ptr pCond
    Static As Integer numberMax
    Static As Integer quit
End Type
Dim As Integer UDT.threadPriorityNumber
Dim As Any Ptr UDT.pMutex
Dim As Any Ptr UDT.pCond
Dim As Integer UDT.numberMax
Dim As Integer UDT.quit

Sub Counter (ByVal pt As UDT Ptr)
    With *pt
        Locate .number, .number, 0
        Sleep 5, 1
        .count += 1
        Print .count;
        Locate .number, 30 + .number, 0
    End With
End Sub

Sub Thread (ByVal p As Any Ptr)
    Dim As Integer myquit
    Dim As UDT Ptr pUDT = p
    With *pUDT
        Do
            MutexLock(.pMutex)
            While .threadPriorityNumber <> .number  '' synchronous condwait for expected condition
                CondWait(.pCond, .pMutex)
            Wend
            Counter(pUDT)
            myquit = .quit
            .threadPriorityNumber = (.threadPriorityNumber + 1) Mod (.numberMax + 1)
            CondBroadcast(.pCond)
            MutexUnlock(.pMutex)
            Sleep .tempo, 1
        Loop Until myquit = 1
    End With
End Sub

UDT.numberMax = 6
Dim As UDT u(0 To UDT.numberMax)
For I As Integer = 0 To UDT.numberMax
    u(I).number = i
    u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
Next I
UDT.pMutex = MutexCreate
UDT.PCond = CondCreate

Dim As Single t = Timer
For I As Integer = 1 To UDT.numberMax
    u(I).pThread = ThreadCreate(@Thread, @u(I))
Next I

Dim As String s
Do
    MutexLock(UDT.pMutex)
    While UDT.threadPriorityNumber <> u(0).number
        CondWait(UDT.pCond, UDT.pMutex)
    Wend
    s = Inkey
    If s <> "" Then
        UDT.quit = 1
    End If
    UDT.threadPriorityNumber = (UDT.threadPriorityNumber + 1) Mod (UDT.numberMax + 1)
    CondBroadcast(UDT.pCond)
    MutexUnlock(UDT.pMutex)
    Sleep u(0).tempo, 1
Loop Until s <> ""

For I As Integer = 1 To UDT.numberMax
    ThreadWait(u(I).pThread)
Next I
t = Timer - t

MutexDestroy(UDT.pMutex)
CondDestroy(UDT.pCond)
Dim As ULongInt c
For I As Integer = 1 To UDT.numberMax
    c += u(I).count
Next I
Locate UDT.numberMax+2, 1
Print CULngInt(c / t) & " increments per second"

Sleep

Output example:
'   116
'    116
'     116
'      116
'       116
'        116
'
'   51 increments per second
Same result values because synchronous counting (despite of increasing tempo values).

  • Weird synchronous algorithm using a mutex for each thread, by self lock and mutual unlock:
- When one thread has run its critical section, it unlocks the mutex of the next thread and attempts to re-obtain its own mutex.
- At initialization all mutexes are locked, except the mutex of the main thread.
' User thread (#N) algorithm (same principle for the main thread):
'
'   Do
'   |
'   |   Mutexlock(own thread mutex (#N))
'   |   |
'   |   Critical section of code
'   |   |
'   |   Mutexunlock(next thread mutex (#N+1))
'   |   |
'   |   Sleep tempo, 1
'   |
'   Loop Until quit = 1
Type UDT
    Dim As Integer number
    Dim As Integer tempo
    Dim As Any Ptr pThread
    Dim As ULongInt count
    Static As Any Ptr pMutex(Any)
    Static As Integer numberMax
    Static As Integer quit
End Type
Dim As Any Ptr UDT.pMutex(Any)
Dim As Integer UDT.numberMax
Dim As Integer UDT.quit

Sub Counter (ByVal pt As UDT Ptr)
    With *pt
        Locate .number, .number, 0
        Sleep 5, 1
        .count += 1
        Print .count;
        Locate .number, 30 + .number, 0
    End With
End Sub

Sub Thread (ByVal p As Any Ptr)
    Dim As Integer quit
    Dim As UDT Ptr pUDT = p
    With *pUDT
        Do
            MutexLock(.pMutex(.number))
            Counter(pUDT)
            quit = .quit
            MutexUnlock(.pMutex((.number + 1) Mod (UDT.numberMax + 1)))
            Sleep .tempo, 1
        Loop Until quit = 1
    End With
End Sub

UDT.numberMax = 6
ReDim UDT.pMutex(UDT.numberMax)
Dim As UDT u(0 To UDT.numberMax)
For I As Integer = 0 To UDT.numberMax
    u(I).number = i
    u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
    UDT.pMutex(I) = MutexCreate
    MutexLock(UDT.pMutex(I))
Next I
MutexUnlock(UDT.pMutex(u(0).number))

Dim As Single t = Timer
For I As Integer = 1 To UDT.numberMax
    u(I).pThread = ThreadCreate(@Thread, @u(I))
Next I

Dim As String s
Do
    MutexLock(UDT.pMutex(u(0).number))
        s = Inkey
        If s <> "" Then
            UDT.quit = 1
        End If
    MutexUnlock(UDT.pMutex((u(0).number + 1) Mod (UDT.numberMax + 1)))
    Sleep u(0).tempo, 1
Loop Until s <> ""

For I As Integer = 1 To UDT.numberMax
    ThreadWait(u(I).pThread)
Next I
t = Timer - t

For I As Integer = 0 To UDT.numberMax
    MutexDestroy(UDT.pMutex(I))
Next I
Dim As ULongInt c
For I As Integer = 1 To UDT.numberMax
    c += u(I).count
Next I
Locate UDT.numberMax+2, 1
Print CULngInt(c / t) & " increments per second"

Sleep

Output example:
'   102
'    102
'     102
'      102
'       102
'        102
'
'   51 increments per second
Same result values because synchronous counting (despite of increasing tempo values).

See also

  • Multi-Threading Overview
  • Threads
  • Mutual Exclusion
  • Conditional Variables
  • Critical Sections FAQ
  • Emulate a TLS (Thread Local Storage) and a TP (Thread Pooling) feature


Critical Sections FAQ
The "Critical Sections" related questions in multi-threading.

'Critical sections' related questions

 1. When is it not mandatory to protect by a mutex one shared variable between several threads?
2. What is the chronology of code execution of 2 critical sections (with a mutex locking and a conditional variable signaling) that compete between 2 threads?
3. What happens if calling 'Condsignal()' or 'Condbroadcast()' without mutex locked?
4. Why is it mandatory to put 'Condwait' within a 'While...Wend' loop for checking a Boolean predicate (set by other thread before activate 'Condsignal' or 'Condbroadcast', and reset after the 'Wend')?
4.1. Is 'Condwait' (and 'Condsignal' or 'Condbroadcast') still useful when there is already a 'While' loop for checking a Boolean predicate set by other thread?
5. How to implement a user input-line function fully thread-safe?
6. How to use 'Screenlock' with multi-threading?
7. How to use 'video paging (double buffering or page flipping)' with multi-threading?
8. How to use the FB runtime library for multi-threaded applications (gfxlib2) with multi-threading?
9. How to use console statements and keyboard inputs with multi-threading?
10. Is it better to take precautions when using the keyword 'Sleep' in threads?
11. Can all tools to handle multi-threading be encapsulated in a base Class (that the user extends with a derived Type for his own implementing)?
12. What is the execution delay of the code of a thread after the thread is created by 'ThreadCreate'?
13. What is the synchronization latency when synchronizing threads either by mutual exclusions or by conditional variables?
14. What happens when multiple threads are waiting on the same condition variable?
15. How to optimize sequencing of successive user tasks executed by threading?
16. Why is multi-threading performance penalized by many shared memory accesses (even more in writing mode)?
17. Why is multi-threading performance heavily penalized by many manipulation of var-len strings and var-len arrays?

'Critical sections' related questions

1. When is it not mandatory to protect by a mutex one shared variable between several threads?

When accessing to shared variables between several threads, all their accesses must be generally put inside blocks Mutexlock...Mutexunlock, in all threads:
- When the shared variable is only one simple predefined numeric type of size <= sizeof(integer) (only one assembler instruction for access), the mutex use may be not mandatory.
- But if this is for example one shared variable LongInt with a win32 compilation, it is advised here to use a mutex (otherwise the reading phase by a thread may be interlaced with the writing phase of another thread).

That is because to access a variable in memory (for reading or for writing), a processor uses its internal registers.
A N-bit processor has N-bit registers but none greater:
- So one only assembler instruction allows it to access a N-bit variable in memory.
- At opposite, to access a 2N-bit variable, it must use 2 assembler instructions.
- If between these two assembler instructions (for writing), another thread accesses this same variable (for reading), the got value may be incoherent (N-bit highest and N-bit lowest incoherent together).

This behavior can be checked with a graphic program using two threads and a shared LongInt (64-bit) without mutex:
- by compiling in 32-bit, many read values are incoherent.
- by compiling in 64-bit, no read value is incoherent.

Compile the below test program:
- in 32-bit => Many erroneous points not on the circle but anywhere in the square containing the circle. If you uncomment the four lines 37/39/58/60 to activate the mutex, then all the got points are now on the circle only.
- in 64-bit => All points are valid, on the circle only, even if the mutex is not activated.
'   - The "user-defined thread" computes the points coordinates on a circle,
'     and write those in a LongInt (32-bit & 32-bit = 64-bit)
'   - The "main thread" plots the points from the LongInt value.
'
'   Behavior:
'      - The first point must be pre-determined.
'      - Nothing prevents that a same calculated point could be plotted several times
'      (depends on execution times of the loops between main thread and user thread).
'      - Nothing prevents that a calculated point could be not plotted
'      (same remark on the loop times).
'
'   Remark:
'      Voluntarily, there is no Sleep in the loop of each thread (normally strongly discouraged),
'      but this is just in this special case to amplify the behavior effects to observe.

Union Point2D
    Dim As LongInt xy
    Type
        Dim As Long y
        Dim As Long x
    End Type
End Union

Dim As Any Ptr handle
Dim Shared As Any Ptr mutex
Dim Shared As Integer quit

Sub Thread (ByVal param As Any Ptr)
    Const pi As Single = 4 * Atn(1)
    Dim As Point2D Ptr p = param
    Do
        Dim As Point2D P2D0
        Dim As Single teta = 2 * pi * Rnd
        P2D0.x = 320 + 200 * Cos(teta)
        P2D0.y = 240 + 200 * Sin(teta)
'        Mutexlock(mutex)
        p->xy = P2D0.xy
'        Mutexunlock(mutex)
'        Sleep 5, 1
    Loop Until quit = 1
End Sub

Screen 12

Dim As Point2D P2D
P2D.x = 520
P2D.y = 240

mutex = MutexCreate
handle = ThreadCreate(@Thread, @P2D)

Dim As Integer c

Do
    Dim As Point2D P2D0
'    Mutexlock(mutex)
    P2D0.xy = P2D.xy
'    Mutexunlock(mutex)
    PSet (P2D0.x, P2D0.y), c
    c = (c Mod 15) + 1
'    Sleep 5, 1
Loop Until Inkey <> ""

quit = 1
ThreadWait(handle)
MutexDestroy(mutex)

Back to top

2. What is the chronology of code execution of 2 critical sections (with a mutex locking and a conditional variable signaling) that compete between 2 threads?

Chronology for one thread signaling which occurs:
a) while another thread is waiting (within a While loop on predicate),
b) before another thread is waiting (within a While loop on predicate).
#define while_loop_on_predicate

Dim As Any Ptr handle
Dim Shared As Any Ptr mutex
Dim Shared As Any Ptr cond
Dim As Integer sleep0
Dim As Integer sleep1
#ifdef while_loop_on_predicate
Dim Shared As Integer ready
#endif

Sub Thread1 (ByVal param As Any Ptr)
    Sleep *Cast(Integer Ptr, param), 1
    MutexLock(mutex)
    Color 11 : Print "        Thread#1 locks the mutex"
    Color 11 : Print "        Thread#1 executes code with exclusion"
    #ifdef while_loop_on_predicate
    ready = 1
    #endif
    Color 11 : Print "        Thread#1 is signaling"
    CondSignal(cond)
    Color 11 : Print "        Thread#1 executes post-code with exclusion"
    Color 11 : Print "        Thread#1 unlocks the mutex"
    MutexUnlock(mutex)
End Sub

Sub Thread0 (ByVal param As Any Ptr)
    Sleep *Cast(Integer Ptr, param), 1
    MutexLock(mutex)
    Color 10 : Print "    Thread#0 locks the mutex"
    Color 10 : Print "    Thread#0 executes pre-code with exclusion"
    #ifdef while_loop_on_predicate
    While ready <> 1
    #endif
        Color 10 : Print "    Thread#0 is waiting"
        CondWait(cond, mutex)
        Color 10 : Print "    Thread#0 is waked"
    #ifdef while_loop_on_predicate
    Wend
    #endif
    Color 10 : Print "    Thread#0 executes code with exclusion"
    #ifdef while_loop_on_predicate
    ready = 0
    #endif
    Color 10 : Print "    Thread#0 unlocks the mutex"
    MutexUnlock(mutex)
End Sub

mutex = MutexCreate
cond = CondCreate

sleep0 = 0
sleep1 = 1000
Color 7 : Print "Chronology for Thread#1 signaling while Thread#0 is waiting:"
handle = ThreadCreate(@Thread1, @sleep1)
Thread0(@sleep0)
ThreadWait(handle)
Color 7 : Print "Thread#1 finished": Print
Sleep 1000, 1

sleep0 = 1000
sleep1 = 0
Color 7 : Print "Chronology for Thread#1 signaling before Thread#0 is waiting:"
handle = ThreadCreate(@Thread1, @sleep1)
Thread0(@sleep0)
ThreadWait(handle)
Color 7 : Print "Thread#1 finished": Print

MutexDestroy(mutex)
CondDestroy(cond)
Sleep

Output part a - Chronology for Thread#1 signaling while Thread#0 is waiting:
Chronology for Thread#1 signaling while Thread#0 is waiting:
    Thread#0 locks the mutex
    Thread#0 executes pre-code with exclusion
    Thread#0 is waiting
        Thread#1 locks the mutex
        Thread#1 executes code with exclusion
        Thread#1 is signaling
        Thread#1 executes post-code with exclusion
        Thread#1 unlocks the mutex
    Thread#0 is waked
    Thread#0 executes code with exclusion
    Thread#0 unlocks the mutex
Thread#1 finished
Output part b - Chronology for Thread#1 signaling before Thread#0 is waiting:
Chronology for Thread#1 signaling before Thread#0 is waiting:
        Thread#1 locks the mutex
        Thread#1 executes code with exclusion
        Thread#1 is signaling
        Thread#1 executes post-code with exclusion
        Thread#1 unlocks the mutex
    Thread#0 locks the mutex
    Thread#0 executes pre-code with exclusion
    Thread#0 executes code with exclusion
    Thread#0 unlocks the mutex
Thread#1 finished
Note: If CondWait is not within a While loop on predicate (by putting in comment the first line of above program), one can check in the second case (thread#1 signaling before thread#0 waiting), that thread#0 remains blocked in its waiting phase (Ctrl-C to quit).

Back to top

3. What happens if calling 'Condsignal()' or 'Condbroadcast()' without mutex locked?

Referring to the example 2 on the Critical Sections, one takes this opportunity to recall that:
- The mutex must always be also locked while executing Condsignal() or Condbroadcast() to wake up a thread (it may be unlocked but only after Condsignal() or Condbroadcast()).
- If the mutex is not locked (or even if the mutex is unlocked only just before executing Condsignal() or Condbroadcast()), the behavior may become unpredictable (it may work or not, depending on the threads configuration and execution real time).

In the example 2 on the Critical Sections "Synchronous method example using a condwait then a condbroadcast (and a mutex) for all threads":
- If one at least Mutexunlock() is moved just before its Condbroadcast(), the program hangs very quickly.
- Although some users certify that the mutex can always be unlocked just before Condsignal() or Condbroadcast(), and others more cautious assert that one can do it only for a Condbroadcast(), experiment shows the opposite!

The general rule is that:
- The condition must not be signaled (by Condsignal() or Condbroadcast()) between the time a thread locks the mutex and the time it waits on the condition variable (CondWait()), otherwise it seems that it may damage the waiting queue of threads on that condition variable.
- Thus to avoid that and follow this rule, it is necessary that the mutex remains locked when the condition is signaled.

Back to top

4. Why is it mandatory to put 'Condwait' within a 'While...Wend' loop for checking a Boolean predicate (set by other thread before activate 'Condsignal' or 'Condbroadcast', and reset after the 'Wend')?

While predicate <> True
Condwait(conditionalid, mutexid)
Wend
predicate = False

In all documentations, it is highly advisable to do so, mainly justified to fight against eventual spurious wake-ups.

This is probably true, but it is also advisable to do so to avoid to loose a CondSignal (or CondBroadcast) if it is prematurely activated while the receiving thread is not yet waiting on CondWait (the signal is lost forever):
- In that case, the receiving thread has even not yet locked the mutex before that CondSignal (or CondBroadcast) is activated.
- So the predicate will already true before the receiving thread reaches the 'While...Wend' loop, inducing that CondWait is downright skipped, so avoiding a definitive blocking phenomenon.

Let two threads (thread #0 in main program, thread #1 in a user procedure, each that prints its number in a loop), having about the same execution time, and each one synchronizing the other in order to well interlace their numbers (by using one mutex, two condition variables and CondSignal/CondWait):
      • Without a 'While...Wend' loop on predicate, the program hangs quickly (Ctrl-C to quit):
'            Thread#0                           XOR + <==>                       Thread#1
'   .....                                                             .....
'   MutexLock(mut) <-------------------------.             .----------> MutexLock(mut)
'   ( atomic_mutex_unlock(mut) ) ------.     |             |            Do_something_with_exclusion
'   CondWait(cond#1, mut) <----------- | --- | ----------- | ---------- CondSignal(cond#1)
'   ( atomic_mutex_re-lock(mut) ) <--- | ----'----.        |     .----- ( atomic_mutex_unlock(mut) )
'   Do_something_with_exclusion        |     .--- | ------ | --- | ---> CondWait(cond#2, mut)
'   CondSignal(cond#2) --------------- | ----'    |    .---'---- | ---> ( atomic_mutex_re-lock(mut) )
'   Do_something_with_exclusion        |     .--- | ---'         |      Do_something_with_exclusion
'   MutexUnlock(mut) ------------------'-----'    '--------------'----- MutexUnlock(mut)
'   .....                                                               .....
'
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
Dim As Any Ptr handle
Dim Shared As Any Ptr mutex
Dim Shared As Any Ptr cond1
Dim Shared As Any Ptr cond2
Dim Shared As Integer quit

Sub Thread (ByVal param As Any Ptr)
    Do
        MutexLock(mutex)
        Print "1";
        CondSignal(cond1)
        CondWait(cond2, mutex)
        If quit = 1 Then
            MutexUnlock(mutex)
            Exit Do
        End If
        MutexUnlock(mutex)
        Sleep 1, 1
    Loop
End Sub

mutex = MutexCreate
cond1 = CondCreate
cond2 = CondCreate
handle = ThreadCreate(@Thread)

Do
    MutexLock(mutex)
    CondWait(cond1, mutex)
    Print "0";
    CondSignal(cond2)
    If Inkey <> "" Then
        quit = 1
        MutexUnlock(mutex)
        Exit Do
    End If
    MutexUnlock(mutex)
    Sleep 1, 1
Loop

ThreadWait(handle)
MutexDestroy(mutex)
CondDestroy(cond1)
CondDestroy(cond2)
Print

Sleep

      • With a 'While...Wend' loop on predicate around each CondWait, no blocking phenomenon:
'            Thread#0                                 XOR + <==>                            Thread#1
'   .....                                                                          .....
'   MutexLock(mut) <-----------------------------.                          .----> MutexLock(mut)
'   While bool#1 <> true <---------------------- | --------.                |      Do_something_with_exclusion
'       ( atomic_mutex_unlock(mut) ) ------.     |         '--------------- | ---- bool#1 = true
'       CondWait(cond#1, mut) <----------- | --- | ------------------------ | ---- CondSignal(cond#1)
'       ( atomic_mutex_re-lock(mut) ) <--- | ----'----.    .--------------- | ---> While bool#2 <> true
'   Wend                                   |          |    |          .---- | -------- ( atomic_mutex_unlock(mut) )
'   bool#1 = false                .------- | -------- | ---'  .------ | --- |--------> CondWait(cond#2, mut)
'   Do_something_with_exclusion   |   .--- | -------- | ------'  .--- | ----'--------> ( atomic_mutex_re-lock(mut) )
'   bool#2 = true ----------------'   |    |     .--- | ---------'    |            Wend
'   CondSignal(cond#2) ---------------'    |     |    |               |            bool#2 = false
'   Do_something_with_exclusion            |     |    |               |            Do_something_with_exclusion
'   MutexUnlock(mut) ----------------------'-----'    '---------------'----------- MutexUnlock(mut)
'   .....                                                                          .....
'
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
Dim As Any Ptr handle
Dim Shared As Any Ptr mutex
Dim Shared As Any Ptr cond1
Dim Shared As Any Ptr cond2
Dim Shared As Integer new1
Dim Shared As Integer new2
Dim Shared As Integer quit

Sub Thread (ByVal param As Any Ptr)
    Do
        MutexLock(mutex)
        Print "1";
        new1 = 1
        CondSignal(cond1)
        While new2 <> 1
            CondWait(cond2, mutex)
        Wend
        new2 = 0
        If quit = 1 Then
            MutexUnlock(mutex)
            Exit Do
        End If
        MutexUnlock(mutex)
        Sleep 1, 1
    Loop
End Sub

mutex = MutexCreate
cond1 = CondCreate
cond2 = CondCreate
handle = ThreadCreate(@Thread)

Do
    MutexLock(mutex)
    While new1 <> 1
        CondWait(cond1, mutex)
    Wend
    new1 = 0
    Print "0";
    new2 = 1
    CondSignal(cond2)
    If Inkey <> "" Then
        quit = 1
        MutexUnlock(mutex)
        Exit Do
    End If
    MutexUnlock(mutex)
    Sleep 1, 1
Loop

ThreadWait(handle)
MutexDestroy(mutex)
CondDestroy(cond1)
CondDestroy(cond2)
Print

Sleep

4.1. Is 'Condwait' (and 'Condsignal' or 'Condbroadcast') still useful when there is already a 'While...Wend' loop for checking a Boolean predicate set by other thread?

(another question induced by the previous one)
- The recommended structure is as follows:
'  Principle of mutual exclusion + CONDWAIT in a While...Wend loop with predicate check, for a thread sub-section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'
'          Thread                                        Other Thread
'      MUTEXLOCK(mutexID) <------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      .......
'      While booleanT <> True <--------------------- from booleanT = True
'          ( atomic_mutex_unlock(mutexID) ) -------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
'          CONDWAIT(conditionalID, mutexID) <------- from CONDSIGNAL(conditionalID)
'          ( atomic_mutex_re-lock(mutexID) ) <------ from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      Wend
'      booleanT = False
'      .......
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )

'  Principle of mutual exclusion + CONDSIGNAL with predicate check, for a thread sub-section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                                        Other Thread
'      MUTEXLOCK(mutexID) <------------------------- from ( atomic_mutex_unlock(mutexID) ) or MUTEXUNLOCK(mutexID)
'      .......
'      booleanOT = True ---------------------------> to While booleanOT <> True
'      CONDSIGNAL(conditionalID) ------------------> to CONDWAIT(conditionalID, mutexID)
'      .......
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID) or ( atomic_mutex_re-lock(mutexID) )
- If 'CondWait' is not used, it is mandatory to put instead a 'Sleep x, 1' instruction in the 'While...Wend' loop on the Boolean flag, in order to release time-slice when looping (in addition, this 'Sleep x, 1' must be put inside a ['Mutexunlock'...'Mutexlock'] block to release another thread):
'  Principle of mutual exclusion + SLEEP in a While...Wend loop with predicate check, for a thread sub-section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                                        Other Thread
'      MUTEXLOCK(mutexID) <------------------------- from MUTEXUNLOCK(mutexID)
'      .......
'      While booleanT <> True <--------------------- from booleanT = True
'          MUTEXUNLOCK(mutexID) -------------------> to MUTEXLOCK(mutexID)
'          SLEEP(tempo, 1)
'          MUTEXLOCK(mutexID) <--------------------- from MUTEXUNLOCK(mutexID)
'      Wend
'      booleanT = False
'      .......
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID)

'  Principle of mutual exclusion + predicate check only, for a thread sub-section
'  (connecting lines join the sender(s) and receiver(s) impacted by each action occurring during the sequence)
'
'          Thread                                        Other Thread
'      MUTEXLOCK(mutexID) <------------------------- from MUTEXUNLOCK(mutexID)
'      .......
'      booleanOT = True ---------------------------> to While booleanOT <> True
'      .......
'      MUTEXUNLOCK(mutexID) -----------------------> to MUTEXLOCK(mutexID)
During 'CondWait', the thread execution is suspended and does not consume any CPU time until the condition variable is signaled.
But if 'Sleep x, 1' is put instead, the waiting time is predetermined and not self adaptive like that of 'CondWait'.

=> 'CondWait' is useful to optimize the execution time.

Back to top

5. How to implement a user input-line function fully thread-safe?

The Input keyword may be not thread-safe, when another thread must also access to input/output resource:
- When executing the Input statement, the other running threads must not change the position of the text cursor, which prohibits instructions such as Locate, Print, ...
- Moreover, one cannot enclosed the Input keyword inside a mutex locking (as we can do it for the Inkey keyword), because while the inputting line would be not completed and validated, the other threads that want to also access to input/output would be fully blocked (waiting for mutex unlocking).

Thread-safe input-line function (versus input/output resource):
Input position, prompt message, sleeping time, line-blanking command, mutex pointer can be passed to the following threadInput() function that simulates a simplified input function, but thread-safe, by using a looping around the Inkey keyword (all input/output keywords must be enclosed inside a mutex locking block, and the cursor position must be restored at each mutex locking block ending):
Function threadInput (ByVal row As Integer, ByVal column As Integer, ByRef prompt As String = "", _
                      ByVal sleeptime As Integer = 15, ByVal blank As Integer = 0, ByVal mutex As Any Ptr = 0 _
                      ) As String
    Dim As String inputchr
    Dim As String inputline
    Dim As Integer cursor
    Dim As Integer cursor0
    Dim As Integer r
    Dim As Integer c

    MutexLock(mutex)
    r = CsrLin()
    c = Pos()
    Locate row, column
    Print prompt & " _";
    cursor0 = Pos() - 1
    Locate r, c
    MutexUnlock(mutex)

    Do
        MutexLock(mutex)
        r = CsrLin()
        c = Pos()
        inputchr = Inkey
        If inputchr <> "" Then
            If inputchr >= Chr(32) And inputchr < Chr(255) Then
                inputline = Left(inputline, cursor) & inputchr & Mid(inputline, cursor + 1)
                cursor += 1
            ElseIf inputchr = Chr(08) And Cursor > 0 Then                         'BkSp
                cursor -= 1
                inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
            ElseIf inputchr = Chr(255) & "S" And Cursor < Len(inputline) Then     'Del
                inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
            ElseIf inputchr = Chr(255) + "M" And Cursor < Len(inputline) Then     'Right
                Cursor += 1
            ElseIf inputchr = Chr(255) + "K" And Cursor > 0 Then                  'Left
                Cursor -= 1
            End If
            If inputchr = Chr(27) Then                                            'Esc
                Locate row, cursor0
                Print Space(Len(inputline) + 1);
                inputline = ""
                cursor = 0
            End If
            Locate row, cursor0
            Print Left(inputline, cursor) & Chr(95) & Mid(inputline, cursor + 1) & " ";
        End If
        Locate r, c
        MutexUnlock(mutex)
        Sleep sleeptime, 1
    Loop Until inputchr = Chr(13)

    If blank <> 0 Then
        MutexLock(mutex)
        r = CsrLin()
        c = Pos()
        Locate row, cursor0
        Print Space(Len(inputline) + 1);
        Locate r, c
        MutexUnlock(mutex)
    End If

    Return inputline
End Function

      • From the example 1 on the Critical Sections page "Asynchronous method example using a mutex for all threads", now the running multi-threading code is waiting for the "quit" command in order to exit the program:
'   User thread algorithm:
'
'     Do
'     |  Mutexlock
'     |  .....
'     |  Critical section of code
'     |  .....
'     |  Mutexunlock
'     |  Sleep my_tempo, 1
'     Loop Until quit = true
'
'   There is no any advantage or disadvantage between threads for running their critical sections.
Function threadInput (ByVal row As Integer, ByVal column As Integer, ByRef prompt As String = "", _
                      ByVal sleeptime As Integer = 15, ByVal blank As Integer = 0, ByVal mutex As Any Ptr = 0 _
                      ) As String
    Dim As String inputchr
    Dim As String inputline
    Dim As Integer cursor
    Dim As Integer cursor0
    Dim As Integer r
    Dim As Integer c

    MutexLock(mutex)
    r = CsrLin()
    c = Pos()
    Locate row, column
    Print prompt & " _";
    cursor0 = Pos() - 1
    Locate r, c
    MutexUnlock(mutex)

    Do
        MutexLock(mutex)
        r = CsrLin()
        c = Pos()
        inputchr = Inkey
        If inputchr <> "" Then
            If inputchr >= Chr(32) And inputchr < Chr(255) Then
                inputline = Left(inputline, cursor) & inputchr & Mid(inputline, cursor + 1)
                cursor += 1
            ElseIf inputchr = Chr(08) And Cursor > 0 Then                         'BkSp
                cursor -= 1
                inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
            ElseIf inputchr = Chr(255) & "S" And Cursor < Len(inputline) Then     'Del
                inputline = Left(inputline, cursor) & Mid(inputline, cursor + 2)
            ElseIf inputchr = Chr(255) + "M" And Cursor < Len(inputline) Then     'Right
                Cursor += 1
            ElseIf inputchr = Chr(255) + "K" And Cursor > 0 Then                  'Left
                Cursor -= 1
            End If
            If inputchr = Chr(27) Then                                            'Esc
                Locate row, cursor0
                Print Space(Len(inputline) + 1);
                inputline = ""
                cursor = 0
            End If
            Locate row, cursor0
            Print Left(inputline, cursor) & Chr(95) & Mid(inputline, cursor + 1) & " ";
        End If
        Locate r, c
        MutexUnlock(mutex)
        Sleep sleeptime, 1
    Loop Until inputchr = Chr(13)

    If blank <> 0 Then
        MutexLock(mutex)
        r = CsrLin()
        c = Pos()
        Locate row, cursor0
        Print Space(Len(inputline) + 1);
        Locate r, c
        MutexUnlock(mutex)
    End If

    Return inputline
End Function

'------------------------------------------------------------------------------

Type UDT
    Dim As Integer number
    Dim As Integer tempo
    Dim As Any Ptr pThread
    Dim As ULongInt count
    Static As Any Ptr pMutex
    Static As Integer numberMax
    Static As Integer quit
End Type
Dim As Any Ptr UDT.pMutex
Dim As Integer UDT.numberMax
Dim As Integer UDT.quit

Sub Counter (ByVal pt As UDT Ptr)
    With *pt
        Locate .number, .number, 0
        Sleep 5, 1
        .count += 1
        Print .count;
    End With
End Sub

Sub Thread (ByVal p As Any Ptr)
    Dim As Integer myquit
    Dim As UDT Ptr pUDT = p
    With *pUDT
        Do
            MutexLock(.pMutex)
            Counter(pUDT)
            myquit = .quit
            MutexUnlock(.pMutex)
            Sleep .tempo, 1
        Loop Until myquit = 1
    End With
End Sub

Screen 12
UDT.numberMax = 6

Dim As UDT u(0 To UDT.numberMax)
For I As Integer = 0 To UDT.numberMax
    u(I).number = i
    u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
Next I
UDT.pMutex = MutexCreate

Dim As Single t = Timer
For I As Integer = 1 To UDT.numberMax
    u(I).pThread = ThreadCreate(@Thread, @u(I))
Next I

Do
Loop Until LCase(threadInput(8, 1, """quit"" for exit?", 10, 1, UDT.pMutex)) = "quit"

UDT.quit = 1

For I As Integer = 1 To UDT.numberMax
    ThreadWait(u(I).pThread)
Next I
t = Timer - t

MutexDestroy(UDT.pMutex)
Dim As ULongInt c
For I As Integer = 1 To UDT.numberMax
    c += u(I).count
Next I
Locate UDT.numberMax + 4, 1
Print CULngInt(c / t) & " increments per second"

Sleep

Note:
Otherwise, by using only graphics keywords (using the only position of the graphic cursor) as Line, Draw String, Put in the thread, induces a thread-safe procedure that is compatible with the Line Input keyword in the main code with no mutex:
Type UDT
    Dim As Integer number
    Dim As Integer tempo
    Dim As Any Ptr pThread
    Dim As ULongInt count
    Dim As Any Ptr img
    Static As Integer numberMax
    Static As Integer quit
End Type
Dim As Integer UDT.numberMax
Dim As Integer UDT.quit

Const As String prompt = "Enter ""quit"" for exit"
Dim As String s

Sub Counter (ByVal pt As UDT Ptr)  ' for a graphic character size 8x16
    With *pt
        Line .img, (0, 0)-(20 * 8 - 1, 16 - 1), 0, BF            ' clearing the image buffer
        Sleep 5, 1
        .count += 1
        Draw String .img, (0, 0), Str(.count)                    ' drawing in the image buffer
        Put ((.number - 1) * 8, (.number - 1) * 16), .img, PSet  ' copying the image buffer to screen
    End With
End Sub

Sub Thread (ByVal p As Any Ptr)    ' for a graphic character size 8x16
    Dim As UDT Ptr pUDT = p
    With *pUDT
        .img = ImageCreate(20 * 8, 16)  ' using an image buffer to avoid flickering
        Do
            Counter(pUDT)
            Sleep .tempo, 1
        Loop Until .quit = 1
        ImageDestroy .img  ' destroying the image buffer
    End With
End Sub

Screen 12
UDT.numberMax = 6

Dim As UDT u(0 To UDT.numberMax)
For I As Integer = 0 To UDT.numberMax
    u(I).number = i
    u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
Next I

Dim As Single t = Timer
For I As Integer = 1 To UDT.numberMax
    u(I).pThread = ThreadCreate(@Thread, @u(I))
Next I

Do
    Locate 8, 1, 0
    Line Input; prompt; s
    Locate , Len(prompt) + 3
    Print Space(Len(s));
Loop Until LCase(s) = "quit"
UDT.quit = 1

For I As Integer = 1 To UDT.numberMax
    ThreadWait(u(I).pThread)
Next I
t = Timer - t

Dim As ULongInt c
For I As Integer = 1 To UDT.numberMax
    c += u(I).count
Next I
Locate UDT.numberMax + 4, 1
Print CULngInt(c / t) & " increments per second"

Sleep

Back to top

6. How to use 'Screenlock' with multi-threading?

- Screenlock...Scrennunlock blocks are not compatible with multi-threading (otherwise, the program hangs). This is why a mutex block must be used around each such block to ensure the mutual exclusion.
- The input keywords (like for keyboard, mouse) cannot be safely run when the screen is locked, therefore a such keyword must be outside of any Screenlock...Screenunlock block, so outside any Screenlock...Screenunlock block in its own thread, and protected of all Screenlock...Screenunlock blocks of other threads by a mutex block. Therefore, Getkey and Input, the statements that wait for keypress or line input are unusable, but Inkey that does not wait can work.

By applying some rules scrupulously, one can use Screenlock/Screenunlock inside the threads.
Principle of coding for all threads including the main code (main thread):
Do
    ' instructions without display (printing/drawing, ...) neither input (input/inkey/mouse getting, ...)
    MutexLock(m)
    Screenlock
    ' instructions with only display (printing/drawing, ...)
    Screenunlock
    ' instructions with only input without waiting (inkey/mouse getting, ...)
    MutexUnlock(m)
    Sleep tempo, 1
Loop Until condition
    • For example, it is mandatory to use one Mutexlock...Mutexunlock block around each Screenlock...Screenunlock block, and one other around the Inkey instruction which itself must always be outside of any Screenlock...Screenunlock bloc:
Type ThreadUDT
    Dim handle As Any Ptr
    Static sync As Any Ptr
    Static quit As Byte
End Type
Dim ThreadUDT.sync As Any Ptr
Dim ThreadUDT.quit As Byte

Function ClockTime () As String
    Return Time
End Function

Function Counter () As Integer
    Static C As Integer
    C = (C + 1) Mod 1000000
    Return C
End Function

Sub ProcedureThread (ByVal param As Any Ptr)
    With *Cast(ThreadUDT Ptr, param)
        Do
            MutexLock(.sync)
            ScreenLock
            Line (544, 0)-(639, 49), 0, BF  'clear the print area
            Sleep 100, 1
            Locate 2, 71
            Print ClockTime();
            ScreenUnlock
            MutexUnlock(.sync)
            Sleep 100, 1
        Loop Until .quit = 1
    End With
End Sub

Screen 12
Locate 30, 2
Print "<q/Q> : quit";

Dim TTptr As ThreadUDT Ptr = New ThreadUDT
ThreadUDT.sync = MutexCreate
TTptr->handle = ThreadCreate(@ProcedureThread, TTptr)

Dim As String s
Do
    MutexLock(ThreadUDT.sync)
    ScreenLock
    Line (296, 208)-(376, 256), 0, BF  'clear the print area
    Sleep 100, 1
    Locate 15,40
    Print Using "######"; Counter();
    ScreenUnlock
    s = Inkey
    MutexUnlock(ThreadUDT.sync)
    Sleep 100, 1
Loop Until LCase(s) = "q"

ThreadUDT.quit = 1
ThreadWait(TTptr->handle)
MutexDestroy(ThreadUDT.sync)
Delete TTptr

Note: The Sleep x, 1 keyword just after the 'clear the print area' lines is only here to highlight the flickering if no screen locking is used.

Back to top

7. How to use 'video paging (double buffering or page flipping)' with multi-threading?

Instead of "screen locking" (see the above paragraph), "video paging (double buffering or page flipping)" can more simply be used with multi-threading, but be careful that many states in the gfxlib2 are thread-dependent like Screenset (and also View settings, graphic cursor position, graphic colors, ...).
Therefore, the setting for the working page and the visible page must always be controlled in each thread code which want to work with a multi-video page configuration.

    • Example for a double buffering method (at each step, each thread needs to update the working page and copy it to the visible page, from within a mutual exclusion mutex code block):
Type ThreadUDT
    Dim handle As Any Ptr
    Static sync As Any Ptr
    Static quit As Byte
End Type
Dim ThreadUDT.sync As Any Ptr
Dim ThreadUDT.quit As Byte

Function ClockTime () As String
    Return Time
End Function

Function Counter () As Integer
    Static C As Integer
    C = (C + 1) Mod 1000000
    Return C
End Function

Sub ProcedureThread (ByVal param As Any Ptr)
    ScreenSet 1, 0  '' setting to define in each thread
    With *Cast(ThreadUDT Ptr, param)
        Do
            MutexLock(.sync)
            Line (544, 0)-(639, 49), 0, BF  '' clear the print area
            Sleep 100, 1
            Locate 2, 71
            Print ClockTime();
            ScreenCopy
            MutexUnlock(.sync)
            Sleep 100, 1
        Loop Until .quit = 1
    End With
End Sub

Screen 12, , 2
ScreenSet 1, 0  '' setting to define in each thread
Locate 30, 2
Print "<q/Q> : quit";
ScreenCopy

Dim TTptr As ThreadUDT Ptr = New ThreadUDT
ThreadUDT.sync = MutexCreate
TTptr->handle = ThreadCreate(@ProcedureThread, TTptr)

Dim s As String
Do
    MutexLock(ThreadUDT.sync)
    Line (296, 208)-(376, 256), 0, BF  '' clear the print area
    Sleep 100, 1
    Locate 15,40
    Print Using "######"; Counter();
    ScreenCopy
    s = Inkey
    MutexUnlock(ThreadUDT.sync)
    Sleep 100, 1
Loop Until LCase(s) = "q"

ThreadUDT.quit = 1
ThreadWait(TTptr->handle)
MutexDestroy(ThreadUDT.sync)
Delete TTptr

Note: The Sleep x, 1 keyword just after the 'clear the print area' lines is only here to highlight the flickering if no double buffering is used.

    • Example for a two page flipping method (at each step, each thread needs to update and flip, from within the same exclusion mutex code block, the two screen pages):
Type ThreadUDT
    Dim handle As Any Ptr
    Static sync As Any Ptr
    Static quit As Byte
End Type
Dim ThreadUDT.sync As Any Ptr
Dim ThreadUDT.quit As Byte

Function ClockTime () As String
    Return Time
End Function

Function Counter () As Integer
    Static C As Integer
    C = (C + 1) Mod 1000000
    Return C
End Function

Sub ProcedureThread (ByVal param As Any Ptr)
    Dim p0 As Integer = 0
    Dim p1 As Integer = 1
    ScreenSet 1, 0  '' setting to define in each thread
    With *Cast(ThreadUDT Ptr, param)
        Do
            MutexLock(.sync)
            Dim s As String = ClockTime()
            For I As Integer = 1 To 2  '' updating the two screen pages
                Line (544, 0)-(639, 49), 0, BF  '' clear the print area
                Sleep 100, 1
                Locate 2, 71
                Print s;
                ScreenSet p0, p1
                Swap p0, p1
            Next I
            MutexUnlock(.sync)
            Sleep 100, 1
        Loop Until .quit = 1
    End With
End Sub

Screen 12, , 2
Dim p0 As Integer = 0
Dim p1 As Integer = 1
ScreenSet 1, 0  '' setting to define in each thread
For I As Integer = 1 To 2  '' updating the two screen pages
    Locate 30, 2
    Print "<q/Q> : quit";
    ScreenSet p0, p1
    Swap p0, p1
Next I

Dim TTptr As ThreadUDT Ptr = New ThreadUDT
ThreadUDT.sync = MutexCreate
TTptr->handle = ThreadCreate(@ProcedureThread, TTptr)

Dim s As String
Do
    MutexLock(ThreadUDT.sync)
    Dim C As Integer = Counter()
    For I As Integer = 1 To 2  '' updating the two screen pages
        Line (296, 208)-(376, 256), 0, BF  '' clear the print area
        Sleep 100, 1
        Locate 15,40
        Print Using "######"; c;
        ScreenSet p0, p1
        Swap p0, p1
    Next I
    s = Inkey
    MutexUnlock(ThreadUDT.sync)
    Sleep 100, 1
Loop Until LCase(s) = "q"

ThreadUDT.quit = 1
ThreadWait(TTptr->handle)
MutexDestroy(ThreadUDT.sync)
Delete TTptr

Note: The Sleep x, 1 keyword just after the 'clear the print area' lines is only here to highlight the flickering if no two page flipping is used.

Note: In these two examples, a mutual exclusion mutex code block is mandatory in the two threads, not only because of using console statements + Inkey, but around also the graphics statements + Screencopy only because of using double buffering method (without anti-flickering process, the graphics statements could be outside the exclusion mutex code block).

Back to top

8. How to use the FB runtime library for multi-threaded applications (gfxlib2) with multi-threading?

The source code of gfxlib2 uses TLS (Thread Local Storage) to store many states, so many things are thread-specific.
Since gfxlib2 is thread-safe, mutual mutex exclusion between threads is not necessary for the graphics statements themselves (including Draw String).
In contrast, console statements such as Locate, Print, ... are not thread-safe as previously mentioned (for example, text cursor position is common to all threads).

    • Simple example showing that graphic states (such as graphic cursor position, graphic colors) are thread-dependent:
Screen 12

Sub thread(ByVal p As Any Ptr)
    Color 10
    PSet(150, 10)
    For I As Integer = 1 To 40
        Line -Step(10, 10)
        Sleep 150, 1
    Next I
    Draw String Step (-40, 10), "user thread"
End Sub

Dim As Any Ptr p = ThreadCreate(@thread)

Color 14
PSet(10, 100)
For I As Integer = 1 To 24
    Line -Step(10, 10)
    Sleep 250, 1
Next I
Draw String Step (-40, 10), "main thread"

ThreadWait(p)

Color 15
Locate 4, 2
Print "Any key for exit"

Sleep

    • Example showing that graphics statements (such as Line and Draw String and Screencopy) in a thread can compete with console statements (such as Inkey) in another thread, without using any exclusion (by mutex):
#include "vbcompat.bi"

Screen 12, , 2
ScreenSet 1, 0
Color 0, 7
Cls

Dim Shared terminate As Integer = 0

Sub thread (ByVal param As Any Ptr)
    ScreenSet 1, 0
    Do
        Line (16, 432)-Step(96, 32), 11, BF  'clear print area
        Sleep 100, 1
        Draw String (24, 432), Format(Now,"dd/mm/yyyy"), 0
        Draw String (32, 448), Format(Now,"hh:mm:ss"), 0
        ScreenCopy
        Sleep 100, 1
    Loop Until terminate = 1
End Sub

Dim As String reply
Locate 2, 2
Print "Enter ""q"" to quit"
ScreenCopy

Dim p As Any Ptr = ThreadCreate(@thread)

Do
    reply = Inkey
    Sleep 100, 1
Loop Until LCase(reply) = "q"

Print " Stop the thread"
ScreenCopy
terminate=1
ThreadWait (p)
Print " Thread terminated"
ScreenCopy

Sleep

Note: The Sleep x, 1 keyword just after the 'clear the print area' line is only here to highlight the flickering if no double buffering is used.

    • From the above example, if the date displaying and the time displaying are now two separate threads, a mutual exclusion mutex code block between these two threads is mandatory, not due to the graphics statements themselves competing, but only due to the double buffering method used (against flickering) that puts competing these two threads:
#include "vbcompat.bi"

Screen 12, , 2
ScreenSet 1, 0
Color 0, 7
Cls

Dim Shared terminate As Integer = 0
Dim Shared mutex As Any Ptr

Sub thread1 (ByVal param As Any Ptr)
    ScreenSet 1, 0
    Do
        MutexLock(mutex)
        Line (16, 432)-Step(96, 16), 11, BF  'clear the print area
        Sleep 200, 1
        Draw String (24, 432), Format(Now,"dd/mm/yyyy"), 0
        ScreenCopy
        MutexUnlock(mutex)
        Sleep 100, 1
    Loop Until terminate = 1
End Sub

Sub thread2 (ByVal param As Any Ptr)
    ScreenSet 1, 0
    Do
        MutexLock(mutex)
        Line (16, 448)-Step(96, 16), 11, BF  'clear the print area
        Sleep 100, 1
        Draw String (32, 448), Format(Now,"hh:mm:ss"), 0
        ScreenCopy
        MutexUnlock(mutex)
        Sleep 100, 1
    Loop Until terminate = 1
End Sub

Dim As String reply
Locate 2, 2
Print "Enter ""q"" to quit"
ScreenCopy

mutex = MutexCreate
Dim p1 As Any Ptr = ThreadCreate(@thread1)
Dim p2 As Any Ptr = ThreadCreate(@thread2)

Do
    reply = Inkey
    Sleep 100, 1
Loop Until LCase(reply) = "q"

Print " Stop the threads"
ScreenCopy
terminate=1
ThreadWait (p1)
ThreadWait (p2)
MutexDestroy(mutex)
Print " Threads terminated"
ScreenCopy

Sleep

Note: The Sleep x, 1 keyword just after the 'clear the print area' lines is only here to highlight the flickering if no double buffering is used, or if no mutex is used.

Back to top

9. How to use console statements and keyboard inputs with multi-threading?

Console statements (such as Locate, Print, Color, ...), as well as Locate and Print on Graphics window (but not Color on Graphics Window), and keyboard inputs (such as Inkey, Getkey, Input, ...) are not thread-safe:
- Thus when they are used in competing sections of different threads, mutual exclusion is mandatory by means of mutex locking blocks in which in addition code can restore states (such as text cursor position, console color, ...) at end of the block (after its own usage), as they were before (at begin of the block).
- But the Getkey or Input keyword cannot be enclosed inside a mutex locking block (as it can be do with the Inkey keyword), because as long as the keyboard input is not completed, the other threads in compete would be fully blocked (waiting for the mutex unlocking).

      • Example showing that the keywords Locate and Print are not thread-safe both when applied on a console window or when applied on a graphics window (the text cursor states being not thread dependent in the two cases):
Sub Thread (ByVal p As Any Ptr)
    Locate Cast(Integer, p), Cast(Integer, p)
    For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
        Sleep 20 * Cast(Integer, p), 1
        Print Str(Cast(Integer, p));
    Next I
End Sub

Sub test ()
    Dim As Any Ptr p(1 To 9)
    For I As Integer = 1 To 9
        p(I) = ThreadCreate(@Thread, Cast(Any Ptr, I))
        Sleep 25, 1
    Next I
    For I As Integer = 1 To 9
        ThreadWait(p(I))
    Next I
End Sub

Screen 0
test()
Locate 15, 1
Print "Any key to continue"
Sleep

Screen 12
test()
Locate 15, 1
Print "Any key to quit"
Sleep

Note: One can see that each thread does not write on its own line corresponding to its thread number (id between 1 and 9), on the console window and on the graphics window.

      • From the above example, the thread code has been completed in its competing sections by mutex locking blocks and by saving/restoring cursor states before/after its own cursor moving:
Dim Shared As Any Ptr mutex

Sub Thread (ByVal p As Any Ptr)
    MutexLock(mutex)
    Dim As Long l0 = Locate()
    Locate Cast(Integer, p), Cast(Integer, p)
    Dim As Long l = Locate()
    Locate HiByte(LoWord(l0)), LoByte(LoWord(l0)), HiWord(l0)
    MutexUnlock(mutex)
    For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
        Sleep 20 * Cast(Integer, p), 1
        MutexLock(mutex)
        l0 = Locate()
        Locate HiByte(LoWord(l)), LoByte(LoWord(l)), HiWord(l)
        Print Str(Cast(Integer, p));
        l = Locate()
        Locate HiByte(LoWord(l0)), LoByte(LoWord(l0)), HiWord(l0)
        MutexUnlock(mutex)
    Next I
End Sub

Sub test ()
    Dim As Any Ptr p(1 To 9)
    For I As Integer = 1 To 9
        p(I) = ThreadCreate(@Thread, Cast(Any Ptr, I))
        Sleep 25, 1
    Next I
    For I As Integer = 1 To 9
        ThreadWait(p(I))
    Next I
End Sub

mutex = MutexCreate

Screen 0
test()
Locate 15, 1
Print "Any key to continue"
Sleep

Screen 12
test()
Locate 15, 1
Print "Any key to quit"
Sleep

MutexDestroy(mutex)

Note: One can see that each thread writes now on its own line corresponding to its thread number (id between 1 and 9), on the console window and on the graphics window.

      • Example showing that the Color keyword is not thread-safe when applied on a console window, but is thread-safe when applied on a graphics window (the color states being thread dependent in that case):
Sub Thread (ByVal p As Any Ptr)
    Color Cast(Integer, p) + 8, Cast(Integer, p)
    For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
        Print " " & Cast(Integer, p) & " ";
        Sleep 20 * Cast(Integer, p), 1
    Next I
End Sub

Sub test ()
    Dim As Any Ptr p(1 To 9)
    Locate 1, 1
    For I As Integer = 1 To 9
        p(I) = ThreadCreate(@Thread, Cast(Any Ptr, I))
        Sleep 25, 1
    Next I
    For I As Integer = 1 To 9
        ThreadWait(p(I))
    Next I
    Locate 16, 1
End Sub

Screen 0
test()
Print "Any key to continue"
Sleep

Screen 12
test()
Print "Any key to quit"
Sleep

Note: One can see that the foreground/background colors are not specific to the thread number (id between 1 and 9) on the console window, but this works great on the graphics window.

      • From the above example, the thread code has been completed in its competing sections by mutex locking blocks and by saving/restoring color states before/after its own color values usage:
Dim Shared As Any Ptr mutex

Sub Thread (ByVal p As Any Ptr)
    MutexLock(mutex)
    Dim As ULong c0 = Color(Cast(Integer, p) + 8, Cast(Integer, p))
    Dim As ULong c = Color()
    Color(LoWord(c0), HiWord(c0))
    MutexUnlock(mutex)
    For I As Integer = 1 To 50 - 2 * Cast(Integer, p)
        MutexLock(mutex)
        c0 = Color(LoWord(c), HiWord(c))
        Print " " & Cast(Integer, p) & " ";
        Color(LoWord(c0), HiWord(c0))
        MutexUnlock(mutex)
        Sleep 20 * Cast(Integer, p), 1
    Next I
End Sub

Sub test ()
    Dim As Any Ptr p(1 To 9)
    Locate 1, 1
    For I As Integer = 1 To 9
        p(I) = ThreadCreate(@Thread, Cast(Any Ptr, I))
        Sleep 25, 1
    Next I
    For I As Integer = 1 To 9
        ThreadWait(p(I))
    Next I
    Locate 16, 1
End Sub

mutex = MutexCreate

Screen 0
test()
Print "Any key to continue"
Sleep

Screen 12
test()
Print "Any key to quit"
Sleep

MutexDestroy(mutex)

Note: One can see that the foreground/background colors are now specific to the thread number (id between 1 and 9) on the console window (obviously this always works on the graphics window).

Therefore, for using Getkey or Input in competing sections of threads:
- Only a single thread (for example, the main thread) can uses Getkey or Input in addition to console statements (such as Locate, Print, Color, ...) and also Inkey, in its competing sections.
- The other threads must not to use in their competing sections any console statement neither any keyboard input keyword, but can use by cons graphics statements (such as Pset, Line, Circle, Draw String, graphic Color, ...) which are themselves thread-safe (they can interlace graphically with the main thread without any problem).
- Input and Getkey also exclude the screen locking usage in competing sections of threads (double buffering is recommended as anti-flickering method).

      • Example showing that graphics statements (such as Line and Draw String and Screencopy) in a thread (user thread here) can compete with console statements (such as Locate and Print and Input) in another thread (main thread here), without using any mutual exclusion (by mutex):
#include "vbcompat.bi"

Screen 12, , 2
ScreenSet 1, 0
Color 0, 7
Cls

Dim Shared terminate As Integer = 0

Sub thread (ByVal param As Any Ptr)
    ScreenSet 1, 0
    Do
        Line (16, 432)-Step(96, 32), 11, BF  'clear the print area
        Sleep 100, 1
        Draw String (24, 432), Format(Now,"dd/mm/yyyy"), 0
        Draw String (32, 448), Format(Now,"hh:mm:ss"), 0
        ScreenCopy
        Sleep 100, 1
    Loop Until terminate = 1
End Sub

Dim As String reply
Locate 2, 2
Print "Enter ""quit"" to quit"
ScreenCopy

Dim p As Any Ptr = ThreadCreate(@thread)

Do
    Locate 3, 2
    Print Space(Len(reply) + 2);
    Locate 3, 2
    Input reply
Loop Until LCase(reply) = "quit"

Print " Stop the thread"
ScreenCopy
terminate=1
ThreadWait (p)
Print " Thread terminated"
ScreenCopy

Sleep

Note: The Sleep x, 1 keyword just after the 'clear the print area' line is only here to highlight the flickering if no double buffering is used (screen locking being forbidden by Input usage).

      • From the above example, if the date displaying and the time displaying are now two separate user threads, a mutual exclusion mutex code block between these two threads only is mandatory, not due to the graphics statements themselves competing, but only due to the double buffering method used (against flickering) that puts competing these two user threads only:
#include "vbcompat.bi"

Screen 12, , 2
ScreenSet 1, 0
Color 0, 7
Cls

Dim Shared terminate As Integer = 0
Dim Shared mutex As Any Ptr

Sub thread1 (ByVal param As Any Ptr)
    ScreenSet 1, 0
    Do
        MutexLock(mutex)
        Line (16, 432)-Step(96, 16), 11, BF  'clear the print area
        Sleep 200, 1
        Draw String (24, 432), Format(Now,"dd/mm/yyyy"), 0
        ScreenCopy
        MutexUnlock(mutex)
        Sleep 100, 1
    Loop Until terminate = 1
End Sub

Sub thread2 (ByVal param As Any Ptr)
    ScreenSet 1, 0
    Do
        MutexLock(mutex)
        Line (16, 448)-Step(96, 16), 11, BF  'clear the print area
        Sleep 100, 1
        Draw String (32, 448), Format(Now,"hh:mm:ss"), 0
        ScreenCopy
        MutexUnlock(mutex)
        Sleep 100, 1
    Loop Until terminate = 1
End Sub

Dim As String reply
Locate 2, 2
Print "Enter ""quit"" to quit"
ScreenCopy

mutex = MutexCreate
Dim p1 As Any Ptr = ThreadCreate(@thread1)
Dim p2 As Any Ptr = ThreadCreate(@thread2)

Do
    Locate 3, 2
    Print Space(Len(reply) + 2);
    Locate 3, 2
    Input reply
Loop Until LCase(reply) = "quit"

Print " Stop the threads"
ScreenCopy
terminate=1
ThreadWait (p1)
ThreadWait (p2)
MutexDestroy(mutex)
Print " Threads terminated"
ScreenCopy

Sleep

Note: The Sleep x, 1 keyword just after the 'clear the print area' lines is only here to highlight the flickering if no double buffering is used (screen locking being forbidden by Input usage).

Back to top

10. Is it better to take precautions when using the keyword 'Sleep' in threads?

There is still some doubt about the perfect behavior of the keyword Sleep in a multi-threading context.

It is therefore advisable to take the following precautions for its use:
- If it is absolutely necessary in a critical section of a thread, the syntax Sleep x or Sleep x, 0, because inducing an internal test of a key-press, must for greatest safety be preferably treated in the same way as the Inkey keyword to avoid as much as possible any concurrent conflict with other threads.
- Otherwise, the syntax Sleep x, 1 (inducing no internal test of key-press) is rather advised when there is no protection by mutual exclusion, which is very often the case in order to release time-slice for the other threads.

Back to top

11. Can all tools to handle multi-threading be encapsulated in a base Class (that the user extends with a derived Type for his own implementing)?

A simple 'threadUDT' base Class can be defined as follows:
- with a private 'Any Ptr' non-static member field for each handle,
- with a private 'Any Ptr' static member field for one shared mutex,
- with a private 'Any Ptr' static member field for one shared conditional variable,
- with its own public member procedures 'Sub()' calling the corresponding built-in procedures for multi-threading (with same procedure names), including also value integrity tests on the 3 above pointers (non-static procedures for the 3 'thread...()' member Subs, and static procedures for the 4 'mutex...()' member Subs and the 5 'cond...()' member Subs),
- with an abstract private 'Sub()' thread to be overridden by another 'Sub()' from inside the user derived Type (therefore its static address is available in the virtual table of the object, and the hidden 'This' parameter passed by reference is compatible with the 'Any Ptr' parameter to be passed to the thread).
#include Once "fbthread.bi"
Type threadUDT Extends Object
    Public:
        Declare Sub ThreadCreate ()
        Declare Sub ThreadWait ()
        Declare Sub ThreadDetach ()
        Declare Static Sub MutexCreate ()
        Declare Static Sub MutexLock ()
        Declare Static Sub MutexUnlock ()
        Declare Static Sub MutexDestroy ()
        Declare Static Sub CondCreate ()
        Declare Static Sub CondWait ()
        Declare Static Sub CondSignal ()
        Declare Static Sub CondBroadcast ()
        Declare Static Sub CondDestroy ()
    Private:
        Declare Abstract Sub thread ()
        Dim As Any Ptr pThread
        Static As Any Ptr pMutex
        Static As Any Ptr pCond
End Type
Dim As Any Ptr threadUDT.pMutex
Dim As Any Ptr threadUDT.pCond

Sub threadUDT.threadCreate ()
    If This.pThread = 0 Then
      This.pThread = .ThreadCreate(Cast(Any Ptr Ptr Ptr, @This)[0][0], @This)
    End If
End Sub

Sub threadUDT.threadWait ()
    If This.pThread > 0 Then
        .ThreadWait(This.pThread)
        This.pThread = 0
    End If
End Sub

Sub threadUDT.threadDetach ()
    If This.pThread > 0 Then
        .ThreadDetach(This.pThread)
        This.pThread = 0
    End If
End Sub

Sub threadUDT.mutexCreate ()
    If threadUDT.pMutex = 0 Then
        threadUDT.pMutex = .MutexCreate
    End If
End Sub

Sub threadUDT.mutexLock ()
    If threadUDT.pMutex > 0 Then
        .MutexLock(threadUDT.pMutex)
    End If
End Sub

Sub threadUDT.mutexUnlock ()
    If threadUDT.pMutex > 0 Then
        .MutexUnlock(threadUDT.pMutex)
    End If
End Sub

Sub threadUDT.mutexDestroy ()
    If threadUDT.pMutex > 0 Then
        .MutexDestroy(threadUDT.pMutex)
        threadUDT.pMutex = 0
    End If
End Sub

Sub threadUDT.condCreate ()
    If threadUDT.pCond = 0 Then
        threadUDT.pCond = .CondCreate
    End If
End Sub

Sub threadUDT.condWait ()
    If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
        .CondWait(threadUDT.pCond, threadUDT.pMutex)
    End If
End Sub

Sub threadUDT.condSignal ()
    If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
        .CondSignal(threadUDT.pCond)
    End If
End Sub

Sub threadUDT.condBroadcast ()
    If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
        .CondBroadcast(threadUDT.pCond)
    End If
End Sub

Sub threadUDT.condDestroy ()
    If threadUDT.pCond > 0 Then
        .CondDestroy(threadUDT.pCond)
        threadUDT.pCond = 0
    End If
End Sub

    • From the example 2 on the Critical Sections page "Synchronous method example using a condwait then a condbroadcast (and a mutex) for all threads", now the user implementation is modified to be compatible with the base Class 'threadUDT':
#include Once "fbthread.bi"
Type threadUDT Extends Object
    Public:
        Declare Sub ThreadCreate ()
        Declare Sub ThreadWait ()
        Declare Sub ThreadDetach ()
        Declare Static Sub MutexCreate ()
        Declare Static Sub MutexLock ()
        Declare Static Sub MutexUnlock ()
        Declare Static Sub MutexDestroy ()
        Declare Static Sub CondCreate ()
        Declare Static Sub CondWait ()
        Declare Static Sub CondSignal ()
        Declare Static Sub CondBroadcast ()
        Declare Static Sub CondDestroy ()
    Private:
        Declare Abstract Sub thread ()
        Dim As Any Ptr pThread
        Static As Any Ptr pMutex
        Static As Any Ptr pCond
End Type
Dim As Any Ptr threadUDT.pMutex
Dim As Any Ptr threadUDT.pCond

Sub threadUDT.threadCreate ()
    If This.pThread = 0 Then
      This.pThread = .ThreadCreate(Cast(Any Ptr Ptr Ptr, @This)[0][0], @This)
    End If
End Sub

Sub threadUDT.threadWait ()
    If This.pThread > 0 Then
        .ThreadWait(This.pThread)
        This.pThread = 0
    End If
End Sub

Sub threadUDT.threadDetach ()
    If This.pThread > 0 Then
        .ThreadDetach(This.pThread)
        This.pThread = 0
    End If
End Sub

Sub threadUDT.mutexCreate ()
    If threadUDT.pMutex = 0 Then
        threadUDT.pMutex = .MutexCreate
    End If
End Sub

Sub threadUDT.mutexLock ()
    If threadUDT.pMutex > 0 Then
        .MutexLock(threadUDT.pMutex)
    End If
End Sub

Sub threadUDT.mutexUnlock ()
    If threadUDT.pMutex > 0 Then
        .MutexUnlock(threadUDT.pMutex)
    End If
End Sub

Sub threadUDT.mutexDestroy ()
    If threadUDT.pMutex > 0 Then
        .MutexDestroy(threadUDT.pMutex)
        threadUDT.pMutex = 0
    End If
End Sub

Sub threadUDT.condCreate ()
    If threadUDT.pCond = 0 Then
        threadUDT.pCond = .CondCreate
    End If
End Sub

Sub threadUDT.condWait ()
    If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
        .CondWait(threadUDT.pCond, threadUDT.pMutex)
    End If
End Sub

Sub threadUDT.condSignal ()
    If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
        .CondSignal(threadUDT.pCond)
    End If
End Sub

Sub threadUDT.condBroadcast ()
    If threadUDT.pCond > 0 And threadUDT.pMutex > 0 Then
        .CondBroadcast(threadUDT.pCond)
    End If
End Sub

Sub threadUDT.condDestroy ()
    If threadUDT.pCond > 0 Then
        .CondDestroy(threadUDT.pCond)
        threadUDT.pCond = 0
    End If
End Sub

'------------------------------------------------------------------------------

Type UDT Extends threadUDT
    Declare Sub counter ()
    Declare Sub thread ()
    Dim As Integer number
    Dim As Integer tempo
    Dim As ULongInt count
    Static As Integer threadPriorityNumber
    Static As Integer numberMax
    Static As Integer quit
End Type
Dim As Integer UDT.threadPriorityNumber
Dim As Integer UDT.numberMax
Dim As Integer UDT.quit

Sub UDT.counter ()
    Locate This.number, This.number, 0
    Sleep 5, 1
    This.count += 1
    Print This.count;
    Locate This.number, 30 + This.number, 0
End Sub

Sub UDT.Thread ()
    Dim As Integer myquit
    Do
        This.mutexLock()
        While UDT.threadPriorityNumber <> This.number  '' synchronous condwait for expected condition
            This.condWait()
        Wend
        This.counter()
        myquit = UDT.quit
        UDT.threadPriorityNumber = (UDT.threadPriorityNumber + 1) Mod (UDT.numberMax + 1)
        This.condBroadcast()
        This.mutexUnlock()
        Sleep This.tempo, 1
    Loop Until myquit = 1
End Sub

UDT.numberMax = 6
Dim As UDT u(0 To UDT.numberMax)
For I As Integer = 0 To UDT.numberMax
    u(I).number = i
    u(I).tempo = 100 + 15 * I - 95 * Sgn(I)
Next I
UDT.mutexCreate()
UDT.condCreate()

Dim As Single t = Timer
For I As Integer = 1 To UDT.numberMax
    u(I).ThreadCreate()
Next I

Dim As String s
Do
    UDT.mutexLock()
    While UDT.threadPriorityNumber <> u(0).number
        UDT.condWait()
    Wend
    s = Inkey
    If s <> "" Then
        UDT.quit = 1
    End If
    UDT.threadPriorityNumber = (UDT.threadPriorityNumber + 1) Mod (UDT.numberMax + 1)
    UDT.condBroadcast()
    UDT.mutexUnlock()
    Sleep u(0).tempo, 1
Loop Until s <> ""

For I As Integer = 1 To UDT.numberMax
    u(I).ThreadWait()
Next I
t = Timer - t

UDT.mutexDestroy()
UDT.condDestroy()
Dim As ULongInt c
For I As Integer = 1 To UDT.numberMax
    c += u(I).count
Next I
Locate UDT.numberMax+2, 1
Print CULngInt(c / t) & " increments per second"

Sleep

Back to top

12. What is the execution delay of the code of a thread after the thread is created by 'ThreadCreate'?

One might think that the first code line of the thread is always executed at least after the 'ThreadCreate()' returns, but this is neither guaranteed nor even observed.

One can estimate the delay (positive or negative) between the 'ThreadCreate()' return and the the thread start, by a time memorization as similar as possible between the line following 'ThreadCreate()' and the first thread code line (the delay calculation is executed after the end of the thread).
After a while of observation, one can find both small negative values and large positive values.

Interesting to see the min time, average time, and max time, between the executing start of thread body and the returning point of 'ThreadCreate()':
Dim As Any Ptr ptid
Dim As Double t0
Dim As Any Ptr p0 = @t0
Dim As Double t1
Dim As Double count
Dim As Single tmean
Dim As Single tmin = 10   '' start value
Dim As Single tmax = -10  '' start value

Sub myThread (ByVal p As Any Ptr)
    *Cast(Double Ptr, p) = Timer  '' similar code line as in main code
End Sub

Print "Tmin/Tmean/Tmax between begin of thread code and return from ThreadCreate() :"
Do
    count += 1
    ptid = ThreadCreate(@myThread, @t1)
    *Cast(Double Ptr, p0) = Timer  '' similar code line as in thread code

    ThreadWait(ptid)

    tmean = (tmean * (count - 1) + (t1 - t0)) / count
    If t1 - t0 < tmin Or t1 - t0 > tmax Then
        If t1 - t0 < tmin Then
            tmin = t1 - t0
        End If
        If t1 - t0 > tmax Then
            tmax = t1 - t0
        End If
        Print Time; Using "    Tmin=+###.###### ms    Tmean=+###.###### ms    Tmax=+###.###### ms"; tmin * 1000; tmean * 1000; tmax * 1000
    End If
Loop Until Inkey <> ""

Output (for example):
Tmin/Tmean/Tmax between begin of thread code and return from ThreadCreate() :
21:30:13    Tmin=  +0.151800 ms    Tmean=  +0.151800 ms    Tmax=  +0.151800 ms
21:30:13    Tmin=  +0.006000 ms    Tmean=  +0.078900 ms    Tmax=  +0.151800 ms
21:30:13    Tmin=  +0.006000 ms    Tmean=  +0.098394 ms    Tmax=  +0.172500 ms
21:30:13    Tmin=  +0.006000 ms    Tmean=  +0.121555 ms    Tmax=  +0.884900 ms
21:30:45    Tmin=  +0.006000 ms    Tmean=  +0.055810 ms    Tmax=  +1.104200 ms
21:30:54    Tmin=  +0.006000 ms    Tmean=  +0.055764 ms    Tmax=  +4.056600 ms
21:31:44    Tmin=  -0.116300 ms    Tmean=  +0.055516 ms    Tmax=  +4.056600 ms
21:32:10    Tmin=  -0.136800 ms    Tmean=  +0.057177 ms    Tmax=  +4.056600 ms
21:32:12    Tmin=  -0.150300 ms    Tmean=  +0.057265 ms    Tmax=  +4.056600 ms
21:33:17    Tmin=  -0.150300 ms    Tmean=  +0.060048 ms    Tmax=  +4.979900 ms
21:33:18    Tmin=  -0.150300 ms    Tmean=  +0.060157 ms    Tmax=  +7.086300 ms
21:33:23    Tmin=  -0.150600 ms    Tmean=  +0.060347 ms    Tmax=  +7.086300 ms
21:33:38    Tmin=  -0.205900 ms    Tmean=  +0.060878 ms    Tmax=  +7.086300 ms
21:35:30    Tmin=  -0.208700 ms    Tmean=  +0.061315 ms    Tmax=  +7.086300 ms
Note:
If the user safely wish to always delay the thread execution at least after some code lines following the 'ThreadCreate()' line, a mutual exclusion between the 'ThreadCreate()' line and the start of the thread body can be used as this principle follows:
Dim Shared As Any Ptr pMutexForThreadStart

'-------------------------------------------

Sub Thread (ByVal p As Any Ptr)
    MutexLock(pMutexForThreadStart)
    MutexUnlock(pMutexForThreadStart)
    '
    ' user thread body
    '
End Sub

'--------------------------------------------

'
' user main code
'
pMutexForThreadStart = MutexCreate()
'
' user main code continues
'
MutexLock(pMutexForThreadStart)
Dim As Any Ptr pThread = ThreadCreate(@Thread)
'
' lines of code to be executed before the executing start of the user body of the thread
'
MutexUnlock(pMutexForThreadStart)
'
' user main code continues
'
ThreadWait(pThread)
MutexDestroy(pMutexForThreadStart)

Back to top

13. What is the synchronization latency when synchronizing threads either by mutual exclusions or by conditional variables?

The synchronization waiting phase of each thread should not consume any CPU resources like 'Sleep', which is the case of 'MutexLock()' and 'CondWait()' instructions.
Thread synchronization by mutual exclusions or by conditional variables adds latency to the initial execution time of the threads, but this latency (a few microseconds) is infinitely shorter than that of a simple simple wait loop (a few milliseconds at best) containing the shortest sleep ('Sleep 1, 1') with a flag test.

The following code allows to estimate this synchronization latency between the main thread and a child thread, by using either simple flags, either mutual exclusions, or conditional variables:
Dim Shared As Any Ptr mutex0, mutex1, mutex2, mutex, cond1, cond2, pt
Dim Shared As Integer flag1, flag2
Dim As Double t

'----------------------------------------------------------------------------------

#if defined(__FB_WIN32__)
Declare Function _setTimer Lib "winmm" Alias "timeBeginPeriod"(ByVal As ULong = 1) As Long
Declare Function _resetTimer Lib "winmm" Alias "timeEndPeriod"(ByVal As ULong = 1) As Long
#endif

Sub ThreadFlag(ByVal p As Any Ptr)
    MutexUnlock(mutex0)  '' unlock mutex for main thread
    For I As Integer = 1 To 100
        While flag1 = 0
            Sleep 1, 1
        Wend
        flag1 = 0
        ' only child thread code runs (location for example)
        flag2 = 1
    Next I
End Sub

mutex0 = MutexCreate()
MutexLock(mutex0)

pt = ThreadCreate(@ThreadFlag)
MutexLock(mutex0)  '' wait for thread launch (mutex unlock from child thread)
Print "Thread synchronization latency by simple flags:"
#if defined(__FB_WIN32__)
    _setTimer()
    Print "(in high resolution OS cycle period)"
#else
    Print "(in normal resolution OS cycle period)"
#endif
t = Timer
For I As Integer = 1 To 100
    flag1 = 1
    While flag2 = 0
        Sleep 1, 1
    Wend
    flag2 = 0
    ' only main thread code runs (location for example)
Next I
t = Timer - t
#if defined(__FB_WIN32__)
    _resetTimer()
#endif
ThreadWait(pt)
Print Using "####.## milliseconds per double synchronization (round trip)"; t * 10
Print

MutexDestroy(mutex0)

'----------------------------------------------------------------------------------

Sub ThreadMutex(ByVal p As Any Ptr)
    MutexUnlock(mutex0)  '' unlock mutex for main thread
    For I As Integer = 1 To 100000
        MutexLock(mutex1)    '' wait for mutex unlock from main thread
        ' only child thread code runs
        MutexUnlock(mutex2)  '' unlock mutex for main thread
    Next I
End Sub

mutex0 = MutexCreate()
mutex1 = MutexCreate()
mutex2 = MutexCreate()
MutexLock(mutex0)
MutexLock(mutex1)
MutexLock(mutex2)

pt = ThreadCreate(@ThreadMutex)
MutexLock(mutex0)  '' wait for thread launch (mutex unlock from child thread)
Print "Thread synchronization latency by mutual exclusions:"
t = Timer
For I As Integer = 1 To 100000
    MutexUnlock(mutex1)  '' mutex unlock for child thread
    MutexLock(mutex2)    '' wait for mutex unlock from child thread
    ' only main thread code runs
Next I
t = Timer - t
ThreadWait(pt)
Print Using "####.## microseconds per double synchronization (round trip)"; t * 10
Print

MutexDestroy(mutex0)
MutexDestroy(mutex1)
MutexDestroy(mutex2)

'----------------------------------------------------------------------------------

Sub ThreadCondVar(ByVal p As Any Ptr)
    MutexUnlock(mutex0)  '' unlock mutex for main thread
    For I As Integer = 1 To 100000
        MutexLock(mutex)
        While flag1 = 0
            CondWait(cond1, mutex)  '' wait for conditional signal from main thread
        Wend
        flag1 = 0
        ' only child thread code runs (location for example)
        flag2 = 1
        CondSignal(cond2)  '' send conditional signal to main thread
        MutexUnlock(mutex)
    Next I
End Sub

mutex0 = MutexCreate()
mutex = MutexCreate()
MutexLock(mutex0)
cond1 = CondCreate()
cond2 = CondCreate()

pt = ThreadCreate(@ThreadCondVar)
MutexLock(mutex0)  '' wait for thread launch (mutex unlock from child thread)
Print "Thread synchronization latency by conditional variables:"
t = Timer
For I As Integer = 1 To 100000
    MutexLock(mutex)
    flag1 = 1
    CondSignal(cond1)  '' send conditional signal to main thread
    While flag2 = 0
        CondWait(Cond2, mutex)  '' wait for conditional signal from child thread
    Wend
    flag2 = 0
    ' only child thread code runs (location for example)
    MutexUnlock(mutex)
Next I
t = Timer - t
ThreadWait(pt)
Print Using "####.## microseconds per double synchronization (round trip)"; t * 10
Print

MutexDestroy(mutex0)
MutexDestroy(mutex)
CondDestroy(cond1)
CondDestroy(cond2)

'----------------------------------------------------------------------------------

Sleep

Example of results:
Thread synchronization latency by simple flags:
(in high resolution OS cycle period)
   2.02 milliseconds per double synchronization (round trip)

Thread synchronization latency by mutual exclusions:
   5.93 microseconds per double synchronization (round trip)

Thread synchronization latency by conditional variables:
   7.54 microseconds per double synchronization (round trip)
Example of thread synchronization for executing in concurrent or exclusive mode by using conditional variable:
Dim Shared As Any Ptr pt, mutex1, mutex2, cond1, cond2
Dim Shared As Integer quit, flag1, flag2

Print "'1': Main thread procedure running (alone)"
Print "'2': Child thread procedure running (alone)"
Print "'-': Main thread procedure running (with the one of child thread)"
Print "'=': Child thread procedure running (with the one of main thread)"
Print

Sub Prnt(ByRef s As String, ByVal n As Integer)
    For I As Integer = 1 To n
        Print s;
        Sleep 20, 1
    Next I
End Sub

Sub ThreadCondCond(ByVal p As Any Ptr)
    Do
        MutexLock(mutex1)
        While flag1 = 0              '' test flag set from main thread
            CondWait(cond1, mutex1)  '' wait for conditional signal from main thread
        Wend
        flag1 = 0                    '' reset flag
        MutexUnlock(mutex1)
        If quit = 1 Then Exit Sub    '' exit the threading loop
        Prnt("=", 10)
        MutexLock(mutex2)
        flag2 = 1                    '' set flag to main thread
        CondSignal(cond2)            '' send conditional signal to main thread
        Prnt("2", 10)
        MutexUnlock(mutex2)
    Loop
End Sub

mutex1 = MutexCreate()
mutex2 = MutexCreate()
cond1 = CondCreate()
cond2 = CondCreate()

pt = ThreadCreate(@ThreadCondCond)
For I As Integer = 1 To 5
    MutexLock(mutex1)
    flag1 = 1                    '' set flag to child thread
    CondSignal(cond1)            '' send conditional signal to child thread
    MutexUnlock(mutex1)
    Prnt("-", 10)
    MutexLock(mutex2)
    While flag2 = 0              '' test flag set from child thread
        CondWait(Cond2, mutex2)  '' wait for conditional signal from child thread
    Wend
    flag2 = 0                    '' reset flag
    Prnt("1", 10)
    MutexUnlock(mutex2)
Next I

MutexLock(mutex1)
quit = 1                         '' set quit for child thread
flag1 = 1
CondSignal(cond1)                '' send conditional signal to child thread
MutexUnlock(mutex1)
ThreadWait(pt)                   '' wait for child thread to end
Print

MutexDestroy(mutex1)
MutexDestroy(mutex2)
CondDestroy(cond1)
CondDestroy(cond2)

Sleep

Output:
'1': Main thread procedure running (alone)
'2': Child thread procedure running (alone)
'-': Main thread procedure running (with the one of child thread)
'=': Child thread procedure running (with the one of main thread)

-==-=-=--==--==-=-=-22222222221111111111-=-=-=-==--==-=--==-22222222221111111111-=-=-==-=-=--=-=-==-22222222221111111111
-=-=-=-=-=-=--==-=-=22222222221111111111-==--==--==-=-=--==-22222222221111111111
Back to top

14. What happens when multiple threads are waiting on the same condition variable?

  • If 'CondSignal()' is used:
The specification states that the thread scheduler arbitrarily notifies one of the threads waiting on the condition variable.
Since this method notifies an arbitrary thread, some documentation also states that it is therefore possible for a waiting thread to never be notified if it is never alone in the queue on the condition variable.
But my tests on Windows seem to show that the order of entry into the queue on a same condition variable determines the order of exit: first in, first out.
I have not tested this on Linux or any other platform, but in any case this behavior I observed is unspecified.

  • If 'CondBroadcast()' is used:
The specification states that the thread scheduler notifies all threads waiting on the condition variable in an arbitrary order.

The example below works with 6 threads (in addition to the main thread).
The first 3 threads (#1 to #3) are waiting on their own condition variable, while the last 3 threads (#4 to #6) are waiting on a same other condition variable.
These last 3 threads are awakened either on 'CondSignal()' or on 'CondBroadcast()'.
Type ThreadData
    Dim As Integer id
    Dim As Any Ptr mutex
    Dim As Any Ptr cond
    Dim As Boolean flag
    Dim As Boolean quit
    Dim As Any Ptr handle
    Declare Static Sub Thread(ByVal p As Any Ptr)
End Type

Sub ThreadData.Thread(ByVal p As Any Ptr)
    Dim As ThreadData Ptr pdata = p
    Print "   thread #" & pdata->id & " is running"
    Do
        MutexLock(pdata->mutex)
            While pdata->flag = False
                CondWait(pdata->cond, pdata->mutex)
            Wend
            pdata->flag = False
        MutexUnlock(pdata->mutex)
        If pdata->quit = False Then
            Print "   thread #" & pdata->id & " is signaled"
        Else
            Exit Do
        End If
    Loop
    Print "   thread #" & pdata->id & " is finishing"
End Sub

Dim As Any Ptr mutex = MutexCreate()
Dim As Any Ptr cond(0 To 3) = {CondCreate(), CondCreate(), CondCreate(), CondCreate()}
Dim As ThreadData mythreads(1 To 6) = {Type(1, mutex, cond(1)), Type(2, mutex, cond(2)), Type(3, mutex, cond(3)), _
                                       Type(4, mutex, cond(0)), Type(5, mutex, cond(0)), Type(6, mutex, cond(0))}

Print "Threads from #1 to #6 are created:"
For I As Integer = LBound(mythreads) To UBound(mythreads)
    mythreads(I).handle = ThreadCreate(@ThreadData.Thread, @mythreads(I))
Next I
Sleep 1000, 1  '' wait for all threads started
Print
Print "----------------------------------------------------------"
Print

For I As Integer = 3 To 1 Step -1
    Print "Send a CondSignal to thread #" & I &":"
    MutexLock(mutex)
        mythreads(I).flag = True
        CondSignal(cond(I))
    MutexUnlock(mutex)
    Sleep 1000, 1  '' wait for the thread loop completed
    Print
Next I
Print "----------------------------------------------------------"
Print

Print "Send a single CondBroadcast to all threads from #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondBroadcast(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for all thread loops completed
Print "Send a single CondBroadcast to all threads from #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondBroadcast(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for all thread loops completed
Print "Send a single CondBroadcast to all threads from #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondBroadcast(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for all thread loops completed
Print
Print "----------------------------------------------------------"
Print

Print "Send a CondSignal to any thread among #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondSignal(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for a thread loop completed
Print "Send a CondSignal to any thread among #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondSignal(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for a thread loop completed
Print "Send a CondSignal to any thread among #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondSignal(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for a thread loop completed
Print

Print "Send a CondSignal to any thread among #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondSignal(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for a thread loop completed
Print "Send a CondSignal to any thread among #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondSignal(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for a thread loop completed
Print "Send a CondSignal to any thread among #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondSignal(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for a thread loop completed
Print

Print "Send a CondSignal to any thread among #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondSignal(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for a thread loop completed
Print "Send a CondSignal to any thread among #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondSignal(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for a thread loop completed
Print "Send a CondSignal to any thread among #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
    Next I
    CondSignal(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for a thread loop completed
Print
Print "----------------------------------------------------------"
Print

For I As Integer = 1 To 3
    Print "Send to finish a CondSignal to thread #" & I &":"
    MutexLock(mutex)
        mythreads(I).flag = True
        mythreads(I).quit = True
        CondSignal(cond(I))
    MutexUnlock(mutex)
    Sleep 1000, 1  '' wait for the thread loop completed
    Print
Next I
Print "----------------------------------------------------------"
Print

Print "Send to finish a single CondBroadcast to all threads from #4 to #6:"
MutexLock(mutex)
    For I As Integer = 4 To 6
        mythreads(I).flag = True
        mythreads(I).quit = True
    Next I
    CondBroadcast(cond(0))
MutexUnlock(mutex)
Sleep 1000, 1  '' wait for all thread loops completed
Print
Print "----------------------------------------------------------"
Print

For I As Integer = 1 To 3
    ThreadWait(mythreads(I).handle)
    CondDestroy(cond(I))
Next I
For I As Integer = 4 To 6
    ThreadWait(mythreads(I).handle)
Next I
Print "All threads from #1 to #6 are finished."
Print

MutexDestroy(mutex)
CondDestroy(cond(0))

Sleep

Output (for example):
Threads from #1 to #6 are created:
   thread #1 is running
   thread #3 is running
   thread #2 is running
   thread #5 is running
   thread #4 is running
   thread #6 is running

----------------------------------------------------------

Send a CondSignal to thread #3:
   thread #3 is signaled

Send a CondSignal to thread #2:
   thread #2 is signaled

Send a CondSignal to thread #1:
   thread #1 is signaled

----------------------------------------------------------

Send a single CondBroadcast to all threads from #4 to #6:
   thread #5 is signaled
   thread #6 is signaled
   thread #4 is signaled
Send a single CondBroadcast to all threads from #4 to #6:
   thread #6 is signaled
   thread #4 is signaled
   thread #5 is signaled
Send a single CondBroadcast to all threads from #4 to #6:
   thread #5 is signaled
   thread #4 is signaled
   thread #6 is signaled

----------------------------------------------------------

Send a CondSignal to any thread among #4 to #6:
   thread #5 is signaled
Send a CondSignal to any thread among #4 to #6:
   thread #4 is signaled
Send a CondSignal to any thread among #4 to #6:
   thread #6 is signaled

Send a CondSignal to any thread among #4 to #6:
   thread #5 is signaled
Send a CondSignal to any thread among #4 to #6:
   thread #4 is signaled
Send a CondSignal to any thread among #4 to #6:
   thread #6 is signaled

Send a CondSignal to any thread among #4 to #6:
   thread #5 is signaled
Send a CondSignal to any thread among #4 to #6:
   thread #4 is signaled
Send a CondSignal to any thread among #4 to #6:
   thread #6 is signaled

----------------------------------------------------------

Send to finish a CondSignal to thread #1:
   thread #1 is finishing

Send to finish a CondSignal to thread #2:
   thread #2 is finishing

Send to finish a CondSignal to thread #3:
   thread #3 is finishing

----------------------------------------------------------

Send to finish a single CondBroadcast to all threads from #4 to #6:
   thread #4 is finishing
   thread #5 is finishing
   thread #6 is finishing

----------------------------------------------------------

All threads from #1 to #6 are finished.
Back to top

15. How to optimize sequencing of successive user tasks executed by threading?

The delay between the return of 'ThreadCreate()' and the start of the thread code (first line of thread code) can be estimated at about 50 microseconds on average, but can go up to a few milliseconds at worst.
(see FAQ 12. What is the execution delay of the code of a thread after the thread is created by 'ThreadCreate'?)

This is why a child thread can be launched only once (by a constructor for example) and execute a permanent waiting loop of user tasks (to avoid a thread launch latency each time), then at end stopped (by a destructor).
The synchronization between the main thread and the child thread (start of each user task and user task completed) can be managed by means of 2 mutexes.

Example to estimate the average time to execute a sequence of user tasks (with empty user procedure body):
- either launched by successive threads,
- or launched by a single thread.
Sub userTask(ByVal p As Any Ptr)   '' task to execute
End Sub

Dim As Double t

'---------------------------------------------------------------------------

Print "Successive (empty) user tasks executed by one thread for each:"
t = Timer
For i As Integer = 1 To 10000
    Dim As Any Ptr p = ThreadCreate(@userTask)
    ThreadWait(p)
Next i
t = Timer - t
Print Using "######.### microdeconds per user task"; t * 100
Print

'---------------------------------------------------------------------------

Type thread
    Public:
        Dim As Sub(ByVal p As Any Ptr) task  '' pointer to user task
        Declare Sub Launch()                 '' launch user task
        Declare Sub Wait()                   '' wait for user task completed
        Declare Constructor()
        Declare Destructor()
    Private:
        Dim As Any Ptr mutex1
        Dim As Any Ptr mutex2
        Dim As Any Ptr handle
        Dim As Boolean quit
        Declare Static Sub proc(ByVal pthread As thread Ptr)
End Type

Constructor thread()
    This.mutex1 = MutexCreate
    This.mutex2 = MutexCreate
    MutexLock(This.mutex1)
    MutexLock(This.mutex2)
    This.handle = ThreadCreate(CPtr(Any Ptr, @thread.proc), @This)
End Constructor

Destructor thread()
    This.quit = True
    MutexUnlock(This.mutex1)
    ThreadWait(This.handle)
    MutexDestroy(This.mutex1)
    MutexDestroy(This.mutex2)
End Destructor

Sub thread.proc(ByVal pthread As thread Ptr)
    Do
        MutexLock(pthread->mutex1)    '' wait for launching task
        If pthread->quit = True Then Exit Sub
        pthread->task(pthread)
        MutexUnlock(pthread->mutex2)  '' task completed
    Loop
End Sub

Sub thread.Launch()
    MutexUnlock(This.mutex1)
End Sub

Sub thread.Wait()
    MutexLock(This.mutex2)
End Sub

Print "Successive (empty) user tasks executed by a single thread for all:"
t = Timer
Dim As thread Ptr pThread = New Thread
pThread->task = @userTask
For i As Integer = 1 To 10000
    pThread->Launch()
    pThread->Wait()
Next i
Delete pThread
t = Timer - t
Print Using "######.### microdeconds per user task"; t * 100
Print

Sleep

Output (for example):
Successive (empty) user tasks executed by one thread for each:
   145.004 microdeconds per user task

Successive (empty) user tasks executed by a single thread for all:
     6.691 microdeconds per user task
Back to top

16. Why is multi-threading performance penalized by many shared memory accesses (even more in writing mode)?

Each core has its own cache memory that allows to buffer the useful data (in read and write) of the shared memory.
Consequently, a cache coherence algorithm between cores is executed, to keep, in case of writing in the cache and for the common memory areas between caches, the most recent values ​​among all the caches concerned.
It is this algorithm which penalizes the performance of multi-threading in the case of multiple accesses in shared memory, even more particularly in write mode.

It is therefore necessary to limit as much as possible the access of threads to shared memory, even more in writing.
For example, all intermediate results of threads could be performed in local memory, and only the final useful ones put in shared memory.

Example of a member thread procedures computing the sum of the first N integers, by accumulation directly in the shared memory ('SumUpTo_1()') or internally in its local memory before copy back ('SumUpTo_2'()'):
Type Thread
    Dim As UInteger valueIN
    Dim As Double valueOUT
    Dim As Any Ptr pHandle
    Declare Static Sub SumUpTo_1(ByVal pt As Thread Ptr)
    Declare Static Sub SumUpTo_2(ByVal pt As Thread Ptr)
End Type

Sub Thread.SumUpTo_1(ByVal pt As Thread Ptr)
    pt->valueOut = 0
    For I As UInteger = 1 To pt->valueIN
        pt->valueOUT += I
    Next I
End Sub

Sub Thread.SumUpTo_2(ByVal pt As Thread Ptr)
    Dim As Double value = 0
    For I As UInteger = 1 To pt->valueIN
        value += I
    Next I
    pt->valueOUT = value
End Sub

Sub MyThreads(ByVal pThread As Any Ptr, ByVal threadNB As UInteger = 1)
    Dim As Thread td(1 To threadNB)
    Dim As Double t

    t = Timer
    For i As Integer = 1 To threadNB
        td(i).valueIN = 100000000 + i
        td(i).pHandle = ThreadCreate(pThread, @td(i))
    Next I
    For i As Integer = 1 To threadNB
        ThreadWait(td(i).pHandle)
    Next I
    t = Timer - t

    For i As Integer = 1 To threadNB
        Print "   SumUpTo(" & td(i).valueIN & ") = " & td(i).valueOUT, _
              "(right result : " & (100000000# + i) * (100000000# + i + 1) / 2 & ")"
    Next I
    Print "      total time : " & t & " s"
    Print
End Sub

Print
For i As Integer = 1 To 4
    Print "Each thread (in parallel) accumulating result directly in shared memory:"
    Mythreads(@Thread.SumUpTo_1, I)
    Print "Each thread (in parallel) accumulating result internally in its local memory:"
    Mythreads(@Thread.SumUpTo_2, I)
    Print "-----------------------------------------------------------------------------"
    Print
Next i

Sleep

Output (for example):
Each thread (in parallel) accumulating result directly in shared memory:
   SumUpTo(100000001) = 5000000150000001  (right result : 5000000150000001)
      total time : 1.668927300015184 s

Each thread (in parallel) accumulating result internally in its local memory:
   SumUpTo(100000001) = 5000000150000001  (right result : 5000000150000001)
      total time : 1.004467599958389 s

-----------------------------------------------------------------------------

Each thread (in parallel) accumulating result directly in shared memory:
   SumUpTo(100000001) = 5000000150000001  (right result : 5000000150000001)
   SumUpTo(100000002) = 5000000250000003  (right result : 5000000250000003)
      total time : 4.314032700025791 s

Each thread (in parallel) accumulating result internally in its local memory:
   SumUpTo(100000001) = 5000000150000001  (right result : 5000000150000001)
   SumUpTo(100000002) = 5000000250000003  (right result : 5000000250000003)
      total time : 1.032165899962706 s

-----------------------------------------------------------------------------

Each thread (in parallel) accumulating result directly in shared memory:
   SumUpTo(100000001) = 5000000150000001  (right result : 5000000150000001)
   SumUpTo(100000002) = 5000000250000003  (right result : 5000000250000003)
   SumUpTo(100000003) = 5000000350000006  (right result : 5000000350000006)
      total time : 6.727616399944395 s

Each thread (in parallel) accumulating result internally in its local memory:
   SumUpTo(100000001) = 5000000150000001  (right result : 5000000150000001)
   SumUpTo(100000002) = 5000000250000003  (right result : 5000000250000003)
   SumUpTo(100000003) = 5000000350000006  (right result : 5000000350000006)
      total time : 1.128656100041894 s

-----------------------------------------------------------------------------

Each thread (in parallel) accumulating result directly in shared memory:
   SumUpTo(100000001) = 5000000150000001  (right result : 5000000150000001)
   SumUpTo(100000002) = 5000000250000003  (right result : 5000000250000003)
   SumUpTo(100000003) = 5000000350000006  (right result : 5000000350000006)
   SumUpTo(100000004) = 5000000450000010  (right result : 5000000450000010)
      total time : 6.829728199980309 s

Each thread (in parallel) accumulating result internally in its local memory:
   SumUpTo(100000001) = 5000000150000001  (right result : 5000000150000001)
   SumUpTo(100000002) = 5000000250000003  (right result : 5000000250000003)
   SumUpTo(100000003) = 5000000350000006  (right result : 5000000350000006)
   SumUpTo(100000004) = 5000000450000010  (right result : 5000000450000010)
      total time : 1.164915200012842 s

-----------------------------------------------------------------------------
One can check that the multi-threading performance is strongly penalized by many shared memory accesses in write mode:
For the case where the thread accumulates the result in shared memory, there is no longer any gain from multi-threading (and even a little loss), whereas for the case where the thread accumulates the result in internal memory, the gain is almost at the theoretical maximum value.

On the other hand, we observe a smaller degradation in multi-threading performance when accessing shared memory in read-only mode.

Back to top

17. Why is multi-threading performance heavily penalized by many manipulation of var-len strings and var-len arrays?

For all pseudo-objects like var-len strings and var-len arrays, only the descriptors can be put into local memory but not the data itself which is always on the heap (only fixed-length data can be put into local memory).
The heap is shared memory and this incurs penality on multi-threading performance as described in the above FAQ.
(see FAQ 16. Why is multi-threading performance penalized by many shared memory accesses (even more in writing mode)?)

For var-len arrays, local fixed-len arrays can be used instead, since this array data is always placed in local scope memory.
Not only do you have to define a fixed maximum size to allocate for each array, but for each of them you have to associate an index variable (per dimension if necessary) that points to the last useful element ('Redim' is replaced by updating this index variable).

For var-len strings, local fix-len [z]strings can be used instead, since these [z]string data are always placed in local scope memory.
All built-in string functions except 'Len()' and 'Asc()' and all string operators should also not be used on [z]strings since they work internally with var-len strings. Instead, use user code that only works on [z]string indexes.
Note:
But fix-len strings ('Dim As String * N') are less convenient to use than fix-len zstrings ('Dim As Zstring * N'), because the former cannot be passed by reference to a procedure (but only by copy), unlike the latter ('Byref As Zstring').

Additionally, all dynamic memory allocation/reallocation/deallocation requests (to be thread-safe) are serialized internally using mutex locking and unlocking.

The following example compares the multithreaded performance of two types of code:
- code with var-len strings using its built-in functions and operators like '= (assign)', 'Instr()', 'Mid()' and 'Ucase()',
- code with fix-len zstrings with user code equivalent to the previous built-in functions and operators, but operating only on zstring indexes.
("Asc()" and "Len()" are the only ones used because they have no impact on performance)
Type Thread
    Dim As UInteger value
    Dim As Any Ptr pHandle
    Declare Static Sub thread1(ByVal pt As Thread Ptr)
    Declare Static Sub thread2(ByVal pt As Thread Ptr)
End Type

Sub Thread.thread1(ByVal pt As Thread Ptr)
    Dim As Integer result
    For n As Integer = 1 To pt->value
        Dim As String s1
        Dim As String s2
        Dim As String s3
        s1 = "FreeBASIC rev 1.20"
        result = InStr(s1, "rev")
        s2 = Mid(s1, result)
        s3 = UCase(s2)
    Next n
End Sub

Sub Thread.thread2(ByVal pt As Thread Ptr)
    Dim As Integer result
    For n As Integer = 1 To pt->value
        Dim As ZString * 256 z1
        Dim As ZString * 256 z2
        Dim As ZString * 256 z3
        ' instead of: z1 = "FreeBASIC rev 1.20"
        For i As Integer = 0 To Len("FreeBASIC rev 1.20")
            z1[i] = ("FreeBASIC rev 1.20")[i]
        Next i
        ' instead of: result = Instr(z1, "rev")
        result = 0
        For i As Integer = 0 To Len(z1) - Len("rev")
            For j As Integer = 0 To Len("rev") - 1
                If z1[i + j] <> ("rev")[j] Then Continue For, For
            Next j
            result = i + 1
            Exit For
        Next i
        ' instead of: z2 = Mid(z1, result)
        For i As Integer = result - 1 To Len(z1)
            z2[i - result + 1] = z1[i]
        Next i
        ' instead of: z3 = Ucase(z2)
        For i As Integer = 0 To Len(z2)
            z3[i] = z2[i]
            If z3[i] >= Asc("a") AndAlso z3[i] <= Asc("z") Then z3[i] -= 32
        Next i
    Next n
End Sub

Sub MyThreads(ByVal pThread As Any Ptr, ByVal threadNB As UInteger = 1)
    Dim As Thread td(1 To threadNB)
    Dim As Double t

    t = Timer
    For i As Integer = 1 To threadNB
        td(i).value = 100000
        td(i).pHandle = ThreadCreate(pThread, @td(i))
    Next I
    For i As Integer = 1 To threadNB
        ThreadWait(td(i).pHandle)
    Next I
    t = Timer - t

    Print "      total time for " & threadNB & " threads in parallel: " & t & " s"
    Print
End Sub

Print
For i As Integer = 1 To 8
    Print "Each thread using var-len strings, with its built-in functions and operators:"
    Mythreads(@Thread.thread1, I)
    Print "Each thread using fix-len zstrings, with user code working on zstring indexes:"
    Mythreads(@Thread.thread2, I)
    Print "------------------------------------------------------------------------------"
    Print
Next i

Sleep

Output (for example):
Each thread using var-len strings, with its built-in functions and operators:
      total time for 1 threads in parallel: 0.08449090004432946 s

Each thread using fix-len zstrings, with user code working on zstring indexes:
      total time for 1 threads in parallel: 0.02201449999120086 s

------------------------------------------------------------------------------

Each thread using var-len strings, with its built-in functions and operators:
      total time for 2 threads in parallel: 0.1947050000308082 s

Each thread using fix-len zstrings, with user code working on zstring indexes:
      total time for 2 threads in parallel: 0.02090729994233698 s

------------------------------------------------------------------------------

Each thread using var-len strings, with its built-in functions and operators:
      total time for 3 threads in parallel: 0.3338784999214113 s

Each thread using fix-len zstrings, with user code working on zstring indexes:
      total time for 3 threads in parallel: 0.0279372000368312 s

------------------------------------------------------------------------------

Each thread using var-len strings, with its built-in functions and operators:
      total time for 4 threads in parallel: 0.4927077000029385 s

Each thread using fix-len zstrings, with user code working on zstring indexes:
      total time for 4 threads in parallel: 0.02361949998885393 s

------------------------------------------------------------------------------

Each thread using var-len strings, with its built-in functions and operators:
      total time for 5 threads in parallel: 0.7089884000597522 s

Each thread using fix-len zstrings, with user code working on zstring indexes:
      total time for 5 threads in parallel: 0.02638950000982732 s

------------------------------------------------------------------------------

Each thread using var-len strings, with its built-in functions and operators:
      total time for 6 threads in parallel: 0.9172402999829501 s

Each thread using fix-len zstrings, with user code working on zstring indexes:
      total time for 6 threads in parallel: 0.0310587000567466 s

------------------------------------------------------------------------------

Each thread using var-len strings, with its built-in functions and operators:
      total time for 7 threads in parallel: 1.159198799985461 s

Each thread using fix-len zstrings, with user code working on zstring indexes:
      total time for 7 threads in parallel: 0.02898070006631315 s

------------------------------------------------------------------------------

Each thread using var-len strings, with its built-in functions and operators:
      total time for 8 threads in parallel: 1.403980100061744 s

Each thread using fix-len zstrings, with user code working on zstring indexes:
      total time for 8 threads in parallel: 0.03312029992230237 s

------------------------------------------------------------------------------
One can check that the multi-threading performance is strongly penalized by many var-len string manipulation:
For the case where the thread uses var-len strings and its built-in functions and operators, there is no longer any gain from multi-threading (and even losses), whereas for the case where the thread uses fix-len zstrings and user code working on zstring indexes only (except 'Asc()' and 'Len()' usage), the gain is almost at the theoretical maximum value.

Back to top

See also

  • Multi-Threading Overview
  • Threads
  • Mutual Exclusion
  • Conditional Variables
  • Critical Sections
  • Emulate a TLS (Thread Local Storage) and a TP (Thread Pooling) feature


Executables
Making a binary executable file from FreeBASIC source files.

Preamble:

A binary file is simply one in a binary (i.e. non-text) format:
- The binary format means that the file's contents should not be transformed for platform-specific reasons (e.g. replacing newlines from \n to \r\n).
- Binary files are not necessarily executable, for example a library compiled to '*.dll' or '*.a' form is a binary but not an executable.
- Executable files are not necessarily binary, for example a script in text form can be made executable on Operating Systems.

An executable file is one which can be executed (it can be run on the command-line by writing the name of the file itself as the command).
On Windows, the file's extension must be one of a fixed set of executable file extensions, including '*.exe'.
On Unix systems, the file's "executable" flag must also be set.

FreeBASIC consists of fbc (the command line compiler/linker), the runtime libraries, and FreeBASIC header files for third-party libraries.
In order to produce executables, fbc uses the GNU binutils (assembler, linker). When compiling for architectures other than 32bit x86, fbc depends on gcc to generate assembly.

FreeBASIC provides the FreeBASIC compiler/linker program (fbc or fbc.exe), as well as the tools and libraries it uses:
- fbc is a command line program that takes FreeBASIC source/include files ('*.bas'/'*.bi') and object/library files ('*.o'/'*.a'), then compiles them into executables.
- fbc is typically invoked by Integrated Development Environments (IDEs) or text editors, from a terminal or command prompt, or through build-systems such as makefiles.
- fbc itself is not a graphical code editor or IDE!

By default, FreeBASIC programs are linked against various system and/or support libraries, depending on the platform.
Those include the DJGPP libraries used by FreeBASIC for DOS and the MinGW/GCC libraries used by FreeBASIC for Windows.

Compiling an executable, in general

fbc is a compiler that takes fbc source code and transforms it in to a file that can be loaded and executed (run) by the operating system.
fbc doesn't do this all on it's own. It uses some intermediate files and other tools to complete this transformation.

The "main" entry point of an executable
An executable needs a starting point.
This starting point which we will call the "main" function or "main" entry point needs to be recorded in the executable so that when the executable file is loaded by the operating system, the operating system knows where to begin execution of the program.

By default, the "main" function or starting point will be the first line of the first basic source file on the command line:
$ fbc program.bas module1.bas module2.bas
"program" becomes the main module because it is first, and fbc will generate an implicit "main" function that will be executed first when the executable is loaded.

This default can be overridden with the '-m module' option to specify a main module that is not the first source file given on the command line:
$ fbc -m program module1.bas module2.bas program.bas
The "-m program" option tells fbc to use "program.bas" as the main module, even though "program.bas" is not listed first.

If no other option is given that will affect the compile process, this "main" function is generated implicitly by fbc.
There can be only one "main" function for an executable. It's not possible to have more than one "main" function.

Compile process for an executable

When fbc compiles basic source code, it translates the source in to another format that can be used by other tools that eventually create an executable.
By default, fbc will use these other tools automatically:
'     .-------------------------------------.
'     |              COMPILER               |
'     '------.----------------------.-------'
'            | GAS backend          | GCC backend
'            |                      |
'     .------V-----.    gcc     .---V----.
'     |  ASM CODE  |<-----------| C CODE |
'     | .s or .asm |  compiler  |   .c   |
'     '------.-----'            '--------'
'            | (G)AS assembler
'            |
'     .------V------.
'     | OBJECT CODE |
'     | .o or .obj  |-------------.
'     '------.------'             |
'            | (G)AR archiver     |
'            |                    |
'    .-------V--------.           |
'    | STATIC LIBRARY |           |
'    |   .a or .lib   |---------->|
'    '----------------'           | (G)LD linker
'                                 |
'                                 |----------------------------.
'                                 |                            |
'                      .----------V-----------.    .-----------V-----------.
'                      |        BINARY        |    |    SHARED LIBRARY     |
'                      | no extension or .exe |    | .so or .dll or .dylib |
'                      '----------------------'    '-----------------------'
'
'
'      Extensions are Unix convention vs Windows (ld does .lib too nowadays).
'      In the case of shared libs, the name for Mac deviates (.dylib).

To see all the steps that fbc uses, specify '-v' on the command line to see the steps.
For example, on win32:
$ fbc a.bas -v
FreeBASIC Compiler - Version 1.08.0 (2021-01-24), built for win32 (32bit)
Copyright (C) 2004-2021 The FreeBASIC development team.
standalone
target:       win32, 486, 32bit
backend:      gas
compiling:    a.bas -o a.asm (main module)
assembling:   D:\fb.git\bin\win32\as.exe --32 --strip-local-absolute "a.asm" -o "a.o"
linking:      D:\fb.git\bin\win32\ld.exe -m i386pe -o "a.exe" -subsystem console
"D:\fb.git\lib\win32\fbextra.x" --stack 1048576,1048576 -s -L "D:\fb.git\lib\win32"
-L "." "D:\fb.git\lib\win32\crt2.o" "D:\fb.git\lib\win32\crtbegin.o" "D:\fb.git\lib\win32\fbrt0.o"
"a.o" "-(" -lfb -lgcc -lmsvcrt -lkernel32 -luser32 -lmingw32 -lmingwex -lmoldname -lgcc_eh "-)"
"D:\fb.git\lib\win32\crtend.o"

Tools:
- [ fbc ] compiler translate *.bas in to *.asm or *.c files
- [ gcc ] compiler translate *.c files in to *.asm files
- [ as ] assembler translate *.asm files in to *.o object files
- [ ld ] linker join *.o files (and other files) in to executable files
- emscripten backend has other tools
- llvm backend has other tools

- GNU assembler 32-bit backend (-gen gas):
*.bas => [ fbc ] => *.asm compile (first stage) to assembly (-r or -rr, -R or -RR)
*.asm => [ as ] => *.o assemble to object file (-c, -C)
*.o => [ ld ] => *[.exe] link to executable

- GNU assembler 64-bit backend (-gen gas64):
*.bas => [ fbc ] => *.asm compile (first stage) to assembly (-r or -rr, -R or -RR)
*.asm => [ as ] => *.o assemble to object file (-c, -C)
*.o => [ ld ] => *[.exe] link to executable

- GCC compiler backend (-gen gcc):
*.bas => [ fbc ] => *.c compile (first stage) to C (-r, -R)
*.c => [ gcc ] => *.asm compile (second stage) to assembly (-rr, -RR)
*.asm => [ as ] => *.o assemble to object file (-c, -C)
*.o => [ ld ] => *[.exe] link to executable

Options controlling compile / assemble / link stages
There are a few options that can control what fbc does with the intermediate files and at what point the process may be stopped early.

-r, -rr, -c : stop the compile / assemble process sometime before the link stage
Compiler Option -r : compile up to first stage, keep file (*.asm/*.c), and stop
Compiler Option -rr : compile up to second stage, keep file (*.asm), and stop
Compiler Option -c : compile up to assembly stage, keep file (*.o), and stop

-R, -RR, -C : keep intermediate files at compile / assemble stages then continue to next stage
Compiler Option -R : don't delete compile (first stage) intermediate file (*.asm/*.c)
Compiler Option -RR : don't delete compile (second stage) intermediate file (*.asm)
Compiler Option -C : don't delete assemble stage intermediate file (*.o)

-r : option overrides -rr, -RR, -c, -C
-rr : overrides overrides -c, -C
-r and -rr : behave the same if there is only one compile stage
-R and -RR : behave the same if there is only one compile stage

-r, -rr, -c : override the default behaviour of creating an implicit "main" entry point, and no "main" function is created by default.
To have a "main" entry point when using the -r, -rr, -c, options, then '-m module' option needs to be used to indicate which module should have an "main" function.

Note:
Before fbc version 1.09.0, -gen gas64 generated a .a64 file extension for assembler (instead of .asm).

Execution order of the different modules

An executable program needs a "main" point of entry (in the main module user code).
fbc may or may not create an implicit main function, depending on options or method of building an executable.
The module constructors of modules run before main, the module destructors of modules run after main.

The module-level codes of other modules (than the main module) are put in implicit module constructors which are consequently executed before the module-level code of the main module.
But it would be good practice that module-level code only be in the main module.
More generally, all module constructors and module destructors should be noted with a big WARNING on their use. Because the corresponding code is run outside of user code, it's quite likely that fbc's error checking and some runtime facilities won't work as expected.

A high level description of the start-up framework:
- At compile time make arrays of addresses for all the module constructors and destructors.
- At link time, store the arrays in the executable.
- At run time, the start-up framework will:
- call all the module constructors in the array,
- call the main module user code,
- on exit (or error), call all the module destructors (usually, depends on how the program failed though).

See also

  • fbc command-line
  • Static Libraries
  • Shared Libraries (DLLs)
  • Profiling with fb's profiler
  • Profiling for gmon/gprof
  • Embed and Access binary Data in Executable


Static Libraries
A static library is compiled code that can be later used when building an executable.

When the compiler makes an executable, the basic source files are first turned in to object files. The object files are then linked together to make an executable. When we compile source code, we don't necessarily have to make an executable. We could instead group all of the object files (made from sources) in to a single file called a static library.

The library is referred to as static, because when the object files which it contains are later linked in to an executable, a copy of all the needed code in the library is added to the executable.

Once the library is made, we can then use the code that it contains just as if we were compiling the source directly with our program.

Exchanging/Sharing variables with static library

A static library allows the direct sharing of variables by using the Common or Extern keyword, both in library code and module code.
Otherwise, passing a parameter (by value or by reference) to a library procedure or returning a variable (by value or by reference) from a library function allows to indirectly exchange data (by value) or share data (by reference) with a shared library.

Simple example of static library

Following is a simple example of creating a static library using these three files:
    • mylib.bas - the source for the library
    • mylib.bi - the header for the library
    • mytest.bas - a test program

Our library will be a single module providing a single function:
'' mylib.bas
'' compile with: fbc -lib mylib.bas

'' Add two numbers together and return the result
Public Function Add2( ByVal x As Integer, ByVal y As Integer ) As Integer
  Return( x + y )
End Function

Compile the library with:
fbc -lib mylib.bas

The -lib option tells the compiler to take the source code, mylib.bas, and turn it in to an object file mylib.o, then store the object file in to a library file, also called an archive, libmylib.a. A library might contain many modules (source files) each with many functions, but for this simple example, it is just one each.

To make use of the library in some other source code, we need some way of telling the compiler what exactly is in the library. A good way to do this is to put the declarations ( also called an interface, or API ) for the library in to a header file:
'' mylib.bi
#inclib "mylib"
Declare Function Add2( ByVal x As Integer, ByVal y As Integer ) As Integer

There is no need to compile the header. We want this in its source form so it can be included with other source files. The #inclib statement will tell the compiler the name of a static library that we need to link with when eventually making an executable.

With our library (.a file) and a header (.bi file) we can try them out in a test program:
'' mytest.bas
'' compile with: fbc mytest.bas
#include Once "mylib.bi"
Print Add2(1,2)

The #include statement tells the compiler to include the source code from mylib.bi just as if we had typed it in to the original source. With the way we have written our include file, it tells the compiler everything it needs to know about the library.

We compile this with:
fbc mytest.bas

Then when we run the mytest executable, we should get the result of:
 3
Advanced example of OOP static library

Following is an advanced example of creating an OOP static library using these three files:
    • varZstring.bi - the header for the library
    • varZstring.bas - the source for the library
    • varZstringTest.bas - a test program

Similar method than above to compile the library, then compile and execute the test program.

Header file for the library:
'' header file: 'varZstring.bi'

Type varZstring Extends ZString
    Public:
        Declare Constructor (ByRef z As Const ZString)
        Declare Operator Cast () ByRef As ZString
        Declare Operator Let (ByRef z As Const ZString)
        Declare Property allocated () As Integer
        Declare Destructor ()
    Private:
        Dim As ZString Ptr _p
        Dim As UInteger _allocated
End Type

Declare Operator Len (ByRef v As varZstring) As Integer  '' mandatory for the user code to call
                                                         ''    the overload Len operator and
                                                         ''    not the prebuilt-in Len operator

Note: Take care to declare also each overload procedure, because the absence of declaration will not necessarily lead to a compilation error if its usage in the user code is syntactically compatible with the pre-built in procedure, but the result will obviously not be as expected.

Source file for the library:
'' library module: 'varZstring.bas'

#include "varZstring.bi"

Constructor varZstring (ByRef z As Const ZString)
    If This._p <> 0 Then
        Deallocate(This._p)
    End If
    This._allocated = Len(z) + 1
    This._p = CAllocate(This._allocated, SizeOf(ZString))
    *This._p = z
End Constructor

Operator varZstring.Cast () ByRef As ZString
    Return *This._p
End Operator

Operator varZstring.Let (ByRef z As Const ZString)
    If This._allocated < Len(z) + 1 Then
    Deallocate(This._p)
    This._allocated = Len(z) + 1
    This._p = CAllocate(This._allocated, SizeOf(ZString))
  End If
  *This._p = z
End Operator

Property varZstring.allocated () As Integer
    Return This._allocated
End Property

Destructor varZstring ()
    Deallocate(This._p)
    This._p = 0
End Destructor

Operator Len (ByRef v As varZstring) As Integer
    Return Len(Type<String>(v))  '' found nothing better than this
End Operator                     ''     (or: 'Return Len(Str(v))')

Test program file:
'' test program: 'varZstringTest.bas'

#include "varZstring.bi"  '' must contain also the overload Len operator declaration,
                          ''    otherwise the prebuilt-in Len operator is called and applied
                          ''    on the object variable (providing the length of its member data)

#inclib "varZstring"      '' one can also put this line in the 'varZstring.bi' file
                          ''   (and not in this file), but it seems a bit crooked

Print "VARIABLE",,, "|     LEN|  SIZEOF|"
Print "------------------------------------------|--------|--------|"

Dim As varZstring v = "FreeBASIC"  '' adjusts memory allocation to minimum
Print "varZstring v:", "'" & v & "'",, "|"; Using "########|"; Len(v); v.allocated

Dim As ZString * 256 z
z = v
Print "Zstring    z:", "'" & z & "'",, "|"; Using "########|"; Len(z); SizeOf(z)

v = z & " & SourceForge"  '' only increases memory allocation if necessazy
Print "varZstring v:", "'" & v & "'", "|"; Using "########|"; Len(v); v.allocated

v = z & " & Wiki" '' only increases memory allocation if necessazy
Print "varZstring v:", "'" & v & "'", "|"; Using "########|"; Len(v); v.allocated

v.Constructor(v)  '' readjusts memory allocation to minimum
Print "varZstring v:", "'" & v & "'", "|"; Using "########|"; Len(v); v.allocated

Sleep

Output:
VARIABLE                                  |     LEN|  SIZEOF|
------------------------------------------|--------|--------|
varZstring v: 'FreeBASIC'                 |       9|      10|
Zstring    z: 'FreeBASIC'                 |       9|     256|
varZstring v: 'FreeBASIC & SourceForge'   |      23|      24|
varZstring v: 'FreeBASIC & Wiki'          |      16|      24|
varZstring v: 'FreeBASIC & Wiki'          |      16|      17|
More than one source module can be used when making a library. And basic programs can use more than one library by including each needed header. Some libraries are so large that they might use several headers. On very large projects, making libraries out of some code modules that seldom change can improve compile times dramatically.

Libraries can optionally contain debugging information specified with the -g command line option.

A library can optionally have module constructors, a main code, and module destructors. The module constructors, then the main code are executed at library load. The module destructors are executed at library unload.

Object files, and therefore libraries, are platform specific and in some cases specific to a particular version of the compiler and FreeBASIC runtime library.

See also

  • Shared Libraries
  • #inclib
  • #include
  • Compiler Option: -lib
  • Compiler Option: -static


Shared Libraries
A shared library is compiled code that can be loaded and used later when running an executable.

When the compiler makes an executable, the basic source files are first turned in to object files. The object files are then linked together to make an executable. A shared library is much like a static library in that it contains object files. But a shared library is also like an executable in that it only gets loaded when the executable is running.

The library is referred to as shared, because the code in the library is loaded by an executable at runtime and can be loaded by more than one executable, even though there might only be one copy of the shared library.

Once the library is made, we can then use the code that it contains just as if we were compiling the source directly with our program.

Shared Library Example
Using Shared Libraries on Windows
Using Shared Libraries on Linux
Using Shared Libraries on DOS => see its own page:Shared Libraries - DOS
Executables that export symbols
Loading Shared Libraries Dynamically
Exchanging/Sharing variables with shared library on Windows
Creating import Libraries from def files, or import libraries or def files from DLL files, on Windows

Shared Library Example

Following is a simple example of creating a shared library using these three files:
    • mylib.bas - the source for the library
    • mylib.bi - the header for the library
    • mytest.bas - a test program

Our library will be a single module providing a single function:
'' mylib.bas
'' compile with: fbc -dll mylib.bas

'' Add two numbers together and return the result
Public Function Add2( ByVal x As Integer, ByVal y As Integer ) As Integer Export
  Return( x + y )
End Function

Compile the library with:
fbc -dll mylib.bas

The -dll option tells the compiler to take the source code, mylib.bas, and turn it in to an object file mylib.o, then store the object file in to a shared library. The name of the shared library will have a .so extension or .dll extension depending on if the platform is the linux or windows version. A library might contain many modules (source files) each with many functions, but for this simple example, it is just one each.

Making a shared library is almost identical to making a static library except for the addition of Export specifier at first line of procedure definition. Export tells the compiler to make the function visible to other executables loading the shared library.

To make use of the library in some other source code, we need some way of telling the compiler what exactly is in the library. A good way to do this is to put the declarations ( also called an interface, or API ) for the library in to a header file:
'' mylib.bi
#inclib "mylib"
Declare Function Add2( ByVal x As Integer, ByVal y As Integer ) As Integer

There is no need to compile the header. We want this in its source form so it can be included with other source files. The #inclib statement will tell the compiler the name of a shared library that we need to link with at runtime running an executable that needs it.

With our library (.dll / .so file) and a header (.bi file) we can try them out in a test program:
'' mytest.bas
'' compile with: fbc mytest.bas
#include Once "mylib.bi"
Print Add2(1,2)

The #include statement tells the compiler to include the source code from mylib.bi just as if we had typed it in to the original source. With the way we have written our include file, it tells the compiler everything it needs to know about the library.

We compile this with:
fbc mytest.bas

Then when we run the mytest executable, we should get the result of:
 3
More than one source module can be used when making a library. And basic programs can use more than one library by including each needed header. Some libraries are so large that they might use several headers. On very large projects, making shared libraries out of some code modules that seldom change can improve compile times and link times dramatically.

Shared libraries can optionally contain debugging information specified with the -g command line option.

A shared library can optionally have module constructors, a main code, and module destructors. The module constructors, then the main code are executed at library load. The module destructors are executed at library unload.

Object files, and therefore shared libraries, are platform specific and in some cases specific to a particular version of the compiler and FreeBASIC runtime library.

Using Shared Libraries on Windows

On Windows, the shared library must be stored in a location where it can be found by the executable that needs it a run-time.

The operating system may search the following directories:
    • The directory from which the executable was loaded.
    • The current directory.
    • The Windows and Windows system folder.
    • Directories list in the PATH environment variable.

The order in which directories are searched may depend on the Windows version in use, and on what settings that the operating system is configured with.

Using Shared Libraries on Linux

By default, Linux will not normally search the current directory or the directory from which the executable was loaded. You will need to either:
    • copy the .so file to a directory that has shared libraries (e.g. /usr/lib) and run ldconfig to configure the library.
    • modify the environment variable LD_LIBRARY_PATH to search the current directory or a specific directory for the newly created shared library.

To run the executable ./mytest/ and temporarily tell linux to search the current directory, use the following shell command:
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./mytest

Executables that export symbols

If an executable has symbols that must be available to other shared libraries when those shared libraries are loaded, use the Export export specifier at first line of procedure definition, and the -export command line option when making (linking) the executable.

The -export option has no extra effect when used with the -dylib or -dll command line options.

Loading Shared Libraries Dynamically

Shared libraries can be loaded and used at run time by dynamically loading the library and its symbols at runtime.
    • DyLibLoad can be used to load and obtain a handle to a shared library.
    • DyLibSymbol is used to obtain the address of a symbol in a loaded shared library (variable names not supported on Windows but only procedure names).
    • DyLibFree is used to unload a shared library when it is no longer needed.

Procedures in the shared library must use the Export specifier at first line of procedure definition to ensure that the symbols name is placed in the shared library's export table:
'' mydll.bas
'' compile as: fbc -dll mydll.bas
'' This will create mydll.dll (and libmydll.dll.a import library) on Windows,
'' and libmydll.so on Linux.
''
'' Note: libmydll.dll.a is an import library, it's only needed when creating
'' an executable that calls any of mydll's functions, only distribute
'' the DLL files with your apps, do not include the import libraries,
'' they are useless to end-users.

'' Simple exported function; the <alias "..."> disables FB's default
'' all-upper-case name mangling, so the DLL will export AddNumbers() instead of
'' ADDNUMBERS().
Function AddNumbers Alias "AddNumbers"( ByVal a As Integer, ByVal b As Integer ) As Integer Export
    Function = a + b
End Function

'' load.bas: Loads mydll.dll (or libmydll.so) at runtime, calls one of mydll's
'' functions and prints the result. mydll is not needed at compile time.
'' compile as: fbc test.bas
''
'' Note: The compiled mydll.dll (or libmydll.so) dynamic library is expected
'' to be available in the current directory.

'' Note we specify just "mydll" as library file name; this is to ensure
'' compatibility between Windows and Linux, where a dynamic library
'' has different file name and extension.
Dim As Any Ptr libhandle = DyLibLoad( "mydll" )
If( libhandle = 0 ) Then
    Print "Failed to load the mydll dynamic library, aborting program..."
    End 1
End If

'' This function pointer will be used to call the function from mydll, after
'' the address has been found. Note: It must have the same calling
'' convention and parameters.
Dim AddNumbers As Function( ByVal As Integer, ByVal As Integer ) As Integer
AddNumbers = DyLibSymbol( libhandle, "AddNumbers" )
If( AddNumbers = 0 ) Then
    Print "Could not retrieve the AddNumbers() function's address from the mydll library, aborting program..."
    End 1
End If

Randomize Timer

Dim As Integer x = Rnd * 10
Dim As Integer y = Rnd * 10

Print x; " +"; y; " ="; AddNumbers( x, y )

'' Done with the library; the OS will automatically unload libraries loaded
'' by a process when it terminates, but we can also force unloading during
'' our program execution to save resources; this is what the next line does.
'' Remember that once you unload a previously loaded library, all the symbols
'' you got from it via dylibsymbol will become invalid, and accessing them
'' will cause the application to crash.
DyLibFree( libhandle )

Exchanging/Sharing variables with shared library on Windows

Windows does not support the Common or Extern keywords for directly sharing variables with a shared library.
Otherwise (running on any platform), passing a parameter (by value or by reference) to a library procedure or returning a variable (by value or by reference) from a library function allows to indirectly exchange data (by value) or share data (by reference) with a shared library.

Example of workaround on Windows for sharing data (by reference) between main and dll code (works on any platform):
(dll loadable statically or dynamically as desired)
' dllShareData.bas to be compile with -dll
' Sharing data between main and dll code

' 'Alias' clause (in addition to 'Export') allows compatibility with dll loaded statically or dynamically

' share main variable
Dim Shared ByRef As Integer Idll = *CPtr(Integer Ptr, 0)
Sub passIntByRef Alias"passIntByRef"(ByRef i As Integer) Export
    Print "   dll code receives by reference main integer"
    @Idll = @i
End Sub

Sub printIdll Alias"printIdll"() Export
    Print "   dll code prints its own reference"
    Print "   " & Idll
End Sub

Sub incrementIdll Alias"incrementIdll"() Export
    Idll += 1
End Sub

' share dll variable
Dim Shared As Integer Jdll = 5
Function returnIntByRef Alias"returnIntByRef"() ByRef As Integer Export
    Print "   dll code returns by reference dll integer"
    Return Jdll
End Function

Sub printJdll Alias"printJdll"() Export
    Print "   dll code prints its dll integer"
    Print "   " & Jdll
End Sub

Sub incrementJdll Alias"incrementJdll"() Export
    Jdll +=1
End Sub

' mainShareData.bas
' Sharing data between main and dll code

' 'Alias' clause allows compatibility with dll loaded statically or dynamically

' dll loaded statically
    #inclib "dllShareData"
    Declare Sub passIntByRef Alias"passIntByRef"(ByRef i As Integer)
    Declare Function returnIntByRef Alias"returnIntByRef"() ByRef  As Integer
    Declare Sub printIdll Alias"printIdll"()
    Declare Sub printJdll Alias"printJdll"()
    Declare Sub incrementIdll Alias"incrementIdll"()
    Declare Sub incrementJdll Alias"incrementJdll"()
 ' or dll loaded dynamically
    'Dim As Any Ptr libhandle = DyLibLoad("dllShareData")
    'Dim As Sub(Byref i As Integer) passIntByRef = DyLibSymbol(libhandle, "passIntByRef")
    'Dim As Function() Byref  As Integer returnIntByRef = DyLibSymbol(libhandle, "returnIntByRef")
    'Dim As Sub() printIdll = DyLibSymbol(libhandle, "printIdll")
    'Dim As Sub() printJdll = DyLibSymbol(libhandle, "printJdll")
    'Dim As Sub() incrementIdll = DyLibSymbol(libhandle, "incrementIdll")
    'Dim As Sub() incrementJdll = DyLibSymbol(libhandle, "incrementJdll")

' share main variable
Dim Shared As Integer Imain = 1
Print "main code passes by reference main integer to dll code"
passIntByref(Imain)
Print "main code requests dll code to print its own reference"
printIdll()
Print "main code increments its main integer value"
Imain += 1
Print "main code requests dll code to print its own reference"
printIdll()
Print "main code requests dll to increments its own reference"
incrementIdll()
Print "main code prints its main integer"
Print "" & Imain

Print

' share dll variable
Dim Shared ByRef As Integer Jdll = *CPtr(Integer Ptr, 0)
Print "main code requests by reference dll integer from dll code"
Dim As Integer Ptr pJdll = @(returnIntByRef())
Print "main code receives by reference dll integer"
@Jdll = pJdll
Print "main code prints its own reference"
Print "" & Jdll
Print "main code requests dll to increment its dll integer value"
incrementJdll()
Print "main code prints its own reference"
Print "" & Jdll
Print "main code increments its own reference"
Jdll += 1
Print "main code requests dll code to print its dll integer"
printJdll()
Print

' for dll loaded dynamically
    'DyLibFree(libhandle)

Sleep

Simpler example of workaround on Windows for sharing data (by a single static function returning by reference) between main and dll code (works on any platform):
(dll loadable statically or dynamically as desired)
' dllShareData2.bas to be compile with -dll
' Sharing data between main and dll code by using static funtion returning by reference

' 'Alias' clause (in addition to 'Export') allows compatibility with dll loaded statically or dynamically

Function returnIntByRef Alias"returnIntByRef"() ByRef As Integer Export
    Static As Integer Jdll = 1
    Return Jdll
End Function

Sub printJdll Alias"printJdll"() Export
    Print "   dll code prints the reference"
    Print "   " & returnIntByRef()
End Sub

Sub incrementJdll Alias"incrementJdll"() Export
    returnIntByRef() +=1
End Sub

' mainShareData2.bas
' Sharing data between main and dll code by using static funtion returning by reference

' 'Alias' clause allows compatibility with dll loaded statically or dynamically

' dll loaded statically
    #inclib "dllShareData2"
    Declare Function returnIntByRef Alias"returnIntByRef"() ByRef  As Integer
    Declare Sub printJdll Alias"printJdll"()
    Declare Sub incrementJdll Alias"incrementJdll"()
 ' or dll loaded dynamically
    'Dim As Any Ptr libhandle = DyLibLoad("dllShareData2")
    'Dim As Function() Byref As Integer returnIntByRef = DyLibSymbol(libhandle, "returnIntByRef")
    'Dim As Sub() printJdll = DyLibSymbol(libhandle, "printJdll")
    'Dim As Sub() incrementJdll = DyLibSymbol(libhandle, "incrementJdll")

Print "main code requests dll code to print the reference"
printJdll()
Print "main code prints the reference"
Print "" & returnIntByRef()
Print
Print "main code requests dll to increment the reference"
incrementJdll()
Print "main code requests dll code to print the reference"
printJdll()
Print "main code prints the reference"
Print "" & returnIntByRef()
Print
Print "main code increments the reference"
returnIntByRef() += 1
Print "main code prints the reference"
Print "" & returnIntByRef()
Print "main code requests dll code to print the reference"
printJdll()
Print

' for dll loaded dynamically
    'DyLibFree(libhandle)

Sleep

Note:
If the shared variable (by reference) is a pointer to any structure (memory buffer, type instance, etc.) then this structure is itself also shared with the library (passing the pointer by reference even allows a dynamic reallocation of the structure).

Creating import Libraries from def files, or import libraries or def files from DLL files, on Windows

When using a third-party dll in FreeBASIC on Windows, it may be necessary to manually build an import library or a def file to satisfy the linker.

Syntax to create the import library file (*.dll.a) for Windows, from the def file (*.def) of an existing DLL (*.dll):
- 32-bit:
drive:\FreeBASIC\bin\win32\dlltool.exe -d XXX.def -l libXXX.dll.a
- 64-bit:
drive:\FreeBASIC\bin\win64\dlltool.exe -m i386:x86-64 --as-flags --64 -d XXX.def -l libXXX.dll.a

Syntax to create the import library file (*.dll.a) for Windows, directly from an existing DLL (*.dll):
- 32-bit:
drive:\FreeBASIC\bin\win32\dlltool -k -d XXX.dll -l libXXX.dll.a
- 64-bit:
drive:\FreeBASIC\bin\win64\dlltool -k -d XXX.dll -l libXXX.dll.a

Syntax to create the def file (*.def) for Windows, from an existing DLL (*.dll):
- 32-bit:
drive:\FreeBASIC\bin\win32\dlltool --dllname XXX.dll --output-def XXX.def
- 64-bit:
drive:\FreeBASIC\bin\win64\dlltool --dllname XXX.dll --output-def XXX.def
or using -D and -z:
- 32-bit:
drive:\FreeBASIC\bin\win32\dlltool -D XXX.dll -z XXX.def
- 64-bit:
drive:\FreeBASIC\bin\win64\dlltool -D XXX.dll -z XXX.def

with:
drive:\FreeBASIC\
FreeBASIC installation path
XXX
name of the lib and def file

See also

  • Shared Libraries - DOS
  • Static Libraries
  • #inclib
  • #include
  • Compiler Option: -dll
  • Compiler Option: -export
  • Compiler Option: -dylib


Profiling with fb's profiler
Profiling can be used to analyze the performance of an application.

Since fbc 1.20.0, FreeBASIC can use fb's profiler for analyzing the execution of an application. The profiler information is collected while the program is running.
This profiling feature may give an easy to use tool to help novice and experienced users analyze their programs.

Some of the capabilities of this profiler implemented through fb:
- easy to use,
- no extra tools needed,
- can profile (analyze) calls to external libraries.

This profiling feature may give an easy to use tool to help novice and experienced users analyze their programs.

The three basic steps to profiling a program are:
- Prepare the program for profiling by passing the the appropriate -profgen ... option to the FreeBASIC compiler or compile the source with appropriate '-profgen ...' #Cmdline option.
- Run the program to build the profiling report stored by default in <filename[.exe]>.prf (where <filename[.exe]> is the name of the executable).
- Analyze directly the profiling report (text file).

The fb's profiler offers two types of profiling:
1. Function call profiling
2. Cycle count profiling (currently a work in progress on -gen gas64 only)

1. Function Call Profiling (with fb's profiler)

The performance of an application might be measured by how many times functions are called, how much time is spent executing those functions, and which functions are calling other functions.
This can help to identify functions that might be taking too long to execute or executed too many times and that might be worth reviewing for optimization.

This feature is a reimplentation of Angelo Mottola's (lillo's) profiler from 2008, rewritten to fix some issues, and then some new features added on. This latest side quest started due to a desire to run some kind of profiling on the fbc compiler itself, and having no luck at all with gprof on windows. Years ago lillo's function call profiler was removed in favor of gmon/gprof.

This tool is not suitable for high resolution timing profiling. However, it helps identify potential problem areas in a program, or gives a better understanding of procedures called as starting point for investigations.

FreeBASIC supports function profiling; not basic-block or line-by-line profiling.

1.1 Preparing a Program for Profiling (with function call fb's profiler)
Only code that is compiled with the -profgen fb command line option can be so profiled.
Pass the -profgen fb option to the FreeBASIC compiler to prepare the program to be profiled or compile the source with '-profgen fb' #Cmdline option:
fbc program.bas -profgen fb
' or
#cmdline "-profgen fb"
This will tell the compiler to insert special startup code at the beginning of the application as well as at the beginning of each function.

1.2 Profiling (with function call fb's profiler) the Program
The information needed to analyze execution of the program is gathered while the program is running.
Run the program to begin collecting the function call information.
The profiling report is automatically stored in a file named by default <filename[.exe]>.prf in the same directory as the program.

1.3 Analyzing the Profiling Report (from function call fb's profiler)
After compiling and running the program, the user can open the default named profiling report <filename[.exe]>.prf (text file).

There are two sections:
- per function results - profile of individual procedures and the procedures called
- global results - profile of all procedures with respect to the whole program run time

Function: the parent function / child function profiled. Parents are on the left, children are indented
Count: number of times the procedure was called
Time: number of seconds executing the procedure
Total%: the percentage of execution time versus the whole run time
Proc%: the percentage of execution time versus the procedure run time

Times are based on fb's built-in TIMER function.

In case of multi-threading, each thread has a separate instance of the profiler storing its data in thread-local storage.
The final profiling report groups together one after the other the elementary profiling reports of each thread (including firstly that of the main thread). There is no inter-thread grouping.

    • Simple example:
#cmdline "-profgen fb"

Sub Pause_100
    Sleep 100, 1
End Sub

Sub Pause_200
    Sleep 200, 1
End Sub

Sub Pause_400
    Sleep 400, 1
End Sub

For i As Integer = 1 To 3
    Pause_100
Next i

Pause_200

Pause_400

Example of profiling report:
' .....
'
'
'   Per function results:
'
'           Function:         Count:       Time:    Total%:    Proc%:
'
'   (main)                         1     0.92307    100.00%
'
'           PAUSE_400              1     0.40091     43.43%    43.43%
'           PAUSE_100              3     0.32153     34.83%    34.83%
'           PAUSE_200              1     0.20060     21.73%    21.73%
'           FB___MAIN              1     0.00000      0.00%     0.00%
'
'   PAUSE_100                      3     0.32153     34.83%
'
'           SLEEP                  3     0.32152     34.83%   100.00%
'
'   PAUSE_200                      1     0.20060     21.73%
'
'           SLEEP                  1     0.20059     21.73%   100.00%
'
'   PAUSE_400                      1     0.40091     43.43%
'
'           SLEEP                  1     0.40086     43.43%    99.99%
'
'
'
'   Global results:
'
'   (main)                         1     0.92307    100.00%
'   FB___MAIN                      1     0.00000      0.00%
'   PAUSE_100                      3     0.32153     34.83%
'   PAUSE_200                      1     0.20060     21.73%
'   PAUSE_400                      1     0.40091     43.43%
'   SLEEP                          5     0.92297     99.99%

1.4 Advanced Profiling Features (with function call fb's profiler)
The following features allow the user to control the information collected by the profiler as well as modify the profiling report, compared to the default operating.

1.4.1 Advanced procedures in the ./inc/fbc-int/profile.bi include file
The ./inc/fbc-int/profile.bi include file allows user to access some advanced procedures (declared inside Namespace: FBC) for working with the fb's profiler.
These procedures expose fb's profiler as an API that can then be called directly and used to generate a profiling report based on whatever structure of call recording is made by the user:
- To set the output report file name at run-time:
function ProfileSetFileName ( byval filename as const zstring ptr ) as long
- To set/get options for the profiler:
function ProfileSetOptions ( byval options as PROFILE_OPTIONS ) as PROFILE_OPTIONS
function ProfileGetOptions ( ) as PROFILE_OPTIONS
both associated with the PROFILE_OPTIONS Enum structure (see ./inc/fbc-int/profile.bi include file)
- To ignore a procedure when profiling:
sub ProfileIgnore ( byval procedurename as zstring ptr )
- To call fb's profiler directly to perform custom profiling and a report:
Examples of procedures:
sub ProfileInit () '' Initialize the profiler
function ProfileEnd ( byval errorcode as long ) as long '' Generate the profiling report and terminate the profiler
function ProfileBeginCall ( byval procedurename as const zstring ptr ) as any ptr '' Begin a call and return a caller_id pointer
sub ProfileEndCall ( byval procctx as any ptr ) '' End a call

      • Example for setting the profiling report file name at run-time:
#cmdline "-profgen fb"
#include Once "fbc-int/profile.bi"
Using FBC

'' automatically change the profile report name
Dim t As String = Time
Dim n As String = Left(t, 2) & Mid(t, 4, 2) & Right(t, 2)
ProfileSetFileName( "prof-" & n & ".txt" )

      • Example for telling the profiler to generate a call tree instead of timings and counts:
#cmdline "-profgen fb"
#include Once "fbc-int/profile.bi"
Using FBC

ProfileSetOptions( PROFILE_OPTION_REPORT_CALLTREE )

Sub A
    Print "A"
End Sub

Sub B
    Print "B"
End Sub

Sub C
    Print "C"
End Sub

Sub D
    Print "D"
    A
    A
    B
    B
    C
    C
End Sub

D

Example of profiling report:
'	.....
'
'
'	Call Tree:
'
'	\- (main)
'	   |- D
'	   |  |- A
'	   |  |  |- FB_PRINTSTRING
'	   |  |  \- FB_STRALLOCTEMPDESCZEX
'	   |  |- B
'	   |  |  |- FB_PRINTSTRING
'	   |  |  \- FB_STRALLOCTEMPDESCZEX
'	   |  |- C
'	   |  |  |- FB_PRINTSTRING
'	   |  |  \- FB_STRALLOCTEMPDESCZEX
'	   |  |- FB_PRINTSTRING
'	   |  \- FB_STRALLOCTEMPDESCZEX
'	   |- FB___MAIN
'	   \- PROFILESETOPTIONS
      • Example for calling fb's profiler directly to perform custom profiling and a report:
#include Once "fbc-int/profile.bi"
Using FBC

#if __FB_PROFILE__ = PROFILE_OPTION_REPORT_CALLS
#error Do Not compile With -profgen fb
#endif

ProfileInit()

Var ctx = ProfileBeginCall( @"Begin" )

Scope
    Var ctx = ProfileBeginCall( @"Step 1" )
        Sleep 100, 1
    ProfileEndCall( ctx )
End Scope

Scope
    Var ctx = ProfileBeginCall( @"Step 2" )
        Sleep 200, 1
    ProfileEndCall( ctx )
End Scope

ProfileEndCall( ctx )

ProfileEnd(0)

Example of profiling report:
' .....
'
'
' Per function results:
'
'         Function:         Count:       Time:    Total%:    Proc%:
'
' (main)                         1     0.30183    100.00%
'
'         Begin                  1     0.30183    100.00%   100.00%
'
' Begin                          1     0.30183    100.00%
'
'         Step 2                 1     0.20057     66.45%    66.45%
'         Step 1                 1     0.10125     33.54%    33.54%
'
'
'
' Global results:
'
' (main)                         1     0.30183    100.00%
' Begin                          1     0.30183    100.00%
' Step 1                         1     0.10125     33.54%
' Step 2                         1     0.20057     66.45%

1.4.2 Conditional generation of profiling code
The user can also control a conditional generation of profiling code:
#Pragma Profile controls the generation of profiling code.
This allows optionally including or excluding sections of source code to be profiled.

User can optionally disable profiling code generation with #pragma profile = false or #pragma push( profile , false )
Profiling code generation can be reactivated with #pragma profile = true or #pragma pop( profile ) respectively

The __FB_OPTION_PROFILE__ intrinsic define allows to indicate that the profiling code is generated or not:
__FB_OPTION_PROFILE__ is set to non-zero when profiling code is generated
__FB_OPTION_PROFILE__ is set to zero when profiling code is not generated

      • Purely academic example:
#cmdline "-profgen fb"

Print "__FB_OPTION_PROFILE__ = " & __FB_OPTION_PROFILE__

#pragma push( profile , False )
Print "__FB_OPTION_PROFILE__ = " & __FB_OPTION_PROFILE__
Sleep 100, 1
#pragma pop( profile )

Print "__FB_OPTION_PROFILE__ = " & __FB_OPTION_PROFILE__

Example of profiling report:
' .....
'
'
' Per function results:
'
'         Function:                     Count:       Time:    Total%:    Proc%:
'
' (main)                                     1     0.11714    100.00%
'
'         FB_PRINTSTRING                     2     0.00517      4.42%     4.42%
'         FB_STRALLOCTEMPDESCZEX             2     0.00001      0.01%     0.01%
'         FB___MAIN                          1     0.00000      0.00%     0.00%
'
'
'
' Global results:
'
' (main)                                     1     0.11714    100.00%
' FB_PRINTSTRING                             2     0.00517      4.42%
' FB_STRALLOCTEMPDESCZEX                     2     0.00001      0.01%
' FB___MAIN                                  1     0.00000      0.00%

The code profiler only takes into account 2 PRINTs (instead of 3) and not the SLEEP.

2. Cycle Count Profiling (with fb's profiler)

This is currently a work in progress on -gen gas64 only.

Cycle count fb's profiler enables cpu cycle count profiling on some targets.
It uses completely different methods than the function call fb's profiler. It stores data in specially named data sections, so will only work with linkers supporting arbitrarily named sections.
After running an executable compiled with this command, it can generate a basic report of cycle counts.

For each called user procedure body, there are two different counts:
- The profiler sums the CPU cycle counts for all internal code blocks that separate possible calls from any other user procedure or built-in procedure (all other called procedures are therefore excluded from this first count).
- The profiler also counts the total number of CPU cycles when executing the entire user procedure body.

2.1 Preparing a Program for Profiling (with cycle count fb's profiler)
Only code that is compiled with the -profgen cycles command line option can be so profiled.
Pass the -profgen cycles option to the FreeBASIC compiler to prepare the program to be profiled or compile the source with '-profgen cycles' #Cmdline option:
fbc program.bas -profgen cycles
' or
#cmdline "-profgen cycles"

2.2 Profiling (with cycle count fb's profiler) the Program
The information needed to analyze execution of the program is gathered while the program is running.
Run the program to begin collecting the function call information.
The profiling report is automatically stored in a file named by default <filename[.exe]>.prf in the same directory as the program.

2.3 Analyzing the Profiling Report (from cycle count fb's profiler)
After compiling and running the program, the user can open the default named profiling report <filename[.exe]>.prf (text file).

Profile of individual procedures per module:
total: the number of cycles between the end of the procedure and its beginning
inside: the number of cycles really "spent" inside the procedure (procedure calls inside the procedure body are not taken into account)
call count: number of times the procedure was called

    • Simple example:
#cmdline "-gen gas64"
#cmdline "-profgen cycles"

Sub A()
    Dim As Integer I
    For J As Integer = 1 To 1000
        I = I + 1
    Next J
End Sub

Sub B()
    A()
End Sub

Sub C()
    B()
    Print "End"
End Sub

C()

Example of profiling report:
' .....
'
'
'   module  function             total             inside         call count
'
'   *.bas
'
'           A
'                                 4242               4242                  1
'           B
'                                 4473                 42                  1
'           C
'                               858301                169                  1
'           main
'                                    0                 63                  1


Profiling for gmon/gprof
Profiling can be used to analyze the performance of an application.

The performance of an application might be measured by how many times functions are called, how much time is spent executing those functions, and which functions are calling other functions. This can help to identify functions that might be taking too long to execute or executed too many times and that might be worth reviewing for optimization.

FreeBASIC can use GPROF for analyzing the execution of an application. The profiler information is collected while the program is running, and GPROF is used to report on the collected data afterward.

The three basic steps to profiling a program are:
    • 1) Prepare the program for profiling by compiling source with the -profile or -profgen gmon option.
    • 2) Run the program to collection information ( stored in gmon.out ).
    • 3) Analyze the information collected using GPROF.

Full documentation on GPROF is available here: https://ftp.gnu.org/old-gnu/Manuals/gprof-2.9.1/html_mono/gprof.html. If the documentation has moved from that location, simply search the web for "GNU GPROF" and a relevant link should be returned.

FreeBASIC supports function profiling; not basic-block or line-by-line profiling.

Preparing a Program for Profiling (for gmon/gprof)

Only code that is compiled with the -profile or -profgen gmon command line option can be so profiled.
Pass the -profile or -profgen gmon option to the FreeBASIC compiler to prepare the program to be profiled.
For example:
fbc program.bas -profile
or
fbc program.bas -profgen gmon
This will tell the compiler to insert special startup code at the beginning of the application as well as at the beginning of each function.

Profiling (for gmon/gprof) the Program

The information needed to analyze execution of the program is gathered while the program is running. Run the program to begin collecting the function call information. This information is automatically stored in a file named gmon.out in the same directory as the program.

Analyzing the Program's Output

Use GPROF to analyze the output. The default report for GPROF includes descriptions on what each of the columns of values mean. If you are new to using GPROF, you may want to first run the default report and read through the descriptions. The output from GPROF can be saved to a file by redirection.

Save output from GPROF to profile.txt:
gprof program[.exe] > profile.txt

Show just the flat report with no descriptions:
gprof program[.exe] --brief --flat-profile > profile.txt
Combining the Results of More than One Session

GPROF also has a '--sum' option for conveniently combining results from multiple execution sessions. Here is an example of usable:
  • Run your program once. This will create gmon.out.
  • Use the command :
mv gmon.out gmon.sum
or
rename gmon.out gmon.sum.
  • Run your program again. This will create new data in gmon.out.
  • Merge the new data in gmon.out into gmon.sum using the command:
gprof --sum program[.exe] gmon.out gmon.sum
  • Repeat the last two steps as often as needed.
  • Analyze the summary data using the command:
gprof program[.exe] gmon.sum > profile.txt

FreeBASIC Profiling (for gmon/gprof) Internals

When the '-profile' option is enabled, one or more bits of code are added to the program.
    • Call to "_monstartup()" at the beginning of the implicit main to initialize the profiling library.
    • Call to "mcount()" at the beginning of each procedure. This is how the profiling library keeps track of what function is being and by which other function.
    • Linking of additional program startup object code. (e.g. gcrt?.o )
The profiling library itself may be in a separate library or part of the C runtime library.
    • mingw will require gcrt2.o and libgmon.a
    • cygwin will require gcrt0.o and libgmon.a
    • dos will require gcrt0.o (profiler code is in libc.a)
    • linux will require gcrt1.o (profiler code is in libc.a)
The details may vary from one port of FreeBASIC to the next, but source code built for profiling with FreeBASIC should be compatible with other languages also supporting GPROF.


Preprocessor
The Preprocessor performs some processing on source code before the next step of compilation.

The preprocessor is a program that parses a text file and makes it submit to certain transformations.
These transformations can be the inclusion of a file, the deletion of a text block or the replacement of a text block.

The preprocessor performs these operations through specific commands that it reads in the file being scanned.

It is automatically called by the compiler, before compilation, to process the files to compile.

Preprocessor commands

All preprocessor commands begin at the beginning of the line with the pound sign ("#").
See 'Preprocessor commands' to get all commands that control the preprocessor.

The main types of commands are the followings:
    • File inclusion:
Text file inclusion allows you to factorize text that is common to many other files (for example, type, constant, function, etc.).
The common text is usually put in a file with the extension ".bi".
Syntax:
#include [once] "file"
file is the name of the file to include.
The included file is also processed by the preprocessor.

File inclusion also allows to include a library in the linking process.
Syntax:
#inclib "libname"
libname is the name of the library file to include in the linking process.

    • Text replacement:
The preprocessor makes it possible to define identifiers which, used in the program, will be replaced textually by their value.
Syntax:
#define identifier body
where identifier is the identifier that will be used in the rest of the program, and body will be the replacement text that the preprocessor will use.
Whenever the identifier identifier is encountered by the preprocessor, it will be replaced by the text body throughout the rest of the program (#undef identifier to undefine an identifier previously defined).

Defines are scoped (they are only visible in the scope they were defined in).
Namespaces on the other hand do not have any effect on the visibility of the defines.

    • Compilation constants and conditional compilation:
See the next 'Conditional Compilation' page of this section.

    • Macros:
See the last page 'Macros' of this section.

    • Other commands:
The preprocessor is able to perform other actions than those mentioned above.
The directives for performing these actions are listed below:
- #assert condition (conditional directive for debugging)
- #error error_text (directive for displaying error message)
- #lang "lang" (directive to set compiler dialect)
- #libpath "path" (directive to add search path for libraries)
- #line number ["name"](directive to set current line number [and file name])
- #pragma / #Cmdline (directive to set compiler options)
- #Pragma Reserve (directive to reserve symbol name)
- #print text (directive to print text)

Example

Example with #include and #define:
#include "vbcompat.bi"
#define TEMPLATE "hh:mm:ss yyyy/mm/dd"

Dim As String * Len(TEMPLATE) hour_date

hour_date = Format(Now, TEMPLATE)

Print hour_date, "(" & TEMPLATE & ")"

Sleep

See also

  • Conditional Compilation
  • Macros


Conditional Compilation
Conditional Compilation allows to change the way code is generated using 'Preprocessor commands'.

The '#define' command allows to define compilation constants, that is constants that describe the parameters of the platform for which the program is compiled.
These constants make it possible to carry out conditional compilations, that is to say to modify the behavior of the program according to parameters defined during its compilation.

The '#define' command is also used to replace identifiers of the program with other identifiers, for example to test several versions of the same function without modifying the entire program.

Compilation constants

A clear distinction will be made between compilation constants defined with the '#define' directive of the preprocessor and the constants defined with the 'Const' keyword.
Indeed, the literal constants do not reserve memory. These are immediate values, defined by the compiler.
On the other hand, the 'Const' can still have a reserved memory space. This is for example the case when manipulating string literals.

A literal constant declared with the '#define' directive of the preprocessor will always retain its value (provided that it is not redefined). These literal constants have no type, which can be very annoying and error-prone in code.
Their use will be reserved for compilation constants only, and the 'Const' keyword will be preferred for all other constants of the program.

The preprocessor sets a number of built-in constants automatically.
These are the 'Intrinsic Defines' (see their list).

Conditional compilation

The definition of identifiers and compilation constants is widely used to perform so-called conditional compilation.
Conditional compilation consists of replacing certain portions of source code with others, depending on the presence or value of compilation constants.
This is achievable using conditional compilation directives.

The most common of which are probably:
#ifdef symbol
' Conditionally included statements
#endif
the text between the #ifdef (ie, "if defined") and the #endif is left as it is if the identifier symbol is known to the preprocessor. Otherwise, it is deleted (the identifier can be declared using just the #define command seen previously).

There are other conditional compilation directives like:
- #ifndef (if not defined ...)
- #else / #elseif (otherwise ... / otherwise, if ...)
- #if (if ...)
the #if directive expects a constant expression as parameter (the text that follows is included in the file if and only if this expression is true).

Another common application of compilation directives is the protection of header files against multiple inclusions:
#ifndef AlreadyThere
#define AlreadyThere
' Text to include only once at most.
#endif
This prevents the text from being included multiple times, as a result of multiple #include calls.
Indeed, at the first call, AlreadyThere is not known to the preprocessor. It is therefore declared and the text is included.
On any subsequent call, AlreadyThere exists, and the text is not included.
This kind of writing is found in header files, for which in general we do not want a multiple inclusion to take place.

Example

Example of conditional compilation using the Intrinsic define __FB_DEBUG__ to debug a recursive function:
Function recursiveFactorial (ByVal n As Integer) As Integer
    If (n = 0) Then                         '' end condition
        #if __FB_DEBUG__ <> 0
            Print "end of recursion and result:";
        #endif
        Return 1
    Else                                    '' recursion loop
        #if __FB_DEBUG__ <> 0
            Print "multiply by: " & n
        #endif
      Return n * recursiveFactorial(n - 1)  '' recursive call
    End If
End Function

Print recursiveFactorial(5)

Sleep

Output after compiling without -g option:
 120
Output after compiling with -g option:
multiply by: 5
multiply by: 4
multiply by: 3
multiply by: 2
multiply by: 1
end of recursion and result: 120
See also

  • Preprocessor Overview
  • Macros


Macros
Macros are named code segments that are substituted to their names each time they are encountered in a program.

Macros offer a powerful way to extend the language and create reusable code.
One reason macros are used is performance.
They are a way of eliminating procedure call overhead because they are always expanded in-line.
There is no alternative for this in FreeBASIC because it does not support inline procedures.

Macros definition

The preprocessor can, during the text replacement mechanism, use parameters supplied to the identifier to be replaced.
These parameters are then replaced without modification in the replacement text.
The replacement text is then called macro.

The syntax to define a macro is as follows (see 'Preprocessor commands'):
- one-line macro:
#define identifier([parameters]) body
parameters turn a define into a function-like macro, allowing text arguments to be passed to the macro.
identifier should be followed by the opening parentheses (() immediately without any white-space in between, otherwise the compiler will treat it as part of the body.
- multi-line macro:
#macro identifier([parameters])
body
#endmacro
#macro is the multi-line version of #define.

The # Stringize operator can be used on macro parameters to turn them into string literals, and the ## Concatenate operator can be used to merge tokens together.
(see 'Preprocessor Operators')

Defines and macros are scoped (they are only visible in the scope they were defined in).
Namespaces on the other hand do not have any effect on the visibility of the defines and macros.

The mechanism of macros allows to do the equivalent of general procedures, which work for all types.
However, care must be taken that parameters passed to a macro are evaluated by it each time they are used in the macro definition. This can cause performance issues or, worse, cause unwanted edge effects.

Parentheses should always be placed around the parameters of the macro:
- Indeed, these parameters can be compound expressions, which must be computed completely before being used in the macro.
- Parentheses force this calculation.
- If they are not set, priority rules can generate a logic error in the macro itself.
Similarly, macros that return a value should be surrounded by parentheses, in order to force their complete evaluation before using them in another expression.

Example of incorrect and correct macros:
#define MUL1(x, y) x * y              '' incorect macro  (parameters must be enclosed in parentheses)
#define MUL2(x, y) ( x ) * ( y )      '' incorrect macro (and returned result must also be in parentheses)
#define MUL3(x, y) ( ( x ) * ( y ) )  '' correct macro

Print MUL1(5-3, 1+2)^2  '' 6  (incorrect result)
Print MUL2(5-3, 1+2)^2  '' 18 (incorrect result)
Print MUL3(5-3, 1+2)^2  '' 36 (correct result)

Sleep

Thus, the parentheses ensure a consistent behavior of the macro (parentheses can add to macro definitions, but they are absolutely necessary).

Macros debug

Using macros can be extremely unsafe and they hide a lot of pitfalls which are very hard to find.
Procedures give type checking and scoping, but macros just substitute the passed argument.
Another disadvantage of the macro is the size of the program. The reason is, the pre-processor will replace all the macros in the program by its real definition prior to the compilation process of the program.

Looking only at the source code file, the only way to find out what the problem is to look at the definition of the macro and try to understand what happened.
The most common error when using macros is the unbalanced open parentheses (inducing error at compile-time).
Another is to forget putting parentheses around arguments (or returned result if exists) in macro definitions. That can cause some pretty nasty side effects because of operator precedence (inducing error at compile-time or bug at run-time).

When the compiler detects an error inside a macro (after expanding), it provides a rustic error message containing only:
- the line number where is the call of the macro,
- the error type,
- the text of the call (of the macro).

When the error is not obvious (type of error reported blurred), the only solution (with only the call line number) is presently to iteratively execute the following 5 steps until correction successful:
Do
1. call fbc on the source file, but with the '-pp' compile option (fbc command emitting only the pre-processed input file, without compiling),
2. recover the pre-processed file,
3. edit and compile directly from this pre-processed file,
4. analyze the error, understand it, correct it, and compile again the pre-processed file thus modified,
5. postpone the equivalent correction in the concerned macro body of the original source file.
Loop

Example of error on a short code:
    • Source file (*.bas):
#macro FIRST(array, Operator)
  For index As Integer = LBound(array) To UBound(array) - 1
    Print " " Operator array(index) Operator ",";
  Next index
  Print array(UBound(array))
#endmacro

#macro Second(array)
  Print #array + ":"
  FIRST(array, +)
#endmacro

Dim As String test1(0 To ... ) => {"First", "Second", "Third"}
Dim As Integer test2(0 To ...) => {1, 2 ,3}

Second(test1)
Print
Second(test2)

Sleep

Compiler output:
...\FBIDETEMP.bas(18) error 20: Type mismatch, found '+' in 'SECOND(test2)'

    • Pre-processed file (*.pp.bas):
Dim As String test1(0 To ... ) => {"First", "Second", "Third"}
Dim As Integer test2(0 To ...) => {1, 2 ,3}

 Print $"test1" + ":"
 For index As Integer =LBound(test1) To UBound(test1) -1
 Print " " + test1(index) + ",";
 Next index
 Print test1(UBound(test1))
Print
 Print $"test2" + ":"
 For index As Integer =LBound(test2) To UBound(test2) -1
 Print " " + test2(index) + ",";
 Next index
 Print test2(UBound(test2))

Sleep

Compiler output:
...\FBIDETEMP.bas(14) error 20: Type mismatch, found '+' in 'Print " " + test2(index) + ",";'

    • Example macro correction in source code (*.bas):
#macro FIRST(array, Operator)
  For index As Integer = LBound(array) To UBound(array) - 1
    Print " " Operator array(index) Operator ",";
  Next index
  Print array(UBound(array))
#endmacro

#macro Second(array)
  Print #array + ":"
  FIRST(array, &)     ''corrected line ("&" instead of "+")
#endmacro

Dim As String test1(0 To ... ) => {"First", "Second", "Third"}
Dim As Integer test2(0 To ...) => {1, 2 ,3}

Second(test1)
Print
Second(test2)

Sleep

Output:
test1:
 First, Second,Third

test2:
 1, 2, 3
Note: Another solution could be a more detailed error message from compiler, relating to the call of the macro (proposal of improved error message already filled in in a feature request).

Variadic macros

Using an ellipsis "..." (3 dots) behind the last parameter in a #macro or #define declaration allows creation of a variadic macro:
#macro identifier([parameters,] variadic_parameter...)
body
#endmacro
or:
#define identifier([parameters,] variadic_parameter...) body

So, it is possible to pass any number of arguments through the variadic_parameter, which can be used in the body as if it was a normal macro parameter.
During macro expansion each occurrence of the variadic_parameter in the macro replacement list is replaced by the passed arguments. The variadic_parameter will expand to the full list of arguments passed to it, including commas, and can also be completely empty.

No direct means is provided to recursively access individual arguments in the variable argument list.
To distinguish between the different arguments passed by the variadic_parameter, one can first convert the variadic_parameter to a string literal using the # Stringize operator, then differentiate in this string literal (#variadic_parameter) each passed argument by locating the separators (a comma).

Examples

Example with one-line and multi-line macros:
#define MIN(x, y) IIf( ( x ) < ( y ), x, y )      '' maximum function-like macro
#define MAX(x, y) IIf( ( x ) > ( y ), x, y )      '' minimum function-like macro
#define MUL(x, y) ( ( x ) * ( y ) )               '' multiply function-like macro

#macro REV_STR(str_dest, str_src)                 '' reverse-string sub-like macro
    For i As Integer = Len(str_src) To 1 Step -1
        str_dest &= Mid(str_src, i, 1)
    Next I
#endmacro

Print MIN(5 - 3, 1 + 2)    '' 2
Print MAX(5 - 3, 1 + 2)    '' 3
Print MUL(5 - 3, 1 + 2)^2  '' 36

Dim As String s
REV_STR(s, "CISABeerF")  '' FreeBASIC
Print s

Sleep

Example with variadic macro:
' macro with a variadic parameter which can contain several sub-parameters:
'   To distinguish between the different arguments passed by variadic_parameter,
'   you can first convert variadic_parameter to a string using the Operator # (Preprocessor Stringize),
'   then differentiate in this string (#variadic_parameter) each passed argument by locating the separators (usually a comma).

#macro average(result, arg...)
    Scope
        Dim As String s = #arg
        If s <> "" Then
            result = 0
            Dim As Integer n
            Do
                Dim As Integer k = InStr(1, s, ",")
                If k = 0 Then
                    result += Val(s)
                    result /= n + 1
                    Exit Do
                End If
                result += Val(Left(s, k - 1))
                n += 1
                s = Mid(s, k + 1)
            Loop
        End If
    End Scope
#endmacro

Dim As Double result
average(result, 1, 2, 3, 4, 5, 6)
Print result

' Output : 3.5

See also

  • Preprocessor Overview
  • Conditional Compilation


Table of ASCII Characters
FreeBASIC graphics programs support in all versions the same "ASCII extended" USA character set the old DOS (and QBasic) supported. It is also called CP437 or Code page 437. Each character is represented with one (1) byte of data. Here is a table. Each entry has decimal code, hex code, and printed representation.

000 | 00 |  | 032 | 20 | sp | 064 | 40 | @ | 096 | 60 | ` | 128 | 80 | Ç | 160 | A0 | á | 192 | C0 | └ | 224 | E0 | α
001 | 01 | ☺ | 033 | 21 | ! | 065 | 41 | A | 097 | 61 | a | 129 | 81 | ü | 161 | A1 | í | 193 | C1 | ┴ | 225 | E1 | ß
002 | 02 | ☻ | 034 | 22 | " | 066 | 42 | B | 098 | 62 | b | 130 | 82 | é | 162 | A2 | ó | 194 | C2 | ┬ | 226 | E2 | Γ
003 | 03 | ♥ | 035 | 23 | # | 067 | 43 | C | 099 | 63 | c | 131 | 83 | â | 163 | A3 | ú | 195 | C3 | ├ | 227 | E3 | π
004 | 04 | ♦ | 036 | 24 | $ | 068 | 44 | D | 100 | 64 | d | 132 | 84 | ä | 164 | A4 | ñ | 196 | C4 | ─ | 228 | E4 | Σ
005 | 05 | ♣ | 037 | 25 | % | 069 | 45 | E | 101 | 65 | e | 133 | 85 | à | 165 | A5 | Ñ | 197 | C5 | ┼ | 229 | E5 | σ
006 | 06 | ♠ | 038 | 26 | & | 070 | 46 | F | 102 | 66 | f | 134 | 86 | å | 166 | A6 | ª | 198 | C6 | ╞ | 230 | E6 | µ
007 | 07 | • | 039 | 27 | ' | 071 | 47 | G | 103 | 67 | g | 135 | 87 | ç | 167 | A7 | º | 199 | C7 | ╟ | 231 | E7 | τ
008 | 08 | ◘ | 040 | 28 | ( | 072 | 48 | H | 104 | 68 | h | 136 | 88 | ê | 168 | A8 | ¿ | 200 | C8 | ╚ | 232 | E8 | Φ
009 | 09 | ○ | 041 | 29 | ) | 073 | 49 | I | 105 | 69 | i | 137 | 89 | ë | 169 | A9 | ⌐ | 201 | C9 | ╔ | 233 | E9 | Θ
010 | 0A | ◙ | 042 | 2A | * | 074 | 4A | J | 106 | 6A | j | 138 | 8A | è | 170 | AA | ¬ | 202 | CA | ╩ | 234 | EA | Ω
011 | 0B | ♂ | 043 | 2B | + | 075 | 4B | K | 107 | 6B | k | 139 | 8B | ï | 171 | AB | ½ | 203 | CB | ╦ | 235 | EB | δ
012 | 0C | ♀ | 044 | 2C | , | 076 | 4C | L | 108 | 6C | l | 140 | 8C | î | 172 | AC | ¼ | 204 | CC | ╠ | 236 | EC | ∞
013 | 0D | ♪ | 045 | 2D | - | 077 | 4D | M | 109 | 6D | m | 141 | 8D | ì | 173 | AD | ¡ | 205 | CD | ═ | 237 | ED | φ
014 | 0E | ♫ | 046 | 2E | . | 078 | 4E | N | 110 | 6E | n | 142 | 8E | Ä | 174 | AE | « | 206 | CE | ╬ | 238 | EE | ε
015 | 0F | ☼ | 047 | 2F | / | 079 | 4F | O | 111 | 6F | o | 143 | 8F | Å | 175 | AF | » | 207 | CF | ╧ | 239 | EF | ∩
016 | 10 | ► | 048 | 30 | 0 | 080 | 50 | P | 112 | 70 | p | 144 | 90 | É | 176 | B0 | ░ | 208 | D0 | ╨ | 240 | F0 | ≡
017 | 11 | ◄ | 049 | 31 | 1 | 081 | 51 | Q | 113 | 71 | q | 145 | 91 | æ | 177 | B1 | ▒ | 209 | D1 | ╤ | 241 | F1 | ±
018 | 12 | ↕ | 050 | 32 | 2 | 082 | 52 | R | 114 | 72 | r | 146 | 92 | Æ | 178 | B2 | ▓ | 210 | D2 | ╥ | 242 | F2 | ≥
019 | 13 | ‼ | 051 | 33 | 3 | 083 | 53 | S | 115 | 73 | s | 147 | 93 | ô | 179 | B3 | │ | 211 | D3 | ╙ | 243 | F3 | ≤
020 | 14 | ¶ | 052 | 34 | 4 | 084 | 54 | T | 116 | 74 | t | 148 | 94 | ö | 180 | B4 | ┤ | 212 | D4 | ╘ | 244 | F4 | ⌠
021 | 15 | § | 053 | 35 | 5 | 085 | 55 | U | 117 | 75 | u | 149 | 95 | ò | 181 | B5 | ╡ | 213 | D5 | ╒ | 245 | F5 | ⌡
022 | 16 | ▬ | 054 | 36 | 6 | 086 | 56 | V | 118 | 76 | v | 150 | 96 | û | 182 | B6 | ╢ | 214 | D6 | ╓ | 246 | F6 | ÷
023 | 17 | ↨ | 055 | 37 | 7 | 087 | 57 | W | 119 | 77 | w | 151 | 97 | ù | 183 | B7 | ╖ | 215 | D7 | ╫ | 247 | F7 | ≈
024 | 18 | ↑ | 056 | 38 | 8 | 088 | 58 | X | 120 | 78 | x | 152 | 98 | ÿ | 184 | B8 | ╕ | 216 | D8 | ╪ | 248 | F8 | °
025 | 19 | ↓ | 057 | 39 | 9 | 089 | 59 | Y | 121 | 79 | y | 153 | 99 | Ö | 185 | B9 | ╣ | 217 | D9 | ┘ | 249 | F9 | ∙
026 | 1A | → | 058 | 3A | : | 090 | 5A | Z | 122 | 7A | z | 154 | 9A | Ü | 186 | BA | ║ | 218 | DA | ┌ | 250 | FA | •
027 | 1B | ← | 059 | 3B | ; | 091 | 5B | [ | 123 | 7B | { | 155 | 9B | ¢ | 187 | BB | ╗ | 219 | DB | █ | 251 | FB | √
028 | 1C | ∟ | 060 | 3C | < | 092 | 5C | \ | 124 | 7C | | | 156 | 9C | £ | 188 | BC | ╝ | 220 | DC | ▄ | 252 | FC | ⁿ
029 | 1D | ↔ | 061 | 3D | = | 093 | 5D | ] | 125 | 7D | } | 157 | 9D | ¥ | 189 | BD | ╜ | 221 | DD | ▌ | 253 | FD | ²
030 | 1E | ▲ | 062 | 3E | > | 094 | 5E | ^ | 126 | 7E | ~ | 158 | 9E | ₧ | 190 | BE | ╛ | 222 | DE | ▐ | 254 | FE | ■
031 | 1F | ▼ | 063 | 3F | ? | 095 | 5F | _ | 127 | 7F | ⌂ | 159 | 9F | ƒ | 191 | BF | ┐ | 223 | DF | ▀ | 255 | FF |

Many of the standard ASCII characters cannot be Printed in FreeBASIC, because the console interprets some characters as controls: 7 is bell, 8 is backspace, 9 is tab, 10 is line feed, 13 is carriage return, and others. There are symbols associated with these characters also, but there is no way in FreeBASIC to output them to the screen.

The acronym ASCII stands for American Standard Code for Information Interchange. For more information, see http://en.wikipedia.org/wiki/Ascii. The symbols for codes 32 through 127 are the same as the standard Latin ISO-8859-1 char set most Windows fonts use. Others are often very different.

In console mode (i.e. Screen (Graphics) 0/ non-graphics mode) the characters less than 32 or greater than 127 may display using different characters, depending on the operating system and code page of the screen / console in use.
UNICODE is a newer standard of character sets involving two or more bytes per character, and may be used to print other characters to a Unicode-enabled console.

In graphics modes, Draw String does not give special meaning to control characters allowing an alternative to display all characters in the set.


Date Serials
A floating point number representing a date and time

Description

A date serial is a number that holds a date and time value in the same format used by PDS or VBDOS. The value is a count of the days from 0:00 AM of December 30,1899; it's mainly used for easy counting of the time between two dates.

The date serial unit is one day and the fractional part represents the time of the day. If a date serial is written into an integer, the time is lost. FreeBASIC date serials are not limited to dates between 1753 and 2078 as in VBDOS. FreeBASIC date serial handling functions use Double arguments.

FreeBASIC date serial handling functions require the inclusion of vbcompat.bi or datetime.bi in the source.

A date serial can be created by the functions Now, TimeSerial+DateSerial, or DateValue+TimeValue.

The functions Year, Month, Weekday, Day, Hour, Minute, Second allow to recover the different components of a date serial.

The Format function has formatting expressions to print date serials in a human readable way.

Example

#include "vbcompat.bi"
Dim a As Double, b As Double

a = 0
Print "The origin of the date serials is:"
Print Format(a, "yyyy/mm/dd hh:mm:ss")
Print

a = Now
Print "The time now is: "
Print Format(a, "yyyy/mm/dd hh:mm:ss")
Print

b = DateSerial(2000,1,1)
Print Int(a-b) & " days have passed since 2000/01/01"


Radian system of measuring angles
All of the built-in trigonometric functions in FreeBASIC express angles in radians.

Let pi the constant equal to the ratio of the circumference of a circle to its diameter. It can be calculated programmatically by multiplying the arctangent of 1 by 4.
A full circle is divided into 2 * pi radians or 360 degrees, which leads to the following conversions:

pi = atn(1) * 4
radians = degrees * pi / 180
degrees = radians * 180 / pi

These 3 formulas can be expressed by using macros:

#define   pi          ( atn(1) * 4 )
#define   radian(x)   ( (x) * pi / 180 )
#define   degree(x)   ( (x) * 180 / pi )
The added parentheses compared to the raw formulas are mandatory to not undergo an unwanted precedence change of operators when the macros are used in any context of expressions.


Event Handling
Handling (querying and processing) keyboard, mouse, and window Events.

Preamble:

Events are basically user actions like key press, clicks, mouse movements, etc., or some occurrence like system generated notifications.

The Event type plus the ScreenEvent function constitute a built-in interface provided by FreeBASIC for accessing events (keyboard, mouse, and window events).
ScreenEvent signals the events so the user can write his own event handlers (which require that he keeps track of the data himself).

By using ScreenEvent, the user can check the events as they are returned by the function.
It is assumed that this function is regularly called to fetch events as they are queued internally by the system.

Event type

The Event type is a pre-defined structure in "fbgfx.bi" (in the FB Namespace for the lang fb dialect only).
When the user calls ScreenEvent passing an instance of Event type, ScreenEvent fills in it with the event data (if an event flag is returned).

Syntax
#include once "fbgfx.bi"
Using FB
Dim variable As Event

"Event" structure
This structure is extracted from "fbgfx.bi".

Type Event Field = 1
    Type As Long
    Union
        Type
            scancode As Long
            ascii As Long
        End Type
        Type
            x As Long
            y As Long
            dx As Long
            dy As Long
        End Type
        button As Long
        z As Long
        w As Long
    End Union
End Type

    • .type field contains the event type ID value corresponding to one of the following symbols defined in "fbgfx.bi":
- For key events, one among:
EVENT_KEY_PRESS
EVENT_KEY_RELEASE
EVENT_KEY_REPEAT
- For mouse event, one among:
EVENT_MOUSE_MOVE
EVENT_MOUSE_BUTTON_PRESS
EVENT_MOUSE_BUTTON_RELEASE
EVENT_MOUSE_DOUBLE_CLICK
EVENT_MOUSE_WHEEL
EVENT_MOUSE_HWHEEL
EVENT_MOUSE_ENTER
EVENT_MOUSE_EXIT
- For window event, one among:
EVENT_WINDOW_GOT_FOCUS
EVENT_WINDOW_LOST_FOCUS
EVENT_WINDOW_CLOSE
    • .scancode and .ascii fields contain respectively the scancode value and the ascii representation (if exists, otherwise 0) of the key impacted by one event among:
EVENT_KEY_PRESS
EVENT_KEY_RELEASE
EVENT_KEY_REPEAT
    • .x, .y and .dx, .dy fields contain respectively the new mouse position (x, y) relative to the upper-left corner of the screen and the motion deltas (dx, dy) induced by the event:
EVENT_MOUSE_MOVE
    • .button field contains the identification (bit 0: left button, bit 1: right button, and bit 2: middle button) of the mouse button impacted by one event among:
EVENT_MOUSE_BUTTON_PRESS
EVENT_MOUSE_BUTTON_RELEASE
EVENT_MOUSE_DOUBLE_CLICK
    • .z field contains the new wheel position induces by the event:
EVENT_MOUSE_WHEEL
    • .w field contains the new horizontal wheel position induces by the event:
EVENT_MOUSE_HWHEEL

Note:
- The last five field blocks [.scancode and .ascii], [.x, .y and .dx, .dy], [.button], [.z] and [.w] are aligned on the same memory location (because declared inside a Union type), so that filling in one field block overwrites the other field blocks previously filled in.
- Therefore for example if the mouse position (mx, my) must be available when the MOUSE_BUTTON_PRESS event is detected, this position must have been previously stored in (mx, my) at each MOUSE_MOVE event from its own event data (.x, .y), because (.x, .y) is overwritten by any other event returned by ScreenEvent.

ScreenEvent function

The ScreenEvent function allows querying and retrieving system events.

Syntax
Declare Function ScreenEvent ( ByVal event_instance_ptr As Any Ptr = 0 ) As Long

Usage
result = ScreenEvent( [ event_instance_ptr ] )
with:
event_instance_ptr: the buffer address where the function should fill in the event data with a structure as the Event type defined above.
result: -1 if there are pending events to be retrieved, 0 otherwise.

Description

The ScreenEvent function checks if there are any pending system events:
- If there are no events, the function simply returns 0 (false).
- If there is any, it returns -1 (true), and if the event_instance_ptr pointer is not NULL, it copies the event data into the user passed structure and removes the event (the latest available) from the internal GfxLib events queue.
- So to simply check if there are any pending events without retrieving them (if there are any), nor even dequeue it from the internal events queue, it is enough to call the function without parameters (or with a NULL parameter).

The user must first call ScreenEvent once to get one event, and then check that one event against each event type he is interested in.
Once the user has handled that event, he must quickly call ScreenEvent again to handle the next event. Otherwise he will almost certainly miss events (like for example EVENT_MOUSE_MOVE at least).
This is why the program loop which tests the successive events must have a period compatible with the rate of the events that the user wants to intercept (for example, difference between keyboard input and mouse move).
Conversely, having an event test loop with a period significantly less than 10 ms unnecessarily hogs the CPU.

Event querying program skeleton
As only one event is served by the ScreenEvent function at a given time, the event querying program skeleton can be properly structured around a [Select Case As Const...End Select] block.
The different cases correspond to the different events that the user wishes to handle.

#include Once "fbgfx.bi"
Using FB
' .....
Dim e As Event
' .....
Do
    If (ScreenEvent(@e)) Then
        Select Case As Const e.type
        Case EVENT_xxx
            ' handle the event xxx
        Case EVENT_yyy
            ' handle the event yyy
        ' .....
        ' .....
        Case EVENT_zzz
            ' handle the event zzz
        Case Else
            ' event not handled
        End Select
    End If
    Sleep 10, 1
Loop
' .....

If for example a key is held down for some seconds, this will induce:
- a first EVENT_KEY_PRESS,
- followed by several EVENT_KEY_REPEAT (for as long as the key is held down),
- and EVENT_KEY_RELEASE when the key is released.

Two successive simple-click actions on a mouse obviously induce four successive mouse events:
EVENT_MOUSE_BUTTON_PRESS
EVENT_MOUSE_BUTTON_RELEASE
EVENT_MOUSE_BUTTON_PRESS
EVENT_MOUSE_BUTTON_RELEASE
An only double-click action on a mouse also induces four successive mouse events, but:
EVENT_MOUSE_BUTTON_PRESS
EVENT_MOUSE_BUTTON_RELEASE
EVENT_MOUSE_DOUBLE_CLICK
EVENT_MOUSE_BUTTON_RELEASE

Note:
- If the user program retrieves a KEY_... event, this does not clear the keyboard buffer. If the keyboard buffer needs to be cleared after user retrieves the event, he will need to clear it manually (see Inkey).
- Event queuing and InKey buffering are two completely independent ways to test the keyboard state. They can coexist, even in two competing threads without any conflict.
- There are currently three ways to test input from the keyboard using gfxlib:
using Inkey (user can test only keys that have an ascii representation),
using MultiKey (user can query the state pressed/released of any key on keyboard),
using ScreenEvent (user can check the KEY_PRESS, KEY_RELEASE and KEY_REPEAT events).

Examples

Example of simple ScreenEvent mechanism to implement a key-pressed events handling:
(click on the window-close button [X] to exit)
'   The main code tests events in a loop:
'       - calls a user Sub each time a key-pressed event is retrieved
'       - exits the loop if a window-close event is retrieved (by click on window-close button)
'   The user Sub prints the character of the key pressed, the ascci code and the scancode.

#include Once "fbgfx.bi"
Using FB

'' user callback Sub definition
Sub printInkeyData (ByVal ascii As Long, ByVal scancode As Long)
    Print "'" & Chr(ascii) & "' (" & ascii & ")", scancode
End Sub

'' user main code
Screen 12
Dim e As Event
Do
    If (ScreenEvent(@e)) Then
        Select Case As Const e.type
        Case EVENT_KEY_PRESS                     '' test key-pressed event
            printInkeyData(e.ascii, e.scancode)
        Case EVENT_WINDOW_CLOSE                  '' test window-close event
            Exit Do
        End Select
    End If
    Sleep 10, 1
Loop

Simple example highlighting that Event queuing and InKey buffering can coexist, even in two competing threads without any conflict:
(ESC to quit)
'   The main code (main thread) tests Inkey in a loop.
'   The other thread tests ScreenEvent in a loop.
'   The ESC character allows to exit the two loops.

#include "fbgfx.bi"
Using FB

Function getAscii (ByVal ascii As Long) As String
    If ((ascii>0) And (ascii<255)) Then
        Return "'" & Chr(ascii) & "'"
    Else
        Return "???"
    End If
End Function

Sub Thread (ByVal p As Any Ptr)
    Dim e As Event
    Do
        If (ScreenEvent(@e)) Then
            Select Case As Const e.Type
            Case EVENT_KEY_PRESS                                      '' test key-pressed event
                Print getAscii(e.ascii) &_
                " is pressed    (from ScreenEvent)   (other thread)"
                If (e.scancode=SC_ESCAPE) Then                        '' test ESC
                    Exit Sub
                End If
            Case EVENT_KEY_RELEASE                                    '' test key-released event
                Print getAscii(e.ascii) &_
                " is released   (from ScreenEvent)   (other thread)"
            Case EVENT_KEY_REPEAT                                     '' test key-repeated event
                Print getAscii(e.ascii) &_
                " is repeated   (from ScreenEvent)   (other thread)"
            End Select
        End If
        Sleep 10, 1
    Loop
End Sub

Screen 12

Dim As String s
Dim As Any Ptr pt
pt = ThreadCreate(@Thread)

Do
    s = Inkey
    If s <> "" Then                                                   '' test inkey return
        Print getAscii(s[0]) &_
        " is viewed     (from Inkey)         (main thread)"
    End If
    Sleep 10, 1
Loop Until s = Chr(27)                                                '' test ESC

ThreadWait(pt)
Print "main and other thread completed"

Sleep

Example of mouse event handling where cursor position and buttons state are stored in a Type derived of EVENT to be available at the time of events other than those that normally provide them:
(click on the window-close button [X] to exit)
' Memorization in (.mx, .my) of the cursor position of the mouse:
'     - at MOUSE_MOVE event : e.mx = e.x, e.my = e.y
' Memorization in .mbutton of the buttons state of the mouse:
'     - at MOUSE_BUTTON_PRESS event and MOUSE_DOUBLE_CLICK event : e.mbutton = e.mbutton Or e.button
'     - at MOUSE_BUTTON_RELEASE event : e.mbutton = e.mbutton Xor e.button

#include Once "fbgfx.bi"
Using FB

Type EVENTstore Extends Event
    mx As Long
    my As Long
    mbutton As Long
End Type

Screen 19
Dim e As EVENTstore
Do
    If ScreenEvent(@e) Then
        Select Case As Const e.Type
        Case EVENT_MOUSE_MOVE
            e.mx = e.x
            e.my = e.y
            Print Using "Mouse move:                  x=####   /   y=####      dx=####  /  dy=####      button=##";_
            e.x; e.y; e.dx; e.dy; e.mbutton
        Case EVENT_MOUSE_BUTTON_PRESS
            e.mbutton = e.mbutton Or e.button
            Print Using "Mouse button press:          button =##               x=####   /   y=####";_
            e.button; e.mx; e.my
        Case EVENT_MOUSE_BUTTON_RELEASE
            e.mbutton = e.mbutton Xor e.button
            Print Using "Mouse button release:        button =##               x=####   /   y=####";_
            e.button; e.mx; e.my
        Case EVENT_MOUSE_DOUBLE_CLICK
            e.mbutton = e.mbutton Or e.button
            Print Using "Mouse button double click:   button =##               x=####   /   y=####";_
            e.button; e.mx; e.my
        Case EVENT_MOUSE_WHEEL
            Print Using "Mouse wheel:                 wheel=  ###########      x=####   /   y=####";_
            e.z; e.mx; e.my
        Case EVENT_MOUSE_HWHEEL
            Print Using "Mouse hwheel:                hwheel= ###########      x=####   /   y=####";_
            e.z; e.mx; e.my
        Case EVENT_WINDOW_CLOSE
            Exit Do
        End Select
    End If
    Sleep 10, 1
Loop

See also

  • Event
  • ScreenEvent
  • Inkey
  • MultiKey
  • GetMouse
  • Keyboard scancodes


Identifier Look-ups in namespaces and types
When referencing to an identifier is encountered in a program, a look-up is performed to locate the declaration that introduced that identifier.

Preamble:

The identifiers to look-up can be unqualified or qualified:
- An unqualified identifier is an identifier that does not have a prefix to specify what scope it comes from.
- A qualified identifier includes a prefix to clarify the interpretive scope by overriding the default scope.

Scope priority hierarchy

Unqualified identifier look-ups verify the priority hierarchy as follows:
    • [1] Current Namespace/Type.
    • [2] Base Types (by Extends), ranked from closest to furthest in inheritance hierarchy.
    • [3] Parent Namespaces (by nesting), ranked from closest to furthest in ancestry hierarchy (including global namespace: always furthest in hierarchy).
    • [4] Imported Namespaces (by Using), without hierarchy between them (regardless of their respective level of nesting).

Unqualified identifiers for look-up can be:
- Variable identifiers.
=> Look-up order: [1], [2], [3], [4].
- Procedure identifiers.
=> Look-up order: [1], [2], [3], [4].
- Type/Union identifiers.
=> Look-up order: [1], [3], [4].
- Enum identifiers, not their field symbols alone.
=> Look-up order: [1], [3], [4].

If a qualified identifier with a prefix referring to a namespace is used instead of an unqualified identifier, the previous rules for unqualified identifiers are amended as follows:
    • The look-up begins at the level of the namespace corresponding to the specified prefix (as in [1]).
    • Failing that, then only namespaces imported into this namespace can be candidates for the look-up (as in [4]).
    • But never the parent namespaces of this namespace nor also the global namespace can be candidates (not as in [3]).
=> Look-up order: [1], [4].

If a qualified identifier with a prefix (object-name/This or Base (Member Access)) referring to a type is used instead of an unqualified identifier, the previous rules for unqualified identifiers are amended as follows:
    • The look-up begins at the level of the type or base type depending on the specified prefix object-name/This or Base (Member Access) (similarly to [1]).
    • Failing that, then only higher base types from closest to furthest can be successively candidates for the look-up (as in [2]).
    • But never any namespaces, the current also excluded, can be candidates (not as in [3] or [4]).
=> Look-up order: [1], [2].

Full process

The full process simplifies the problem by dealing with it in two successive phases:
    • Using the scope priority hierarchy rules above, selection of a single scope among all those which are accessible by only taking into account the identifier for look-up, regardless of the signature supplied by the caller (if at least two scopes containing this identifier have equivalent accessibility priority, then the process is stopped with an ambiguity status).
    • Within the single scope retained if it exists, one final overload resolution taking into account the full signature is carried out (if there is no compatibility with the declared signature(s), the process is stopped without testing another accessible scope).

The problem is thus simplified to one single full overload resolution within a same scope (instead of a full overload resolution within each scope candidate, and then the difficult choice of the best result among the scope candidates).

The coding of a very optimized resolution process (the best of human intelligence) for a procedure identifier is cumbersome, because having to take into account from the start the complete signature of the called procedure (identifier of the procedure + type and number of parameters + calling convention + return type if it exists).
In addition, it must be taken into account that the signature deduced from the call may not necessarily be identical but just compatible with certain declared ones in the scope candidates.

Identifier look-ups for the overload non-member operators
Currently, the look-up for the overload non-member operator identifiers is simplified by the fact that the compiler always define these operators in the global namespace even if the user declared them in named namespaces.
But the global namespace is always a scope candidate when looking-up for a non-member overload operator identifier from another scope, because this operator identifier cannot be used with a qualifier prefix but always as an unqualified identifier (seen from the operator call, the global namespace is either the current namescape or a parent namescape).

Identifier look-ups for the Using commands
The look-up for the Using command identifiers follows rules similar to those of a Type.

Examples

The following four codes test different look-ups both for one unqualified and one qualified identifier of procedures defined in different scope cases (from the highest to the lowest priority hierarchy):
Sub duplicateSub()
    Print "   ..duplicateSub"
End Sub

Namespace M
    Sub duplicateSub()
        Print "   M.duplicateSub"
    End Sub
End Namespace

Namespace N
    Sub duplicateSub()
        Print "   N.duplicateSub"
    End Sub
    Namespace P
        Using M
        Sub duplicateSub()
            Print "   N.P.duplicateSub"
        End Sub
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            duplicateSub()
        End Sub
    End Namespace
End Namespace

Print "From Namespace:"
' "N.P.test()" calls the unqualified identifier "duplicateSub"
' "N.P.duplicateSub()" calls the qualified identifier "N.P.duplicateSub"

N.P.test()               '' "N.P.duplicateSub" expected : in (1) current namespace/type
N.P.duplicateSub()       '' "N.P.duplicateSub" expected : in (1) current namespace/type

Print
Sleep

Sub duplicateSub()
    Print "   ..duplicateSub"
End Sub

Namespace M
    Sub duplicateSub()
        Print "   M.duplicateSub"
    End Sub
End Namespace

Namespace N
    Sub duplicateSub()
        Print "   N.duplicateSub"
    End Sub
    Namespace P
        Using M
        'Sub duplicateSub()
        '    Print "   N.P.duplicateSub"
        'End Sub
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            duplicateSub()
        End Sub
    End Namespace
End Namespace

Print "From Namespace:"
' "N.P.test()" calls the unqualified identifier "duplicateSub"
' "N.P.duplicateSub()" calls the qualified identifier "N.P.duplicateSub"

N.P.test()               '' "N.duplicateSub" expected : in [3] parent namespaces (by nesting)
N.P.duplicateSub()       '' "M.duplicateSub" expected : in [4] imported namespaces (by 'Using')

Print
Sleep

Sub duplicateSub()
    Print "   ..duplicateSub"
End Sub

Namespace M
    Sub duplicateSub()
        Print "   M.duplicateSub"
    End Sub
End Namespace

Namespace N
    'Sub duplicateSub()
    '    Print "   N.duplicateSub"
    'End Sub
    Namespace P
        Using M
        'Sub duplicateSub()
        '    Print "   N.P.duplicateSub"
        'End Sub
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            duplicateSub()
        End Sub
    End Namespace
End Namespace

Print "From Namespace:"
' "N.P.test()" calls the unqualified identifier "duplicateSub"
' "N.P.duplicateSub()" calls the qualified identifier "N.P.duplicateSub"

N.P.test()               '' "..duplicateSub" expected : in [3] parent namespaces (by nesting)
N.P.duplicateSub()       '' "M.duplicateSub" expected : in [4] imported namespaces (by 'Using')

Print
Sleep

'Sub duplicateSub()
'    Print "   ..duplicateSub"
'End Sub

Namespace M
    Sub duplicateSub()
        Print "   M.duplicateSub"
    End Sub
End Namespace

Namespace N
    'Sub duplicateSub()
    '    Print "   N.duplicateSub"
    'End Sub
    Namespace P
        Using M
        'Sub duplicateSub()
        '    Print "   N.P.duplicateSub"
        'End Sub
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            duplicateSub()
        End Sub
    End Namespace
End Namespace

Print "From Namespace:"
' "N.P.test()" calls the unqualified identifier "duplicateSub"
' "N.P.duplicateSub()" calls the qualified identifier "N.P.duplicateSub"

N.P.test()               '' "M.duplicateSub" expected : in [4] imported namespaces (by 'Using')
N.P.duplicateSub()       '' "M.duplicateSub" expected : in [4] imported namespaces (by 'Using')

Print
Sleep

The following six codes test different look-ups both for one unqualified and one qualified identifier of variables defined in different scope cases (from the highest to the lowest priority hierarchy):
Dim Shared As ZString * 32 duplicateVar = "   ..duplicateVar"

Namespace M
    Dim As ZString *32 duplicateVar = "   M.duplicateVar"
End Namespace

Namespace N
    Using M
    Dim As ZString * 32 duplicateVar = "   N.duplicateVar"
    Type Parent Extends Object
        Dim As ZString * 32 duplicateVar = "   N.Parent.duplicateVar"
    End Type
    Type Child Extends Parent
        Dim As ZString * 32 duplicateVar = "   N.Child.duplicateVar"
    End Type
    Type GrandChild Extends Child
        Dim As ZString * 32 duplicateVar = "   N.GrandChild.duplicateVar"
        Declare Sub test()
    End Type
    Sub GrandChild.test()
        Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
        Print duplicateVar
    End Sub
End Namespace

Print "From Type:"
Dim As N.GrandChild gc
' "gc.test()" calls the unqualified identifier "duplicateVar"
' "Print gc.duplicateVar" calls the qualified identifier "gc.duplicateVar"

gc.test()                '' "N.GrandChild.duplicateVar" expected : in [1] current namespace/type
Print gc.duplicateVar    '' "N.GrandChild.duplicateVar" expected : in [1] current namespace/type

Print
Sleep

Dim Shared As ZString * 32 duplicateVar = "   ..duplicateVar"

Namespace M
    Dim As ZString *32 duplicateVar = "   M.duplicateVar"
End Namespace

Namespace N
    Using M
    Dim As ZString * 32 duplicateVar = "   N.duplicateVar"
    Type Parent Extends Object
        Dim As ZString * 32 duplicateVar = "   N.Parent.duplicateVar"
    End Type
    Type Child Extends Parent
        Dim As ZString * 32 duplicateVar = "   N.Child.duplicateVar"
    End Type
    Type GrandChild Extends Child
        'Dim As Zstring * 32 duplicateVar = "   N.GrandChild.duplicateVar"
        Declare Sub test()
    End Type
    Sub GrandChild.test()
        Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
        Print duplicateVar
    End Sub
End Namespace

Print "From Type:"
Dim As N.GrandChild gc
' "gc.test()" calls the unqualified identifier "duplicateVar"
' "Print gc.duplicateVar" calls the qualified identifier "gc.duplicateVar"

gc.test()                '' "N.Child.duplicateVar" expected : in [2] base types (by 'Extends')
Print gc.duplicateVar    '' "N.Child.duplicateVar" expected : in [2] base types (by 'Extends')

Print
Sleep

Dim Shared As ZString * 32 duplicateVar = "   ..duplicateVar"

Namespace M
    Dim As ZString *32 duplicateVar = "   M.duplicateVar"
End Namespace

Namespace N
    Using M
    Dim As ZString * 32 duplicateVar = "   N.duplicateVar"
    Type Parent Extends Object
        Dim As ZString * 32 duplicateVar = "   N.Parent.duplicateVar"
    End Type
    Type Child Extends Parent
        'Dim As Zstring * 32 duplicateVar = "   N.Child.duplicateVar"
    End Type
    Type GrandChild Extends Child
        'Dim As Zstring * 32 duplicateVar = "   N.GrandChild.duplicateVar"
        Declare Sub test()
    End Type
    Sub GrandChild.test()
        Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
        Print duplicateVar
    End Sub
End Namespace

Print "From Type:"
Dim As N.GrandChild gc
' "gc.test()" calls the unqualified identifier "duplicateVar"
' "Print gc.duplicateVar" calls the qualified identifier "gc.duplicateVar"

gc.test()                '' "N.Parent.duplicateVar" expected : in [2] base types (by 'Extends')
Print gc.duplicateVar    '' "N.Parent.duplicateVar" expected : in [2] base types (by 'Extends')

Print
Sleep

Dim Shared As ZString * 32 duplicateVar = "   ..duplicateVar"

Namespace M
    Dim As ZString *32 duplicateVar = "   M.duplicateVar"
End Namespace

Namespace N
    Using M
    Dim As ZString * 32 duplicateVar = "   N.duplicateVar"
    Type Parent Extends Object
        'Dim As Zstring * 32 duplicateVar = "   N.Parent.duplicateVar"
    End Type
    Type Child Extends Parent
        'Dim As Zstring * 32 duplicateVar = "   N.Child.duplicateVar"
    End Type
    Type GrandChild Extends Child
        'Dim As Zstring * 32 duplicateVar = "   N.GrandChild.duplicateVar"
        Declare Sub test()
    End Type
    Sub GrandChild.test()
        Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
        Print duplicateVar
    End Sub
End Namespace

Print "From Type:"
Dim As N.GrandChild gc
' "gc.test()" calls the unqualified identifier "duplicateVar"
' "Print gc.duplicateVar" calls the qualified identifier "gc.duplicateVar"

gc.test()                '' "N.duplicateVar" expected : in [3] parent namespaces (by nesting)
'Print gc.duplicateVar   '' error

Print
Sleep

Dim Shared As ZString * 32 duplicateVar = "   ..duplicateVar"

Namespace M
    Dim As ZString *32 duplicateVar = "   M.duplicateVar"
End Namespace

Namespace N
    Using M
    'Dim As Zstring * 32 duplicateVar = "   N.duplicateVar"
    Type Parent Extends Object
        'Dim As Zstring * 32 duplicateVar = "   N.Parent.duplicateVar"
    End Type
    Type Child Extends Parent
        'Dim As Zstring * 32 duplicateVar = "   N.Child.duplicateVar"
    End Type
    Type GrandChild Extends Child
        'Dim As Zstring * 32 duplicateVar = "   N.GrandChild.duplicateVar"
        Declare Sub test()
    End Type
    Sub GrandChild.test()
        Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
        Print duplicateVar
    End Sub
End Namespace

Print "From Type:"
Dim As N.GrandChild gc
' "gc.test()" calls the unqualified identifier "duplicateVar"
' "Print gc.duplicateVar" calls the qualified identifier "gc.duplicateVar"

gc.test()                '' "..duplicateVar" expected : in [3] parent namespaces (by nesting)
'Print gc.duplicateVar   '' error

Print
Sleep

'Dim Shared As Zstring * 32 duplicateVar = "   ..duplicateVar"

Namespace M
    Dim As ZString *32 duplicateVar = "   M.duplicateVar"
End Namespace

Namespace N
    Using M
    'Dim As Zstring * 32 duplicateVar = "   N.duplicateVar"
    Type Parent Extends Object
        'Dim As Zstring * 32 duplicateVar = "   N.Parent.duplicateVar"
    End Type
    Type Child Extends Parent
        'Dim As Zstring * 32 duplicateVar = "   N.Child.duplicateVar"
    End Type
    Type GrandChild Extends Child
        'Dim As Zstring * 32 duplicateVar = "   N.GrandChild.duplicateVar"
        Declare Sub test()
    End Type
    Sub GrandChild.test()
        Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
        Print duplicateVar
    End Sub
End Namespace

Print "From Type:"
Dim As N.GrandChild gc
' "gc.test()" calls the unqualified identifier "duplicateVar"
' "Print gc.duplicateVar" calls the qualified identifier "gc.duplicateVar"

gc.test()                '' "M.duplicateVar" expected : in [4] imported namespaces (by 'Using')
'Print gc.duplicateVar   '' error

Print
Sleep

The following four codes test different look-ups both for one unqualified and one qualified identifier of types defined in different scope cases (from the highest to the lowest priority hierarchy):
Type duplicateType
    Dim As ZString * 32 root = "   ..duplicateType"
End Type

Namespace M
    Type duplicateType
        Dim As ZString * 32 root = "   M.duplicateType"
    End Type
End Namespace

Namespace N
    Type duplicateType
        Dim As ZString * 32 root = "   N.duplicateType"
    End Type
    Namespace P
        Using M
        Type duplicateType
            Dim As ZString * 32 root = "   N.P.duplicateType"
        End Type
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            Print Type<duplicateType>.root
        End Sub
    End Namespace
End Namespace

' "N.P.test()" calls the unqualified identifier "duplicateType"
' "Print Type<N.P.duplicateType>.root" calls the qualified identifier "N.P.duplicateType"

Print "From Namespace:"
N.P.test()                          '' "N.P.duplicateSub" expected : in (1) current namespace/type
Print Type<N.P.duplicateType>.root  '' "N.P.duplicateSub" expected : in (1) current namespace/type

Print
Sleep

Type duplicateType
    Dim As ZString * 32 root = "   ..duplicateType"
End Type

Namespace M
    Type duplicateType
        Dim As ZString * 32 root = "   M.duplicateType"
    End Type
End Namespace

Namespace N
    Type duplicateType
        Dim As ZString * 32 root = "   N.duplicateType"
    End Type
    Namespace P
        Using M
        'Type duplicateType
        '    Dim As Zstring * 32 root = "   N.P.duplicateType"
        'End type
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            Print Type<duplicateType>.root
        End Sub
    End Namespace
End Namespace

' "N.P.test()" calls the unqualified identifier "duplicateType"
' "Print Type<N.P.duplicateType>.root" calls the qualified identifier "N.P.duplicateType"

Print "From Namespace:"
N.P.test()                          '' "N.duplicateSub" expected : in [3] parent namespaces (by nesting)
Print Type<N.P.duplicateType>.root  '' "M.duplicateSub" expected : in [4] imported namespaces (by 'Using')

Print
Sleep

Type duplicateType
    Dim As ZString * 32 root = "   ..duplicateType"
End Type

Namespace M
    Type duplicateType
        Dim As ZString * 32 root = "   M.duplicateType"
    End Type
End Namespace

Namespace N
    'Type duplicateType
    '    Dim As Zstring * 32 root = "   N.duplicateType"
    'End type
    Namespace P
        Using M
        'Type duplicateType
        '    Dim As Zstring * 32 root = "   N.P.duplicateType"
        'End type
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            Print Type<duplicateType>.root
        End Sub
    End Namespace
End Namespace

' "N.P.test()" calls the unqualified identifier "duplicateType"
' "Print Type<N.P.duplicateType>.root" calls the qualified identifier "N.P.duplicateType"

Print "From Namespace:"
N.P.test()                          '' "..duplicateSub" expected : in [3] parent namespaces (by nesting)
Print Type<N.P.duplicateType>.root  '' "M.duplicateSub" expected : in [4] imported namespaces (by 'Using')

Print
Sleep

'Type duplicateType
'    Dim As Zstring * 32 root = "   ..duplicateType"
'End type

Namespace M
    Type duplicateType
        Dim As ZString * 32 root = "   M.duplicateType"
    End Type
End Namespace

Namespace N
    'Type duplicateType
    '    Dim As Zstring * 32 root = "   N.duplicateType"
    'End type
    Namespace P
        Using M
        'Type duplicateType
        '    Dim As Zstring * 32 root = "   N.P.duplicateType"
        'End type
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            Print Type<duplicateType>.root
        End Sub
    End Namespace
End Namespace

' "N.P.test()" calls the unqualified identifier "duplicateType"
' "Print Type<N.P.duplicateType>.root" calls the qualified identifier "N.P.duplicateType"

Print "From Namespace:"
N.P.test()                          '' "M.duplicateSub" expected : in [4] imported namespaces (by 'Using')
Print Type<N.P.duplicateType>.root  '' "M.duplicateSub" expected : in [4] imported namespaces (by 'Using')

Print
Sleep

The following four codes test different look-ups both for one unqualified and one qualified identifier of enums defined in different scope cases (from the highest to the lowest priority hierarchy):
Dim Shared As ZString * 32 root(...) = {"   ..duplicateEnum", "   M.duplicateEnum", "   N.duplicateEnum", "   N.P.duplicateEnum"}

Enum duplicateEnum
    nb = 0
End Enum

Namespace M
    Enum duplicateEnum
        nb = 1
    End Enum
End Namespace

Namespace N
    Enum duplicateEnum
        nb = 2
    End Enum
    Namespace P
        Using M
        Enum duplicateEnum
            nb = 3
        End Enum
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            Print root(duplicateEnum.nb)
        End Sub
    End Namespace
End Namespace

' "N.P.test()" calls the unqualified identifier "duplicateEnum"
' "Print root(N.P.duplicateEnum.nb)" calls the qualified identifier "N.P.duplicateEnum"

Print "From Namespace:"
N.P.test()                        '' "N.P.duplicateEnum" expected : in (1) current namespace/type
Print root(N.P.duplicateEnum.nb)  '' "N.P.duplicateEnum" expected : in (1) current namespace/type

Print
Sleep

Dim Shared As ZString * 32 root(...) = {"   ..duplicateEnum", "   M.duplicateEnum", "   N.duplicateEnum", "   N.P.duplicateEnum"}

Enum duplicateEnum
    nb = 0
End Enum

Namespace M
    Enum duplicateEnum
        nb = 1
    End Enum
End Namespace

Namespace N
    Enum duplicateEnum
        nb = 2
    End Enum
    Namespace P
        Using M
        'Enum duplicateEnum
        '    nb = 3
        'End Enum
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            Print root(duplicateEnum.nb)
        End Sub
    End Namespace
End Namespace

' "N.P.test()" calls the unqualified identifier "duplicateEnum"
' "Print root(N.P.duplicateEnum.nb)" calls the qualified identifier "N.P.duplicateEnum"

Print "From Namespace:"
N.P.test()                        '' "N.duplicateEnum" expected : in [3] parent namespaces (by nesting)
Print root(N.P.duplicateEnum.nb)  '' "M.duplicateEnum" expected : in [4] imported namespaces (by 'Using')

Print
Sleep

Dim Shared As ZString * 32 root(...) = {"   ..duplicateEnum", "   M.duplicateEnum", "   N.duplicateEnum", "   N.P.duplicateEnum"}

Enum duplicateEnum
    nb = 0
End Enum

Namespace M
    Enum duplicateEnum
        nb = 1
    End Enum
End Namespace

Namespace N
    'Enum duplicateEnum
    '    nb = 2
    'End Enum
    Namespace P
        Using M
        'Enum duplicateEnum
        '    nb = 3
        'End Enum
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            Print root(duplicateEnum.nb)
        End Sub
    End Namespace
End Namespace

' "N.P.test()" calls the unqualified identifier "duplicateEnum"
' "Print root(N.P.duplicateEnum.nb)" calls the qualified identifier "N.P.duplicateEnum"

Print "From Namespace:"
N.P.test()                        '' "..duplicateEnum" expected : in [3] parent namespaces (by nesting)
Print root(N.P.duplicateEnum.nb)  '' "M.duplicateEnum" expected : in [4] imported namespaces (by 'Using')

Print
Sleep

Dim Shared As ZString * 32 root(...) = {"   ..duplicateEnum", "   M.duplicateEnum", "   N.duplicateEnum", "   N.P.duplicateEnum"}

'Enum duplicateEnum
'    nb = 0
'End Enum

Namespace M
    Enum duplicateEnum
        nb = 1
    End Enum
End Namespace

Namespace N
    'Enum duplicateEnum
    '    nb = 2
    'End Enum
    Namespace P
        Using M
        'Enum duplicateEnum
        '    nb = 3
        'End Enum
        Sub test()
            Using M  '' useless, but just to demonstrate that does not increase priority level of imported namespace
            Print root(duplicateEnum.nb)
        End Sub
    End Namespace
End Namespace

' "N.P.test()" calls the unqualified identifier "duplicateEnum"
' "Print root(N.P.duplicateEnum.nb)" calls the qualified identifier "N.P.duplicateEnum"

Print "From Namespace:"
N.P.test()                        '' "M.duplicateEnum" expected : in [4] imported namespaces (by 'Using')
Print root(N.P.duplicateEnum.nb)  '' "M.duplicateEnum" expected : in [4] imported namespaces (by 'Using')

Print
Sleep

Version

  • Since fbc 1.09.0.
  • Before fbc 1.09.0, the priority hierarchy was poorly managed with inconsistencies.

See also

  • Identifier Rules


C Standard Library Functions
This is a list of function prototypes in the standard C library in alphabetical order and a list of prototypes grouped by functionality.

Alphabetical List
Buffer Manipulation
Character Classification and Conversion
Data Conversion
Directory Manipulation
File Manipulation
Stream I/O
Low level I/O
Mathematics
Memory Allocation
Process Control
Searching and Sorting
String Manipulation
Time

Description

The Comments column contains a very brief description of the use of the function. The list is not complete, however it provides information on the major functions in the C Runtime Library. It should, at the very least, indicate what functions are available in the standard C library allow you to do more investigation on your own. Some of the C library functions documented elsewhere may not be available in FreeBASIC. Check the appropriate include file for more information.

Note: The following prototypes are not the official FreeBASIC prototypes (see the include files), however, they will give you enough information to properly use the functions.

The Include File column contains the name of the file which you must include, using the #include directive at the beginning of your program. If you don't include the appropriate include file, the program either will not compile, or it will compile apparently correctly but give incorrect results when run. All of the C Runtime headers are located in the crt directory; for example, if the specified header is math.bi, use #include "crt/math.bi" or #include "crt\math.bi", our just #include "crt.bi" including all the others.

The Prototype column contains the following information:
    • The name of the function;
    • The parameters required for the function in parenthesis, together with the data-type of the parameters;
    • The data-type of the value returned by the function.

For example atoi(a as zstring ptr) as integer means that the function atoi returns a value of type integer and requires a character zstring ptr as its argument.

Note: In order to make calling the C runtime functions very easy, any string type argument may be directly passed to a procedure referring to a parameter declared as 'zstring ptr'. The compiler performs itself an automatic conversion (without warning message) between any string type argument and the 'zstring ptr' type parameter.

Alphabetical List

Name | Prototype (with parameters) | Include File | Comments
abs_ | abs_(n as integer) as integer | stdlib.bi | Returns the absolute value (i.e. positive value)
acos_ | acos_(a as double) as double | math.bi | Returns the inverse cosine (angle in radians)
asin_ | asin_(a as double) as double | math.bi | Returns the inverse sine (angle in radians)
atan_ | atan_(a as double) as double | math.bi | Returns the inverse tan (angle in radians)
atan2_ | atan2_(y as double, x as double) as double | math.bi | Returns the inverse tan (pass the opposite as y and the adjacent as x)
atoi | atoi(s as zstring ptr) as integer | stdlib.bi | Converts a character zstring of digits to a number of type integer.
atof | atof(s as zstring ptr) as double | stdlib.bi | Converts a character zstring of digits to a number of type double.
calloc | calloc(NumElts as integer, EltSiz as integer) as any ptr | stdlib.bi | Allocates memory. Returns a pointer to a buffer for an array having NumElts elements, each of size EltSiz bytes.
ceil | ceil(d as double) as double | math.bi | Returns the nearest whole number above the value passed.
clearerr | clearerr(s as FILE ptr) | stdio.bi | Clears the error indicators on a file stream (read or write).
cos_ | cos_(ar as double) as double | math.bi | Returns the cosine of an angle measured in radians.
cosh | cosh(x as double) as double | math.bi | Returns the hyperbolic cosine of an angle measured in radians.
div | div(num as integer, denom as integer) as div_t | stdlib.bi | Returns the quotient and remainder of a division as a structure of type div_t.
ecvt | ecvt(x as double) as zstring ptr | math.bi | Converts a number to a zstring.
exit_ | exit_(status as integer) | stdlib.bi | Exits a program. It will flush file buffers and closes all opened files, and run any functions called by atexit().
exp_ | exp_(a as double) as double | math.bi | Returns the value of e raised to the power of the argument (Inverse to natural logarithm).
fabs | fabs(d as double) as double | math.bi | Returns the absolute value (i.e. positive value) of type double.
fclose | fclose(s as FILE ptr) as FILE ptr | stdio.bi | Closes a file. Returns 0 if successful otherwise EOF.
feof | feof(s as FILE ptr) as integer | stdio.bi | Returns value of end-of-file indicator . (0 if not eof). Indicator will clear itself but can be reset by clearerr().
ferror | ferror(s as FILE ptr) as integer | stdio.bi | Returns error indicator for a stream (0 if no error). Error indicator is reset by clearerr() or rewind().
fflush | fflush(s as FILE ptr) as integer | stdio.bi | Flushes (i.e. deletes) a stream (use stdin to flush the stream from the keyboard). Returns 0 if successful.
fgetc | fgetc(s as FILE ptr) as integer | stdio.bi | Single character input (in ASCII) from passed stream (stdin for keyboard).
fgetpos | fgetpos(s as FILE ptr, c as fpos_t ptr) as integer | stdio.bi | Saves the position of the file pointer on stream s at the location pointed to by c.
fgets | fgets(b as zstring ptr, n as integer, s as FILE ptr) as zstring ptr | stdio.bi | From the stream s reads up to n-1 characters to buffer b.
floor | floor(d as double) as double | math.bi | Returns the nearest whole number below the value passed.
fmod | fmod(x as double, y as double) as double | math.bi | Calculates the remainder of x divided by y.
fopen | fopen(file as zstring ptr, mode as zstring ptr) as FILE ptr | stdio.bi | Opens a file. Pass the DOS name of the file and a code to indicate whether for reading, writing, or appending. Codes are r for read, w for write, + for read and write, a for append and b to indicate binary.
fprintf | fprintf(s as FILE ptr, fmt as zstring ptr, ...) as integer | stdio.bi | Prints on stream s as many items as there are single % signs in fmt that have matching arguments in the list.
fputc | fputc(c as integer, s as FILE ptr) as integer | stdio.bi | Outputs the single character c to the stream s.
fputs | fputs(b as zstring ptr, s as FILE ptr) as integer | stdio.bi | Sends the character stream in b to stream s, returns 0 if the operation fails.
fread | fread(buf as any ptr, b as size_t, c as size_t, s as FILE ptr) as integer | stdio.bi | Reads the number c items of data of size b bytes from file s to the buffer buf. Returns the number of data items actually read.
free | free(p as any ptr) | stdlib.bi | Frees the memory allocation for a pointer p to enable this memory to be used.
freopen | freopen(file as zstring ptr, mode as zstring ptr, s as FILE ptr) as FILE ptr | stdio.bi | Opens a file for redirecting a stream. e.g. freopen("myfile", "w", stdout) will redirect the standard output to the opened "myfile".
frexp | frexp(x as double, p as integer ptr) as double | math.bi | Calculates a value m so that x equals m times 2 to some power. p is a pointer to m.
fscanf | fscanf(s as FILE ptr, fmt as zstring ptr, ...) as integer | stdio.bi | Reads from stream s as many items as there are % signs in fmt with corresponding listed pointers.
fseek | fseek(s as FILE ptr, offset as integer, origin as integer) as integer | stdio.bi | Locates a file pointer. With origin 0, 1 or 2 for the beginning, offset bytes into and at the end of the stream.
fsetpos | fsetpos(s as FILE ptr, p as fpos_t ptr) as integer | stdio.bi | Sets the file pointer for the stream s to the value pointed to by p.
ftell | ftell(s as FILE ptr) as long | stdio.bi | Locates the position of the file pointer for the stream s.
fwrite | fwrite(buf as any ptr, b as integer, c as integer, s as FILE ptr) as integer | stdio.bi | Writes the number c items of data of size b bytes from the buffer buf to the file s. Returns the number of data items actually written.
getc | getc(s as FILE ptr) as integer | stdio.bi | Macro for single character input (in ASCII) from passed stream. (stdin for keyboard)
getchar | getchar() as integer | stdio.bi | Single character input from the standard input
gets | gets(b as zstring ptr) as zstring ptr | stdio.bi | Reads a stream of characters from the standard input until it meets \n or EOF.
hypot | hypot(x as double, y as double) as double | math.bi | Calculates the hypotenuse from the sides x and y.
isalnum | isalnum(c as integer) as integer | ctype.bi | Returns a non zero value if character c is alphabetic or a digit.
isalpha | isalpha(c as integer) as integer | ctype.bi | Returns a non zero value if character c is alphabetic.
iscntrl | iscntrl(c as integer) as integer | ctype.bi | Returns a non zero value if character c is a control character.
isdigit | isdigit(c as integer) as integer | ctype.bi | Returns a non zero value if character c is a digit.
isgraph | isgraph(c as integer) as integer | ctype.bi | Returns a non zero value if character c is alphabetic.
islower | islower(c as integer) as integer | ctype.bi | Returns a non-zero value if character c is a lower case character.
isprint | isprint(c as integer) as integer | ctype.bi | Returns a non zero value if character c is printable.
ispunct | ispunct(c as integer) as integer | ctype.bi | Returns a non zero value if character c is a punctuation character.
isspace | isspace(c as integer) as integer | ctype.bi | Returns a non zero value if character c denotes a space.
isupper | isupper(c as integer) as integer | ctype.bi | Returns a non-zero value if character c is an upper case character.
isxdigit | isxdigit(c as integer) as integer | ctype.bi | Returns a non-zero value if character c is a hex digit (0 to F or f).
ldexp | ldexp(x as double, n as integer) as double | math.bi | Returns the product of x and 2 to the power n.
ldiv | ldiv(num as long, denom as long) as ldiv_t | stdlib.bi | Returns the quotient and remainder of a division as a structure of type ldiv_t.
log_ | log_(a as double) as double | math.bi | Returns the natural logarithm of the argument.
log10 | log10(a as double) as double | math.bi | Returns the logarithm to the base 10 of the argument.
malloc | malloc(bytes as integer) as any ptr | stdlib.bi | Allocates memory. Returns a pointer to a buffer comprising storage for the specified size.
modf | modf(d as double, p as double ptr) as double | math.bi | Returns the fractional part of a floating point number d. p points to the integral part expressed as a float.
perror | perror(mess as zstring ptr) | stdio.bi | Prints on the stream stderr a message passed as the argument.
pow | pow(x as double, y as double) as double | math.bi | Returns x to the power y.
pow10 | pow10(x as double) as double | math.bi | Returns 10 to the power x (inverse function to log10()).
printf | printf(fmt as zstring ptr, ...) as integer | stdio.bi | Prints on standard output as many items as there are single % signs in fmt with matching arguments in the list.
putc | putc(c as integer, s as FILE ptr) as integer | stdio.bi | Macro to output the single character c to the stream s.
putchar | putchar(c as integer) as integer | stdio.bi | Macro to output the single character c to the standard output.
puts | puts(b as zstring ptr) as integer | stdio.bi | Sends the character stream in b to the standard output, returns 0 if operation fails.
rand | rand() as integer | stdlib.bi | Returns a pseudo random number. A seed is required. The seed is set with srand.
realloc | realloc(p as any ptr, newsize as size_t) as any ptr | stdlib.bi | Allocates memory. Returns a pointer to a buffer for a change in size of object pointed to by p.
rewind | rewind(s as FILE ptr) | stdio.bi | Clears the error indicators on a file stream (read or write). Necessary before reading an amended file.
scanf | scanf(fmt as zstring ptr, ...) as integer | stdio.bi | Reads from standard input as many items as there are % signs in fmt with corresponding listed pointers.
sin_ | sin_(ar as double) as double | math.bi | Returns the sine of an angle measured in radians.
sinh | sinh(x as double) as double | math.bi | Returns the hyperbolic sine of an angle measured in radians.
sprintf | sprintf(p as zstring ptr, fmt as zstring ptr, ...) as integer | stdio.bi | Prints on zstring p as many items as there are single % signs in fmt that have matching arguments in the list.
sqrt | sqrt(a as double) as double | math.bi | Returns the square root of the value passed. Domain error if value is negative.
srand | srand(seed as uinteger) | stdlib.bi | Sets the seed for a random number. A possible seed is the current time.
sscanf | sscanf(b as zstring ptr, fmt as zstring ptr, ...) as integer | stdio.bi | Reads from buffer b as many items as there are % signs in fmt with corresponding listed pointers.
strcat | strcat(s1 as zstring ptr, s2 as zstring ptr) as zstring ptr | string.bi | Concatenates (appends) zstring s2 to s1.
strchr | strchr(s as zstring ptr, c as integer) as zstring ptr | string.bi | Returns a pointer to the first occurrence of c in s or NULL if it fails to find one.
strcmp | strcmp(s1 as zstring ptr, s2 as zstring ptr) as integer | string.bi | Compares zstring s2 to s1. Returns 0 or signed difference in ASCII values of first non matching character.
strcpy | strcpy(s1 as zstring ptr, s2 as zstring ptr) as zstring ptr | string.bi | Copies s2 into s1.
strcspn | strcspn(s1 as zstring ptr, s2 as zstring ptr) as integer | string.bi | Returns the number of characters in s1 encountered before meeting any of the characters in s2.
strerror | strerror(n as integer) as zstring ptr | string.bi | Returns a pointer to a system error message corresponding to the passed error number.
strlen | strlen(s as zstring ptr) as integer | string.bi | Returns the number of bytes in the null terminated zstring pointed to by s (does not count null).
strncat | strncat(s1 as zstring ptr, s2 as zstring ptr, n as integer) as zstring ptr | string.bi | Concatenates (appends) n bytes from zstring s2 to s1.
strncmp | strncmp(s1 as zstring ptr, s2 as any ptr, n as integer) as integer | string.bi | Compares n bytes of zstring s2 to the same of s1. Returns 0 or signed difference in ASCII values of first non matching character.
strncpy | strncpy(s1 as zstring ptr, s2 as zstring ptr, n as integer) as zstring ptr | string.bi | Copies n bytes from s2 into s1.
strpbrk | strpbrk(s1 as zstring ptr, s2 as zstring ptr) as zstring ptr | string.bi | Returns a pointer to the first character encountered in s1 that is also in s2.
strrchr | strrchr(s as zstring ptr, c as integer) as zstring ptr | string.bi | Returns a pointer to the last occurrence of c in s or NULL if it fails to find one.
strspn | strspn(s1 as zstring ptr, s2 as zstring ptr) as integer | string.bi | Returns the number of characters in s1 encountered before meeting a character which is not in s2.
strstr | strstr(s1 as zstring ptr, s2 as zstring ptr) as zstring ptr | string.bi | Finds the location of the zstring s2 in s1 and returns a pointer to its leading character.
strtod | strtod(s as zstring ptr, p as zstring ptr) as double | stdlib.bi | Converts a zstring to double, provided the zstring is written in the form of a number.
strtok | strtok(s1 as zstring ptr, s2 as zstring ptr) as zstring ptr | string.bi | Returns pointers to successive tokens utilizing the zstring s1. Tokens regarded as separators are listed in s2.
system | system(command as zstring ptr) as integer | stdlib.bi | Executes, from within a program, a command addressed to the operating system written as a zstring (e.g. DIR on Windows and DOS and LS on Linux).
tan_ | tan_(ar as double) as double | math.bi | Returns the tangent of an angle measured in radians.
tanh | tanh(x as double) as double | math.bi | Returns the hyperbolic tangent of an angle measured in radians.
tolower | tolower(c as integer) as integer | ctype.bi | Converts a character from upper case to lower case (uses ASCII code).
toupper | toupper(c as integer) as integer | ctype.bi | Converts a character from lower case to upper case (uses ASCII code).
ungetc | ungetc(c as integer, s as FILE ptr) as integer | stdio.bi | Pushes a character c back into the stream s, returns EOF if unsuccessful. Do not push more than one character.

Buffer Manipulation

#include "crt/string.bi"

Prototype (with parameters) | Comments
memchr(s as any ptr, c as integer, n as size_t) as any ptr | Search for a character in a buffer.
memcmp(s1 as any ptr, s2 as any ptr, n as size_t) as integer | Compare two buffers.
memcpy(dest as any ptr, src as any ptr, n as size_t) as any ptr | Copy one buffer into another .
memmove(dest as any ptr, src as any ptr, n as size_t) as any ptr | Move a number of bytes from one buffer lo another.
memset(s as any ptr, c as integer, n as size_t) as any ptr | Set all bytes of a buffer to a given character.

Character Classification and Conversion

#include "crt/ctype.bi"

Prototype (with parameters) | Comments
isalnum(c as integer) as integer | True if c is alphanumeric.
isalpha(c as integer) as integer | True if c is a letter.
isascii(c as integer) as integer | True if c is ASCII .
iscntrl(c as integer) as integer | True if c is a control character.
isdigit(c as integer) as integer | True if c is a decimal digit.
isgraph(c as integer) as integer | True if c is a graphical character.
islower(c as integer) as integer | True if c is a lowercase letter.
isprint(c as integer) as integer | True if c is a printable character.
ispunct(c as integer) as integer | True if c is a punctuation character.
isspace(c as integer) as integer | True if c is a space character.
isupper(c as integer) as integer | True if c is an uppercase letter.
isxdigit(c as integer) as integer | True if c is a hexadecimal digit.
toascii(c as integer) as integer | Convert c to ASCII .
tolower(c as integer) as integer | Convert c to lowercase.
toupper(c as integer) as integer | Convert c to uppercase.

Data Conversion

#include "crt/stdlib.bi"

Prototype (with parameters) | Comments
atof(string1 as zstring ptr) as double | Convert zstring to floating point value.
atoi(string1 as zstring ptr) as integer | Convert zstring to an integer value.
atol(string1 as zstring ptr) as integer | Convert zstring to a long integer value.
itoa(value as integer, zstring as zstring ptr, radix as integer) as zstring ptr | Convert an integer value to a zstring using given radix.
ltoa(value as long, zstring as zstring ptr, radix as integer) as zstring ptr | Convert long integer to zstring in a given radix.
strtod(string1 as zstring ptr, endptr as zstring ptr) as double | Convert zstring to a floating point value.
strtol(string1 as zstring ptr, endptr as zstring ptr, radix as integer) as long | Convert zstring to a long integer using a given radix.
strtoul(string1 as zstring ptr, endptr as zstring ptr, radix as integer) as ulong | Convert zstring to unsigned long.

Directory Manipulation

#include "crt/io.bi"

Prototype (with parameters) | Comments
_chdir(path as zstring ptr) as integer | Change current directory to given path.
_getcwd(path as zstring ptr, numchars as integer) as zstring ptr | Returns name of current working directory.
_mkdir(path as zstring ptr) as integer | Create a directory using given path name.
_rmdir(path as zstring ptr) as integer | Delete a specified directory.

File Manipulation

#include "crt/sys/stat.bi"
#include "crt/io.bi"

Prototype (with parameters) | Comments
chmod(path as zstring ptr, pmode as integer) as integer | Change permission settings of a file.
fstat(handle as integer, buffer as type stat ptr) as integer | Get file status information.
remove(path as zstring ptr) as integer | Delete a named file.
rename_(oldname as zstring ptr, newname as zstring ptr) as integer | rename a file.
stat(path as zstring ptr, buffer as type stat ptr) as integer | Get file status information of named file.
umask(pmode as uinteger) as uinteger | Set file permission mask.

Stream I/O

#include "crt/stdio.bi"

Prototype (with parameters) | Comments
clearerr(file_pointer as FILE ptr) | Clear error indicator of stream,
fclose(file_pointer as FILE ptr) as integer | Close a file,
feof(file_pointer as FILE ptr) as integer | Check if end of file occurred on a stream.
ferror(file_pointer as FILE ptr) as integer | Check if any error occurred during file I/0.
fflush(file_pointer as FILE ptr) as integer | Write out (flush) buffer to file.
fgetc(file_pointer as FILE ptr) as integer | Get a character from a stream.
fgetpos(file_pointer as FILE ptr, fpos_t current_pos) as integer | Get the current position in a stream.
fgets(string1 as zstring ptr, maxchar as integer, file_pointer as FILE ptr) as zstring ptr | Read a zstring from a file.
fopen(filename as zstring ptr, access_mode as zstring ptr) as FILE ptr | Open a file for buffered I/0.
fprintf(file_pointer as FILE ptr, format_string as zstring ptr, args) as integer | Write formatted output to a file,
fputc(c as integer, file_pointer as FILE ptr) as integer | Write a character to a stream.
fputchar(c as integer) as integer | Write a character to stdout.
fputs(string1 as zstring ptr, file_pointer as FILE ptr) as integer | Write a zstring to a stream.
fread(buffer as zstring ptr, size as size_t count as size_t, file_pointer as FILE ptr) as size_t | Read unformatted data from a stream into a buffer.
freopen(filename as zstring ptr, access as zstring ptr mode, file_pointer as FILE ptr) as FILE ptr | Reassign a file pointer to a different file.
fscanf(file_pointer as FILE ptr, format as zstring ptr zstring, args) as integer | Read formatted input from a stream.
fseek(file_pointer as FILE ptr, offset as long, origin as integer) as integer | Set current position in file to a new location.
fsetpos(file_pointer as FILE ptr, current_pos as fpos_t) as integer | Set current position in file to a new location.
ftell(file_pointer as FILE ptr) as long | Get current location in file.
fwrite(buffer as zstring ptr, size as size_t, count as size_t file_pointer as FILE ptr) as size_t | Write unformatted data from a buffer to a stream.
getc(file_pointer as FILE ptr) as integer | Read a character from a stream.
getchar() as integer | Read a character from stdin.
gets(buffer as zstring ptr) as zstring ptr | Read a line from stdin into a buffer.
printf(format as zstring ptr _string, args) as integer | Write formatted output to stdout.
putc(c as integer, file_pointer as FILE ptr) as integer | Write a character to a stream.
putchar(c as integer) as integer | Write a character to stdout.
puts(string1 as zstring ptr) as integer | Write a zstring to stdout.
rewind(file_pointer as FILE ptr) | Rewind a file.
scanf(format_string as zstring ptr, args) as integer | Read formatted input from stdin.
setbuf(file_pointer as FILE ptr, buffer as zstring ptr) | Set up a new buffer for the stream.
setvbuf(file_pointer as FILE ptr, buffer as zstring ptr, buf_type as integer, buf as size_t size) as integer | Set up new buffer and control the level of buffering on a stream.
sprintf(string1 as zstring ptr, format_string as zstring ptr, args) as integer | Write formatted output to a zstring.
sscanf(buffer as zstring ptr, format_string as zstring ptr, args) as integer | Read formatted input from a zstring.
tmpfile() as FILE ptr | Open a temporary file.
tmpnam(file_name as zstring ptr) as zstring ptr | Get temporary file name.
ungetc(c as integer, file_pointer as FILE ptr) as integer | Push back character into stream' s buffer

Low level I/O

#include "crt/io.bi"

So far Win32 only, connects to MSVCRT.DLL (headers missing for other platforms)

Prototype (with parameters) | Comments
_close(handle as integer) as integer | Close a file opened for unbuffered I/O.
_creat(filename as zstring ptr, pmode as integer) as integer | Create a new file with specified permission setting.
_eof(handle as integer) as integer | Check for end of file.
_lseek(handle as integer, offset as long, origin as integer) as long | Go to a specific position in a file.
_open(filename as zstring ptr, oflag as integer, pmode as uinteger) as integer | Open a file for low-level I/O.
_read(handle as integer, buffer as zstring ptr, length as uinteger) as integer | Read binary data from a file into a buffer.
_write(handle as integer, buffer as zstring ptr, count as uinteger) as integer | Write binary data from a buffer to a file.

Mathematics

#include "crt/math.bi"

Prototype (with parameters) | Comments
abs_(n as integer) as integer | Get absolute value of an integer.
acos_(x as double) as double | Compute arc cosine of x.
asin_(x as double) as double | Compute arc sine of x.
atan_(x as double) as double | Compute arc tangent of x.
atan2_(y as double, x as double) as double | Compute arc tangent of y/x.
ceil(x as double) as double | Get smallest integral value that exceeds x.
cos_(x as double) as double | Compute cosine of angle in radians.
cosh(x as double) as double | Compute the hyperbolic cosine of x.
div(number as integer, denom as integer) as div_t | Divide one integer by another.
exp_(x as double) as double | Compute exponential of x.
fabs(x as double) as double | Compute absolute value of x.
floor(x as double) as double | Get largest integral value less than x.
fmod(x as double, y as double) as double | Divide x by y with integral quotient and return remainder.
frexp(x as double, expptr as integer ptr) as double | Breaks down x into mantissa and exponent of no.
labs(n as long) as long | Find absolute value of long integer n.
ldexp(x as double, exp as integer) as double | Reconstructs x out of mantissa and exponent of two.
ldiv(number as long, denom as long) as ldiv_t | Divide one long integer by another.
log_(x as double) as double | Compute log(x).
log10(x as double) as double | Compute log to the base 10 of x.
modf(x as double, intptr as double ptr) as double | Breaks x into fractional and integer parts.
pow(x as double, y as double) as double | Compute x raised to the power y.
rand() as integer | Get a random integer between 0 and 32767.
random(max_num as integer) as integer | Get a random integer between 0 and max_num.
randomize() | Set a random seed for the random number generator.
sin_(x as double) as double | Compute sine of angle in radians.
sinh(x as double) as double | Compute the hyperbolic sine of x.
sqrt(x as double) as double | Compute the square root of x.
srand(seed as uinteger) | Set a new seed for the random number generator (rand).
tan_(x as double) as double | Compute tangent of angle in radians.
tanh(x as double) as double | Compute the hyperbolic tangent of x.

Memory Allocation

#include "crt/stdlib.bi"

Prototype (with parameters) | Comments
calloc(num as size_t elems, elem_size as size_t) as any ptr | Allocate an array and initialise all elements to zero .
free(mem_address as any ptr) | Free a block of memory.
malloc(num as size_t bytes) as any ptr | Allocate a block of memory.
realloc(mem_address as any ptr, newsize as size_t) as any ptr | Reallocate (adjust size) a block of memory.

Process Control

#include "crt/stdlib.bi"

Prototype (with parameters) | Comments
abort() | Abort a process.
execl(path as zstring ptr, arg0 as zstring ptr, arg1 as zstring ptr,..., NULL) as integer | Launch a child process (pass command line).
execlp(path as zstring ptr, arg0 as zstring ptr, arg1 as zstring ptr,..., NULL) as integer | Launch child (use PATH, pass command line).
execv(path as zstring ptr, argv as zstring ptr) as integer | Launch child (pass argument vector).
execvp(path as zstring ptr, argv as zstring ptr) as integer | Launch child (use PATH, pass argument vector).
exit_(status as integer) | Terminate process after flushing all buffers.
getenv(varname as zstring ptr) as zstring ptr | Get definition of environment variable,
perror(string1 as zstring ptr) | Print error message corresponding to last system error.
putenv(envstring as zstring ptr) as integer | Insert new definition into environment table.
raise(signum as integer) as integer | Generate a C signal (exception).
system_(string1 as zstring ptr) as integer | Execute a resident operating system command.

Searching and Sorting

#include "crt/stdlib.bi"
Note: The compare callback function required by bsearch and qsort must be declared as cdecl. It must return a value <0 if its first argument should be located before the second one in the sorted array, >0 if the first argument should be located after the second one, and zero if their relative positions are indifferent (equal values).
Prototype (with parameters) | Comments
bsearch(key as any ptr, base as any ptr, num as size_t, width as size_t, compare as function(elem1 as any ptr, elem2 as any ptr) as integer) as any ptr | Perform binary search.
qsort(base as any ptr, num as size_t, width as size_t, compare as function(elem1 as any ptr, elem2 as any ptr) as integer) | Use the quicksort algorithm to sort an array.

String Manipulation

#include "crt/string.bi"

Prototype (with parameters) | Comments
stpcpy(dest as zstring ptr, src as zstring ptr) as zstring ptr | Copy one zstring into another.
strcmp(string1 as zstring ptr, string2 as zstring ptr) as integer | Compare string1 and string2 to determine alphabetic order.
strcpy(string1 as zstring ptr, string2 as zstring ptr) as zstring ptr | Copy string2 to string1.
strerror(errnum as integer) as zstring ptr | Get error message corresponding to specified error number.
strlen(string1 as zstring ptr) as integer | Determine the length of a zstring.
strncat(string1 as zstring ptr, string2 as zstring ptr, n as size_t) as zstring ptr | Append n characters from string2 to string1.
strncmp(string1 as zstring ptr, string2 as zstring ptr, n as size_t) as integer | Compare first n characters of two strings.
strncpy(string1 as zstring ptr, string2 as zstring ptr, n as size_t) as zstring ptr | Copy first n characters of string2 to string1.
strnset(string1 as zstring ptr, c as integer, size _t n) as zstring ptr | Set first n characters of zstring to c.
strrchr(string1 as zstring ptr, c as integer) as zstring ptr | Find last occurrence of character c in zstring.

Time

#include "crt/time.bi"

Prototype (with parameters) | Comments
asctime(time as type tm ptr) as zstring ptr | Convert time from type tm to zstring.
clock() as clock_t | Get elapsed processor time in clock ticks.
ctime(time as time_t ptr) as zstring ptr | Convert binary time to zstring.
difftime(time_t time2, time_t time1) as double | Compute the difference between two times in seconds.
gmtime(time as time_t ptr) as type tm ptr | Get Greenwich Mean Time (GMT) in a tm structure.
localtime(time as time_t ptr) as type tm ptr | Get the local time in a tm structure.
time_(timeptr as time_t ptr) as time_t | Get current time as seconds elapsed since 0 hours GMT 1/1/70.

See also

  • #include


File I/O with FreeBASIC
In FreeBASIC, there are 4 possible ways to perform file I/O:

1. Using the built-in BASIC commands like Open , Get, Put, and Close. This way is mostly portable across all platforms supported by FreeBASIC. Open files are identified by "file numbers", that are specific to FreeBASIC and can't be passed into functions from below.

2. Using the C stream I/O functions like fopen, fread, ftell, fclose (see Stream I/O in C Standard Library Functions) of the C library FreeBASIC relies on. This is slightly faster than and adds a few features beyond method above, and still well portable. Open files are identified by file pointers, as in the C language, again unique to this access method. The FileAttr function can be used to return a stream I/O pointer from a file number as in 1. above.

3. Using the C low-level I/O functions like _open, _read, _write, _close (see Low Level I/O in C Standard Library Functions). Those functions should be portable, but so far headers are available for Win32 only, so code using them will not compile to any other platform by now.

4. Talk directly to the OS kernel (DOS: use DOS and DPMI INT's , Win32: use API calls like CreateFile, WriteFile). This is no longer portable. Files are identified by handles generated by and specific to the OS kernel.

This example shows methods 1. and 2. described above:

Example

'==== File I/O example / 2018-05-18 ====

Dim As String fileName = "test_123.tmp"
Dim As ULong buffer(0 To 99) '100 x 4 bytes
Dim As Integer numItems, result

Print !"\n==== Using the C Runtime (CRT) file I/O ====\n"

#include Once "crt/stdio.bi"

Dim As FILE Ptr filePtr

'open in binary writing mode
filePtr = fopen(fileName, "wb")
If filePtr <> 0 Then
    'write 75 x 4 = 300 bytes
    numItems = fwrite(@buffer(0), SizeOf(buffer(0)), 75, filePtr)
    Print "Number of bytes written: " & Str(numItems * SizeOf(buffer(0)))
    Print "Number of items written: " & Str(numItems)
    fclose(filePtr)
Else
    Print "Failed to open " & fileName & " for writing"
End If

'open in binary reading mode
filePtr = fopen(fileName, "rb")
If filePtr <> 0 Then
    'skip the first 25 items
    If fseek(filePtr, SizeOf(buffer(0)) * 25, SEEK_SET) <> 0 Then
        Print "Failed to seek (set file stream position)"
    End If
    'try to read the next 100 items
    numItems = fread(@buffer(0), SizeOf(buffer(0)), 100, filePtr)
    Print "Number of bytes read: " & Str(numItems * SizeOf(buffer(0)))
    Print "Number of items read: " & Str(numItems)
    fclose(filePtr)
Else
    Print "Failed to open " & fileName & " for reading"
End If

result = remove(fileName) 'delete file
If result = 0 Then Print "Removed: " & fileName

Print !"\n==== Using the FreeBASIC file I/O ====\n"

Dim As Long fileNum
Dim As Integer numBytes

fileNum = FreeFile
'open in binary writing mode
If Open(fileName, For Binary, Access Write, As fileNum) = 0 Then
    'write 75 x 4 = 300 bytes
    result = Put(fileNum, , buffer(0), 75) 'No @buffer(0)
    numBytes = Seek(fileNum) - 1 'FreeBASIC file position is 1-based
    Print "Number of bytes written: " & Str(numBytes)
    Print "Number of items written: " & Str(numBytes \ SizeOf(buffer(0)))
    Close(fileNum)
Else
    Print "Failed to open " & fileName & " for writing"
End If

'open in binary reading mode
If Open(fileName, For Binary, Access Read, As fileNum) = 0 Then
    'skip the first 25 items
    Seek fileNum, 25 * SizeOf(buffer(0)) + 1 'Note: +1 & seek(...) not allowed
    'try to read the next 100 items
    result = Get(fileNum, , buffer(0), 100, numBytes)
    Print "Number of bytes read: " & Str(numBytes)
    Print "Number of items read: " & Str(numBytes \ SizeOf(buffer(0)))
    Close(fileNum)
Else
    Print "Failed to open " & fileName & " for reading"
End If

result = Kill(fileName) 'delete file
If result = 0 Then Print "Killed: " & fileName

Print !"\n==== End ====\n"

See also

  • File I/O Functions
  • C Standard Library Functions
  • Get (File I/O Command)


Dynamic memory management with FreeBASIC
Managing Dynamic Memory (Allocation / Deallocation) with FreeBASIC

Preamble:

FreeBASIC supports three basic types of memory allocation:
- Static allocation occurs for static and global variables: Memory is allocated once when the program runs and persists throughout the life of the program.
- Stack allocation occurs for procedure parameters and local variables: Memory is allocated when the corresponding block is entered, and released when the block is left, as many times as necessary.
- Dynamic allocation is the subject of this page.

Static allocation and stack allocation have two things in common:
- The size of the variable must be known at compile time.
- Memory allocation and deallocation occur automatically (when the variable is instantiated then destroyed). The user can not anticipate the destruction of such a variable.

Most of the time, that is fine. However, there are situations where one or other of these constraints cause problems (when the memory needed depends on user input, the sizing can only be determined during run-time).

If the size of everything must be declared at compile time, the best is try to estimate the maximum size of the variables needed and hope this will be enough.
This is a bad solution for at least third reasons:
- First, it leads to wasting memory if it is not fully used.
- Second, most normal variables are allocated in a part of the memory called the stack. The amount of stack memory for a program is usually quite low (1 MB or 2 MB by default). If exceeding this number, the stack overflow will occur, and the program will abort.
- Third, and most importantly, this can lead to artificial limitations and / or overflows. What happens if the required memory becomes greater than the reserved memory (stopping the program, emitting message to user, ...).

Fortunately, these problems are easily solved through the dynamic allocation of memory. Dynamic memory allocation is a way of running programs to request memory from the operating system when needed. This memory does not come from the program's limited stack memory, but it is rather allocated from a much larger memory pool managed by the operating system called heap. On modern machines, the heap can have a size of several gigabytes.

Keywords for dynamic memory allocation

There are two sets of keyword for dynamic allocation / deallocation:
- Allocate / CAllocate / Reallocate / Deallocate: for raw memory allocation then deallocation, for simple pre-defined types or buffers (as numeric pre-defined types, user buffer, ...).
- New / Delete: for memory allocation + construction then destruction + deallocation, for object type (as var-len strings, UDTs, ...).

Mixing keywords between these two sets is very strongly discouraged when managing a same memory block.

Dynamic memory allocation management by using Allocate / Callocate / Reallocate / Deallocate

For each keyword, see the detailed syntax, precise description and examples in the individual documentation pages.

Additional functionalities and tips for use:
- Allocate / CAllocate / Reallocate allows to know if memory allocation is successful (otherwise a null pointer is returned).
- Even if the allocated memory size is requested for 0 Byte, a non null pointer is returned and its value should be used to then release the allocation (except for Reallocate(pointer, 0) which behaves similar to Deallocate).
- For memory deallocation, Deallocate can be called on any type of pointer (with the right value anyway).
- If the user absolutely wants to use this type of allocation for an object (for example to be able to do reallocation), it's up to him to manually call if necessary the constructor and the destructor (by using member access operator) at the right way.
- After deallocation, the pointer value becomes invalid (pointing to an invalid memory address), and its reuse (for dereferencing or calling Deallocate again) will result in undefined behavior.

Dynamic memory allocation management by using New / Delete

For each keyword, see the detailed syntax, precise description and examples in the individual documentation pages.

Additional functionalities and tips for use:
- Before, New did not signal if memory allocation was successful (program hangs). Problem solved from fbc rev 1.06, by returning a null pointer if New fails.
- Even if the allocated memory size is requested for 0 Byte (New predefined_datatype[0]), a non null pointer is returned and its value should be used to then release the allocation.
- For object destruction and memory deallocation, Delete must be called on a proper typed pointer (otherwise the object destructor will not be called or miscalled, and that may result in a memory leak or even a crash with Delete[]).
- The user can also use this type of allocation for a simple pre-defined types (except for the fix-len strings), but this does not functionally add anything, except a simpler usage syntax for allocation.
- After destruction + deallocation, the pointer value becomes invalid (pointing to an invalid memory address), and its reuse (for dereferencing or calling Delete again) will result in undefined behavior.
- If used, the special Placement New (using memory already allocated) induces only object construction, so use Delete is forbidden (to avoid double request of deallocation). If necessary, the only destruction of the object must be manually do by user (calling the destructor by using member access operator).

Variant by using Redim / Erase

FreeBASIC also supports dynamic arrays (variable-length arrays).
The memory used by a dynamic array to store its elements is allocated at run-time in the heap. Dynamic arrays can contain simple types as well as complex objects.
By using ReDim, the user does not need to call the constructor / destructor because ReDim does this automatically when adding / removing element. Erase then destroys all the remaining elements to completely free the memory allocated to them.

Use cases by comparing 4 methods

Usage example on a set of objects, by comparing 4 methods:
- 3 then 4 objects: CAllocate, Reallocate, Deallocate, (+ .constructor + .destructor).
- 3 objects: New, Delete.
- 3 objects: Placement New, (+ .destructor).
- 3 then 4 objects: ReDim, Erase.
Type UDT
    Dim As String S = "FreeBASIC"              '' induce an implicit constructor and destructor
End Type

' 3 then 4 objects: Callocate, Reallocate, Deallocate, (+ .constructor + .destructor)
Dim As UDT Ptr p1 = CAllocate(3, SizeOf(UDT))  '' allocate cleared memory for 3 elements (string descriptors cleared,
                                               ''     but maybe useless because of the constructor's call right behind)
For I As Integer = 0 To 2
    p1[I].Constructor()                        '' call the constructor on each element
Next I
For I As Integer = 0 To 2
    p1[I].S &= Str(I)                          '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
    Print "'" & p1[I].S & "'",                 '' print each element string
Next I
Print
p1 = Reallocate(p1, 4 * SizeOf(UDT))           '' reallocate memory for one additional element
Clear p1[3], 0, 3 * SizeOf(Integer)            '' clear the descriptor of the additional element,
                                               ''     but maybe useless because of the constructor's call right behind
p1[3].Constructor()                            '' call the constructor on the additional element
p1[3].S &= Str(3)                              '' add the element number to the string of the additional element
For I As Integer = 0 To 3
    Print "'" & p1[I].S & "'",                 '' print each element string
Next I
Print
For I As Integer = 0 To 3
    p1[I].Destructor()                         '' call the destructor on each element
Next I
Deallocate(p1)                                 '' deallocate the memory
Print

' 3 objects: New, Delete
Dim As UDT Ptr p2 = New UDT[3]                 '' allocate memory and construct 3 elements
For I As Integer = 0 To 2
    p2[I].S &= Str(I)                          '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
    Print "'" & p2[I].S & "'",                 '' print each element string
Next I
Print
Delete [] p2                                   '' destroy the 3 element and deallocate the memory
Print

' 3 objects: Placement New, (+ .destructor)
ReDim As Byte array(0 To 3 * SizeOf(UDT) - 1)  '' allocate buffer for 3 elements
Dim As Any Ptr p = @array(0)
Dim As UDT Ptr p3 = New(p) UDT[3]              '' only construct the 3 elements in the buffer (placement New)
For I As Integer = 0 To 2
    p3[I].S &= Str(I)                          '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
    Print "'" & p3[I].S & "'",                 '' print each element string
Next I
Print
For I As Integer = 0 To 2
    p3[I].Destructor()                         '' call the destructor on each element
Next I
Erase array                                    '' deallocate the buffer
Print

' 3 then 4 objects: Redim, Erase
ReDim As UDT p4(0 To 2)                        '' define a dynamic array of 3 elements
For I As Integer = 0 To 2
    p4(I).S &= Str(I)                          '' add the element number to the string of each element
Next I
For I As Integer = 0 To 2
    Print "'" & p4(I).S & "'",                 '' print each element string
Next I
Print
ReDim Preserve p4(0 To 3)                      '' resize the dynamic array for one additional element
p4(3).S &= Str(3)                              '' add the element number to the string of the additional element
For I As Integer = 0 To 3
    Print "'" & p4(I).S & "'",                 '' print each element string
Next I
Print
Erase p4                                       '' erase the dynamic array
Print

Sleep

Output:
'FreeBASIC0'  'FreeBASIC1'  'FreeBASIC2'
'FreeBASIC0'  'FreeBASIC1'  'FreeBASIC2'  'FreeBASIC3'

'FreeBASIC0'  'FreeBASIC1'  'FreeBASIC2'

'FreeBASIC0'  'FreeBASIC1'  'FreeBASIC2'

'FreeBASIC0'  'FreeBASIC1'  'FreeBASIC2'
'FreeBASIC0'  'FreeBASIC1'  'FreeBASIC2'  'FreeBASIC3'
Specific dynamic image buffer allocation by using Imagecreate / Imagedestroy

ImageCreate allocates and initializes storage for an image.
The resulting image buffer can be used in drawing procedures while in any screen mode, and across mode changes, as long as the color depth of the image buffer matches that of the graphics screen.
ImageDestroy destroys and deallocates storage for an image.

See also

  • New and Delete
  • Variable-length Arrays
  • Dynamic Object and Data Lifetime


Replace Recursion with Iteration
How to replace any recursion with simple iteration, or unlimited iteration with its own stack.

Preamble:

Iteration and recursion are two very useful ways to program, especially to perform a certain number of times a certain script, and thus allow optimization of the code. If iteration is relatively easy to understand, recursion is a concept not necessarily obvious at the beginning.
When speaking of a recursive procedure (subroutine or function), we refer to a syntactic characteristic: the procedure, in its own definition, refers to itself (it calls itself).
But when talking about recursive process, linear or tree, we are interested in the process flow, not in the syntax of the procedure's writing.
Thus, a procedure can have a recursive definition but correspond to an iterative process.

Some treatments are naturally implemented as a recursive algorithm (although this is not always the most optimal solution).
The main problem of the recursive approach is that it consumes potentially a lot of space on the execution stack: from a certain level of "depth" of recursion, the space allocated for the execution stack of the thread is exhausted, and causes an error of type "stack overflow".
Repeatedly calling the same procedure can also make the execution slower, although this may make the code easier.
To increase the speed of execution, simple recursive algorithms can be recreated in little more complicated iterative algorithms using loops that execute much faster.

What is the use of recursion if it increases the execution time and memory space compared to an iterative solution?
There are still cases where it is not possible to do otherwise, where iterative translation does not exist or, where it exists, is much heavier to implement (requiring for example a dynamic storage capacity to substitute for the execution stack).

Table of Contents
1. Recursion and iteration definition
2. Problem of replacing recursion with iteration
3. Replace tail recursion with simple iteration
4. Replace non-tail recursion with more complex iteration

1. Recursion and iteration definition

Recursion and iteration both repeatedly execute the instruction set:
- Recursion occurs when an instruction in a procedure calls the procedure itself repeatedly.
- Iteration occurs when a loop executes repeatedly until the control condition becomes false.

The main difference between recursion and iteration is that recursion is a process always applied to a procedure, while iteration is applied to a set of instructions to execute repeatedly.

Definition of Recursion
FreeBASIC allows a procedure to call itself in its code. This means that the procedure definition has a procedure call to itself.
The set of local variables and parameters used by the procedure are newly created each time the procedure is called and are stored at the top of the execution stack.
But every time a procedure calls itself, it does not create a new copy of that procedure.
The recursive procedure does not significantly reduce the size of the code and does not even improve the memory usage, but it does a little bit compared to iteration.

To end recursion, a condition must be tested to force the return of the procedure without giving a recursive call to itself.
The absence of a test of a condition in the definition of a recursive procedure would leave the procedure in infinite recursion once called.

Note: When the parameters of a recursive procedure are passed by reference, take care to work with local variables when the code body needs to modify their values.

Simple example with a recursive function which returns the factorial of the integer:
The code body of the recursive function is defined by using the recursive definition of the factorial function:
Case (n = 0) : factorial(0) = 1
Case (n > 0) : factorial(n) = n * factorial(n-1)

The first line allows to determine the end condition: If (n = 0) Then Return 1
The second line allows to determine the statement syntax which calls the function itself: Return n * factorial(n - 1)

Full code:
Function recursiveFactorial (ByVal n As Integer) As Integer
    If (n = 0) Then                           '' end condition
        Return 1
    Else                                      '' recursion loop
        Return n * recursiveFactorial(n - 1)  '' recursive call
    End If
End Function

Definition of Iteration
Iteration is a process of repeatedly executing a set of instructions until the iteration condition becomes false.
The iteration block includes the initialization, the comparison, the execution of the instructions to be iterated and finally the update of the control variable.
Once the control variable is updated, it is compared again and the process is repeated until the condition in the iteration is false.
Iteration blocks are "for" loop, "while" loop, ...

The iteration block does not use the execution stack to store the variables at each cycle. Therefore, the execution of the iteration block is faster than the recursion block.
In addition, iteration does not have the overhead of repeated procedure calls that also make its execution faster than a recursion.
The iteration is complete when the control condition becomes false.

Simple example with a iterative function which returns the factorial of the integer:
The code body of the iterative function is defined by using the iterative definition of the factorial function:
Case (n = 0) : factorial(0) = 1
Case (n > 0) : factorial(n) = (1) * ..... * (n - 2) * (n - 1) * (n)

The first line allows to determine the cumulative variable initialization: result = 1
The second line allows to determine the statement syntax which accumulates: result = result * I

Full code:
Function iterativeFactorial (ByVal n As Integer) As Integer
    Dim As Integer result = 1  '' variable initialization
    For I As Integer = 1 To n  '' iteration loop
        result = result * I    '' iterative accumulation
    Next I
    Return result
End Function

Back to top

2. Problem of replacing recursion with iteration

Whatever the problem to be solved, there is the choice between the writing of an iterative procedure and that of a recursive procedure.
If the problem has a natural recursive structure, then the recursive program is a simple adaptation of the chosen structure. This is the case of the factorial functions (seen above) for example.
The recursive approach, however, has drawbacks: some languages ​​do not allow recursion (like the machine language!), and a recursive procedure is often expensive in memory (for execution stack) as in execution time.

These disadvantages can be overcome by transforming the recursive procedure, line by line, into an iterative procedure: it is always possible.
Replace a recursion with an iteration allows to suppress the limitation on the number of cycles due to the execution stack size available.
But for an iteration with its own storage stack, the time spent to calls to the procedures for pushing and pulling stack data is generally greater than the one for passing the parameters of a recursive procedure at each calling cycle.

The complexity of the iterative procedure obtained by such a transformation depends on the structure of the recursive procedure:
- For some form of recursive procedure (see below the tail recursion), the transformation into an iterative procedure is very simple by means of just defining local variables corresponding to the parameters of the recursive procedure (passed arguments).
- At opposite for other forms of recursive procedure (non-tail recursions), the use of a user storage stack in the iterative procedure is necessary to save the context, as the recursive calls do (values ​​of the passed arguments at each call):
- when executing a recursive procedure, each recursive call leads to push the context on execution stack,
- when the condition of stopping recursion occurs, the different contexts are progressively popped from execution stack to continue executing the procedure.

Back to top

3. Replace tail recursion with simple iteration

The recursive procedure is a tail recursive procedure if the only recursive call is at the end of the recursion and is therefore not followed by any other statement:
- For a recursive subroutine, the only recursive call is at the end of the recursion.
- For a recursive function, the only recursive call is at the end of the recursion and consists in taking into account the return of the function without any other additional operation on it.

A tail recursive procedure is easy to transform into an iterative procedure.
The principle is that if the recursive call is the last instruction of a procedure, it is not necessary to keep on the execution stack the context of the current call, since it is not necessary to return to it:
- It suffices to replace the parameters by their new values, and resume execution at the beginning of the procedure.
- The recursion is thus transformed into iteration, so that there is no longer any risk of causing an overflow of the execution stack.

Some non-tail recursive procedures can be transformed into tail recursive procedures, sometimes with a little more complex code, but even before they are subsequently transformed into iterative procedures, these tail recursive procedures often already gain in memory usage and execution time.

Example with the simple "factorial" recursive function:
Non-tail recursive form (already presented above):
Function recursiveFactorial (ByVal n As Integer) As Integer
    If (n = 0) Then                           '' end condition
        Return 1
    Else                                      '' recursion loop
        Return n * recursiveFactorial(n - 1)  '' recursive call
    End If
End Function

This function has a non-tail recursive form because even though the recursive call is at the end of the function, this recursive call is not the last instruction of the function because one has to multiplied again by n when recursiveFactorial(n - 1) is got.
This calculation is done when popping context from execution stack.

It is quite easy to transform this function so that the recursion is a tail recursion.
To achieve this, it is necessary to add a new parameter to the function: the result parameter which will serve as accumulator:
Function tailRecursiveFactorial (ByVal n As Integer, ByVal result As Integer = 1) As Integer
    If (n = 0) Then                                       '' end condition
        Return result
    Else                                                  '' recursion loop
        Return tailRecursiveFactorial(n - 1, result * n)  '' tail recursive call
    End If
End Function

This time, the calculation is done when pushing context on execution stack.

Tail recursion is more explicit by calculating n - 1 and result * n just before the recursive call:
Function explicitTailRecursiveFactorial (ByVal n As Integer, ByVal result As Integer = 1) As Integer
    If (n = 0) Then                                       '' end condition
        Return result
    Else                                                  '' recursion loop
        result = result * n
        n = n - 1
        Return explicitTailRecursiveFactorial(n, result)  '' tail recursive call
    End If
End Function

Now it is sufficient to resume execution at the beginning of the procedure by a Goto begin instead of the function call, to obtain an iterative function:
Function translationToIterativeFactorial (ByVal n As Integer, ByVal result As Integer = 1) As Integer
    begin:
    If (n = 0) Then          '' end condition
        Return result
    Else                     '' iteration loop
        result = result * n  '' iterative accumulation
        n = n - 1
        Goto begin           '' iterative jump
    End If
End Function

Finally it is better to avoid the If ... Goto ... End If instructions by using for example a While ... Wend block instead, and the added result parameter can be transformed into a local variable:
Function  betterTranslationToIterativeFactorial (ByVal n As Integer) As Integer
    Dim As Integer result = 1
    While Not (n = 0)        '' end condition of iterative loop
        result = result * n  '' iterative accumulation
        n = n - 1
    Wend
    Return result
End Function

Similar transformation steps for the simple "reverse string" recursive function following:
Function recursiveReverse (ByVal s As String) As String
    If (s = "") Then                                     '' end condition
        Return s
    Else                                                 '' recursion loop
        Return recursiveReverse(Mid(s, 2)) & Left(s, 1)  '' recursive call
    End If
End Function

Function tailRecursiveReverse (ByVal s As String, ByVal cumul As String = "") As String
    If (s = "") Then                                                '' end condition
        Return cumul
    Else                                                            '' recursion loop
        Return tailRecursiveReverse(Mid(s, 2), Left(s, 1) & cumul)  '' tail recursive call
    End If
End Function

Note: As the & operator (string concatenation) is not a symmetric operator ((a & b) <> (b & a), while (x * y) = (y * x) like previously), the two operand order must to be reversed when pushing context on execution stack instead of before when popping context from execution stack.
Function explicitTailRecursiveReverse (ByVal s As String, ByVal cumul As String = "") As String
    If (s = "") Then                                   '' end condition
        Return cumul
    Else                                               '' recursion loop
        cumul = Left(s, 1) & cumul
        s = Mid(s, 2)
        Return explicitTailRecursiveReverse(s, cumul)  '' tail recursive call
    End If
End Function

Function translationToIterativeReverse (ByVal s As String, ByVal cumul As String = "") As String
    begin:
    If (s = "") Then                '' end condition
        Return cumul
    Else                            '' iteration loop
        cumul = Left(s, 1) & cumul  '' iterative accumulation
        s = Mid(s, 2)
        Goto begin                  '' iterative jump
    End If
End Function

Function betterTranslationToIterativeReverse (ByVal s As String) As String
    Dim As String cumul = ""
    While Not (s = "")              '' end condition of iterative loop
        cumul = Left(s, 1) & cumul  '' iterative accumulation
        s = Mid(s, 2)
    Wend
    Return cumul
End Function

As less simple example, the "Fibonacci series" non-tail recursive function:
Sometimes, the transformation to a tail recursive function is less obvious.
The code body of the recursive function is defined by using the recursive definition of the Fibonacci series:
Case (n = 0) : F(0) = 0
Case (n = 1) : F(1) = 1
Case (n > 1) : F(n) = F(n-1) + F(n-2)

The first two lines allow to determine the end condition: If n = 0 Or n = 1 Then Return n
The third line allows to determine the statement syntax which calls the function itself: Return F(n - 1) + F(n - 2)

Non-tail recursive form code:
Function recursiveFibonacci (ByVal n As UInteger) As LongInt
    If n = 0 Or n = 1 Then                                            '' end condition
        Return n
    Else                                                              '' recursion loop
        Return recursiveFibonacci(n - 1) + recursiveFibonacci(n - 2)  '' recursive call
    End If
End Function

The execution time duration for the highest values becomes no more negligible.
Indeed, to compute F(n), there are 2^(n-1) calls: about one milliard for n=31.

Try to make the recursive algorithm linear, using a recursive function which have 2 other parameters corresponding to the previous value and the last value of the series, let f(n, a, b).
We obtain:
Case (n = 1): a = F(0) = 0, b = F(1) = 1
Case (n-1): a = F(n-2), b = F(n-1)
Case (n): F(n-1) = b, F(n) = F(n-1) + F(n-2) = a + b

Consequently, for this new function f(n, a, b), the recursive call becomes f(n-1, b, a+b), and there are only (n-1) calls.

Tail recursive form code:
Function tailRecursiveFibonacci (ByVal n As UInteger, ByVal a As UInteger = 0, ByVal b As UInteger = 1) As LongInt
    If n <= 1 Then                                      '' end condition
        Return b * n
    Else                                                '' recursion loop
        Return tailRecursiveFibonacci(n - 1, b, a + b)  '' tail recursive call
    End If
End Function

Then, similar transformations as previously in order to obtain the iterative form:
Function explicitTailRecursiveFibonacci (ByVal n As UInteger, ByVal a As UInteger = 0, ByVal b As UInteger = 1) As LongInt
    If n <= 1 Then                                      '' end condition
        Return b * n
    Else                                                '' recursion loop
        n = n - 1
        Swap a, b
        b = b + a
        Return explicitTailRecursiveFibonacci(n, a, b)  '' tail recursive call
    End If
End Function

Function translationToIterativeFibonacci (ByVal n As UInteger, ByVal a As UInteger = 0, ByVal b As UInteger = 1) As LongInt
    begin:
    If n <= 1 Then    '' end condition
        Return b * n
    Else              '' iteration loopp
        n = n - 1
        Swap a, b
        b = b + a
        Goto begin    '' iterative jump
    End If
End Function

Function betterTranslationToIterativeFibonacci (ByVal n As UInteger) As LongInt
    Dim As UInteger a = 0, b = 1
    While Not (n <= 1)  '' end condition of iterative loop
        n = n - 1
        Swap a, b
        b = b + a
    Wend
    Return b * n
End Function

Back to top

4. Replace non-tail recursion with more complex iteration

The recursive procedure is a non-tail recursive procedure if there is at least one recursive call followed by at least one instruction.
A non-tail recursion cannot be normally transformed into a simple iteration, or it could have been transformed already into tail recursion.

To avoid limitation due to the execution stack size, a non-tail recursive algorithm can always (more or less easily) be replaced by an iterative algorithm, by pushing the parameters that would normally be passed to the recursive procedure onto an own storage stack.
In fact, the execution stack is replaced by a user stack (less limited in size).

In the following examples, the below user stack macro (compatible with any datatype) is used:
'' save as file: "DynamicUserStackTypeCreateMacro.bi"

#macro DynamicUserStackTypeCreate(typename, datatype)

    Type typename
        Public:
            Declare Constructor ()                       '' pre-allocating user stack memory
            Declare Property push (ByRef i As datatype)  '' pushing on the user stack
            Declare Property pop () ByRef As datatype    '' popping from the user stack
            Declare Property used () As Integer          '' outputting number of used elements in the user stack
            Declare Property allocated () As Integer     '' outputting number of allocated elements in the user stack
            Declare Destructor ()                        '' deallocating user stack memory
        Private:
            Dim As datatype ae (Any)  '' array of elements
            Dim As Integer nue        '' number of used elements
            Dim As Integer nae        '' number of allocated elements
            Dim As Integer nae0       '' minimum number of allocated elements
    End Type

    Constructor typename ()
        This.nae0 = 2^Int(Log(1024 * 1024 / SizeOf(datatype)) / Log(2) + 1) '' only a power of 2 (1 MB < stack memory < 2 MB here)
        This.nue = 0
        This.nae = This.nae0
        ReDim This.ae(This.nae - 1)                                         '' pre-allocating user stack memory
    End Constructor

    Property typename.push (ByRef i As datatype)  '' pushing on the user stack
        This.nue += 1
        If This.nue > This.nae0 And This.nae < This.nue * 2 Then
            This.nae *= 2
            ReDim Preserve This.ae(This.nae - 1)  '' allocating user stack memory for double used elements at least
        End If
        This.ae(This.nue - 1) = i
    End Property

    Property typename.pop () ByRef As datatype  '' popping from the user stack
        If This.nue > 0 Then
            Property = This.ae(This.nue - 1)
            This.nue -= 1
            If This.nue > This.nae0 And This.nae > This.nue * 2 Then
                This.nae \= 2
                ReDim Preserve This.ae(This.nae - 1)  '' allocating user stack memory for double used elements at more
            End If
        Else
            Static As datatype d
            Dim As datatype d0
            d = d0
            Property = d
            AssertWarn(This.nue > 0)  '' warning if popping while empty user stack and debug mode (-g compiler option)
        End If
    End Property

    Property typename.used () As Integer  '' outputting number of used elements in the user stack
        Property = This.nue
    End Property

    Property typename.allocated () As Integer  '' outputting number of allocated elements in the user stack
        Property = This.nae
    End Property

    Destructor typename  '' deallocating user stack memory
        This.nue = 0
        This.nae = 0
        Erase This.ae  '' deallocating user stack memory
    End Destructor

#endmacro

Translation Quite Simple from Final Recursive Procedure (non-tail) to Iterative Procedure
A non-tail recursive procedure is final when the recursive call(s) is(are) placed at the end of executed code (no executable instruction line after and between for several recursive calls).

In the 3 following examples, the transformation of a recursive procedure into an iterative procedure is quite simple because the recursive calls are always at the end of executed code block, and without order constraints:
- Make the procedure parameters (and the return value for a function) as local ones.
- Push the initial parameter values in the user stack.
- Enter in a While ... Wend loop to empty the user stack:
- Pull the variables from the user stack.
- Process the variables similarly to the recursive procedure body.
- Accumulate the "return" variable for a recursive function (the final value will be returned at function body end).
- Replace the recursive calls by pushing the corresponding variables on the user stack.

First example (for console window): Computation of the combination coefficients nCp (binomial coefficients calculation) and display of the Pascal's triangle:
The first function recursiveCombination is the recursive form (not a tail recursion because there are two recursive calls with summation in the last active statement).
The second function translationToIterativeCombinationStack is the iterative form using an own stack.

In the two functions, a similar structure is conserved to enlighten the conversion method.
From recursive function to iterative stacking function:
- Ahead, declaration of 1 local variable for the accumulator.
- Pushing the two initial parameters values in the user stack.
- Entering in the While ... Wend loop to empty the user stack.
- Pulling parameters from the user stack.
- Return 1 is replaced by cumul = cumul + 1.
- Return recursiveCombination(n - 1, p) + recursiveCombination(n - 1, p - 1) is replaced by S.push = n - 1 : S.push = p and S.push = n - 1 : S.push = p - 1.

Function recursiveCombination (ByVal n As UInteger, ByVal p As UInteger) As LongInt
    If p = 0 Or p = n Then
        Return 1
    Else
        Return recursiveCombination(n - 1, p) + recursiveCombination(n - 1, p - 1)
    End If
End Function

'---------------------------------------------------------------------------

#include "DynamicUserStackTypeCreateMacro.bi"
DynamicUserStackTypeCreate(DynamicUserStackTypeForUinteger, UInteger)

Function translationToIterativeCombinationStack (ByVal n As UInteger, ByVal p As UInteger) As LongInt
    Dim cumul As LongInt = 0
    Dim As DynamicUserStackTypeForUinteger S
    S.push = n : S.push = p
    While S.used > 0
        p = S.pop : n = S.pop
        If p = 0 Or p = n Then
            cumul = cumul + 1
        Else
            S.push = n - 1 : S.push = p
            S.push = n - 1 : S.push = p - 1
        End If
    Wend
    Return cumul
End Function

'---------------------------------------------------------------------------

Sub Display(ByVal Combination As Function (ByVal n As UInteger, ByVal p As UInteger) As LongInt, ByVal n As Integer)
    For I As UInteger = 0 To n
        For J As UInteger = 0 To I
            Locate , 6 * J + 3 * (n - I) + 3
            Print Combination(I, J);
        Next J
        Print
    Next I
End Sub

'---------------------------------------------------------------------------

Print " recursion:";
Display(@recursiveCombination, 12)

Print
Print
Print " iteration with own storage stack:";
Display(@translationToIterativeCombinationStack, 12)

Sleep

Second example (for graphics window), using a non-tail recursive subroutine (recursive drawing of circles):
Similar transformation steps:
Sub recursiveCircle (ByVal x As Integer, ByVal y As Integer, ByVal r As Integer)
    Circle (x, y), r
    If r > 16 Then
        recursiveCircle(x + r / 2, y, r / 2)
        recursiveCircle(x - r / 2, y, r / 2)
        recursiveCircle(x, y + r / 2, r / 2)
        recursiveCircle(x, y - r / 2, r / 2)
    End If
End Sub

'---------------------------------------------------------------------------

#include "DynamicUserStackTypeCreateMacro.bi"
DynamicUserStackTypeCreate(DynamicUserStackTypeForInteger, Integer)

Sub recursiveToIterativeCircleStack (ByVal x As Integer, ByVal y As Integer, ByVal r As Integer)
    Dim As DynamicUserStackTypeForInteger S
    S.push = x : S.push = y : S.push = r
    Do While S.used > 0
        r = S.pop : y = S.pop : x = S.pop
        Circle (x, y), r
        If r > 16 Then
            S.push = x + r / 2 : S.push = y : S.push = r / 2
            S.push = x - r / 2 : S.push = y : S.push = r / 2
            S.push = x : S.push = y + r / 2 : S.push = r / 2
            S.push = x : S.push = y - r / 2 : S.push = r / 2
        End If
    Loop
End Sub

'---------------------------------------------------------------------------

Screen 12

Locate 2, 2
Print "recursion:"
recursiveCircle(160, 160, 150)

Locate 10, 47
Print "iteration with own storage stack:"
recursiveToIterativeCircleStack(480, 320, 150)

Sleep

Third example (for console window), using a non-tail recursive subroutine (Quick Sort algorithm):
Similar transformation steps:
Dim Shared As UByte t(99)

Sub recursiveQuicksort (ByVal L As Integer, ByVal R As Integer)
    Dim As Integer pivot = L, I = L, J = R
    Do
        If t(I) >= t(J) Then
            Swap t(I), t(J)
            pivot = L + R - pivot
        End If
        If pivot = L Then
            J = J - 1
        Else
            I = I + 1
        End If
    Loop Until I = J
    If L < I - 1 Then
        recursiveQuicksort(L, I - 1)
    End If
    If R > J + 1 Then
        recursiveQuicksort(J + 1, R)
    End If
End Sub

#include "DynamicUserStackTypeCreateMacro.bi"
DynamicUserStackTypeCreate(DynamicUserStackTypeForInteger, Integer)

Sub translationToIteraticeQuicksortStack (ByVal L As Integer, ByVal R As Integer)
    Dim As DynamicUserStackTypeForInteger S
    S.push = L : S.push = R
    While S.used > 0
        R = S.pop : L = S.pop
        Dim As Integer pivot = L, I = L, J = R
        Do
            If t(I) >= t(J) Then
                Swap t(I), t(J)
                pivot = L + R - pivot
            End If
            If pivot = L Then
                J = J - 1
            Else
                I = I + 1
            End If
        Loop Until I = J
        If L < I - 1 Then
            S.push = L : S.push = I - 1
        End If
        If R > J + 1 Then
            S.push = J + 1 : S.push = R
        End If
    Wend
End Sub

Randomize
For I As Integer = LBound(t) To UBound(t)
    t(i) = Int(Rnd * 256)
Next I
Print "raw memory:"
For K As Integer = LBound(t) To UBound(t)
    Print Using "####"; t(K);
Next K
Print

recursiveQuicksort(LBound(t), UBound(t))

Print "sorted memory by recursion:"
For K As Integer = LBound(t) To UBound(t)
    Print Using "####"; t(K);
Next K
Print
Print

Randomize
For I As Integer = LBound(t) To UBound(t)
    t(i) = Int(Rnd * 256)
Next I
Print "raw memory:"
For K As Integer = LBound(t) To UBound(t)
    Print Using "####"; t(K);
Next K
Print

translationToIteraticeQuicksortStack(LBound(t), UBound(t))

Print "sorted memory by iteration with stack:"
For K As Integer = LBound(t) To UBound(t)
    Print Using "####"; t(K);
Next K
Print

Sleep

Translation Little More Complex from Non-Final Recursive Procedure to Iterative Procedure
For theses examples, the transformation of the non-final recursive procedure into an iterative procedure is a little more complex because the recursive call(s) is(are) not placed at the end of executed code.

The general method used hereafter is to first transform original recursive procedure into a "final" recursive procedure where the recursive call(s) is(are) now placed at the end of executed code block (no executable instruction line between or after).

First example (for console window), using a non-tail recursive subroutine (tower of Hanoi algorithm):
For this example, the two recursive calls are at the end of executed code block but separated by an instruction line and there is an order constraint.
In the two functions, a similar structure is conserved to enlighten the conversion method.
From recursive function to iterative stacking function:
- The first step consists in removing the instruction line between the two recursive calls by adding its equivalent at top of the recursive code body (2 parameters are added to the procedure to pass the corresponding useful data).
- Then the process of translation to iterative form is similar to the previous examples (using a own storage stack) but reversing the order of the 2 recursive calls when pushing on the storage stack.

Sub recursiveHanoi (ByVal n As Integer, ByVal departure As String, ByVal middle As String, ByVal arrival As String)
    If n > 0 Then
        recursiveHanoi(n - 1, departure, arrival, middle)
        Print "  move one disk from " & departure & " to " & arrival
        recursiveHanoi(n -1 , middle, departure, arrival)
    End If
End Sub

Sub finalRecursiveHanoi (ByVal n As Integer, ByVal departure As String, ByVal middle As String, ByVal arrival As String, ByVal dep As String = "", ByVal arr As String = "")
    If dep <> "" Then Print "  move one disk from " & dep & " to " & arr
    If n > 0 Then
        finalRecursiveHanoi(n - 1, departure, arrival, middle, "")
        finalRecursiveHanoi(n - 1, middle, departure, arrival, departure, arrival)
    End If
End Sub

#include "DynamicUserStackTypeCreateMacro.bi"
DynamicUserStackTypeCreate(DynamicUserStackTypeForString, String)

Sub translationToIterativeHanoi (ByVal n As Integer, ByVal departure As String, ByVal middle As String, ByVal arrival As String)
    Dim As String dep = "", arr = ""
    Dim As DynamicUserStackTypeForString S
    S.push = Str(n) : S.push = departure : S.push = middle : S.push = arrival : S.push = dep : S.push = arr
    While S.used > 0
        arr = S.pop : dep = S.pop : arrival = S.pop : middle = S.pop : departure = S.pop : n = Val(S.pop)
        If dep <> "" Then Print "  move one disk from " & dep & " to " & arr
        If n > 0 Then
            S.push = Str(n - 1) : S.push = middle : S.push = departure : S.push = arrival : S.push = departure : S.push = arrival
            S.push = Str(n - 1) : S.push = departure : S.push = arrival : S.push = middle : S.push = "" : S.push = ""
        End If
    Wend
End Sub

Print "recursive tower of Hanoi:"
recursiveHanoi(3, "A", "B", "C")
Print

Print "final recursive tower of Hanoi:"
finalRecursiveHanoi(3, "A", "B", "C")
Print

Print "iterative tower of Hanoi:"
translationToIterativeHanoi(3, "A", "B", "C")
Print

Sleep

Second example (for console window), using a non-tail recursive subroutine (counting-down from n, then re-counting up to n):
For this example, the recursive call is followed by an instruction line before the end of executed code block.
In the two functions, a similar structure is conserved to enlighten the conversion method.
From recursive function to iterative stacking function:
- The first step consists in replacing the instruction line at the end of executed code block by a new recursive call (a parameter is added to the procedure to pass the corresponding useful data).
- An equivalent instruction line is added at top of the recursive code body (using the passed data), executed in this case instead of the normal code.
- Then the process of translation to iterative form is similar to the previous example (using a own storage stack) and reversing the order of the 2 recursive calls when pushing on the storage stack.

Sub recursiveCount (ByVal n As Integer)
    If n >= 0 Then
        Print n & " ";
        If n = 0 Then Print
        recursiveCount(n - 1)
        Print n & " ";
    End If
End Sub

Sub finalRecursiveCount (ByVal n As Integer, ByVal recount As String = "")
    If recount <> "" Then
        Print recount & " ";
    Else
        If n >= 0 Then
            Print n & " ";
            If n = 0 Then Print
            finalRecursiveCount(n - 1, "")
            finalRecursiveCount(n - 1, Str(n))
        End If
    End If
End Sub

#include "DynamicUserStackTypeCreateMacro.bi"
DynamicUserStackTypeCreate(DynamicUserStackTypeForString, String)

Sub translationToIterativeCount (ByVal n As Integer)
    Dim As String recount = ""
    Dim As DynamicUserStackTypeForString S
    S.push = Str(n) : S.push = recount
    While S.used > 0
        recount = S.pop : n = Val(S.pop)
        If recount <> "" Then
            Print recount & " ";
        Else
            If n >= 0 Then
                Print n & " ";
                If n = 0 Then Print
                S.push = Str(n - 1) : S.push = Str(n)
                S.push = Str(n - 1) : S.push = ""
            End If
        End If
    Wend
End Sub

Print "recursive counting-down then re-counting up:"
recursiveCount(9)
Print
Print

Print "final recursive counting-down then re-counting up:"
finalRecursiveCount(9)
Print
Print

Print "iterative counting-down then re-counting up:"
translationToIterativeCount(9)
Print
Print

Sleep

Translation from Other Non-Obvious Recursive Procedure to Iterative Procedure
Two other cases of translation from recursion to iteration are presented here by means of simple examples:
- For mutual recursion.
- For nested recursion.

Two functions are said to be mutually recursive if the first calls the second, and in turn the second calls the first.
A recursive function is said nested if an argument passed to the function refers to the function itself.

Example using mutual recursive functions ('even()' and 'odd()' functions):
From mutual recursive procedures to iterative stacking procedures (for the general case):
- The first step consists in transforming the recursive procedures into "final" recursive procedures.
- Then, the method is similar than that already described, with besides an additional parameter (an index) which is also pushed on the user stack in order to select the right code body to execute when pulling data from the stack.
- Therefore, each iterative procedure contains the translation (for stacking) of all code bodies from the recursive procedures.

In this following examples, the simple mutual recursive functions are here processed as in the general case (other very simple iterative solutions exist):
Declare Function recursiveIsEven(ByVal n As Integer) As Boolean
Declare Function recursiveIsOdd(ByVal n As Integer) As Boolean

Function recursiveIsEven(ByVal n As Integer) As Boolean
    If n = 0 Then
        Return True
    Else
        Return recursiveIsOdd(n - 1)
    End If
End Function

Function recursiveIsOdd(ByVal n As Integer) As Boolean
    If n = 0 Then
        Return False
    Else
        Return recursiveIsEven(n - 1)
    End If
End Function

#include "DynamicUserStackTypeCreateMacro.bi"
DynamicUserStackTypeCreate(DynamicUserStackTypeForInteger, Integer)

Function iterativeIsEven(ByVal n As Integer) As Boolean
    Dim As Integer i = 1
    Dim As DynamicUserStackTypeForInteger S
    S.push = n : S.push = i
    While S.used > 0
        i = S.pop : n = S.pop
        If i = 1 Then
            If n = 0 Then
                Return True
            Else
                S.push = n - 1 : S.push = 2
            End If
        ElseIf i = 2 Then
            If n = 0 Then
                Return False
            Else
                S.push = n - 1 : S.push = 1
            End If
        End If
    Wend
End Function

Function iterativeIsOdd(ByVal n As Integer) As Boolean
    Dim As Integer i = 2
    Dim As DynamicUserStackTypeForInteger S
    S.push = n : S.push = i
    While S.used > 0
        i = S.pop : n = S.pop
        If i = 1 Then
            If n = 0 Then
                Return True
            Else
                S.push = n - 1 : S.push = 2
            End If
        ElseIf i = 2 Then
            If n = 0 Then
                Return False
            Else
                S.push = n - 1 : S.push = 1
            End If
        End If
    Wend
End Function

Print recursiveIsEven(16), recursiveIsOdd(16)
Print recursiveIsEven(17), recursiveIsOdd(17)
Print

Print iterativeIsEven(16), iterativeIsOdd(16)
Print iterativeIsEven(17), iterativeIsOdd(17)
Print

Sleep

Example using nested recursive function ('Ackermann()' function):
From nested recursive function to iterative stacking function:
- Use 2 independent storage stacks, one for the first parameter m and another for the second parameter n of the function, because of the nested call on one parameter.
- Return expression is transformed into a pushing the expression on the stack dedicated to the parameter where the nesting call is.
- Therefore a Return of data popping from the same stack is added at code end.

Function recursiveAckermann (ByVal m As Integer, ByVal n As Integer) As Integer
    If m = 0 Then
        Return n + 1
    Else
        If n = 0 Then
            Return recursiveAckermann(m - 1, 1)
        Else
            Return recursiveAckermann(m - 1, recursiveAckermann(m, n - 1))
        End If
    End If
End Function

#include "DynamicUserStackTypeCreateMacro.bi"
DynamicUserStackTypeCreate(DynamicUserStackTypeForInteger, Integer)

Function iterativeAckermann (ByVal m As Integer, ByVal n As Integer) As Integer
    Dim As DynamicUserStackTypeForInteger Sm, Sn
    Sm.push = m : Sn.push = n
    While Sm.used > 0
        m = Sm.pop : n = Sn.pop
        If m = 0 Then
            Sn.push = n + 1                                      ' Return n + 1 (and because of nested call)
        Else
            If n = 0 Then
                Sm.push = m - 1 : Sn.push = 1                    ' Return Ackermann(m - 1, 1)
            Else
                Sm.push = m - 1 : Sm.push = m : Sn.push = n - 1  ' Return Ackermann(m - 1, Ackermann(m, n - 1))
            End If
        End If
    Wend
    Return Sn.pop                                                ' (because of Sn.push = n + 1)
End Function

Print recursiveAckermann(3, 0), recursiveAckermann(3, 1), recursiveAckermann(3, 2), recursiveAckermann(3, 3), recursiveAckermann(3, 4)
Print iterativeAckermann(3, 0), iterativeAckermann(3, 1), iterativeAckermann(3, 2), iterativeAckermann(3, 3), iterativeAckermann(3, 4)

Sleep

Back to top

See also

  • Recursion


OBJECT built-in and RTTI info
How the OBJECT built-in implements the capacity of inheritance polymorphism and the Run-Time Type Information for identification.

Preamble:

The Object built-in type provides to all types derived (using the Extends declaration):
- The ability to redefine a method (using the Abstract / Virtual keywords) in a derived-type (sub-type) inheriting from a base-type (super-type). It is then possible to call the method of an object without worrying about its intrinsic type: it is the inheritance polymorphism (sub-type polymorphism).
- The capacity of determining the real type of an object at run-time, which can be different of its at compile-time. The operator Is (Run-Time Type Information) uses it to check if an object is compatible to a type derived from its compile-time type, because RTTI provides not only the run-time typename of the object but also all names of its different base-types, up to the Object built-in type.

Table of Contents
1. Mechanism under the hood for inheritance polymorphism and RTTI info
2. Inheritance polymorphism mechanism demonstrated by both true operating and faithful emulation
3. Demangle typenames from RTTI info

1. Mechanism under the hood for inheritance polymorphism and RTTI info

The abstract/virtual member procedures are implemented using virtual procedure tables (vtbl). vtbl is, simply explained, a table of static procedures pointers.
The compiler fills a vtbl for each polymorphic type, i.e. a type defining at least an abstract/virtual procedure or a type derived from the former.
vtbl contains entries for all abstract/virtual procedures available in the type, including the abstract/virtual procedures defined in upper level of inheritance hierarchy (for abstract procedure not still implemented, a null pointer is set in the vtbl).

Each vtbl contains the correct addresses of procedures for each abstract/virtual procedure in corresponding type. Here correct means the address of the corresponding procedure of the most derived-type that defines/overrides that procedure:
- When the type is instantiated, the instance will contain a pointer (vptr) to the virtual procedure table (vtbl) of the instantiated type.
- When an object of a derived-type is referenced within a pointer/reference of base-type, then abstract/virtual procedure feature really performs. The call of an abstract/virtual procedure is somehow translated at run-time and the corresponding procedure from the virtual procedure table of the type of underlying object (not of the pointer/reference type) is chosen.
- Thus, what procedure is called depends on what the real type of object the pointer/reference points to, which can't be known at compile-time, that is why the abstract/virtual procedure call is decided at run-time.

Therefore, the abstract/virtual procedure call (by means of a pointer or a reference) is not an ordinary call and has a little performance overhead, which may turn into a huge if we have numerous calls.
The abstract/virtual procedure call is converted by compiler to something else by using the proper vtbl addressed by the vptr value (located at offset 0 in the instance data):
Let be 'method1()', 'method2()', 'method3()' the first three abstract or virtual member procedures declared in an inheritance type structure, and 'pt' a based pointer to a derived object:
pt->method1()
pt->method2()
pt->method3()
are about translated by the compiler into respectively:
Cptr(Sub (Byref As typename), Cptr(Any Ptr Ptr Ptr, pt)[0][0])(*pt)
Cptr(Sub (Byref As typename), Cptr(Any Ptr Ptr Ptr, pt)[0][1])(*pt)
Cptr(Sub (Byref As typename), Cptr(Any Ptr Ptr Ptr, pt)[0][2])(*pt)
- The first indirection [0] allows to access to the value of the vptr from the address of the instance. This value correspond to the address of the vtbl.
- The second indirection [0] or [1] or [2] allows to access in the vtbl to the static address of the virtual procedures 'method1()' or 'method2()' or 'method3()' respectively (in the declaration order of the abstract or virtual procedures of the Type structure).

For the vptr value setting:
- The compiler generates some extra code in the constructor of each type (from the base-type up to the instantiated type), which it adds before the user code. Even if the user does not define a constructor, the compiler generates a default one, and the initialization of vptr is there (from the vtbl address of the base-type up to the one of the instantiated type). So each time an object of a polymorphic type is created, vptr is correctly initialized and finally points to the vtbl of that instantiated type.
- At the end, when the object is destructed, the destructors are called in the reverse order (from the instantiated type up to the base-type). The compiler also generates some extra code in the destructor of each type, which it adds before the user code. Even if the user does not define a destructor, the compiler generates a default one, and the de-initialization of vptr is there (from the vtbl address of the instantiated type up to the one of the base-type).
- This initialization/de-initialization of the vptr value by step is mandatory so that the user code in each constructor/destructor can call the polymorphic procedures at the correct type level during the successive steps of construction/destruction.

The built-in Object type also provides the RTTI (Run-Time Type Information) capacity for all types derived from it using the Extends declaration:
- The RTTI capacity allows to determine the real type of an object at run-time, which can be different of its at compile-time.
- The operator Is (rtti) uses it to check if an object is compatible to a type derived from its compile-time type, because RTTI provides not only the real runtime type-name of the object but also all type-names of its base types, up to the Object built-in type.
- Nevertheless these type-names stored by RTTI (referenced by a specific pointer in the vtbl) are mangled names inaccessible directly from a FreeBASIC keyword.

How are chained the entities: object instance, vptr, vtbl (vtable), and RTTI info:
- Instance -> Vptr -> Vtbl -> RTTI info chaining:
- For any type derived (directly or indirectly) from the Object built-in type, a hidden pointer vptr is added at beginning (located at offset 0) of its data fields (own or inherited). This vptr points to the virtual table vtbl of the considered type.
- The vtbl contains the list of the addresses of all abstract/virtual procedures (from the offset 0). The vtbl also contains (located at offset -1) a pointer to the Run Time Type Information (RTTI) info block of the considered type.
- The RTTI info block contains (located at offset +1) a pointer to the mangled-typename of the considered type (ascii characters). The RTTI info block also contains (located at offset +2) a pointer to the RTTI info block of its Base. All RTTI info blocks for up-hierarchy are so chained.

- Instance -> Vptr -> Vtbl -> RTTI info diagram:
'                                      vtbl (vtable)
'                                  .-------------------.
'                              [-2]|   reserved (0)    |               RTTI info                Mangled Typename
'                                  |-------------------|       .-----------------------.       .---------------.
'         Instance of UDT      [-1]| Ptr to RTTI info  |--->[0]|     reserved (0)      |       |Typename string|
'      .-------------------.       |-------------------|       |-----------------------|       |     with      |
'   [0]| vptr: Ptr to vtbl |--->[0]|Ptr to virt proc #1|   [+1]|Ptr to Mangled Typename|--->[0]| length (ASCII)|
'      |-------------------|       |-------------------|       |-----------------------|       |       &       |
'      |UDT member field #a|   [+1]|Ptr to virt proc #2|   [+2]| Ptr to Base RTTI info |---.   |  name (ASCII) |
'      |-------------------|       |-------------------|       |_______________________|   |   |      for      |
'      |UDT member field #b|   [+2]|Ptr to virt proc #3|   ________________________________|   |each component |
'      |-------------------|       :- - - - - - - - - -:  |                                    |_______________|
'      |UDT member field #c|       :                   :  |             Base RTTI info
'      :- - - - - - - - - -:       :                   :  |       .----------------------------.
'      :                   :       |___________________|  '--->[0]|        reserved (0)        |
'      :                   :                                      |----------------------------|
'      |___________________|                                  [+1]|Ptr to Mangled Base Typename|--->
'                                                                 |----------------------------|
'                                                             [+2]| Ptr to Base.Base RTTI info |---.
'                                                                 |____________________________|   |
'                                                                                                  |
'                                                                                                  V
Back to top

2. Inheritance polymorphism mechanism demonstrated by both true operating and faithful emulation

In the below proposed example, the polymorphic part is broken down to better bring out all the elements necessary for the mechanics of polymorphism.

Example of inheritance polymorphism, true operating: 'Animal type collection'
The generic base-type chosen is any 'animal' (abstraction).
The specialized derived-types are a 'dog', a 'cat', and a 'bird' (each defining a non-static string member containing its type-name).
The abstract procedures declared in the generic base-type, and which must be defined in each specialized derived-type, are:
- 'addr_override_fct()': returns the instance address,
- 'speak_override_fct()': returns the way of speaking,
- 'type_override_sub()': prints the type-name (from a string member with initialyzer).

    • 'animal' type declaration (generic base-type):
- Three public abstract procedures ('addr_override_fct()', 'speak_override_fct()', 'type_override_sub()') are declared (but without any body defining them).
- This base-type is non-instantiable, because containing an abstract procedure at least.
'Base-type animal:
    Type animal Extends Object
        Public:
            Declare Abstract Function addr_override_fct () As animal Ptr
            Declare Abstract Function speak_override_fct () As String
            Declare Abstract Sub type_override_sub ()
    End Type
    • 'dog', 'cat', 'bird' types declarations (specialized derived-types):
- For each derived-type, the three same public procedures ('addr_override_fct()', 'speak_override_fct()', 'type_override_sub()') are declared virtual, and their bodies are specialized for each derived-type.
- For each derived-type, a non-static string member initialized with its type-name.
- Each derived-type is instantiable, because implementing all abstract procedures declared in its base.
'Derived-type dog:
    Type dog Extends animal
        Public:
            Declare Virtual Function addr_override_fct () As animal Ptr Override
            Declare Virtual Function speak_override_fct () As String Override
            Declare Virtual Sub type_override_sub () Override
        Private:
            Dim As String animal_type = "dog"
    End Type

'Derived-type cat:
    Type cat Extends animal
        Public:
            Declare Virtual Function addr_override_fct () As animal Ptr Override
            Declare Virtual Function speak_override_fct () As String Override
            Declare Virtual Sub type_override_sub () Override
        Private:
            Dim As String animal_type = "cat"
    End Type

'Derived-type bird:
    Type bird Extends animal
        Public:
            Declare Virtual Function addr_override_fct () As animal Ptr Override
            Declare Virtual Function speak_override_fct () As String Override
            Declare Virtual Sub type_override_sub () Override
        Private:
            Dim As String animal_type = "bird"
    End Type
    • Full code of example:
- To be able to trigger polymorphism, a base-type pointer array ('animal_list') is declared then initialized with instances of different derived-types (a dog, a cat, a bird), in order to constitute a collection of objects from different types (but all having a common base-type).
- So, the same compiled code line, put in a loop (iterator 'I'), processes all instances from different types ('animal_list(I)->addr_override_fct()', 'animal_list(I)->speak_override_fct()', 'animal_list(I)->type_override_sub()'), because the polymorphism mechanic allows to call each specialized procedure at run-time.
'Base-type animal:
    Type animal Extends Object
        Public:
            Declare Abstract Function addr_override_fct () As animal Ptr
            Declare Abstract Function speak_override_fct () As String
            Declare Abstract Sub type_override_sub ()
    End Type

'Derived-type dog:
    Type dog Extends animal
        Public:
            Declare Virtual Function addr_override_fct () As animal Ptr Override
            Declare Virtual Function speak_override_fct () As String Override
            Declare Virtual Sub type_override_sub () Override
        Private:
            Dim As String animal_type = "dog"
    End Type

    'override_sub procedures for dog object:
        Virtual Function dog.addr_override_fct () As animal Ptr
            Return @This
        End Function
        Virtual Function dog.speak_override_fct () As String
            Return "Woof!"
        End Function
        Virtual Sub dog.type_override_sub ()
            Print This.animal_type
        End Sub

'Derived-type cat:
    Type cat Extends animal
        Public:
            Declare Virtual Function addr_override_fct () As animal Ptr Override
            Declare Virtual Function speak_override_fct () As String Override
            Declare Virtual Sub type_override_sub () Override
        Private:
            Dim As String animal_type = "cat"
    End Type

    'override_sub mehods for cat object:
        Virtual Function cat.addr_override_fct () As animal Ptr
            Return @This
        End Function
        Virtual Function cat.speak_override_fct () As String
            Return "Meow!"
        End Function
        Virtual Sub cat.type_override_sub ()
            Print This.animal_type
        End Sub

'Derived-type bird:
    Type bird Extends animal
        Public:
            Declare Virtual Function addr_override_fct () As animal Ptr Override
            Declare Virtual Function speak_override_fct () As String Override
            Declare Virtual Sub type_override_sub () Override
        Private:
            Dim As String animal_type = "bird"
    End Type

    'override_sub mehods for bird object:
        Virtual Function bird.addr_override_fct () As animal Ptr
            Return @This
        End Function
        Virtual Function bird.speak_override_fct () As String
            Return "Cheep!"
        End Function
        Virtual Sub bird.type_override_sub ()
            Print This.animal_type
        End Sub

'Create a dog and cat and bird dynamic instances referred through an animal pointer list:
    Dim As dog Ptr p_my_dog = New dog
    Dim As cat Ptr p_my_cat = New cat
    Dim As bird Ptr p_my_bird = New bird
    Dim As animal Ptr animal_list (1 To ...) = {p_my_dog, p_my_cat, p_my_bird}

'Have the animals speak and eat:
    Print "INHERITANCE POLYMORPHISM", "@object", "speak", "type"
    Print "   true operating"
    For I As Integer = LBound(animal_list) To UBound(animal_list)
        Print "      animal #" & I & ":",
        Print animal_list(I)->addr_override_fct(),   'real polymorphism
        Print animal_list(I)->speak_override_fct(),  'real polymorphism
        animal_list(I)->type_override_sub()          'real polymorphism
    Next I

Sleep

Delete p_my_dog
Delete p_my_cat
Delete p_my_bird

Ouput:
INHERITANCE POLYMORPHISM    @object       speak         type
   true operating
      animal #1:            11479616      Woof!         dog
      animal #2:            11479688      Meow!         cat
      animal #3:            11479760      Cheep!        bird
Example of polymorphism emulation very close to real operating of 'Animal type collection'
This following emulation of sub-type polymorphism is very close to the real operating:
- A static procedure pointer table 'callback_table()' is defined for each derived-type to emulate the vtbl (an instance reference will be passed as first parameter to each static procedure to emulate the hidden 'This' reference passed to any non-static member procedure).
'Derived-type dog:
    Type dog Extends animal
        Private:
            Static As Any Ptr callback_table(0 To 2)
        Public:
            Declare Static Function addr_callback_fct (Byref As dog) As animal Ptr
            Declare Static Function speak_callback_fct (Byref As dog) As String
            Declare Static Sub type_callback_sub (Byref As dog)
            Declare Constructor ()
        Private:
            Dim As String animal_type = "dog"
    End Type
    Static As Any Ptr dog.callback_table(0 To 2) = {@dog.addr_callback_fct, @dog.speak_callback_fct, @dog.type_callback_sub}

'Derived-type cat:
    Type cat Extends animal
        Private:
            Static As Any Ptr callback_table(0 To 2)
        Public:
            Declare Static Function addr_callback_fct (Byref As cat) As animal Ptr
            Declare Static Function speak_callback_fct (Byref As cat) As String
            Declare Static Sub type_callback_sub (Byref As cat)
            Declare Constructor ()
        Private:
            Dim As String animal_type = "cat"
    End Type
    Static As Any Ptr cat.callback_table(0 To 2) = {@cat.addr_callback_fct, @cat.speak_callback_fct, @cat.type_callback_sub}

'Derived-type bird:
    Type bird Extends animal
        Private:
            Static As Any Ptr callback_table(0 To 2)
        Public:
            Declare Static Function addr_callback_fct (Byref As bird) As animal Ptr
            Declare Static Function speak_callback_fct (Byref As bird) As String
            Declare Static Sub type_callback_sub (Byref As bird)
            Declare Constructor ()
        Private:
            Dim As String animal_type = "bird"
    End Type
    Static As Any Ptr bird.callback_table(0 To 2) = {@bird.addr_callback_fct, @bird.speak_callback_fct, @bird.type_callback_sub}
- At the base-type level, a non static pointer 'callback_ptr' is allocated for any derived-type instance to emulate the vptr (its value, initialized by the constructor, will depend on what derived-type is constructed: address of the following described table).
- At the base-type level, each abstract procedure is replaced by a member procedure calling the proper derived procedure through the 'callback_ptr' / 'callback_table(I)' ('I' being the index inside the table corresponding to this procedure).
'Base-type animal:
    Type animal
        Protected:
            Dim As Any Ptr Ptr callback_ptr
        Public:
            Declare Function addr_callback_fct () As animal Ptr
            Declare Function speak_callback_fct () As String
            Declare Sub type_callback_sub ()
    End Type

    Function animal.addr_callback_fct () As animal Ptr
        Return Cptr(Function (Byref As animal) As animal Ptr, This.callback_ptr[0])(This)
    End Function
    Function animal.speak_callback_fct () As String
        Return Cptr(Function (Byref As animal) As String, This.callback_ptr[1])(This)
    End Function
    Sub animal.type_callback_sub ()
        Cptr(Sub (Byref As animal), This.callback_ptr[2])(This)
    End Sub
    • Full code of emulation:
' Emulation of polymorphism is very close to the real operating:
' - a non static pointer is allocated for any derived-type instance to emulate the vptr
'   (its value will depend on what derived-type is constructed: address of the following table)
' - a static procedure pointer table is defined for each derived type to emulate the vtable
'   (an instance reference is passed as first parameter to each static procedure to emulate the hidden 'This' reference passed to any non-static member procedure)

'Base-type animal:
    Type animal
        Protected:
            Dim As Any Ptr Ptr callback_ptr
        Public:
            Declare Function addr_callback_fct () As animal Ptr
            Declare Function speak_callback_fct () As String
            Declare Sub type_callback_sub ()
    End Type

    Function animal.addr_callback_fct () As animal Ptr
        Return CPtr(Function (ByRef As animal) As animal Ptr, This.callback_ptr[0])(This)
    End Function
    Function animal.speak_callback_fct () As String
        Return CPtr(Function (ByRef As animal) As String, This.callback_ptr[1])(This)
    End Function
    Sub animal.type_callback_sub ()
        CPtr(Sub (ByRef As animal), This.callback_ptr[2])(This)
    End Sub

'Derived-type dog:
    Type dog Extends animal
        Private:
            Static As Any Ptr callback_table(0 To 2)
        Public:
            Declare Static Function addr_callback_fct (ByRef As dog) As animal Ptr
            Declare Static Function speak_callback_fct (ByRef As dog) As String
            Declare Static Sub type_callback_sub (ByRef As dog)
            Declare Constructor ()
        Private:
            Dim As String animal_type = "dog"
    End Type
    Static As Any Ptr dog.callback_table(0 To 2) = {@dog.addr_callback_fct, @dog.speak_callback_fct, @dog.type_callback_sub}

'callback_sub methods + constructor for dog object:
    Static Function dog.addr_callback_fct (ByRef d As dog) As animal Ptr
        Return @d
    End Function
    Static Function dog.speak_callback_fct (ByRef d As dog) As String
        Return "Woof!"
    End Function
    Static Sub dog.type_callback_sub (ByRef d As dog)
        Print d.animal_type
    End Sub
    Constructor dog ()
        This.callback_ptr = @callback_table(0)
    End Constructor

'Derived-type cat:
    Type cat Extends animal
        Private:
            Static As Any Ptr callback_table(0 To 2)
        Public:
            Declare Static Function addr_callback_fct (ByRef As cat) As animal Ptr
            Declare Static Function speak_callback_fct (ByRef As cat) As String
            Declare Static Sub type_callback_sub (ByRef As cat)
            Declare Constructor ()
        Private:
            Dim As String animal_type = "cat"
    End Type
    Static As Any Ptr cat.callback_table(0 To 2) = {@cat.addr_callback_fct, @cat.speak_callback_fct, @cat.type_callback_sub}

'callback_sub mehods + constructor for cat object:
    Static Function cat.addr_callback_fct (ByRef c As cat) As animal Ptr
        Return @c
    End Function
    Static Function cat.speak_callback_fct (ByRef c As cat) As String
        Return "Meow!"
    End Function
    Static Sub cat.type_callback_sub (ByRef c As cat)
        Print c.animal_type
    End Sub
    Constructor cat ()
        This.callback_ptr = @callback_table(0)
    End Constructor

'Derived-type bird:
    Type bird Extends animal
        Private:
            Static As Any Ptr callback_table(0 To 2)
        Public:
            Declare Static Function addr_callback_fct (ByRef As bird) As animal Ptr
            Declare Static Function speak_callback_fct (ByRef As bird) As String
            Declare Static Sub type_callback_sub (ByRef As bird)
            Declare Constructor ()
        Private:
            Dim As String animal_type = "bird"
    End Type
    Static As Any Ptr bird.callback_table(0 To 2) = {@bird.addr_callback_fct, @bird.speak_callback_fct, @bird.type_callback_sub}

'callback_sub mehods + constructor for bird object:
    Static Function bird.addr_callback_fct (ByRef b As bird) As animal Ptr
        Return @b
    End Function
    Static Function bird.speak_callback_fct (ByRef b As bird) As String
        Return "Cheep!"
    End Function
    Static Sub bird.type_callback_sub (ByRef b As bird)
        Print b.animal_type
    End Sub
    Constructor bird ()
        This.callback_ptr = @callback_table(0)
    End Constructor

'Create a dog and cat and bird dynamic instances referred through an animal pointer list:
    Dim As dog Ptr p_my_dog = New dog
    Dim As cat Ptr p_my_cat = New cat
    Dim As bird Ptr p_my_bird = New bird
    Dim As animal Ptr animal_list (1 To ...) = {p_my_dog, p_my_cat, p_my_bird}

'Have the animals speak and eat:
    Print "SUB-TYPE POLYMORPHISM", "@object", "speak", "type"
    Print "   by emulation"
    For I As Integer = LBound(animal_list) To UBound(animal_list)
        Print "      animal #" & I & ":",
        Print animal_list(I)->addr_callback_fct(),   'emulated polymorphism
        Print animal_list(I)->speak_callback_fct(),  'emulated polymorphism
        animal_list(I)->type_callback_sub()          'emulated polymorphism
    Next I

Sleep

Delete p_my_dog
Delete p_my_cat
Delete p_my_bird

Output:
SUB-TYPE POLYMORPHISM       @object       speak         type
   by emulation
      animal #1:            12462656      Woof!         dog
      animal #2:            12462728      Meow!         cat
      animal #3:            12462800      Cheep!        bird
Same example, with both real code and emulation code of 'Animal type collection'
The real code and emulation code are nested in a single code for easier comparison:
' Emulated polymorphism (with explicit callback member procedures)
' and
' True polymorphism (with abstract/virtual member procedures),
' both in an inheritance structure.

'Base-type animal:
    Type animal Extends Object  'Extends Object' useful for true polymorphism only
    ' for true polymorphism:
        Public:
            Declare Abstract Function addr_override_fct () As animal Ptr
            Declare Abstract Function speak_override_fct () As String
            Declare Abstract Sub type_override_sub ()
    ' for polymorphism emulation:
        Protected:
            Dim As Any Ptr Ptr callback_ptr
        Public:
            Declare Function addr_callback_fct () As animal Ptr
            Declare Function speak_callback_fct () As String
            Declare Sub type_callback_sub ()
    End Type

    ' for polymorphism emulation:
        Function animal.addr_callback_fct () As animal Ptr
            Return CPtr(Function (ByRef As animal) As animal Ptr, This.callback_ptr[0])(This)
        End Function
        Function animal.speak_callback_fct () As String
            Return CPtr(Function (ByRef As animal) As String, This.callback_ptr[1])(This)
        End Function
        Sub animal.type_callback_sub ()
            CPtr(Sub (ByRef As animal), This.callback_ptr[2])(This)
        End Sub

'Derived-type dog:
    Type dog Extends animal
    ' for true polymorphism:
        Public:
            Declare Virtual Function addr_override_fct () As animal Ptr Override
            Declare Virtual Function speak_override_fct () As String Override
            Declare Virtual Sub type_override_sub () Override
    ' for polymorphism emulation:
        Private:
            Static As Any Ptr callback_table(0 To 2)
        Public:
            Declare Static Function addr_callback_fct (ByRef As dog) As animal Ptr
            Declare Static Function speak_callback_fct (ByRef As dog) As String
            Declare Static Sub type_callback_sub (ByRef As dog)
            Declare Constructor ()
    ' for all:
        Private:
            Dim As String animal_type = "dog"
    End Type

    ' for true polymorphism:
        ' override_sub methods for dog object:
            Virtual Function dog.addr_override_fct () As animal Ptr
                Return @This
            End Function
            Virtual Function dog.speak_override_fct () As String
                Return "Woof!"
            End Function
            Virtual Sub dog.type_override_sub ()
                Print This.animal_type
            End Sub

    ' for polymorphism emulation:
        Static As Any Ptr dog.callback_table(0 To 2) = {@dog.addr_callback_fct, @dog.speak_callback_fct, @dog.type_callback_sub}
        'callback_sub methods + constructor for dog object:
            Static Function dog.addr_callback_fct (ByRef d As dog) As animal Ptr
                Return @d
            End Function
            Static Function dog.speak_callback_fct (ByRef d As dog) As String
                Return "Woof!"
            End Function
            Static Sub dog.type_callback_sub (ByRef d As dog)
                Print d.animal_type
            End Sub
            Constructor dog ()
                This.callback_ptr = @callback_table(0)
            End Constructor

'Derived-type cat:
    Type cat Extends animal
    ' for true polymorphism:
        Public:
            Declare Virtual Function addr_override_fct () As animal Ptr Override
            Declare Virtual Function speak_override_fct () As String Override
            Declare Virtual Sub type_override_sub () Override
    ' for polymorphism emulation:
        Private:
            Static As Any Ptr callback_table(0 To 2)
        Public:
            Declare Static Function addr_callback_fct (ByRef As cat) As animal Ptr
            Declare Static Function speak_callback_fct (ByRef As cat) As String
            Declare Static Sub type_callback_sub (ByRef As cat)
            Declare Constructor ()
    ' for all:
        Private:
            Dim As String animal_type = "cat"
    End Type

    ' for true polymorphism:
        ' override_sub mehods for cat object:
            Virtual Function cat.addr_override_fct () As animal Ptr
                Return @This
            End Function
            Virtual Function cat.speak_override_fct () As String
                Return "Meow!"
            End Function
            Virtual Sub cat.type_override_sub ()
                Print This.animal_type
            End Sub

    ' for polymorphism emulation:
        Static As Any Ptr cat.callback_table(0 To 2) = {@cat.addr_callback_fct, @cat.speak_callback_fct, @cat.type_callback_sub}
        ' callback_sub mehods + constructor for cat object:
            Static Function cat.addr_callback_fct (ByRef c As cat) As animal Ptr
                Return @c
            End Function
            Static Function cat.speak_callback_fct (ByRef c As cat) As String
                Return "Meow!"
            End Function
            Static Sub cat.type_callback_sub (ByRef c As cat)
                Print c.animal_type
            End Sub
            Constructor cat ()
                This.callback_ptr = @callback_table(0)
            End Constructor

'Derived-type bird:
    Type bird Extends animal
    ' for true polymorphism:
        Public:
            Declare Virtual Function addr_override_fct () As animal Ptr Override
            Declare Virtual Function speak_override_fct () As String Override
            Declare Virtual Sub type_override_sub () Override
    ' for polymorphism emulation:
        Private:
            Static As Any Ptr callback_table(0 To 2)
        Public:
            Declare Static Function addr_callback_fct (ByRef As bird) As animal Ptr
            Declare Static Function speak_callback_fct (ByRef As bird) As String
            Declare Static Sub type_callback_sub (ByRef As bird)
            Declare Constructor ()
    ' for all:
        Private:
            Dim As String animal_type = "bird"
    End Type

    ' for true polymorphism:
        ' override_sub mehods for bird object:
            Virtual Function bird.addr_override_fct () As animal Ptr
                Return @This
            End Function
            Virtual Function bird.speak_override_fct () As String
                Return "Cheep!"
            End Function
            Virtual Sub bird.type_override_sub ()
                Print This.animal_type
            End Sub

    ' for polymorphism emulation:
        Static As Any Ptr bird.callback_table(0 To 2) = {@bird.addr_callback_fct, @bird.speak_callback_fct, @bird.type_callback_sub}
        ' callback_sub mehods + constructor for bird object:
            Static Function bird.addr_callback_fct (ByRef b As bird) As animal Ptr
                Return @b
            End Function
            Static Function bird.speak_callback_fct (ByRef b As bird) As String
                Return "Cheep!"
            End Function
            Static Sub bird.type_callback_sub (ByRef b As bird)
                Print b.animal_type
            End Sub
            Constructor bird ()
                This.callback_ptr = @callback_table(0)
            End Constructor

'Create a dog and cat and bird dynamic instances referred through an animal pointer list:
    Dim As dog Ptr p_my_dog = New dog
    Dim As cat Ptr p_my_cat = New cat
    Dim As bird Ptr p_my_bird = New bird
    Dim As animal Ptr animal_list (1 To ...) = {p_my_dog, p_my_cat, p_my_bird}

'Have the animals speak and eat:
    Print "SUB-TYPE POLYMORPHISM", "@object", "speak", "type"
    For I As Integer = LBound(animal_list) To UBound(animal_list)
        Print "   animal #" & I & ":"
        ' for override_sub:
            Print "      true operating:",
            Print animal_list(I)->addr_override_fct(),   'real polymorphism
            Print animal_list(I)->speak_override_fct(),  'real polymorphism
            animal_list(I)->type_override_sub()          'real polymorphism
        ' for polymorphism emulation:
            Print "      by emulation:",
            Print animal_list(I)->addr_callback_fct(),   'emulated polymorphism
            Print animal_list(I)->speak_callback_fct(),  'emulated polymorphism
            animal_list(I)->type_callback_sub()          'emulated polymorphism
    Next I

Sleep

Delete p_my_dog
Delete p_my_cat
Delete p_my_bird

Output:
SUB-TYPE POLYMORPHISM       @object       speak         type
   animal #1:
      true operating:       11217472      Woof!         dog
      by emulation:         11217472      Woof!         dog
   animal #2:
      true operating:       11217552      Meow!         cat
      by emulation:         11217552      Meow!         cat
   animal #3:
      true operating:       11217632      Cheep!        bird
      by emulation:         11217632      Cheep!        bird
Back to top

3. Demangle typenames from RTTI info

Extraction of the mangled typename from the RTTI info:
- From the instance address, the RTTI info pointer of the type of the instance is accessed through a double indirection (with offsets: [0][-1]).
- The RTTI info pointer chaining described above allows to access RTTI info of the selected type in the inheritance hierarchy (up to the Object built-in type). This is done by means of an iteration on the pointer indirection (with offset: [+2]).
- Then the selected mangled typename is accessed (final indirection with offset: [+1])

Function 'mangledTypeNameFromRTTI()' to extract the mangled typenames:
Function mangledTypeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String
    ' Function to get any mangled-typename in the inheritance up hierarchy
    ' of the type of an instance (address: 'po') compatible with the built-in 'Object'
    '
    ' ('baseIndex =  0' to get the mangled-typename of the instance)
    ' ('baseIndex = -1' to get the base mangled-typename of the instance, or "" if not existing)
    ' ('baseIndex = -2' to get the base.base mangled-typename of the instance, or "" if not existing)
    ' (.....)
    '
        Dim As String s
        Dim As Zstring Ptr pz
        Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1]  ' Ptr to RTTI info
        For I As Integer = baseIndex To -1
            p = Cptr(Any Ptr Ptr, p)[2]                  ' Ptr to Base RTTI info of previous RTTI info
            If p = 0 Then Return s
        Next I
        pz = Cptr(Any Ptr Ptr, p)[1]                         ' Ptr to mangled-typename
        s = *pz
        Return s
End Function
Example of mangled typenames extraction from RTTI info, for an inheritance structure (three derived level) declared inside a namespace block:
Namespace oop
    Type parent Extends Object
    End Type

    Type child Extends parent
    End Type

    Type grandchild Extends child
    End Type
End Namespace

Function mangledTypeNameFromRTTI (ByVal po As Object Ptr, ByVal baseIndex As Integer = 0) As String
    ' Function to get any mangled-typename in the inheritance up hierarchy
    ' of the type of an instance (address: 'po') compatible with the built-in 'Object'
    '
    ' ('baseIndex =  0' to get the mangled-typename of the instance)
    ' ('baseIndex = -1' to get the base mangled-typename of the instance, or "" if not existing)
    ' ('baseIndex = -2' to get the base.base mangled-typename of the instance, or "" if not existing)
    ' (.....)
    '
        Dim As String s
        Dim As ZString Ptr pz
        Dim As Any Ptr p = CPtr(Any Ptr Ptr Ptr, po)[0][-1]  ' Ptr to RTTI info
        For I As Integer = baseIndex To -1
            p = CPtr(Any Ptr Ptr, p)[2]                      ' Ptr to Base RTTI info of previous RTTI info
            If p = 0 Then Return s
        Next I
        pz = CPtr(Any Ptr Ptr, p)[1]                         ' Ptr to mangled-typename
        s = *pz
        Return s
End Function

Dim As Object Ptr p = New oop.grandchild

Print "Mangled typenames list, from RTTI info:"
Print "  " & mangledTypeNameFromRTTI(p, 0)
Print "  " & mangledTypeNameFromRTTI(p, -1)
Print "  " & mangledTypeNameFromRTTI(p, -2)
Print "  " & mangledTypeNameFromRTTI(p, -3)
Delete p

Sleep

Output:
Mangled typenames list, from RTTI info:
  N3OOP10GRANDCHILDE
  N3OOP5CHILDE
  N3OOP6PARENTE
  6OBJECT
Implementation of the mangled typenames
From the above output, the mangling process on typenames can be highlighted with the following formatting:
N3OOP10GRANDCHILDE
(for 'oop.grandchild')

N3OOP5CHILDE
(for 'oop.child')

N3OOP6PARENTE
(for 'oop.parent')

6Object
(for 'Object')

Details on the the mangling process on typenames in the RTTI info:
- The mangled typename is a Zstring (ended by the null character).
- Each component (one dot as separator) of the full typename (converted to uppercase) is preceded by its number of characters encoded in ASCII itself (based on length-prefixed strings).
- When the type is inside at least one namespace, the mangled typename string begins with an additional "N" and ends with an additional "E".
(prefix "N" and suffix "E" from Nested-name ... Ending)

Extract the typenames (demangled) from RTTI info
The previous function ('mangledTypeNameFromRTTI()') can be now completed with a demangling process.

Function 'typeNameFromRTTI()' to extract the demangled typenames:
Function typeNameFromRTTI (Byval po As Object Ptr, Byval baseIndex As Integer = 0) As String
    ' Function to get any typename in the inheritance up hierarchy
    ' of the type of an instance (address: 'po') compatible with the built-in 'Object'
    '
    ' ('baseIndex =  0' to get the typename of the instance)
    ' ('baseIndex = -1' to get the base.typename of the instance, or "" if not existing)
    ' ('baseIndex = -2' to get the base.base.typename of the instance, or "" if not existing)
    ' (.....)
    '
        Dim As String s
        Dim As Zstring Ptr pz
        Dim As Any Ptr p = Cptr(Any Ptr Ptr Ptr, po)[0][-1]     ' Ptr to RTTI info
        For I As Integer = baseIndex To -1
            p = Cptr(Any Ptr Ptr, p)[2]                     ' Ptr to Base RTTI info of previous RTTI info
            If p = 0 Then Return s
        Next I
        pz = Cptr(Any Ptr Ptr, p)[1]                            ' Ptr to mangled-typename
        Do
            Do While (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0")
                If (*pz)[0] = 0 Then Return s
                pz += 1
            Loop
            Dim As Integer N = Val(*pz)
            Do
                pz += 1
            Loop Until (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0")
            If s <> "" Then s &= "."
            s &= Left(*pz, N)
            pz += N
        Loop
End Function
Previous example completed with the above function:
Namespace oop
    Type parent Extends Object
    End Type

    Type child Extends parent
    End Type

    Type grandchild Extends child
    End Type
End Namespace

Function mangledTypeNameFromRTTI (ByVal po As Object Ptr, ByVal baseIndex As Integer = 0) As String
    ' Function to get any mangled-typename in the inheritance up hierarchy
    ' of the type of an instance (address: 'po') compatible with the built-in 'Object'
    '
    ' ('baseIndex =  0' to get the mangled-typename of the instance)
    ' ('baseIndex = -1' to get the base mangled-typename of the instance, or "" if not existing)
    ' ('baseIndex = -2' to get the base.base mangled-typename of the instance, or "" if not existing)
    ' (.....)
    '
        Dim As String s
        Dim As ZString Ptr pz
        Dim As Any Ptr p = CPtr(Any Ptr Ptr Ptr, po)[0][-1]  ' Ptr to RTTI info
        For I As Integer = baseIndex To -1
            p = CPtr(Any Ptr Ptr, p)[2]                      ' Ptr to Base RTTI info of previous RTTI info
            If p = 0 Then Return s
        Next I
        pz = CPtr(Any Ptr Ptr, p)[1]                         ' Ptr to mangled-typename
        s = *pz
        Return s
End Function

Function typeNameFromRTTI (ByVal po As Object Ptr, ByVal baseIndex As Integer = 0) As String
    ' Function to get any typename in the inheritance up hierarchy
    ' of the type of an instance (address: 'po') compatible with the built-in 'Object'
    '
    ' ('baseIndex =  0' to get the typename of the instance)
    ' ('baseIndex = -1' to get the base.typename of the instance, or "" if not existing)
    ' ('baseIndex = -2' to get the base.base.typename of the instance, or "" if not existing)
    ' (.....)
    '
        Dim As String s
        Dim As ZString Ptr pz
        Dim As Any Ptr p = CPtr(Any Ptr Ptr Ptr, po)[0][-1]          ' Ptr to RTTI info
        For I As Integer = baseIndex To -1
            p = CPtr(Any Ptr Ptr, p)[2]                              ' Ptr to Base RTTI info of previous RTTI info
            If p = 0 Then Return s
        Next I
        pz = CPtr(Any Ptr Ptr, p)[1]                                 ' Ptr to mangled-typename
        Do
            Do While (*pz)[0] > Asc("9") OrElse (*pz)[0] < Asc("0")
                If (*pz)[0] = 0 Then Return s
                pz += 1
            Loop
            Dim As Integer N = Val(*pz)
            Do
                pz += 1
            Loop Until (*pz)[0] > Asc("9") OrElse (*pz)[0] < Asc("0")
            If s <> "" Then s &= "."
            s &= Left(*pz, N)
            pz += N
        Loop
End Function

Dim As Object Ptr p = New oop.grandchild

Print "Mangled typenames list, from RTTI info:"
Print "  " & mangledTypeNameFromRTTI(p, 0)
Print "  " & mangledTypeNameFromRTTI(p, -1)
Print "  " & mangledTypeNameFromRTTI(p, -2)
Print "  " & mangledTypeNameFromRTTI(p, -3)
Print
Print "Typenames (demangled) list, from RTTI info:"
Print "  " & typeNameFromRTTI(p, 0)
Print "  " & typeNameFromRTTI(p, -1)
Print "  " & typeNameFromRTTI(p, -2)
Print "  " & typeNameFromRTTI(p, -3)
Delete p

Sleep

Output:
Mangled typenames list, from RTTI info:
  N3OOP10GRANDCHILDE
  N3OOP5CHILDE
  N3OOP6PARENTE
  6OBJECT

Typenames (demangled) list, from RTTI info:
  OOP.GRANDCHILD
  OOP.CHILD
  OOP.PARENT
  OBJECT
Extract at once the Typename (demangled) and all those of its base-types hierarchy, from RTTI info
Simply by calling the previous function in a loop with a decreasing parameter 'baseIndex' (from the value 0) and to stop it as soon as an empty string is returned. Finaly by returning a string containing the different typenames with a hierarchic separator between each.

Function 'typeNameHierarchyFromRTTI()' to extract the Typename (demangled) and all those of its base-types hierarchy:
Function typeNameHierarchyFromRTTI (Byval po As Object Ptr) As String
    ' Function to get the typename inheritance up hierarchy
    ' of the type of an instance (address: po) compatible with the built-in 'Object'
    '
        Dim As String s = TypeNameFromRTTI(po)
        Dim As Integer i = -1
        Do
            Dim As String s0 = typeNameFromRTTI(po, i)
            If s0 = "" Then Exit Do
            s &= "->" & s0
            i -= 1
        Loop
        Return s
End Function
Previous example again completed with the above function:
Namespace oop
    Type parent Extends Object
    End Type

    Type child Extends parent
    End Type

    Type grandchild Extends child
    End Type
End Namespace

Function mangledTypeNameFromRTTI (ByVal po As Object Ptr, ByVal baseIndex As Integer = 0) As String
    ' Function to get any mangled-typename in the inheritance up hierarchy
    ' of the type of an instance (address: 'po') compatible with the built-in 'Object'
    '
    ' ('baseIndex =  0' to get the mangled-typename of the instance)
    ' ('baseIndex = -1' to get the base mangled-typename of the instance, or "" if not existing)
    ' ('baseIndex = -2' to get the base.base mangled-typename of the instance, or "" if not existing)
    ' (.....)
    '
        Dim As String s
        Dim As ZString Ptr pz
        Dim As Any Ptr p = CPtr(Any Ptr Ptr Ptr, po)[0][-1]  ' Ptr to RTTI info
        For I As Integer = baseIndex To -1
            p = CPtr(Any Ptr Ptr, p)[2]                      ' Ptr to Base RTTI info of previous RTTI info
            If p = 0 Then Return s
        Next I
        pz = CPtr(Any Ptr Ptr, p)[1]                         ' Ptr to mangled-typename
        s = *pz
        Return s
End Function

Function typeNameFromRTTI (ByVal po As Object Ptr, ByVal baseIndex As Integer = 0) As String
    ' Function to get any typename in the inheritance up hierarchy
    ' of the type of an instance (address: 'po') compatible with the built-in 'Object'
    '
    ' ('baseIndex =  0' to get the typename of the instance)
    ' ('baseIndex = -1' to get the base.typename of the instance, or "" if not existing)
    ' ('baseIndex = -2' to get the base.base.typename of the instance, or "" if not existing)
    ' (.....)
    '
        Dim As String s
        Dim As ZString Ptr pz
        Dim As Any Ptr p = CPtr(Any Ptr Ptr Ptr, po)[0][-1]          ' Ptr to RTTI info
        For I As Integer = baseIndex To -1
            p = CPtr(Any Ptr Ptr, p)[2]                              ' Ptr to Base RTTI info of previous RTTI info
            If p = 0 Then Return s
        Next I
        pz = CPtr(Any Ptr Ptr, p)[1]                                 ' Ptr to mangled-typename
        Do
            Do While (*pz)[0] > Asc("9") OrElse (*pz)[0] < Asc("0")
                If (*pz)[0] = 0 Then Return s
                pz += 1
            Loop
            Dim As Integer N = Val(*pz)
            Do
                pz += 1
            Loop Until (*pz)[0] > Asc("9") OrElse (*pz)[0] < Asc("0")
            If s <> "" Then s &= "."
            s &= Left(*pz, N)
            pz += N
        Loop
End Function

Function typeNameHierarchyFromRTTI (ByVal po As Object Ptr) As String
    ' Function to get the typename inheritance up hierarchy
    ' of the type of an instance (address: po) compatible with the built-in 'Object'
    '
        Dim As String s = TypeNameFromRTTI(po)
        Dim As Integer i = -1
        Do
            Dim As String s0 = typeNameFromRTTI(po, i)
            If s0 = "" Then Exit Do
            s &= "->" & s0
            i -= 1
        Loop
        Return s
End Function

Dim As Object Ptr p = New oop.grandchild

Print "Mangled typenames list, from RTTI info:"
Print "  " & mangledTypeNameFromRTTI(p, 0)
Print "  " & mangledTypeNameFromRTTI(p, -1)
Print "  " & mangledTypeNameFromRTTI(p, -2)
Print "  " & mangledTypeNameFromRTTI(p, -3)
Print
Print "Typenames (demangled) list, from RTTI info:"
Print "  " & typeNameFromRTTI(p, 0)
Print "  " & typeNameFromRTTI(p, -1)
Print "  " & typeNameFromRTTI(p, -2)
Print "  " & typeNameFromRTTI(p, -3)
Print
Print "Typename (demangled) and all those of its base-types hierarchy, from RTTI info:"
Print "  " & typeNameHierarchyFromRTTI(p)
Delete p

Sleep

Output:
Mangled typenames list, from RTTI info:
  N3OOP10GRANDCHILDE
  N3OOP5CHILDE
  N3OOP6PARENTE
  6OBJECT

Typenames (demangled) list, from RTTI info:
  OOP.GRANDCHILD
  OOP.CHILD
  OOP.PARENT
  OBJECT

Typename (demangled) and all those of its base-types hierarchy, from RTTI info:
  OOP.GRANDCHILD->OOP.CHILD->OOP.PARENT->OBJECT
Compare the typename (demangled) extracted from RTTI info to a string variable
As the various steps of demangling, the successive elements of the typname extracted from the RTTI info are compared with those of the chain provided (as soon as an element is different, "false" is returned immediately).

Function 'typeNameEqualFromRTTI()' to compared the typename (demangled) extracted from RTTI info to a string variable:
Function typeNameEqualFromRTTI (Byval po As Object Ptr, Byref typeName As String) As Boolean
    ' Function to get true if the instance typename (address: po) is the same than the passed string
    '
        Dim As String t = Ucase(typeName)
        Dim As ZString Ptr pz = Cptr(Any Ptr Ptr Ptr Ptr, po)[0][-1][1] ' Ptr to Mangled Typename
        Dim As Integer i = 1
        Do
            Do While (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0")
                If (*pz)[0] = 0 Then Return True
                pz += 1
            Loop
            Dim As Integer N = Val(*pz)
            Do
                pz += 1
            Loop Until (*pz)[0] > Asc("9") Orelse (*pz)[0] < Asc("0")
            If i > 1 Then
                If Mid(t, i, 1) <> "." Then Return False Else i += 1
            End If
            If Mid(t, i, N) <> Left(*pz, N) Then Return False Else pz += N : i += N
        Loop
End Function
Previous example finally completed with the above function:
Namespace oop
    Type parent Extends Object
    End Type

    Type child Extends parent
    End Type

    Type grandchild Extends child
    End Type
End Namespace

Function mangledTypeNameFromRTTI (ByVal po As Object Ptr, ByVal baseIndex As Integer = 0) As String
    ' Function to get any mangled-typename in the inheritance up hierarchy
    ' of the type of an instance (address: 'po') compatible with the built-in 'Object'
    '
    ' ('baseIndex =  0' to get the mangled-typename of the instance)
    ' ('baseIndex = -1' to get the base mangled-typename of the instance, or "" if not existing)
    ' ('baseIndex = -2' to get the base.base mangled-typename of the instance, or "" if not existing)
    ' (.....)
    '
        Dim As String s
        Dim As ZString Ptr pz
        Dim As Any Ptr p = CPtr(Any Ptr Ptr Ptr, po)[0][-1]  ' Ptr to RTTI info
        For I As Integer = baseIndex To -1
            p = CPtr(Any Ptr Ptr, p)[2]                      ' Ptr to Base RTTI info of previous RTTI info
            If p = 0 Then Return s
        Next I
        pz = CPtr(Any Ptr Ptr, p)[1]                         ' Ptr to mangled-typename
        s = *pz
        Return s
End Function

Function typeNameFromRTTI (ByVal po As Object Ptr, ByVal baseIndex As Integer = 0) As String
    ' Function to get any typename in the inheritance up hierarchy
    ' of the type of an instance (address: 'po') compatible with the built-in 'Object'
    '
    ' ('baseIndex =  0' to get the typename of the instance)
    ' ('baseIndex = -1' to get the base.typename of the instance, or "" if not existing)
    ' ('baseIndex = -2' to get the base.base.typename of the instance, or "" if not existing)
    ' (.....)
    '
        Dim As String s
        Dim As ZString Ptr pz
        Dim As Any Ptr p = CPtr(Any Ptr Ptr Ptr, po)[0][-1]          ' Ptr to RTTI info
        For I As Integer = baseIndex To -1
            p = CPtr(Any Ptr Ptr, p)[2]                              ' Ptr to Base RTTI info of previous RTTI info
            If p = 0 Then Return s
        Next I
        pz = CPtr(Any Ptr Ptr, p)[1]                                 ' Ptr to mangled-typename
        Do
            Do While (*pz)[0] > Asc("9") OrElse (*pz)[0] < Asc("0")
                If (*pz)[0] = 0 Then Return s
                pz += 1
            Loop
            Dim As Integer N = Val(*pz)
            Do
                pz += 1
            Loop Until (*pz)[0] > Asc("9") OrElse (*pz)[0] < Asc("0")
            If s <> "" Then s &= "."
            s &= Left(*pz, N)
            pz += N
        Loop
End Function

Function typeNameHierarchyFromRTTI (ByVal po As Object Ptr) As String
    ' Function to get the typename inheritance up hierarchy
    ' of the type of an instance (address: po) compatible with the built-in 'Object'
    '
        Dim As String s = TypeNameFromRTTI(po)
        Dim As Integer i = -1
        Do
            Dim As String s0 = typeNameFromRTTI(po, i)
            If s0 = "" Then Exit Do
            s &= "->" & s0
            i -= 1
        Loop
        Return s
End Function

Function typeNameEqualFromRTTI (ByVal po As Object Ptr, ByRef typeName As String) As Boolean
    ' Function to get true if the instance typename (address: po) is the same than the passed string
    '
        Dim As String t = UCase(typeName)
        Dim As ZString Ptr pz = CPtr(Any Ptr Ptr Ptr Ptr, po)[0][-1][1] ' Ptr to Mangled Typename
        Dim As Integer i = 1
        Do
            Do While (*pz)[0] > Asc("9") OrElse (*pz)[0] < Asc("0")
                If (*pz)[0] = 0 Then Return True
                pz += 1
            Loop
            Dim As Integer N = Val(*pz)
            Do
                pz += 1
            Loop Until (*pz)[0] > Asc("9") OrElse (*pz)[0] < Asc("0")
            If i > 1 Then
                If Mid(t, i, 1) <> "." Then Return False Else i += 1
            End If
            If Mid(t, i, N) <> Left(*pz, N) Then Return False Else pz += N : i += N
        Loop
End Function

Dim As Object Ptr p = New oop.grandchild

Print "Mangled typenames list, from RTTI info:"
Print "  " & mangledTypeNameFromRTTI(p, 0)
Print "  " & mangledTypeNameFromRTTI(p, -1)
Print "  " & mangledTypeNameFromRTTI(p, -2)
Print "  " & mangledTypeNameFromRTTI(p, -3)
Print
Print "Typenames (demangled) list, from RTTI info:"
Print "  " & typeNameFromRTTI(p, 0)
Print "  " & typeNameFromRTTI(p, -1)
Print "  " & typeNameFromRTTI(p, -2)
Print "  " & typeNameFromRTTI(p, -3)
Print
Print "Typename (demangled) and all those of its base-types hierarchy, from RTTI info:"
Print "  " & typeNameHierarchyFromRTTI(p)
Delete p
Print
p = New oop.child
Print "Is the typename of an oop.child instance the same as ""child""?"
Print "  " & typeNameEqualFromRTTI(p, "child")
Print "Is the typename of an oop.child instance the same as ""oop.child""?"
Print "  " & typeNameEqualFromRTTI(p, "oop.child")
Print "Is the typename of an oop.child instance the same as ""oop.grandchild""?"
Print "  " & typeNameEqualFromRTTI(p, "oop.grandchild")
Print "Is the typename of an oop.child instance the same as ""oop.parent""?"
Print "  " & typeNameEqualFromRTTI(p, "oop.parent")
Delete p

Sleep

Output:
Mangled typenames list, from RTTI info:
  N3OOP10GRANDCHILDE
  N3OOP5CHILDE
  N3OOP6PARENTE
  6OBJECT

Typenames (demangled) list, from RTTI info:
  OOP.GRANDCHILD
  OOP.CHILD
  OOP.PARENT
  OBJECT

Typename (demangled) and all those of its base-types hierarchy, from RTTI info:
  OOP.GRANDCHILD->OOP.CHILD->OOP.PARENT->OBJECT

Is the typename of an oop.child instance the same as "child"?
  false
Is the typename of an oop.child instance the same as "oop.child"?
  true
Is the typename of an oop.child instance the same as "oop.grandchild"?
  false
Is the typename of an oop.child instance the same as "oop.parent"?
  false
Back to top

See also

  • Composition, Aggregation, Inheritance
  • Inheritance Polymorphism


Embed and Access binary Data in Executable
How to embed binary data into an executable file at compile time, and access it from program code.
(based from forum posts of caseih and coderJeff)

This article describes a way to include binary data in a compiled program, and how to access them.

Principle

The (G)LD linker (included in the FreeBASIC distribution) can process binary data in a file, by turning them into an object file. This object file exports symbols that can be accessed from the program code to read the data.
The linker will so build two byte symbols based on the file name provided:
- a symbol for the beginning of the data block (the first data byte),
- another symbol for the end of the data block (just after the last data byte).
By compiling the user program with the object file, the addresses of these byte symbols allow to get pointers to the beginning and end of the data.

Note: It may be advantageous to apply this principle for example to an audio data file which can thus be included in the executable file. Both a pointer to the start of the audio data and the size of the audio data can be passed to any audio player function.

Example

Simple example to only demonstrate the principle (tested on linux/win 32/64-bit gas/gas64/gcc).
This example uses as binary data the ASCII code bytes of a text inserted in a hello.txt text file:
hello.txt text file for example:
Hello!
Welcome to the forum.
Invoke the LD linker manually to turn the hello.txt text file into an hello.o object file exporting symbols:
...\ld -r -b binary -o hello.o hello.txt

Two byte symbols are exported from the hello.o object file:
- on Windows 32-bit:
binary_hello_txt_start : beginning of the data block (the first data byte)
binary_hello_txt_end : end of the data block (just after the last data byte)
- otherwise:
_binary_hello_txt_start : beginning of the data block (the first data byte)
_binary_hello_txt_end : end of the data block (just after the last data byte)

Include the hello.o object file in the command-line when compiling the following program (which prints the ASCII code bytes embedded in the executable):
Extern "C"
    #if defined(__FB_WIN32__) And Not defined(__FB_64BIT__)
        Extern hello_txt_start Alias "binary_hello_txt_start" As Const Byte
        Extern hello_txt_end Alias "binary_hello_txt_end" As Const Byte
    #else
        Extern hello_txt_start Alias "_binary_hello_txt_start" As Const Byte
        Extern hello_txt_end Alias "_binary_hello_txt_end" As Const Byte
    #endif
End Extern

Dim hello_ptr As Const Byte Const Ptr = @hello_txt_start
Dim hello_length As Const UInteger = @hello_txt_end - @hello_txt_start

For i As UInteger = 0 To hello_length - 1
    Print Chr(hello_ptr[i]);
Next
Print

Sleep

See also

  • Executables
  • fbc command-line


Use Implicit / Overload New([]) and Delete([]) Operators with Inheritance Polymorphism
How to use the implicit or overload New and Delete operators and their New[] and Delete[] array-versions, with inheritance polymorphism (sub-type polymorphism), and how workaround some unexpected or unsuitable behaviors.

Preamble:

Some definitions and introductions to start with.

Different operators New and Delete (may be confusion in the user mind despite the documentation that distinguishes them from each other through different pages)

Implicit New/Delete operator (inaccessible by user):
- It is a static function/sub that only allocates/frees memory (it is not very different from Allocate/Deallocate).
Overload New/Delete operator (defined by user):
- It is a member operator (static function/sub) that can overload the 'Implicit New/Delete operator' only for user-defined types.
- So the user can define its own dynamic memory allocation/deallocation process part (the following/previous process part for implicit object construction/destruction can not be modified).

New Expression operator:
- It starts by using the 'Implicit/Overload New operator' (the implicit, or the overload if exists) to allocate memory.
- Then it invokes the constructor for the right type of object. If that object contains any other objects (either embedded or as base types) those constructors as invoked as well.
- So the final result is memory allocated and object constructed.
Delete Statement operator:
- It starts by invoking the destructor for the right type of object. If that object contains any other objects (either embedded or as base types) those destructors as invoked as well.
- Then it uses the 'Implicit/Overload Delete operator' (the implicit, or the overload if exists) to deallocate memory.
- So the final result is object destroyed and memory freed.

Placement New Operator:
- It constructs an object at a specified memory address (already allocated by another process).
- Consequently there is no 'Placement Delete operator'.
- It the object has a destructor (implicit or explicit), the user can call it with syntax as for a member method by using member access operator.

Similar definition for 'Implicit/overload New[]/Delete[] operators', 'New[] expression operator' and 'Delete[] statement operator', 'Placement New[] operator', which are only the (one-dimensional) array-versions of the previous operators (there is construction/destruction loop on the array elements).

Inheritance Polymorphism (ability of calling from the base type the member procedures of derived-types without worrying about the real type of the processed objects)

Thanks to the 'abstract'/'virtual' procedures, one can write a code using only the base type that will automatically call the derived-type procedures.
It is then possible to call the procedure of an object without worrying about its intrinsic type.

By using the same procedure name for several different types, the polymorphism allows a much more generic programming (abstraction).
The coder does not have to know, when calling a base procedure, the precise type of object on which the procedure will apply. He just needs to know that this type will implement the procedure.

Thus, a base-typed pointer (or reference), pointing to an instance of a derived-type, can be used to manipulate such an object.
Considering a collection of objects whose instantiate types are derived-types from a base type, then all these objects can be manipulated in an uniform way by considering them as objects of the base type.

Content of the following (6 parts and their associated examples)

6 main parts by increasing difficulty:
Preamble
1. Use Implicit New and Delete operators with inheritance polymorphism (sub-type polymorphism)
2. Use Placement New operator with inheritance polymorphism (sub-type polymorphism)
3. Use Implicit New[] and Delete[] operators with inheritance polymorphism (sub-type polymorphism)
4. Use Placement New[] operator with inheritance polymorphism (sub-type polymorphism)
5. Use Overload New and Delete operators with inheritance polymorphism (sub-type polymorphism)
6. Use Overload New[] and Delete[] operators with inheritance polymorphism (sub-type polymorphism)
Conclusion

6 associated examples:
Starting from the same common body of polymorphism by inheritance (polymorphism by sub-type):
- Structure hierarchy: Animal as base Type, Cat and Dog as derived Types.
- Member data: string ('name') in Animal, string ('favorite') in Cat and in Dog.
- Procedure fields: abstract/virtual subs 'Init()', abstract/virtual functions 'get_attributes()', virtual destructors 'Destructor()'.
- Base constructor is protected and base copy-constructor is private, in order to disallow any construction or copy-construction for base object.
- 4 objects constructed: 2 instances of Cat, 2 instances of Dog.
Then, other member procedures are added according to the part to be processed, and to the unexpected or unsuitable behaviors to be workarounded.

1. Use Implicit New and Delete operators with inheritance polymorphism (sub-type polymorphism)

A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer addresses a single derived object of any derived type.
No New/Delete operator overload.

This is the starting point of the study (simple case of polymorphism).
No unexpected or unsuitable behavior.

No unsuitable or unexpected behavior
Although the Implicit Delete operator is static, everything works fine when calling the static Delete statement on a base-typed pointer:
- The object is completely destroyed (derived part and base part) thanks to the virtual destructor in each derived type which overrides that of the base type.
- Then, the total memory is well deallocated even by calling the Implicit Delete operator of the base type, because this process only uses the value of the pointer provided.

Example
' Code for using implicit 'New'/'Delete' operators from base-typed pointer array in polymorphic inheritance context

Type Animal Extends Object
    Public:
        Declare Abstract Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Abstract Function get_attributes() As String
        Declare Virtual Destructor()
    Protected:
        Dim As String Name
        Declare Constructor()
    Private:
        Declare Constructor(ByRef _a As Animal)
End Type

Destructor Animal ()
    Print "Animal destructor: ", "object address: " & @This
End Destructor

Constructor Animal ()
    Print "Animal constructor: ", "object address: " & @This
End Constructor

Type Cat Extends Animal
    Public:
        Declare Constructor()
        Declare Virtual Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Virtual Function get_attributes() As String
        Declare Virtual Destructor()
    Private:
        Dim As String favorite
End Type

Constructor Cat ()
    Print "  Cat constructor: ", "  object address: " & @This
End Constructor

Sub Cat.Init(ByRef _name As String, ByRef _favorite As String = "")
    This.Name = _name
    This.favorite = _favorite
End Sub

Function Cat.get_attributes() As String
    Return This.Name & ": Cat, Meow, " & This.favorite
End Function

Destructor Cat()
    Print "  Cat destructor: ", "  object address: " & @This
End Destructor

Type Dog Extends Animal
    Public:
        Declare Constructor()
        Declare Virtual Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Virtual Function get_attributes() As String
        Declare Virtual Destructor()
    Private:
        Dim As String favorite
End Type

Constructor Dog()
    Print "  Dog constructor: ", "  object address: " & @This
End Constructor

Sub Dog.Init(ByRef _name As String, ByRef _favorite As String)
    This.Name = _name
    This.favorite = _favorite
End Sub

Function Dog.get_attributes() As String
    Return This.Name & ": Dog, Woof, " & This.favorite
End Function

Destructor Dog()
    Print "  Dog destructor: ", "  object address: " & @This
End Destructor

'------------------------------------------------------------------------------

Dim As Animal Ptr pa(0 To ...) = {New Cat(), New Cat(), New Dog(), New Dog()}

pa(0)->Init("Tiger", "Salmon")
pa(1)->Init("Kitty", "Sardine")
pa(2)->Init("Buddy", "Lamb")
pa(3)->Init("Molly", "Beef")

For I As Integer = LBound(pa) To UBound(pa)
    Print "    " & pa(I)->get_attributes()
Next I

For I As Integer = LBound(pa) To UBound(pa)
    Delete pa(I)
Next I

Sleep

Output (64-bit):
Animal constructor:         object address: 16455952
  Cat constructor:            object address: 16455952
Animal constructor:         object address: 16473360
  Cat constructor:            object address: 16473360
Animal constructor:         object address: 16473424
  Dog constructor:            object address: 16473424
Animal constructor:         object address: 16473488
  Dog constructor:            object address: 16473488
    Tiger: Cat, Meow, Salmon
    Kitty: Cat, Meow, Sardine
    Buddy: Dog, Woof, Lamb
    Molly: Dog, Woof, Beef
  Cat destructor:             object address: 16455952
Animal destructor:          object address: 16455952
  Cat destructor:             object address: 16473360
Animal destructor:          object address: 16473360
  Dog destructor:             object address: 16473424
Animal destructor:          object address: 16473424
  Dog destructor:             object address: 16473488
Animal destructor:          object address: 16473488
Back to top

2. Use Placement New operator with inheritance polymorphism (sub-type polymorphism)

A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer addresses a single derived object of any derived type.
The memory allocation/deallocation is done by another process (Allocate/Deallocate).

From the previous example
For the memory allocation and object construction:
replace:
Dim As Animal Ptr pa(0 To ...) = {New Cat(), New Cat(), New Dog(), New Dog()}

with:
Dim As Any Ptr pc1 = Allocate(SizeOf(Cat))
Dim As Any Ptr pc2 = Allocate(SizeOf(Cat))
Dim As Any Ptr pd1 = Allocate(SizeOf(Dog))
Dim As Any Ptr pd2 = Allocate(SizeOf(Dog))

Dim As Animal Ptr pa(0 To ...) = {New(pc1) Cat(), New(pc2) Cat(), New(pd1) Dog(), New(pd2) Dog()}

For the object destruction and memory deallocation:
replace:
For I As Integer = LBound(pa) To UBound(pa)
    Delete pa(I)
Next I

with:
For I As Integer = LBound(pa) To UBound(pa)
    pa(I)->Destructor()
Next I

Deallocate(pc1)
Deallocate(pc2)
Deallocate(pd1)
Deallocate(pd2)

Back to top

3. Use Implicit New[] and Delete[] operators with inheritance polymorphism (sub-type polymorphism)

A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer is a buffer pointer allowing to address several derived object of a same derived type.
No New[]/Delete[] operator overload.

One unsuitable behavior and one unexpected behavior are encountered
The calculation of the right address of any derived object is generally false (because it takes into account as object size the one corresponding to the pointer type and not the real object type), except obviously for the first object:
- To access to the right address of any derived object (when derived type contains one data field at least) in the array buffer constructed by the Implicit New[] operator, and from a base-typed pointer, a solution consists in overloading the '[]' operator, with a virtual '[]' operator in each derived type which overrides that (abstract) of the base type.
- Finally, to well call the overload '[]' operator (and not the implicit '[]' operator), it must be called on a dereferenced pointer (and not on the rough pointer that would called the implicit '[]' operator).
- If 'p' is the base-typed pointer, the right expression is '(*p)[n]' and not 'p[n]'.
- Warning: to calculate the right address of the nth derived object, the virtual '[]' operator is always called on the '*p' reference corresponding to the first object of the array buffer, so a correct overriding of the operator assumes that this first object of the array buffer is always a valid derived object (not destroyed for example).
The static Delete[] statement does not allow to retrieve the real run-time type of the object:
- So the calculation of the address of each object to destroy (if it is necessary) is generally false (because it takes into account as object size the one corresponding to the pointer type and not the real object type), except obviously for the first object.
- However, the total memory would be well deallocated even by calling the Implicit Delete[] operator of the base type, because this process only uses the value of the pointer provided.
- The safest is to use a virtual launcher for the Delete[] statement in each derived type which overrides that (abstract) of the base type, and in this case (as by calling the Delete[] statement on a derived type pointer), this is not mandatory of defining a virtual destructor but remains still recommended.
- In the following example, the called virtual launcher is 'DeleteSB_launcher()'.

Example
' Code for using implicit 'New[]'/'Delete[]' operators from base-typed pointer array in polymorphic inheritance context
'    Added member procedures to workaround unsuitable or unexpected behaviors:
'       - Abstract/Virtual 'Operator []()' to access to the right address of any derived object from a base-typed pointer
'       - Abstract/Virtual 'DeleteSB_launcher()' to destroy the right objects from a base-typed pointer

Type Animal Extends Object
    Public:
        Declare Abstract Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Abstract Function get_attributes() As String
        Declare Virtual Destructor()
        Declare Abstract Operator [](ByVal _n As Integer) ByRef As Animal
        Declare Abstract Sub DeleteSB_launcher()
    Protected:
        Dim As String Name
        Declare Constructor()
    Private:
        Declare Constructor(ByRef _a As Animal)
End Type

Destructor Animal ()
    Print "Animal destructor: ", "object address: " & @This
End Destructor

Constructor Animal ()
    Print "Animal constructor: ", "object address: " & @This
End Constructor

Type Cat Extends Animal
    Public:
        Declare Constructor()
        Declare Virtual Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Virtual Function get_attributes() As String
        Declare Virtual Destructor()
        Declare Virtual Operator [](ByVal _n As Integer) ByRef As Cat
        Declare Virtual Sub DeleteSB_launcher()
    Private:
        Dim As String favorite
End Type

Constructor Cat()
    Print "  Cat constructor: ", "  object address: " & @This
End Constructor

Sub Cat.Init(ByRef _name As String, ByRef _favorite As String = "")
    This.Name = _name
    This.favorite = _favorite
End Sub

Function Cat.get_attributes() As String
    Return This.Name & ": Cat, Meow, " & This.favorite
End Function

Destructor Cat()
    Print "  Cat destructor: ", "  object address: " & @This
End Destructor

Operator Cat.[](ByVal _n As Integer) ByRef As Cat
    Return (@This)[_n]
End Operator

Sub Cat.DeleteSB_launcher()
    Delete[] @This
End Sub

Type Dog Extends Animal
    Public:
        Declare Constructor()
        Declare Virtual Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Virtual Function get_attributes() As String
        Declare Virtual Destructor()
        Declare Virtual Operator [](ByVal _n As Integer) ByRef As Dog
        Declare Virtual Sub DeleteSB_launcher()
    Private:
        Dim As String favorite
End Type

Constructor Dog()
    Print "  Dog constructor: ", "  object address: " & @This
End Constructor

Sub Dog.Init(ByRef _name As String, ByRef _favorite As String)
    This.Name = _name
    This.favorite = _favorite
End Sub

Function Dog.get_attributes() As String
    Return This.Name & ": Dog, Woof, " & This.favorite
End Function

Destructor Dog()
  Print "  Dog destructor: ", "  object address: " & @This
End Destructor

Operator Dog.[](ByVal _n As Integer) ByRef As Dog
    Return (@This)[_n]
End Operator

Sub Dog.DeleteSB_launcher()
    Delete[] @This
End Sub

'------------------------------------------------------------------------------

Dim As Animal Ptr pa(0 To ...) = {New Cat[2], New Dog[2]}

'pa(0)[0].Init("Tiger", "Salmon")   '' does not work
'pa(0)[1].Init("Kitty", "Sardine")  '' does not work
'pa(1)[0].Init("Buddy", "Lamb")     '' does not work
'pa(1)[1].Init("Molly", "Beef")     '' does not work
(*pa(0))[0].Init("Tiger", "Salmon")
(*pa(0))[1].Init("Kitty", "Sardine")
(*pa(1))[0].Init("Buddy", "Lamb")
(*pa(1))[1].Init("Molly", "Beef")

For I As Integer = LBound(pa) To UBound(pa)
    For J As Integer = 0 To 1
'        Print "    " & pa(I)[J].get_attributes()  '' does not work
        Print "    " & (*pa(I))[J].get_attributes()
    Next J
Next I

For I As Integer = LBound(pa) To UBound(pa)
'    Delete[] pa(I)  '' does not work
    pa(I)->DeleteSB_launcher()
Next I

Sleep

Output (64-bit):
Animal constructor:         object address: 7101688
  Cat constructor:            object address: 7101688
Animal constructor:         object address: 7101744
  Cat constructor:            object address: 7101744
Animal constructor:         object address: 7101816
  Dog constructor:            object address: 7101816
Animal constructor:         object address: 7101872
  Dog constructor:            object address: 7101872
    Tiger: Cat, Meow, Salmon
    Kitty: Cat, Meow, Sardine
    Buddy: Dog, Woof, Lamb
    Molly: Dog, Woof, Beef
  Cat destructor:             object address: 7101744
Animal destructor:          object address: 7101744
  Cat destructor:             object address: 7101688
Animal destructor:          object address: 7101688
  Dog destructor:             object address: 7101872
Animal destructor:          object address: 7101872
  Dog destructor:             object address: 7101816
Animal destructor:          object address: 7101816
Back to top

4. Use Placement New[] operator with inheritance polymorphism (sub-type polymorphism)

A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer is a buffer pointer allowing to address several derived object of a same derived type.
The memory allocation/deallocation is done by another process (Allocate/Deallocate).

From the previous example
For the memory allocation and object construction:
replace:
Dim As Animal Ptr pa(0 To ...) = {New Cat[2], New Dog[2]}

with:
Dim As Any Ptr pc = Allocate(2 * SizeOf(Cat))
Dim As Any Ptr pd = Allocate(2 * SizeOf(Dog))

Dim As Animal Ptr pa(0 To ...) = {New(pc) Cat[2], New(pd) Dog[2]}

For the object destruction and memory deallocation:
replace:
For I As Integer = LBound(pa) To UBound(pa)
'    Delete[] pa(I)  '' does not work
    pa(I)->DeleteSB_launcher()
Next I

with:
For I As Integer = LBound(pa) To UBound(pa)
    For J As Integer = 1 To 0 Step -1  '' reverse order for destruction is mandatory
                                       ''    (see warning on '[]' operator usage)
'        pa(I)[J]. Destructor()   '' does not work
        (*pa(I))[J].Destructor()
    Next J
Next I

Deallocate(pc)
Deallocate(pd)

Warning when using the virtual operator '[]' in a context of inheritance polymorphism (sub-type polymorphism):
- To calculate the correct address of the nth derived object in a buffer, from a base-typed pointer 'p', a virtual operator '[]' can be used to return (using: 'Return (@This)[n]') a reference to this nth derived object.
- This virtual operator '[]' is always called on the same reference ('*p') corresponding to the first object of the buffer (operator called by: '(*p)[n]').
- So, a correct overriding of this operator (to calculate the correct address of the nth derived object) assumes that the first object in the buffer is always a valid derived object (not destroyed for example).
- This is why the destruction loop of the objects of the buffer (by: '(*p)[n].Destructor()'), in case of 'Placement New[]' usage as above, must always be done in the reverse order (end with the first object of the buffer).

Back to top

5. Use Overload New and Delete operators with inheritance polymorphism (sub-type polymorphism)

A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer addresses a single derived object of any derived type.
New/Delete operators overloaded.

One unexpected behavior is encountered
The static Delete statement does not allow to retrieve the real run-time type of the object:
- So, the user code of the overload Delete operator of the derived type is not executed because the Delete operator of the base type is called instead.
- The safest is to use a virtual launcher for the Delete statement in each derived type which overrides that (abstract) of the base type, and in this case (as by calling the Delete statement on a derived type pointer), this is not mandatory of defining a virtual destructor but remains still recommended.
- In the following example, the called virtual launcher is 'Delete_launcher()'.

Example
' Code for using overload 'New'/'Delete' operators from base-typed pointer array in polymorphic inheritance context
'    Added member procedure to workaround unexpected behavior:
'       - Abstract/Virtual 'Delete_launcher()' to call the overload Delete operator of the derived type from a base-typed pointer

Type Animal Extends Object
    Public:
        Declare Abstract Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Abstract Function get_attributes() As String
        Declare Virtual Destructor()
        Declare Abstract Sub Delete_launcher()
    Protected:
        Dim As String Name
        Declare Constructor()
    Private:
        Declare Constructor(ByRef _a As Animal)
End Type

Destructor Animal ()
    Print "  Animal destructor: ", "  object address: " & @This
End Destructor

Constructor Animal ()
    Print "  Animal constructor: ", "  object address: " & @This
End Constructor

Type Cat Extends Animal
    Public:
        Declare Constructor()
        Declare Virtual Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Virtual Function get_attributes() As String
        Declare Virtual Destructor()
        Declare Operator New(ByVal size As UInteger) As Any Ptr
        Declare Operator Delete(ByVal buf As Any Ptr)
        Declare Virtual Sub Delete_launcher()
    Private:
        Dim As String favorite
End Type

Constructor Cat ()
    Print "    Cat constructor: ", "    object address: " & @This
End Constructor

Sub Cat.Init(ByRef _name As String, ByRef _favorite As String = "")
    This.Name = _name
    This.favorite = _favorite
End Sub

Function Cat.get_attributes() As String
    Return This.Name & ": Cat, Meow, " & This.favorite
End Function

Destructor Cat()
    Print "    Cat destructor: ", "    object address: " & @This
End Destructor

Operator Cat.New(ByVal size As UInteger) As Any Ptr
    Dim As Any Ptr p = CAllocate(size)
    Print "Cat New operator: ", "buffer address: " & p
    Return p
End Operator

Operator Cat.Delete(ByVal buf As Any Ptr)
    Print "Cat Delete operator: ", "object address: " & buf
    Deallocate(buf)
End Operator

Sub Cat.Delete_launcher()
    Delete @This
End Sub

Type Dog Extends Animal
    Public:
        Declare Constructor()
        Declare Virtual Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Virtual Function get_attributes() As String
        Declare Virtual Destructor()
        Declare Operator New(ByVal size As UInteger) As Any Ptr
        Declare Operator Delete(ByVal buf As Any Ptr)
        Declare Virtual Sub Delete_launcher()
    Private:
        Dim As String favorite
End Type

Constructor Dog()
    Print "    Dog constructor: ", "    object address: " & @This
End Constructor

Sub Dog.Init(ByRef _name As String, ByRef _favorite As String)
    This.Name = _name
    This.favorite = _favorite
End Sub

Function Dog.get_attributes() As String
    Return This.Name & ": Dog, Woof, " & This.favorite
End Function

Destructor Dog()
    Print "    Dog destructor: ", "    object address: " & @This
End Destructor

Operator Dog.New(ByVal size As UInteger) As Any Ptr
    Dim As Any Ptr p = CAllocate(size)
    Print "Dog New operator: ", "buffer address: " & p
    Return p
End Operator

Operator Dog.Delete(ByVal buf As Any Ptr)
    Print "Dog Delete operator: ", "buffer address: " & buf
    Deallocate(buf)
End Operator

Sub Dog.Delete_launcher()
    Delete @This
End Sub

'------------------------------------------------------------------------------

Dim As Animal Ptr pa(0 To ...) = {New Cat(), New Cat(), New Dog(), New Dog()}

pa(0)->Init("Tiger", "Salmon")
pa(1)->Init("Kitty", "Sardine")
pa(2)->Init("Buddy", "Lamb")
pa(3)->Init("Molly", "Beef")

For I As Integer = LBound(pa) To UBound(pa)
    Print "      " & pa(I)->get_attributes()
Next I

For I As Integer = LBound(pa) To UBound(pa)
'    Delete pa(I)  '' does not work
    pa(I)->Delete_launcher()
Next I

Sleep

Output (64-bit):
Cat New operator:           buffer address: 13900048
  Animal constructor:         object address: 13900048
    Cat constructor:            object address: 13900048
Cat New operator:           buffer address: 13917456
  Animal constructor:         object address: 13917456
    Cat constructor:            object address: 13917456
Dog New operator:           buffer address: 13917520
  Animal constructor:         object address: 13917520
    Dog constructor:            object address: 13917520
Dog New operator:           buffer address: 13917584
  Animal constructor:         object address: 13917584
    Dog constructor:            object address: 13917584
      Tiger: Cat, Meow, Salmon
      Kitty: Cat, Meow, Sardine
      Buddy: Dog, Woof, Lamb
      Molly: Dog, Woof, Beef
    Cat destructor:             object address: 13900048
  Animal destructor:          object address: 13900048
Cat Delete operator:        object address: 13900048
    Cat destructor:             object address: 13917456
  Animal destructor:          object address: 13917456
Cat Delete operator:        object address: 13917456
    Dog destructor:             object address: 13917520
  Animal destructor:          object address: 13917520
Dog Delete operator:        buffer address: 13917520
    Dog destructor:             object address: 13917584
  Animal destructor:          object address: 13917584
Dog Delete operator:        buffer address: 13917584
Back to top

6. Use Overload New[] and Delete[] operators with inheritance polymorphism (sub-type polymorphism)

A collection of base-typed pointers in an array (base-typed Ptr array), where each pointer is a buffer pointer allowing to address several derived object of a same derived type.
New[]/Delete[] operators overloaded.

One unsuitable behavior and one unexpected behavior are encountered
The calculation of the right address of any derived object is generally false (because it takes into account as object size the one corresponding to the pointer type and not the real object type), except obviously for the first object:
- To access to the right address of any derived object (when derived type contains one data field at least) in the array buffer constructed by the Overload New[] operator, and from a base-typed pointer, a solution consists in overloading the '[]' operator, with a virtual '[]' operator in each derived type which overrides that (abstract) of the base type.
- To well call the overload '[]' operator (and not the implicit '[]' operator), it must be called on a dereferenced pointer (and not on the rough pointer that would called the implicit '[]' operator).
- If 'p' is the base-typed pointer, the right expression is '(*p)[n]' and not 'p[n]'.
- Warning: to calculate the right address of the nth derived object, the virtual '[]' operator is always called on the '*p' reference corresponding to the first object of the array buffer, so a correct overriding of the operator assumes that this first object of the array buffer is always a valid derived object (not destroyed for example).
The static Delete[] statement does not allow to retrieve the real run-time type of the object:
- So the calculation of the address of each object to destroy (if it is necessary) is generally false (because it takes into account as object size the one corresponding to the pointer type and not the real object type), except obviously for the first object.
- Also, the user code of the overload Delete[] operator of the derived type is not executed because the Delete[] operator of the base type is called instead.
- The safest for these two consequences is to always use a virtual launcher for the Delete[] statement in each derived type which overrides that (abstract) of the base type, and in this case (as by calling the Delete[] statement on a derived type pointer), this is not mandatory of defining a virtual destructor but remains still recommended.
- In the following example, the called virtual launcher is 'DeleteSB_launcher()'.

Example
' Code for using overload 'New[]'/'Delete[]' operators from base-typed pointer array in polymorphic inheritance context
'    Added member procedures to workaround unsuitable or unexpected behaviors:
'       - Abstract/Virtual 'Operator []()' to access to the right address of any derived object from a base-typed pointer
'       - Abstract/Virtual 'DeleteSB_launcher()' to destroy the right objects from a base-typed pointer, and to call the overload Delete[] operator of the derived type from a base-typed pointer

Type Animal Extends Object
    Public:
        Declare Abstract Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Abstract Function get_attributes() As String
        Declare Virtual Destructor()
        Declare Abstract Operator [](ByVal _n As Integer) ByRef As Animal
        Declare Abstract Sub DeleteSB_launcher()
    Protected:
        Dim As String Name
        Declare Constructor()
    Private:
        Declare Constructor(ByRef _a As Animal)
End Type

Destructor Animal ()
    Print "  Animal destructor: ", "  object address: " & @This
End Destructor

Constructor Animal ()
    Print "  Animal constructor: ", "  object address: " & @This
End Constructor

Type Cat Extends Animal
    Public:
        Declare Constructor()
        Declare Virtual Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Virtual Function get_attributes() As String
        Declare Virtual Destructor()
        Declare Virtual Operator [](ByVal _n As Integer) ByRef As Cat
        Declare Operator New[](ByVal size As UInteger) As Any Ptr
        Declare Operator Delete[](ByVal buf As Any Ptr)
        Declare Virtual Sub DeleteSB_launcher()
    Private:
        Dim As String favorite
End Type

Constructor Cat()
    Print "    Cat constructor: ", "    object address: " & @This
End Constructor

Sub Cat.Init(ByRef _name As String, ByRef _favorite As String = "")
    This.Name = _name
    This.favorite = _favorite
End Sub

Function Cat.get_attributes() As String
    Return This.Name & ": Cat, Meow, " & This.favorite
End Function

Destructor Cat()
    Print "    Cat destructor: ", "    object address: " & @This
End Destructor

Operator Cat.[](ByVal _n As Integer) ByRef As Cat
    Return (@This)[_n]
End Operator

Operator Cat.New[](ByVal size As UInteger) As Any Ptr
    Dim As Any Ptr p = CAllocate(size)
    Print "Cat New[] operator: ", "buffer address: " & p
    Return p
End Operator

Operator Cat.Delete[](ByVal buf As Any Ptr)
    Print "Cat Delete[] operator: ", "buffer address: " & buf
    Deallocate(buf)
End Operator

Sub Cat.DeleteSB_launcher()
    Delete[] @This
End Sub

Type Dog Extends Animal
    Public:
        Declare Constructor()
        Declare Virtual Sub Init(ByRef _name As String, ByRef _favorite As String)
        Declare Virtual Function get_attributes() As String
        Declare Virtual Destructor()
        Declare Virtual Operator [](ByVal _n As Integer) ByRef As Dog
        Declare Operator New[](ByVal size As UInteger) As Any Ptr
        Declare Operator Delete[](ByVal buf As Any Ptr)
        Declare Virtual Sub DeleteSB_launcher()
    Private:
        Dim As String favorite
End Type

Constructor Dog()
    Print "    Dog constructor: ", "    object address: " & @This
End Constructor

Sub Dog.Init(ByRef _name As String, ByRef _favorite As String)
    This.Name = _name
    This.favorite = _favorite
End Sub

Function Dog.get_attributes() As String
    Return This.Name & ": Dog, Woof, " & This.favorite
End Function

Destructor Dog()
  Print "    Dog destructor: ", "    object address: " & @This
End Destructor

Operator Dog.[](ByVal _n As Integer) ByRef As Dog
    Return (@This)[_n]
End Operator

Operator Dog.New[](ByVal size As UInteger) As Any Ptr
    Dim As Any Ptr p = CAllocate(size)
    Print "Dog New[] operator: ", "buffer address: " & p
    Return p
End Operator

Operator Dog.Delete[](ByVal buf As Any Ptr)
    Print "Dog Delete[] operator: ", "buffer address: " & buf
    Deallocate(buf)
End Operator

Sub Dog.DeleteSB_launcher()
    Delete[] @This
End Sub

'------------------------------------------------------------------------------

Dim As Animal Ptr pa(0 To ...) = {New Cat[2], New Dog[2]}

'pa(0)[0].Init("Tiger", "Salmon")   '' does not work
'pa(0)[1].Init("Kitty", "Sardine")  '' does not work
'pa(1)[0].Init("Buddy", "Lamb")     '' does not work
'pa(1)[1].Init("Molly", "Beef")     '' does not work
(*pa(0))[0].Init("Tiger", "Salmon")
(*pa(0))[1].Init("Kitty", "Sardine")
(*pa(1))[0].Init("Buddy", "Lamb")
(*pa(1))[1].Init("Molly", "Beef")

For I As Integer = LBound(pa) To UBound(pa)
    For J As Integer = 0 To 1
'        Print "      " & pa(I)[J].get_attributes()  '' does not work
        Print "      " & (*pa(I))[J].get_attributes()
    Next J
Next I

For I As Integer = LBound(pa) To UBound(pa)
'    Delete[] pa(I)  '' does not work
    pa(I)->DeleteSB_launcher()
Next I

Sleep

Output (64-bit):
Cat New[] operator:         buffer address: 17128688
  Animal constructor:         object address: 17128696
    Cat constructor:            object address: 17128696
  Animal constructor:         object address: 17128752
    Cat constructor:            object address: 17128752
Dog New[] operator:         buffer address: 17128816
  Animal constructor:         object address: 17128824
    Dog constructor:            object address: 17128824
  Animal constructor:         object address: 17128880
    Dog constructor:            object address: 17128880
      Tiger: Cat, Meow, Salmon
      Kitty: Cat, Meow, Sardine
      Buddy: Dog, Woof, Lamb
      Molly: Dog, Woof, Beef
    Cat destructor:             object address: 17128752
  Animal destructor:          object address: 17128752
    Cat destructor:             object address: 17128696
  Animal destructor:          object address: 17128696
Cat Delete[] operator:      buffer address: 17128688
    Dog destructor:             object address: 17128880
  Animal destructor:          object address: 17128880
    Dog destructor:             object address: 17128824
  Animal destructor:          object address: 17128824
Dog Delete[] operator:      buffer address: 17128816
Note:
- As the derived type has a destructor, an extra uinteger is allocated by the New[] operator at the head of the memory buffer, in order to store the number of elements as part of the allocation, so that the Delete[] operator can determine the count of destructors to call.
- This is why the memory buffer address is different from the first object address in the memory buffer.

Back to top

Conclusion:

To be compatible at the same time with any usage (simple/array-version, implicit/overload) of the New([])/Delete([]) operators in an inheritance polymorphism (sub-type polymorphism), 3 member procedures must be added to workaround unsuitable or unexpected behaviors:
- Abstract/Virtual 'Operator []()' to access to the right address of any derived object from a base-typed pointer.
- Abstract/Virtual 'Delete_launcher()' to call the overload Delete operator of the derived type from a base-typed pointer.
- Abstract/Virtual 'DeleteSB_launcher()' to destroy the objects at the right addresses from a base-typed pointer, and to call the overload Delete[] operator of the derived type from a base-typed pointer.

'Virtual' declarations and bodies of the added member procedures in each derived type, but only 'abstract' declarations in the base type:
Type base_type Extends Object
    Declare Abstract Operator [](ByVal n As Integer) ByRef As base_type
    Declare Abstract Sub Delete_launcher()
    Declare Abstract Sub DeleteSB_launcher()
End Type

'------------------------------------------------------------------------

Type derived_type Extends base_type
    Declare Virtual Operator [](ByVal n As Integer) ByRef As derived_type
    Declare Virtual Sub Delete_launcher()
    Declare Virtual Sub DeleteSB_launcher()
End Type

Operator derived_type.[](ByVal n As Integer) ByRef As derived_type
    Return (@This)[n]
End Operator

Sub derived_type.Delete_launcher()
    Delete @This
End Sub

Sub derived_type.DeleteSB_launcher()
    Delete[] @This
End Sub

Note:
- For the virtual '[]' operator in the derived type, declaring returning by derived-typed reference is not mandatory (just for aesthetics by using the covariance). Returning by base-typed reference is sufficient.
- The important thing is the derived operator body which uses a derived-typed pointer indexing, allowing to return a based-typed reference but rightly pointing to the derived object.

Abstract
- The New([]) expression returns a typed pointer corresponding to the called type (a derived type in our case), but the principle of inheritance polymorphism (sub-type polymorphism) is precisely to immediately up-cast this derived type to a base type.
- Using a base-typed pointer to a derived object buffer does not generally allows to correctly index the different addresses of the derived objects (except if all member data fields are in the base type only).
- The Implicit/Overload operators New([]) or Delete([]) are all static because their only role is to allocate or free memory, and at that time the object does not exist yet or no longer exists. So this is the Implicit/Overload Delete([]) operator of the base type which is called instead of the one of the derived type.
- Thus, to nevertheless use all these New([])/Delete([]) functionalities correctly without derogating from the exclusive usage of base-typed pointers, it is necessary to use a solution as developed above (adding one virtual overload operator and two virtual procedures).

Back to top

See also

  • New and Delete
  • Inheritance Polymorphism


Emulate a TLS (Thread Local Storage) and a TP (Thread Pooling) feature
How emulate a kind of TLS (Thread Local Storage) and a kind of TP (Thread Pooling) feature with FreeBASIC.

Preamble:

TLS (Thread Local Storage)
Static variables are normally shared across all the threads. If we modify a static variable, it is visible so modified to all the threads.
Unlike normal static variable, if we create a TLS static variable, every thread must have its own copy of the variable (but with the same access name), i.e. any change to the variable is local to the thread (locally stored).
This allows to create a thread-safe procedure, because each call to this procedure gets its own copy of the same declared static variables.
In normal procedure with static variables, the content of that variables can be updated by multiple threads, but with TLS, we can think of these as static data but local to each thread.

TLS data is similar to static data, but the only difference is that TLS data are unique to each thread.

TP (Thread Pooling)
A thread pool is a set of threads that can be used to run tasks based on user needs.
The thread pool is accessible via a Type structure.

Creating a new thread is an expensive act in terms of resources, both from a processor (CPU) point of view and from a memory point of view.
Also, in the event that a program requires the execution of many tasks, the creation and deletion of a thread for each of them would greatly penalize the performance of the application.
Therefore, it would be interesting to be able to share the creation of threads so that a thread that has finished executing a task is available for the execution of a future task.

1. How emulate a kind of TLS (Thread Local Storage) feature with FreeBASIC

The principle of this TLS emulation for FreeBASIC is to use a static array for each requested TLS variable, where each thread has its own unique index (hidden) to access the array element.
This unique index relating to the thread is deduced from the thread handle value:
- With fbc version >= 1.08, the thread handle value is simply returned from the 'Threadself()' function calling (new function) from any thread.
- With fbc version < 1.08, the code is more twisted:
- The thread handle value is only accessible from the 'ThreadCreate()' return in the parent (or main) thread when creating it.
- There is no way to properly emulate the 'Threadself()' function, but only by a twisted method.
- In the example below (for fbc version < 1.08), a 'Threadself()' function (returning by reference) value is initialized before each use by the thread (with its own thread handle), and all of this (initialization + use) protected by a mutex as for its corresponding 'ThreadCreate()'.

In the below example, the TLS static variable is an integer which is used in a single and generic counting procedure ('counter()') with none passed parameter). This counting procedure is called by each thread (thus each thread counts independently of each other but by calling the same single counting procedure).
A single macro allows to define any TLS variable (except array) of any type.

    • Code with preprocessor conditional directives depending on fbc version:
#include Once "crt/string.bi"

#if __FB_VERSION__ < "1.08"
    ' Emulation of the function Threadself() of FreeBASIC
    ' Before each use, the thread must refresh this function value with its own thread handle,
    ' and all of this (refreshing + use) protected by a mutex.
    Function ThreadSelf () ByRef As Any Ptr
        Static As Any Ptr handle
        Return handle
    End Function
#else
    #include Once "fbthread.bi"
#endif

#macro CreateTLSdatatypeVariableFunction (variable_function_name, variable_datatype)
' Creation of a "variable_function_name" function to emulate a static datatype variable (not an array),
' with a value depending on the thread using it.
    Namespace TLS
        Function variable_function_name (ByVal cd As Boolean = True) ByRef As variable_datatype
        ' Function emulating (creation/access/destruction) a static datatype variable with value depending on thread using it:
            ' If calling without parameter (or with 'True') parameter, this allows to [create and] access the static datatype variable.
            ' If calling with the 'False' parameter, this allows to destroy the static datatype variable.
            Dim As Integer bound = 0
            Static As Any Ptr TLSindex(bound)
            Static As variable_datatype TLSdata(bound)
            Dim As Any Ptr Threadhandle = ThreadSelf()
            Dim As Integer index = 0
            For I As Integer = 1 To UBound(TLSindex)  ' search existing TLS variable (existing array element) for the running thread
                If TLSindex(I) = Threadhandle Then
                    index = I
                    Exit For
                End If
            Next I
            If index = 0 And cd = True Then  ' create a new TLS variable (new array element) for a new thread
                index = UBound(TLSindex) + 1
                ReDim Preserve TLSindex(index)
                TLSindex(index) = Threadhandle
                ReDim Preserve TLSdata(index)
            ElseIf index > 0 And cd = False Then  ' destroy a TLS variable (array element) and compact the array
                If index < UBound(TLSindex) Then  ' reorder the array elements
                    memmove(@TLSindex(index), @TLSindex(index + 1), (UBound(TLSindex) - index) * SizeOf(Any Ptr))
                    Dim As variable_datatype Ptr p = Allocate(SizeOf(variable_datatype))  ' for compatibility to object with destructor
                    memmove(p, @TLSdata(index), SizeOf(variable_datatype))                ' for compatibility to object with destructor
                    memmove(@TLSdata(index), @TLSdata(index + 1), (UBound(TLSdata) - index) * SizeOf(variable_datatype))
                    memmove(@TLSdata(UBound(TLSdata)), p, SizeOf(variable_datatype))      ' for compatibility to object with destructor
                    Deallocate(p)                                                         ' for compatibility to object with destructor
                End If
                ReDim Preserve TLSindex(UBound(TLSindex) - 1)
                ReDim Preserve TLSdata(UBound(TLSdata) - 1)
                index = 0
            End If
            Return TLSdata(index)
        End Function
    End Namespace
#endmacro

'------------------------------------------------------------------------------

Type threadData
    Dim As Any Ptr handle
    Dim As String prefix
    Dim As String suffix
    Dim As Double tempo
    #if __FB_VERSION__ < "1.08"
        Static As Any Ptr mutex
    #endif
End Type
#if __FB_VERSION__ < "1.08"
    Dim As Any Ptr threadData.mutex
#endif

CreateTLSdatatypeVariableFunction (count, Integer)  ' create a TLS static integer function

Function counter() As Integer  ' definition of a generic counter with counting depending on thread calling it
    TLS.count() += 1            ' increment the TLS static integer
    Return TLS.count()          ' return the TLS static integer
End Function

Sub Thread(ByVal p As Any Ptr)
    Dim As threadData Ptr ptd = p
    Dim As UInteger c
    Do
        #if __FB_VERSION__ < "1.08"
            MutexLock(threadData.mutex)
            ThreadSelf() = ptd->handle
        #endif
            c = counter()
        #if __FB_VERSION__ < "1.08"
            MutexUnlock(threadData.mutex)
        #endif
        Print ptd->prefix & c & ptd->suffix & " ";  ' single print with concatenated string avoids using a mutex
        Sleep ptd->tempo, 1
    Loop Until c = 12
    #if __FB_VERSION__ < "1.08"
        MutexLock(threadData.mutex)
        ThreadSelf() = ptd->handle
    #endif
    TLS.count(False)  ' destroy the TLS static integer
    #if __FB_VERSION__ < "1.08"
        MutexUnlock(threadData.mutex)
    #endif
End Sub

'------------------------------------------------------------------------------

Print "|x| : counting from thread a"
Print "(x) : counting from thread b"
Print "[x] : counting from thread c"
Print

#if __FB_VERSION__ < "1.08"
    threadData.mutex = MutexCreate()
#endif

Dim As threadData mtlsa
mtlsa.prefix = "|"
mtlsa.suffix = "|"
mtlsa.tempo = 100
#if __FB_VERSION__ < "1.08"
    MutexLock(threadData.mutex)
#endif
mtlsa.handle = ThreadCreate(@Thread, @mtlsa)
#if __FB_VERSION__ < "1.08"
    MutexUnlock(threadData.mutex)
#endif

Dim As threadData mtlsb
mtlsb.prefix = "("
mtlsb.suffix = ")"
mtlsb.tempo = 150
#if __FB_VERSION__ < "1.08"
    MutexLock(threadData.mutex)
#endif
mtlsb.handle = ThreadCreate(@Thread, @mtlsb)
#if __FB_VERSION__ < "1.08"
    MutexUnlock(threadData.mutex)
#endif

Dim As threadData mtlsc
mtlsc.prefix = "["
mtlsc.suffix = "]"
mtlsc.tempo = 250
#if __FB_VERSION__ < "1.08"
    MutexLock(threadData.mutex)
#endif
mtlsc.handle = ThreadCreate(@Thread, @mtlsc)
#if __FB_VERSION__ < "1.08"
    MutexUnlock(threadData.mutex)
#endif

ThreadWait(mtlsa.handle)
ThreadWait(mtlsb.handle)
ThreadWait(mtlsc.handle)
#if __FB_VERSION__ < "1.08"
    MutexDestroy(threadData.mutex)
#endif

Print
Print
Print "end of threads"

Sleep

Output example
|x| : counting from thread a
(x) : counting from thread b
[x] : counting from thread c

|1| (1) [1] |2| (2) |3| [2] (3) |4| |5| (4) [3] |6| (5) |7| [4] (6) |8| |9| (7) [5] |10| (8) |11| |12| (9) [6] (10) [7] (11) (12) [8] [9] [10] [11] [12]

end of threads
2. How emulate a kind of TP (Thread Pooling) feature with FreeBASIC

The objective of thread pooling is to pool the threads in order to avoid untimely creation or deletion of threads, and thus allow their reuse.
So when a task needs to be executed, it will be more resource efficient to check if the thread pool contains an available thread.
If so, it will be used while the task is running, and then freed when the task is completed.
If there is no thread available, a new thread can be created, and at the end of the task, the thread would be in turn available in the pool of threads.

Two Type structures are first proposed below:
        • ThreadInitThenMultiStart.
        • ThreadPooling.
These two structures make it possible to use one thread per instance created, and to chain on this dedicated thread the execution of user procedures one after the other, but without the thread stopping between each:
- The 'ThreadInitThenMultiStart' structure requires a manual start after initialization (and manual wait for completion) for each user procedure to be executed in sequence in the thread.
- The 'ThreadPooling' structure allows to register a sequence of user thread procedure submissions in a queue, while at same time the user procedures start to be executed in the thread without waiting (a last registered wait command is enough to test for full sequence completion).
By creating and using several instances, these two structures make it possible to execute sequences of user procedures in several threads, therefore executed in parallel (temporally).

A last structure is finally proposed:
        • ThreadDispatching.
This last structure is an over-structure of the ThreadPooling structure, dispatching user thread procedures over a given max number of secondary threads.

These 3 different structures are then compared from the point of view:
        • Execution time gain (between them).
        • Time wasted when running a user task (among themselves and in relation to other methods).

    • ThreadInitThenMultiStart Type:
        • Principle:
The 'ThreadInitThenMultiStart' Type below operationally provides to user 3 (4 actually) main public methods (plus a constructor and a destructor), and internally uses 9 private data members plus 1 private subroutine (static) member.

The public methods are:
- ThreadInit : Initialize the instance with the parameters of the requested user procedure to be executed in a thread.
- ThreadStart : Start the user procedure execution in the thread (2 overload methods).
- ThreadWait : Wait for the completion of the user procedure in the thread.

By creating several instances each associated with a thread, we can obtain a kind of thread pooling feature.
The 'ThreadInitThenMultiStart' Type does not manage any pending thread queue.
It is up to the user to choose an existing instance or to create a new instance with which to run his thread procedure.

        • Description:
Each user procedure (to be executed in a thread) must be available under the following function signature:
Function userproc (Byval puserdata As Any Ptr) As String
in order to be compatible with the parameters of the 'ThreadInit' method:
Declare Sub ThreadInit (Byval pThread As Function (Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
and perform the instance ('t') initialization by:
t.ThreadInit(@userproc [, puserdata])

The other methods are called on the instance ('t'):
t.ThreadStart() or t.ThreadStart(puserdata)
t.ThreadWait()

The different methods must be called respecting the order of the following sequence:
ThreadInit, [user code,] ThreadStart, [user code,] ThreadWait, [user code,] ThreadStart, [user code,] ThreadWait, [user code,] .....

After any 'ThreadStart'...'ThreadWait' sequence, a new user thread procedure can be initialized by calling the 'ThreadInit' method again on the same instance.
On the other hand, 'ThreadStart'...'ThreadWait' sequences can also be chained on different instances already initialized.
If using several instances (so several threads), the ordered sequences on each instance can be interlaced between instances because calling methods on different instances.
The overload method 'ThreadStart(Byval p As Any Ptr)' allows to start the user thread procedure by specifying a new parameter value, without having to call 'ThreadInit' first. The overload method 'ThreadStart()' starts the user thread procedure without modifying the parameter value.

The 'ThreadWait' method returns a 'As String' Type (by value), like the user thread function is declared (a string variable return allows to also pass a numeric value).

This user data return from the user function is accessed through the 'ThreadWait' return. It is always safe (because in this case, the user thread function has been always fully executed).
If the user doesn't want to use the return value of his thread function (to be used like for a subroutine):
- He ends his user thread function with Return "" for example.
- He calls 'ThreadWait' as a subroutine and not as a function (not accessing the value potentially returned by 'ThreadWait').
If the user want to use an existing thread subroutine, he can define a wrapper function (with an "any pointer" as parameter, calling the thread subroutine, and returning a string), and pass this function to 'ThreadInit'.

Warning: The information supplied to the user thread procedure via the passed pointer (by 'ThreadInit' or 'ThreadStart') should not be changed between 'ThreadStart' and 'ThreadWait' due to the time uncertainty on the real call of the user thread procedure in this interval.

        • Under the hood:
In fact, each instance is associated with an internal thread that runs continuously in a loop as soon as a first initialization ('ThreadInit') has taken place. This internal thread runs the private subroutine (static) member.
It is this private subroutine (static) member that will call (on a 'ThreadStart') the user procedure to be executed, like a classic function call. The value returned by the user function is stored to be subsequently returned to the user through the returned value by 'ThreadWait'.

So, for each new 'ThreadInitThenMultiStart' instance, an internal thread is started on the first 'ThreadInit' method (calling the 'ThreadCreate' FreeBASIC keyword), then the user thread procedure is started on the 'ThreadStart' method request.
As each initialized instance is associated with a running internal thread, using local scopes or dynamic instances allow to stop internal threads that are no longer used.

In the 'ThreadInitThenMultiStart' Type, an additional property 'ThreadState' is available to returns (in a Ubyte) the current internal state of the process.
This property allows to sample at any time the state of the internal thread.
This property can also be used during the debugging phase (allowing in addition to identify the case of blocking in the user thread procedure running).

ThreadState flags:
0 -> disabled (internal thread stopped, waiting for 'ThreadInit')
1 -> available (waiting for 'ThreadStart' or another 'ThreadInit')
2 -> busy (user thread procedure running)
4 -> completing (user thread procedure completed, but waiting for 'ThreadWait')
(possible Ubyte values : 0, 1, 2, 4)

Internally, the Type uses 3 mutexes (by self locking and mutual unlocking) to ensure the ordered sequence of methods called as defined above and wait for the end of the user thread function or for a new user thread function to call.
So, no waiting loop is used in the methods coding but only mutexes locking/unlocking requests, so that the halted thread (on a mutex to be locked) has its execution suspended and does not consume any CPU time until the mutex is unlocked.
The constructor is responsible for creating and locking the 3 mutexes, while the destructor stops the thread (if it exists) then destroys the 3 mutexes.

Note: An advised user can stop the internal thread (linked to instance 't') by using the non-breaking sequence: t.Destructor() : t.Constructor(). Then a t.ThreadInit(...) is necessary to start a new internal thread.

        • Example:
Chronology of the user code:
- A single 'ThreadInitThenMultiStart' instance is created in order to use a single thread.
- The instance is initialized ('ThreadInit') with a first user thread function: 'UserThreadS' (internal thread creation by using the 'ThreadCreate' FreeBASIC keyword).
- A sequence of 9 'ThreadStart...ThreadWait' is requested for this first user thread function, used like a thread subroutine.
- The same instance is reinitialized ('ThreadInit') with a second user thread function: 'UserThreadF' (the previous pending thread will be reused).
- A sequence of 9 'ThreadStart...ThreadWait' is also requested for this second user thread function, used like a thread function.

Full code with the 'ThreadInitThenMultiStart' Type:
Type ThreadInitThenMultiStartData
    Dim As Function(ByVal p As Any Ptr) As String _pThread
    Dim As Any Ptr _p
    Dim As Any Ptr _mutex1
    Dim As Any Ptr _mutex2
    Dim As Any Ptr _mutex3
    Dim As Any Ptr _pt
    Dim As Byte _end
    Dim As String _returnF
    Dim As UByte _state
End Type

Type ThreadInitThenMultiStart
    Public:
        Declare Constructor()
        Declare Sub ThreadInit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
        Declare Sub ThreadStart()
        Declare Sub ThreadStart(ByVal p As Any Ptr)
        Declare Function ThreadWait() As String

        Declare Property ThreadState() As UByte

        Declare Destructor()
    Private:
        Dim As ThreadInitThenMultiStartData Ptr _pdata
        Declare Static Sub _Thread(ByVal p As Any Ptr)
        Declare Constructor(ByRef t As ThreadInitThenMultiStart)
        Declare Operator Let(ByRef t As ThreadInitThenMultiStart)
End Type

Constructor ThreadInitThenMultiStart()
    This._pdata = New ThreadInitThenMultiStartData
    With *This._pdata
        ._mutex1 = MutexCreate()
        MutexLock(._mutex1)
        ._mutex2 = MutexCreate()
        MutexLock(._mutex2)
        ._mutex3 = MutexCreate()
        MutexLock(._mutex3)
    End With
End Constructor

Sub ThreadInitThenMultiStart.ThreadInit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
    With *This._pdata
        ._pThread = pThread
        ._p = p
        If ._pt = 0 Then
            ._pt= ThreadCreate(@ThreadInitThenMultiStart._Thread, This._pdata)
            MutexUnlock(._mutex3)
            ._state = 1
        End If
    End With
End Sub

Sub ThreadInitThenMultiStart.ThreadStart()
    With *This._pdata
        MutexLock(._mutex3)
        MutexUnlock(._mutex1)
    End With
End Sub

Sub ThreadInitThenMultiStart.ThreadStart(ByVal p As Any Ptr)
    With *This._pdata
        MutexLock(._mutex3)
        ._p = p
        MutexUnlock(._mutex1)
    End With
End Sub

Function ThreadInitThenMultiStart.ThreadWait() As String
    With *This._pdata
        MutexLock(._mutex2)
        MutexUnlock(._mutex3)
        ._state = 1
        Return ._returnF
    End With
End Function

Property ThreadInitThenMultiStart.ThreadState() As UByte
    Return This._pdata->_state
End Property

Sub ThreadInitThenMultiStart._Thread(ByVal p As Any Ptr)
    Dim As ThreadInitThenMultiStartData Ptr pdata = p
    With *pdata
        Do
            MutexLock(._mutex1)
            If ._end = 1 Then Exit Sub
            ._state = 2
            ._returnF = ._pThread(._p)
            ._state = 4
            MutexUnlock(._mutex2)
        Loop
    End With
End Sub

Destructor ThreadInitThenMultiStart()
    With *This._pdata
        If ._pt > 0 Then
            ._end = 1
            MutexUnlock(._mutex1)
            ..ThreadWait(._pt)
        End If
        MutexDestroy(._mutex1)
        MutexDestroy(._mutex2)
        MutexDestroy(._mutex3)
    End With
    Delete This._pdata
End Destructor

'---------------------------------------------------

Function UserThreadS(ByVal p As Any Ptr) As String
    Dim As UInteger Ptr pui = p
    Print *pui * *pui
    Return ""
End Function

Function UserThreadF(ByVal p As Any Ptr) As String
    Dim As UInteger Ptr pui = p
    Dim As UInteger c = (*pui) * (*pui)
    Return Str(c)
End Function

Dim As ThreadInitThenMultiStart t

Print "First user function executed like a thread subroutine:"
t.ThreadInit(@UserThreadS)  '' initializes the user thread function (used as subroutine)
For I As UInteger = 1 To 9
    Print I & "^2 = ";
    t.ThreadStart(@I)       '' starts the user thread procedure code body
    t.ThreadWait()          '' waits for the user thread procedure code end
Next I
Print

Print "Second user function executed like a thread function:"
t.ThreadInit(@UserThreadF)  '' initializes the user thread function (used as function)
For I As UInteger = 1 To 9
    Print I & "^2 = ";
    t.ThreadStart(@I)       '' starts the user thread procedure code body
    Print t.ThreadWait()    '' waits for the user thread procedure code end and prints result
Next I
Print

Sleep

Output:
First user function executed like a thread subroutine:
1^2 = 1
2^2 = 4
3^2 = 9
4^2 = 16
5^2 = 25
6^2 = 36
7^2 = 49
8^2 = 64
9^2 = 81

Second user function executed like a thread function:
1^2 = 1
2^2 = 4
3^2 = 9
4^2 = 16
5^2 = 25
6^2 = 36
7^2 = 49
8^2 = 64
9^2 = 81

    • ThreadPooling Type:
        • Principle:
The 'ThreadPooling' Type below operationally provides to user 2 (3 actually) main public methods (plus a constructor and a destructor), and internally uses 11 private data members plus 1 private subroutine (static) member.

The public methods are:
- PoolingSubmit : Enter a user thread procedure in the queue.
- PoolingWait : Wait for full emptying of the queue (with last user procedure executed).

By creating several instances each associated with a thread, we can obtain a kind of thread pooling feature.
The 'ThreadPooling' Type manages a pending thread queue by instance (so, by thread).
It is up to the user to choose an existing instance or to create a new instance with which to run his thread procedure sequence.

On each 'ThreadPooling' Type instance, the submitted user thread procedures are immediately entered in a queue specific to the instance.
These buffered user thread procedures are sequentially as soon as possible executed in the thread dedicated to the instance.

        • Description:
Each user procedure (to be executed in a thread) must be available under the following function signature:
Function userproc (Byval puserdata As Any Ptr) As String
in order to be compatible with the parameters of the 'PoolingSubmit()' method:
Declare Sub PoolingSubmit (Byval pThread As Function (Byval As Any Ptr) As String, Byval p As Any Ptr = 0)
and perform the instance ('t') submission in the queue by:
t.PoolingSubmit(@userproc [, puserdata])

The other method is called on the instance ('t'):
t.PoolingWait() or t.PoolingWait(returndata())

The different methods must be called respecting the order of the following sequence:
PoolingSubmit, [user code,] [PoolingSubmit, [user code,] [PoolingSubmit, [user code, ...]] PoolingWait, [user code,] ...

After any 'PoolingSubmit'...'PoolingWait' sequence, a new user thread procedure sequence can be submitted by calling another 'PoolingSubmit'...'PoolingWait' sequence again on the same instance.
On the other hand, 'PoolingSubmit'...'PoolingWait' sequences can also be chained on different instances already initialized.
If using several instances (so several threads), the ordered sequences on each instance can be interlaced between instances because calling methods on different instances.

The 'PoolingWait(returndata())' method fills in a String array with the user thread function returns (a string variable return allows to also pass a numeric value).
These user data returns from the user functions is accessed through the argument of 'PoolingWait(returndata())' method. It is always safe (because in this case, the user thread functions has been always fully executed).
If the user doesn't want to use the return values of his thread functions (to be used like for subroutines):
- He ends his user thread functions with Return "" for example.
- He calls the 'PoolingWait()' method without parameter.
If the user want to use an existing thread subroutine, he can define a wrapper function (with an "any pointer" as parameter, calling the thread subroutine, and returning a string), and pass this function to 'PoolingSubmit()'.

Warning: The information supplied to the user thread procedure via the passed pointer (by 'PoolingSubmit') should not be changed between 'PoolingSubmit' and 'PoolingWait' due to the time uncertainty on the real call of the user thread procedure in this interval.

        • Under the hood:
In fact, each instance is associated with an internal thread that runs continuously in a loop as soon as the instance is constructed. This internal thread runs the private subroutine (static) member.
It is this private subroutine (static) member that will call the user procedures of the sequence to be executed, like classic function calls. The value returned by each user function is stored in an internal string array to be finally returned to the user through the argument of 'PoolingWait(returndata())'.

So, for each new 'ThreadPooling' instance, an internal thread is started by the constructor, then each user thread procedure is started on each dequeuing of the registered submissions.
As each initialized instance is associated with a running internal thread, using local scopes or dynamic instances allow to stop internal threads that are no longer used.

In the 'ThreadPooling' Type, an additional property 'PoolingState' is available to returns (in a Ubyte) the current internal state of the process.
This property allows to sample at any time the state of the internal thread.
This property can also be used during the debugging phase (allowing in addition to identify the case of blocking in the user thread procedure running).

PoolingState flags:
0 -> User thread procedures sequence execution completed (after 'PoolingWait' acknowledge or new instance creation)
1 -> Beginning of user thread procedure sequence submitted but no still executing (after first 'PoolingSubmit')
2 -> User thread procedure running
4 -> User thread procedure sequence execution pending (for 'PoolingWait' acknowledge or new user thread procedure submission)
8 -> User thread procedure submission queue not empty
(possible Ubyte values : 0, 1, 2, 4, 9, 10)

An overload method 'PoolingWait(values() As String)' is added.
'PoolingWait(values()' As String) fills out a user-supplied dynamic array with the return value sequence from the latest user thread functions (then internally clear these same supplied return data).
The other overload method 'PoolingWait()' (without passed parameter) also clears the internal return values.

'ThreadPooling' Type allows to manage kind of "FIFOs" (First In First Out) via dynamic arrays:
- Arrays are filled in as user submissions (from the main thread).
- Arrays are automatically emptied on the fly by the secondary thread which executes their requests as and when.
- So, the inputs and outputs of the "FIFOs" are therefore asynchronous with an optimized throughput on each side.

With 'ThreadPooling' the execution time of a 'PoolingSubmit' method in the main thread, corresponds only to the time spent to register the user procedure submission.

It is necessary to be able to do (for the 'PoolingSubmit', 'PoolingWait' and 'Destructeur' methods, all in competition with '_Thread' subroutine) atomic mutex unlockings, which is not possible with simple mutexlocks / mutexunlocks.
This therefore requires the use of conditional variables (condwait / condsignal).

The constructor is responsible for creating the 2 conditional variables and the associated mutex, while the destructor stops the thread then destroys the 2 conditional variables and the associated mutex.

        • Example:
Chronology of the user code:
- A single 'ThreadPooling' instance is created in order to use a single thread.
- A first sequence (a) of 3 'PoolingSubmit' is requested for the first three user thread functions, ended by a 'PoolingWait' without parameter.
- A second sequence (b) of 3 'PoolingSubmit' is requested for the last three user thread functions, ended by a 'PoolingWait' with a dynamic string array as argument (so, only the returns from the last three user thread functions will fill out in the dynamic string array).

Full code with the 'ThreadPooling' Type:
#include Once "crt/string.bi"

Type ThreadPoolingData
    Dim As Function(ByVal p As Any Ptr) As String _pThread0
    Dim As Any Ptr _p0
    Dim As Function(ByVal p As Any Ptr) As String _pThread(Any)
    Dim As Any Ptr _p(Any)
    Dim As Any Ptr _mutex
    Dim As Any Ptr _cond1
    Dim As Any Ptr _cond2
    Dim As Any Ptr _pt
    Dim As Byte _end
    Dim As String _returnF(Any)
    Dim As UByte _state
End Type

Type ThreadPooling
    Public:
        Declare Constructor()
        Declare Sub PoolingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
        Declare Sub PoolingWait()
        Declare Sub PoolingWait(values() As String)

        Declare Property PoolingState() As UByte

        Declare Destructor()
    Private:
        Dim As ThreadPoolingData Ptr _pdata
        Declare Static Sub _Thread(ByVal p As Any Ptr)
        Declare Constructor(ByRef t As ThreadPooling)
        Declare Operator Let(ByRef t As ThreadPooling)
End Type

Constructor ThreadPooling()
    This._pdata = New ThreadPoolingData
    With *This._pdata
        ReDim ._pThread(0)
        ReDim ._p(0)
        ReDim ._returnF(0)
        ._mutex = MutexCreate()
        ._cond1 = CondCreate()
        ._cond2 = CondCreate()
        ._pt= ThreadCreate(@ThreadPooling._Thread, This._pdata)
    End With
End Constructor

Sub ThreadPooling.PoolingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
    With *This._pdata
        MutexLock(._mutex)
        ReDim Preserve ._pThread(UBound(._pThread) + 1)
        ._pThread(UBound(._pThread)) = pThread
        ReDim Preserve ._p(UBound(._p) + 1)
        ._p(UBound(._p)) = p
        CondSignal(._cond2)
        ._state = 1
        MutexUnlock(._mutex)
    End With
End Sub

Sub ThreadPooling.PoolingWait()
    With *This._pdata
        MutexLock(._mutex)
        While (._state And 11) > 0
            CondWait(._Cond1, ._mutex)
        Wend
        ReDim ._returnF(0)
        ._state = 0
        MutexUnlock(._mutex)
    End With
End Sub

Sub ThreadPooling.PoolingWait(values() As String)
    With *This._pdata
        MutexLock(._mutex)
        While (._state And 11) > 0
            CondWait(._Cond1, ._mutex)
        Wend
        If UBound(._returnF) > 0 Then
            ReDim values(1 To UBound(._returnF))
            For I As Integer = 1 To UBound(._returnF)
                values(I) = ._returnF(I)
            Next I
            ReDim ._returnF(0)
        Else
            Erase values
        End If
        ._state = 0
        MutexUnlock(._mutex)
    End With
End Sub

Property ThreadPooling.PoolingState() As UByte
    With *This._pdata
        If UBound(._p) > 0 Then
            Return 8 + ._state
        Else
            Return ._state
        End If
    End With
End Property

Sub ThreadPooling._Thread(ByVal p As Any Ptr)
    Dim As ThreadPoolingData Ptr pdata = p
    With *pdata
        Do
            MutexLock(._mutex)
            If UBound(._pThread) = 0 Then
                ._state = 4
                CondSignal(._cond1)
                While UBound(._pThread) = 0
                    If ._end = 1 Then Exit Sub
                    CondWait(._cond2, ._mutex)
                Wend
            End If
            ._pThread0 = ._pThread(1)
            ._p0 = ._p(1)
            If UBound(._pThread) > 1 Then
                memmove(@._pThread(1), @._pThread(2), (UBound(._pThread) - 1) * SizeOf(._pThread))
                memmove(@._p(1), @._p(2), (UBound(._p) - 1) * SizeOf(._p))
            End If
            ReDim Preserve ._pThread(UBound(._pThread) - 1)
            ReDim Preserve ._p(UBound(._p) - 1)
            MutexUnlock(._mutex)
            ReDim Preserve ._ReturnF(UBound(._returnF) + 1)
            ._state = 2
            ._returnF(UBound(._returnF)) = ._pThread0(._p0)
        Loop
    End With
End Sub

Destructor ThreadPooling()
    With *This._pdata
        MutexLock(._mutex)
        ._end = 1
        CondSignal(._cond2)
        MutexUnlock(._mutex)
        ..ThreadWait(._pt)
        MutexDestroy(._mutex)
        CondDestroy(._cond1)
        CondDestroy(._cond2)
    End With
    Delete This._pdata
End Destructor

'---------------------------------------------------

Sub Prnt (ByRef s As String, ByVal p As Any Ptr)
    Dim As String Ptr ps = p
    If ps > 0 Then Print *ps;
    For I As Integer = 1 To 10
        Print s;
        Sleep 100, 1
    Next I
End Sub

Function UserCode1 (ByVal p As Any Ptr) As String
    Prnt("1", p)
    Return "UserCode #1"
End Function

Function UserCode2 (ByVal p As Any Ptr) As String
    Prnt("2", p)
    Return "UserCode #2"
End Function

Function UserCode3 (ByVal p As Any Ptr) As String
    Prnt("3", p)
    Return "UserCode #3"
End Function

Function UserCode4 (ByVal p As Any Ptr) As String
    Prnt("4", p)
    Return "UserCode #4"
End Function

Function UserCode5 (ByVal p As Any Ptr) As String
    Prnt("5", p)
    Return "UserCode #5"
End Function

Function UserCode6 (ByVal p As Any Ptr) As String
    Prnt("6", p)
    Return "UserCode #6"
End Function

Dim As String sa = "  Sequence #a: "
Dim As String sb = "  Sequence #b: "
Dim As String s()

Dim As ThreadPooling t

t.PoolingSubmit(@UserCode1, @sa)
t.PoolingSubmit(@UserCode2)
t.PoolingSubmit(@UserCode3)
Print " Sequence #a of 3 user thread functions fully submitted "
t.PoolingWait()
Print
Print " Sequence #a completed"
Print

t.PoolingSubmit(@UserCode4, @sb)
t.PoolingSubmit(@UserCode5)
t.PoolingSubmit(@UserCode6)
Print " Sequence #b of 3 user thread functions fully submitted "
t.PoolingWait(s())
Print
Print " Sequence #b completed"
Print

Print " List of returned values from sequence #b only"
For I As Integer = LBound(s) To UBound(s)
    Print "  " & I & ": " & s(I)
Next I
Print

Sleep

Output example
 Sequence #a of 3 user thread functions fully submitted
  Sequence #a: 111111111122222222223333333333
 Sequence #a completed

 Sequence #b of 3 user thread functions fully submitted
  Sequence #b: 444444444455555555556666666666
 Sequence #b completed

 List of returned values from sequence #b only
  1: UserCode #4
  2: UserCode #5
  3: UserCode #6
Note: If the first user thread procedure of each sequence starts very quickly, the acknowledgement message of each sequence of 3 submissions may appear inserted after the beginning of the text printed by the first user procedure of the sequence.
That is not the case here.

    • ThreadDispatching Type, over-structure of ThreadPooling Type, dispatching user thread procedures over a given max number of secondary threads:
        • Principle:
The maximum number of secondary threads that can be used is fixed when constructing the 'ThreadDispatching' instance (1 secondary thread by default), and also the minimum number of initialized secondary threads (0 secondary thread by default).
'ThreadDispatching' manages an internal dynamic array of pointers to 'ThreadPooling' instances.

If a secondary thread is available (already existing instance of 'ThreadPooling' pending), it is used to submit the user thread procedure.
Otherwise, a new secondary thread is created (new instance of 'ThreadPooling' created) by respecting the number of secondary threads allowed.
As long as all potential secondary threads are already in use, each new user thread procedure is distributed evenly over them.

        • Description:
Methods:
- Constructor : Construct a 'ThreadDispatching' instance and set the maximum number of usable secondary threads (1 by default) and set the minimum number of initialized secondary thread (0 by default).
- DispatchingSubmit : Enter a user thread procedure in the queue of the "best" secondary thread among the usable ones.
- DispatchingWait : Wait for the complete emptying of the queues of all secondary threads used (with all last user procedures executed).
- DispatchingThread : Return the number of internal threads really started.
- Destructor : Stop and complete the secondary threads used.

In the 'ThreadDispatching' Type, an additional sub 'DispatchingState(state() As Ubyte)' is available to returns (in a Ubyte array) the current state of each internal thread started.
This sub allows to sample at any time the state of the internal threads started.
This sub can also be used during the debugging phase (allowing in addition to identify the case of blocking in the user thread procedure running).

DispatchingState flags (a Ubyte for each internal thread started):
0 -> User thread procedures sequence execution completed (after 'DispatchingWait' acknowledge or new instance creation)
1 -> Beginning of user thread procedure sequence submitted but no still executing (after first 'DispatchingSubmit')
2 -> User thread procedure running
4 -> User thread procedure sequence execution pending (for 'DispatchingWait' acknowledge or new user thread procedure submission)
8 -> User thread procedure submission queue not empty
(possible Ubyte values : 0, 1, 2, 4, 9, 10)

The 'DispatchingWait(returndata())' method fills in a String array with the user thread function returns (a string variable return allows to also pass a numeric value). In the array, the user thread function returns are grouped by internal threads really used, in the order they were started.
These user data returns from the user functions is accessed through the argument of 'DispatchingWait(returndata())' method. It is always safe (because in this case, the user thread functions has been always fully executed).
If the user doesn't want to use the return values of his thread functions (to be used like for subroutines):
- He ends his user thread functions with Return "" for example.
- He calls the 'DispatchingWait()' method without parameter.
If the user want to use an existing thread subroutine, he can define a wrapper function (with an "any pointer" as parameter, calling the thread subroutine, and returning a string), and pass this function to 'DispatchingSubmit()'.

        • Example:
Example of use of 'ThreadDispatching' (whatever the allowed number of secondary threads, the submission sequence syntax is always the same):
#include Once "crt/string.bi"

Type ThreadPoolingData
    Dim As Function(ByVal p As Any Ptr) As String _pThread0
    Dim As Any Ptr _p0
    Dim As Function(ByVal p As Any Ptr) As String _pThread(Any)
    Dim As Any Ptr _p(Any)
    Dim As Any Ptr _mutex
    Dim As Any Ptr _cond1
    Dim As Any Ptr _cond2
    Dim As Any Ptr _pt
    Dim As Byte _end
    Dim As String _returnF(Any)
    Dim As UByte _state
End Type

Type ThreadPooling
    Public:
        Declare Constructor()
        Declare Sub PoolingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
        Declare Sub PoolingWait()
        Declare Sub PoolingWait(values() As String)

        Declare Property PoolingState() As UByte

        Declare Destructor()
    Private:
        Dim As ThreadPoolingData Ptr _pdata
        Declare Static Sub _Thread(ByVal p As Any Ptr)
        Declare Constructor(ByRef t As ThreadPooling)
        Declare Operator Let(ByRef t As ThreadPooling)
End Type

Constructor ThreadPooling()
    This._pdata = New ThreadPoolingData
    With *This._pdata
        ReDim ._pThread(0)
        ReDim ._p(0)
        ReDim ._returnF(0)
        ._mutex = MutexCreate()
        ._cond1 = CondCreate()
        ._cond2 = CondCreate()
        ._pt= ThreadCreate(@ThreadPooling._Thread, This._pdata)
    End With
End Constructor

Sub ThreadPooling.PoolingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
    With *This._pdata
        MutexLock(._mutex)
        ReDim Preserve ._pThread(UBound(._pThread) + 1)
        ._pThread(UBound(._pThread)) = pThread
        ReDim Preserve ._p(UBound(._p) + 1)
        ._p(UBound(._p)) = p
        CondSignal(._cond2)
        ._state = 1
        MutexUnlock(._mutex)
    End With
End Sub

Sub ThreadPooling.PoolingWait()
    With *This._pdata
        MutexLock(._mutex)
        While (._state And 11) > 0
            CondWait(._Cond1, ._mutex)
        Wend
        ReDim ._returnF(0)
        ._state = 0
        MutexUnlock(._mutex)
    End With
End Sub

Sub ThreadPooling.PoolingWait(values() As String)
    With *This._pdata
        MutexLock(._mutex)
        While (._state And 11) > 0
            CondWait(._Cond1, ._mutex)
        Wend
        If UBound(._returnF) > 0 Then
            ReDim values(1 To UBound(._returnF))
            For I As Integer = 1 To UBound(._returnF)
                values(I) = ._returnF(I)
            Next I
            ReDim ._returnF(0)
        Else
            Erase values
        End If
        ._state = 0
        MutexUnlock(._mutex)
    End With
End Sub

Property ThreadPooling.PoolingState() As UByte
    With *This._pdata
        If UBound(._p) > 0 Then
            Return 8 + ._state
        Else
            Return ._state
        End If
    End With
End Property

Sub ThreadPooling._Thread(ByVal p As Any Ptr)
    Dim As ThreadPoolingData Ptr pdata = p
    With *pdata
        Do
            MutexLock(._mutex)
            If UBound(._pThread) = 0 Then
                ._state = 4
                CondSignal(._cond1)
                While UBound(._pThread) = 0
                    If ._end = 1 Then Exit Sub
                    CondWait(._cond2, ._mutex)
                Wend
            End If
            ._pThread0 = ._pThread(1)
            ._p0 = ._p(1)
            If UBound(._pThread) > 1 Then
                memmove(@._pThread(1), @._pThread(2), (UBound(._pThread) - 1) * SizeOf(._pThread))
                memmove(@._p(1), @._p(2), (UBound(._p) - 1) * SizeOf(._p))
            End If
            ReDim Preserve ._pThread(UBound(._pThread) - 1)
            ReDim Preserve ._p(UBound(._p) - 1)
            MutexUnlock(._mutex)
            ReDim Preserve ._ReturnF(UBound(._returnF) + 1)
            ._state = 2
            ._returnF(UBound(._returnF)) = ._pThread0(._p0)
        Loop
    End With
End Sub

Destructor ThreadPooling()
    With *This._pdata
        MutexLock(._mutex)
        ._end = 1
        CondSignal(._cond2)
        MutexUnlock(._mutex)
        ..ThreadWait(._pt)
        MutexDestroy(._mutex)
        CondDestroy(._cond1)
        CondDestroy(._cond2)
    End With
    Delete This._pdata
End Destructor

'---------------------------------------------------

Type ThreadDispatching
    Public:
        Declare Constructor(ByVal nbMaxSecondaryThread As Integer = 1, ByVal nbMinSecondaryThread As Integer = 0)
        Declare Sub DispatchingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
        Declare Sub DispatchingWait()
        Declare Sub DispatchingWait(values() As String)

        Declare Property DispatchingThread() As Integer
        Declare Sub DispatchingState(state() As UByte)

        Declare Destructor()
    Private:
        Dim As Integer _nbmst
        Dim As Integer _dstnb
        Dim As ThreadPooling Ptr _tp(Any)
        Declare Constructor(ByRef t As ThreadDispatching)
        Declare Operator Let(ByRef t As ThreadDispatching)
End Type

Constructor ThreadDispatching(ByVal nbMaxSecondaryThread As Integer = 1, ByVal nbMinSecondaryThread As Integer = 0)
    This._nbmst = nbMaxSecondaryThread
    If nbMinSecondaryThread > nbMaxSecondaryThread Then
        nbMinSecondaryThread = nbMaxSecondaryThread
    End If
    If nbMinSecondaryThread > 0 Then
        ReDim This._tp(nbMinSecondaryThread - 1)
        For I As Integer = 0 To nbMinSecondaryThread - 1
            This._tp(I) = New ThreadPooling
        Next I
    End If
End Constructor

Sub ThreadDispatching.DispatchingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
    For I As Integer = 0 To UBound(This._tp)
        If (This._tp(I)->PoolingState And 11) = 0 Then
            This._tp(I)->PoolingSubmit(pThread, p)
            Exit Sub
        End If
    Next I
    If UBound(This._tp) < This._nbmst - 1 Then
        ReDim Preserve This._tp(UBound(This._tp) + 1)
        This._tp(UBound(This._tp)) = New ThreadPooling
        This._tp(UBound(This._tp))->PoolingSubmit(pThread, p)
    ElseIf UBound(This._tp) >= 0 Then
        This._tp(This._dstnb)->PoolingSubmit(pThread, p)
        This._dstnb = (This._dstnb + 1) Mod This._nbmst
    End If
End Sub

Sub ThreadDispatching.DispatchingWait()
    For I As Integer = 0 To UBound(This._tp)
        This._tp(I)->PoolingWait()
    Next I
End Sub

Sub ThreadDispatching.DispatchingWait(values() As String)
    Dim As String s()
    For I As Integer = 0 To UBound(This._tp)
        This._tp(I)->PoolingWait(s())
        If UBound(s) >= 1 Then
            If UBound(values) = -1 Then
                ReDim Preserve values(1 To UBound(values) + UBound(s) + 1)
            Else
                ReDim Preserve values(1 To UBound(values) + UBound(s))
            End If
            For I As Integer = 1 To UBound(s)
                values(UBound(values) - UBound(s) + I) = s(I)
            Next I
        End If
    Next I
End Sub

Property ThreadDispatching.DispatchingThread() As Integer
    Return UBound(This._tp) + 1
End Property

Sub ThreadDispatching.DispatchingState(state() As UByte)
    If UBound(This._tp) >= 0 Then
        ReDim state(1 To UBound(This._tp) + 1)
        For I As Integer = 0 To UBound(This._tp)
            state(I + 1) = This._tp(I)->PoolingState
        Next I
    End If
End Sub

Destructor ThreadDispatching()
    For I As Integer = 0 To UBound(This._tp)
        Delete This._tp(I)
    Next I
End Destructor

'---------------------------------------------------

Sub Prnt (ByRef s As String, ByVal p As Any Ptr)
    Dim As String Ptr ps = p
    If ps > 0 Then Print *ps;
    For I As Integer = 1 To 10
        Print s;
        Sleep 100, 1
    Next I
End Sub

Function UserCode1 (ByVal p As Any Ptr) As String
    Prnt("1", p)
    Return "UserCode #1"
End Function

Function UserCode2 (ByVal p As Any Ptr) As String
    Prnt("2", p)
    Return "UserCode #2"
End Function

Function UserCode3 (ByVal p As Any Ptr) As String
    Prnt("3", p)
    Return "UserCode #3"
End Function

Function UserCode4 (ByVal p As Any Ptr) As String
    Prnt("4", p)
    Return "UserCode #4"
End Function

Function UserCode5 (ByVal p As Any Ptr) As String
    Prnt("5", p)
    Return "UserCode #5"
End Function

Function UserCode6 (ByVal p As Any Ptr) As String
    Prnt("6", p)
    Return "UserCode #6"
End Function

Sub SubmitSequence(ByRef t As ThreadDispatching, ByVal ps As String Ptr)
    t.DispatchingSubmit(@UserCode1, ps)
    t.DispatchingSubmit(@UserCode2)
    t.DispatchingSubmit(@UserCode3)
    t.DispatchingSubmit(@UserCode4)
    t.DispatchingSubmit(@UserCode5)
    t.DispatchingSubmit(@UserCode6)
End Sub

Dim As String sa = "  Sequence #a: "
Dim As String sb = "  Sequence #b: "
Dim As String sc = "  Sequence #c: "
Dim As String sd = "  Sequence #d: "
Dim As String se = "  Sequence #e: "
Dim As String sf = "  Sequence #f: "
Dim As String s()

Dim As ThreadDispatching t1, t2 = 2, t3 = 3, t4 = 4, t5 = 5, t6 = 6

Print " Sequence #a of 6 user thread functions dispatched over 1 secondary thread:"
SubmitSequence(t1, @sa)
t1.DispatchingWait()
Print
Print

Print " Sequence #b of 6 user thread functions dispatched over 2 secondary threads:"
SubmitSequence(t2, @sb)
t2.DispatchingWait()
Print
Print

Print " Sequence #c of 6 user thread functions dispatched over 3 secondary threads:"
SubmitSequence(t3, @sc)
t3.DispatchingWait()
Print
Print

Print " Sequence #d of 6 user thread functions dispatched over 4 secondary threads:"
SubmitSequence(t4, @sd)
t4.DispatchingWait()
Print
Print

Print " Sequence #e of 6 user thread functions dispatched over 5 secondary threads:"
SubmitSequence(t5, @se)
t5.DispatchingWait()
Print
Print

Print " Sequence #f of 6 user thread functions dispatched over 6 secondary threads:"
SubmitSequence(t6, @sf)
t6.DispatchingWait(s())
Print

Print "  List of returned values from sequence #f:"
For I As Integer = LBound(s) To UBound(s)
    Print "   " & I & ": " & s(I)
Next I

Sleep

Output example:
 Sequence #a of 6 user thread functions dispatched over 1 secondary thread:
  Sequence #a: 111111111122222222223333333333444444444455555555556666666666

 Sequence #b of 6 user thread functions dispatched over 2 secondary threads:
  Sequence #b: 122112121212122112213434344343344343344356566565565656565665

 Sequence #c of 6 user thread functions dispatched over 3 secondary threads:
  Sequence #c: 123123312321213132321231213321465654546465546546456654654564

 Sequence #d of 6 user thread functions dispatched over 4 secondary threads:
  Sequence #d: 134243211234432114322341413241233124413256655656566556655656

 Sequence #e of 6 user thread functions dispatched over 5 secondary threads:
  Sequence #e: 134255243141235325415143215234342511524343521251346666666666

 Sequence #f of 6 user thread functions dispatched over 6 secondary threads:
  Sequence #f: 534126216354456132241365563142421365316524245613361245365421
  List of returned values from sequence #f:
   1: UserCode #1
   2: UserCode #2
   3: UserCode #3
   4: UserCode #4
   5: UserCode #5
   6: UserCode #6

    • Execution time gain checking with ThreadInitThenMultiStart, ThreadPooling, and ThreadDispatching Types:
        • Execution time gain checking with different multi-threading configurations:
A user task is defined:
- Display 64 characters (2*32) on the screen, each separated by an identical time achieved by a [For ... Next] loop (no Sleep keyword so as not to free up CPU resources).
- For 'ThreadInitThenMultiStart' and 'ThreadPooling': Depending on the number of threads chosen 1/2/4/8/16/32, this same user task is split in 1/2/4/8/16/32 sub-tasks, each being executed on a thread.
- For 'ThreadDispatching': 32 sub-tasks are always used and the distribution of these sub-tasks over the available threads (max = 1/2/4/8/16/32) is automatic.

Full code with the 'ThreadInitThenMultiStart', 'ThreadPooling', and 'ThreadDispatching' Types:
Type ThreadInitThenMultiStartData
    Dim As Function(ByVal p As Any Ptr) As String _pThread
    Dim As Any Ptr _p
    Dim As Any Ptr _mutex1
    Dim As Any Ptr _mutex2
    Dim As Any Ptr _mutex3
    Dim As Any Ptr _pt
    Dim As Byte _end
    Dim As String _returnF
    Dim As UByte _state
End Type

Type ThreadInitThenMultiStart
    Public:
        Declare Constructor()
        Declare Sub ThreadInit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
        Declare Sub ThreadStart()
        Declare Sub ThreadStart(ByVal p As Any Ptr)
        Declare Function ThreadWait() As String

        Declare Property ThreadState() As UByte

        Declare Destructor()
    Private:
        Dim As ThreadInitThenMultiStartData Ptr _pdata
        Declare Static Sub _Thread(ByVal p As Any Ptr)
        Declare Constructor(ByRef t As ThreadInitThenMultiStart)
        Declare Operator Let(ByRef t As ThreadInitThenMultiStart)
End Type

Constructor ThreadInitThenMultiStart()
    This._pdata = New ThreadInitThenMultiStartData
    With *This._pdata
        ._mutex1 = MutexCreate()
        MutexLock(._mutex1)
        ._mutex2 = MutexCreate()
        MutexLock(._mutex2)
        ._mutex3 = MutexCreate()
        MutexLock(._mutex3)
    End With
End Constructor

Sub ThreadInitThenMultiStart.ThreadInit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
    With *This._pdata
        ._pThread = pThread
        ._p = p
        If ._pt = 0 Then
            ._pt= ThreadCreate(@ThreadInitThenMultiStart._Thread, This._pdata)
            MutexUnlock(._mutex3)
            ._state = 1
        End If
    End With
End Sub

Sub ThreadInitThenMultiStart.ThreadStart()
    With *This._pdata
        MutexLock(._mutex3)
        MutexUnlock(._mutex1)
    End With
End Sub

Sub ThreadInitThenMultiStart.ThreadStart(ByVal p As Any Ptr)
    With *This._pdata
        MutexLock(._mutex3)
        ._p = p
        MutexUnlock(._mutex1)
    End With
End Sub

Function ThreadInitThenMultiStart.ThreadWait() As String
    With *This._pdata
        MutexLock(._mutex2)
        MutexUnlock(._mutex3)
        ._state = 1
        Return ._returnF
    End With
End Function

Property ThreadInitThenMultiStart.ThreadState() As UByte
    Return This._pdata->_state
End Property

Sub ThreadInitThenMultiStart._Thread(ByVal p As Any Ptr)
    Dim As ThreadInitThenMultiStartData Ptr pdata = p
    With *pdata
        Do
            MutexLock(._mutex1)
            If ._end = 1 Then Exit Sub
            ._state = 2
            ._returnF = ._pThread(._p)
            ._state = 4
            MutexUnlock(._mutex2)
        Loop
    End With
End Sub

Destructor ThreadInitThenMultiStart()
    With *This._pdata
        If ._pt > 0 Then
            ._end = 1
            MutexUnlock(._mutex1)
            ..ThreadWait(._pt)
        End If
        MutexDestroy(._mutex1)
        MutexDestroy(._mutex2)
        MutexDestroy(._mutex3)
    End With
    Delete This._pdata
End Destructor

'---------------------------------------------------

#include Once "crt/string.bi"

Type ThreadPoolingData
    Dim As Function(ByVal p As Any Ptr) As String _pThread0
    Dim As Any Ptr _p0
    Dim As Function(ByVal p As Any Ptr) As String _pThread(Any)
    Dim As Any Ptr _p(Any)
    Dim As Any Ptr _mutex
    Dim As Any Ptr _cond1
    Dim As Any Ptr _cond2
    Dim As Any Ptr _pt
    Dim As Byte _end
    Dim As String _returnF(Any)
    Dim As UByte _state
End Type

Type ThreadPooling
    Public:
        Declare Constructor()
        Declare Sub PoolingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
        Declare Sub PoolingWait()
        Declare Sub PoolingWait(values() As String)

        Declare Property PoolingState() As UByte

        Declare Destructor()
    Private:
        Dim As ThreadPoolingData Ptr _pdata
        Declare Static Sub _Thread(ByVal p As Any Ptr)
        Declare Constructor(ByRef t As ThreadPooling)
        Declare Operator Let(ByRef t As ThreadPooling)
End Type

Constructor ThreadPooling()
    This._pdata = New ThreadPoolingData
    With *This._pdata
        ReDim ._pThread(0)
        ReDim ._p(0)
        ReDim ._returnF(0)
        ._mutex = MutexCreate()
        ._cond1 = CondCreate()
        ._cond2 = CondCreate()
        ._pt= ThreadCreate(@ThreadPooling._Thread, This._pdata)
    End With
End Constructor

Sub ThreadPooling.PoolingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
    With *This._pdata
        MutexLock(._mutex)
        ReDim Preserve ._pThread(UBound(._pThread) + 1)
        ._pThread(UBound(._pThread)) = pThread
        ReDim Preserve ._p(UBound(._p) + 1)
        ._p(UBound(._p)) = p
        CondSignal(._cond2)
        ._state = 1
        MutexUnlock(._mutex)
    End With
End Sub

Sub ThreadPooling.PoolingWait()
    With *This._pdata
        MutexLock(._mutex)
        While (._state And 11) > 0
            CondWait(._Cond1, ._mutex)
        Wend
        ReDim ._returnF(0)
        ._state = 0
        MutexUnlock(._mutex)
    End With
End Sub

Sub ThreadPooling.PoolingWait(values() As String)
    With *This._pdata
        MutexLock(._mutex)
        While (._state And 11) > 0
            CondWait(._Cond1, ._mutex)
        Wend
        If UBound(._returnF) > 0 Then
            ReDim values(1 To UBound(._returnF))
            For I As Integer = 1 To UBound(._returnF)
                values(I) = ._returnF(I)
            Next I
            ReDim ._returnF(0)
        Else
            Erase values
        End If
        ._state = 0
        MutexUnlock(._mutex)
    End With
End Sub

Property ThreadPooling.PoolingState() As UByte
    With *This._pdata
        If UBound(._p) > 0 Then
            Return 8 + ._state
        Else
            Return ._state
        End If
    End With
End Property

Sub ThreadPooling._Thread(ByVal p As Any Ptr)
    Dim As ThreadPoolingData Ptr pdata = p
    With *pdata
        Do
            MutexLock(._mutex)
            If UBound(._pThread) = 0 Then
                ._state = 4
                CondSignal(._cond1)
                While UBound(._pThread) = 0
                    If ._end = 1 Then Exit Sub
                    CondWait(._cond2, ._mutex)
                Wend
            End If
            ._pThread0 = ._pThread(1)
            ._p0 = ._p(1)
            If UBound(._pThread) > 1 Then
                memmove(@._pThread(1), @._pThread(2), (UBound(._pThread) - 1) * SizeOf(._pThread))
                memmove(@._p(1), @._p(2), (UBound(._p) - 1) * SizeOf(._p))
            End If
            ReDim Preserve ._pThread(UBound(._pThread) - 1)
            ReDim Preserve ._p(UBound(._p) - 1)
            MutexUnlock(._mutex)
            ReDim Preserve ._ReturnF(UBound(._returnF) + 1)
            ._state = 2
            ._returnF(UBound(._returnF)) = ._pThread0(._p0)
        Loop
    End With
End Sub

Destructor ThreadPooling()
    With *This._pdata
        MutexLock(._mutex)
        ._end = 1
        CondSignal(._cond2)
        MutexUnlock(._mutex)
        ..ThreadWait(._pt)
        MutexDestroy(._mutex)
        CondDestroy(._cond1)
        CondDestroy(._cond2)
    End With
    Delete This._pdata
End Destructor

'---------------------------------------------------

Type ThreadDispatching
    Public:
        Declare Constructor(ByVal nbMaxSecondaryThread As Integer = 1, ByVal nbMinSecondaryThread As Integer = 0)
        Declare Sub DispatchingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
        Declare Sub DispatchingWait()
        Declare Sub DispatchingWait(values() As String)

        Declare Property DispatchingThread() As Integer
        Declare Sub DispatchingState(state() As UByte)

        Declare Destructor()
    Private:
        Dim As Integer _nbmst
        Dim As Integer _dstnb
        Dim As ThreadPooling Ptr _tp(Any)
        Declare Constructor(ByRef t As ThreadDispatching)
        Declare Operator Let(ByRef t As ThreadDispatching)
End Type

Constructor ThreadDispatching(ByVal nbMaxSecondaryThread As Integer = 1, ByVal nbMinSecondaryThread As Integer = 0)
    This._nbmst = nbMaxSecondaryThread
    If nbMinSecondaryThread > nbMaxSecondaryThread Then
        nbMinSecondaryThread = nbMaxSecondaryThread
    End If
    If nbMinSecondaryThread > 0 Then
        ReDim This._tp(nbMinSecondaryThread - 1)
        For I As Integer = 0 To nbMinSecondaryThread - 1
            This._tp(I) = New ThreadPooling
        Next I
    End If
End Constructor

Sub ThreadDispatching.DispatchingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
    For I As Integer = 0 To UBound(This._tp)
        If (This._tp(I)->PoolingState And 11) = 0 Then
            This._tp(I)->PoolingSubmit(pThread, p)
            Exit Sub
        End If
    Next I
    If UBound(This._tp) < This._nbmst - 1 Then
        ReDim Preserve This._tp(UBound(This._tp) + 1)
        This._tp(UBound(This._tp)) = New ThreadPooling
        This._tp(UBound(This._tp))->PoolingSubmit(pThread, p)
    ElseIf UBound(This._tp) >= 0 Then
        This._tp(This._dstnb)->PoolingSubmit(pThread, p)
        This._dstnb = (This._dstnb + 1) Mod This._nbmst
    End If
End Sub

Sub ThreadDispatching.DispatchingWait()
    For I As Integer = 0 To UBound(This._tp)
        This._tp(I)->PoolingWait()
    Next I
End Sub

Sub ThreadDispatching.DispatchingWait(values() As String)
    Dim As String s()
    For I As Integer = 0 To UBound(This._tp)
        This._tp(I)->PoolingWait(s())
        If UBound(s) >= 1 Then
            If UBound(values) = -1 Then
                ReDim Preserve values(1 To UBound(values) + UBound(s) + 1)
            Else
                ReDim Preserve values(1 To UBound(values) + UBound(s))
            End If
            For I As Integer = 1 To UBound(s)
                values(UBound(values) - UBound(s) + I) = s(I)
            Next I
        End If
    Next I
End Sub

Property ThreadDispatching.DispatchingThread() As Integer
    Return UBound(This._tp) + 1
End Property

Sub ThreadDispatching.DispatchingState(state() As UByte)
    If UBound(This._tp) >= 0 Then
        ReDim state(1 To UBound(This._tp) + 1)
        For I As Integer = 0 To UBound(This._tp)
            state(I + 1) = This._tp(I)->PoolingState
        Next I
    End If
End Sub

Destructor ThreadDispatching()
    For I As Integer = 0 To UBound(This._tp)
        Delete This._tp(I)
    Next I
End Destructor

'---------------------------------------------------

Dim Shared As Double array(1 To 800000)  '' only used by the [For...Next] waiting loop in UserCode()

Function UserCode (ByVal p As Any Ptr) As String
    Dim As String Ptr ps = p
    For I As Integer = 1 To 2
        Print *ps;
        For J As Integer = 1 To 800000
            array(J) = Tan(J) * Atn(J) * Exp(J) * Log(J)  '' [For...Next] waiting loop not freeing any CPU resource
        Next J
    Next I
    Return ""
End Function

Dim As String s(0 To 31)
For I As Integer = 0 To 15
    s(I) = Str(Hex(I))
Next I
For I As Integer = 16 To 31
    s(I) = Chr(55 + I)
Next I

'---------------------------------------------------

#macro ThreadInitThenMultiStartSequence(nbThread)
Scope
    ReDim As ThreadInitThenMultiStart ts(nbThread - 1)
    Print "   ";
    Dim As Double t = Timer
    For I As Integer = 0 To 32 - nbThread Step nbThread
        For J As Integer = 0 To nbThread - 1
            ts(J).ThreadInit(@UserCode, @s(I + J))
            ts(J).ThreadStart()
        Next J
        For J As Integer = 0 To nbThread - 1
            ts(J).ThreadWait()
        Next J
    Next I
    t = Timer - t
    Print Using " : ####.## s"; t
End Scope
#endmacro

#macro ThreadPoolingSequence(nbThread)
Scope
    ReDim As ThreadPooling tp(nbThread - 1)
    Print "   ";
    Dim As Double t = Timer
    For I As Integer = 0 To 32 - nbThread Step nbThread
        For J As Integer = 0 To nbThread - 1
            tp(J).PoolingSubmit(@UserCode, @s(I + J))
        Next J
    Next I
    For I As Integer = 0 To nbThread - 1
        tp(I).PoolingWait()
    Next I
    t = Timer - t
    Print Using " : ####.## s"; t
End Scope
#endmacro

#macro ThreadDispatchingSequence(nbThreadmax)
Scope
    Dim As ThreadDispatching td##nbThreadmax = nbThreadmax
    Print "   ";
    Dim As Double t = Timer
    For I As Integer = 0 To 31
        td##nbThreadmax.DispatchingSubmit(@UserCode, @s(I))
    Next I
    td##nbThreadmax.DispatchingWait()
    t = Timer - t
    Print Using " : ####.## s"; t
End Scope
#endmacro

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 1 secondary thread:"
ThreadInitThenMultiStartSequence(1)

Print "'ThreadPooling' with 1 secondary thread:"
ThreadPoolingSequence(1)

Print "'ThreadDispatching' with 1 secondary thread max:"
ThreadDispatchingSequence(1)
Print

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 2 secondary threads:"
ThreadInitThenMultiStartSequence(2)

Print "'ThreadPooling' with 2 secondary threads:"
ThreadPoolingSequence(2)

Print "'ThreadDispatching' with 2 secondary threads max:"
ThreadDispatchingSequence(2)
Print

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 4 secondary threads:"
ThreadInitThenMultiStartSequence(4)

Print "'ThreadPooling' with 4 secondary threads:"
ThreadPoolingSequence(4)

Print "'ThreadDispatching' with 4 secondary threads max:"
ThreadDispatchingSequence(4)
Print

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 8 secondary threads:"
ThreadInitThenMultiStartSequence(8)

Print "'ThreadPooling' with 8 secondary threads:"
ThreadPoolingSequence(8)

Print "'ThreadDispatching' with 8 secondary threads max:"
ThreadDispatchingSequence(8)
Print

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 16 secondary threads:"
ThreadInitThenMultiStartSequence(16)

Print "'ThreadPooling' with 16 secondary threads:"
ThreadPoolingSequence(16)

Print "'ThreadDispatching' with 16 secondary threads max:"
ThreadDispatchingSequence(16)
Print

'---------------------------------------------------

Print "'ThreadInitThenMultiStart' with 32 secondary threads:"
ThreadInitThenMultiStartSequence(32)

Print "'ThreadPooling' with 32 secondary threads:"
ThreadPoolingSequence(32)

Print "'ThreadDispatching' with 32 secondary threads max:"
ThreadDispatchingSequence(32)
Print

Sleep

Output example:
'ThreadInitThenMultiStart' with 1 secondary thread:
   00112233445566778899AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVV :    5.40 s
'ThreadPooling' with 1 secondary thread:
   00112233445566778899AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVV :    5.39 s
'ThreadDispatching' with 1 secondary thread max:
   00112233445566778899AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVV :    5.42 s

'ThreadInitThenMultiStart' with 2 secondary threads:
   01012323454567768998ABABCDCDEFEFGHGHIJJIKLKLMNNMOPOPQRRQSTSTUVVU :    2.88 s
'ThreadPooling' with 2 secondary threads:
   01012323455476769898BAABCDCDFEFEHGHGJIJILKLKNMNMPOPORQRQTSTSVUVU :    2.83 s
'ThreadDispatching' with 2 secondary threads max:
   01103232545476769898BABADCDCFEFHEGHJGJILILKNKNMPMPORORQTQTSVSVUU :    2.96 s

'ThreadInitThenMultiStart' with 4 secondary threads:
   012312304576645789ABA98BCEDFCEFDGIHJGIJHLKMNNLKMOQPRPOQRSTVUTSUV :    1.72 s
'ThreadPooling' with 4 secondary threads:
   012313204576457689AB89ABCDFECFDEGJHIGJHINKLMNKMLROPQROQPVSUTVSUT :    1.71 s
'ThreadDispatching' with 4 secondary threads max:
   012320316475674A5B89AB98EFDCEFDCIJHGIJHMGNLKMNLKQRPORQPOVTUSVTUS :    1.76 s

'ThreadInitThenMultiStart' with 8 secondary threads:
   01324567706415328B9ACDEFBE8D9CAFGHIJKNMLGNLMKHIJOQRPSVUTVOQRUTPS :    1.19 s
'ThreadPooling' with 8 secondary threads:
   01234567032415678BAEC9DF8BEA9CDFGJIHLMKNGJIHLMKNORQTPSUVORTPQSUV :    1.05 s
'ThreadDispatching' with 8 secondary threads max:
   0123456776415032FE9CABD8FE9ACB8DNMIHKJLGMNIHKGLJVUQPSTORVUQPSOTR :    1.09 s

'ThreadInitThenMultiStart' with 16 secondary threads:
   013A4567892BCDEF1A2B7903C8465DEFGHIJKNMLPOQRSTVULJGKNMIRHTSOPUQV :    1.14 s
'ThreadPooling' with 16 secondary threads:
   0124356789ABCDEF512A04D639E7B8CKJNGILHFQTPMOURSGJNQHLTKIVORPUMSV :    1.10 s
'ThreadDispatching' with 16 secondary threads max:
   0123456B897ACDEFFEDA798031C56B42TPOGJQNUKVMSILRHJQGTUOPKLMINSVHR :    1.11 s

'ThreadInitThenMultiStart' with 32 secondary threads:
   01243675AB89ECFDGHIJKLMNOPQ146RSTGVBA3IEFJTSNM5082CDHU7KLO9RQPVU :    1.06 s
'ThreadPooling' with 32 secondary threads:
   0I32456789ABCDEFHG1JKLMNOPRSQTVUN7260534FKBEGIHCD98A1OJLQTSRUPVM :    1.07 s
'ThreadDispatching' with 32 secondary threads max:
   012345A7896BCDFGE4C89D76B5A0321EGHIJKLMNOQRUVPSTFLKHMIJNOTSPVUQR :    1.07 s
Note: From a certain number of threads used, the gain in execution time becomes more or less constant (even slightly decreasing), which corresponds roughly to the number of threads the used CPU really has (8 in the case above).

'ThreadInitThenMultiStart' and 'ThreadPooling':
From fbc 1.10.0, and in order to have a single structure (for 'ThreadInitThenMultiStart' or 'ThreadPooling'), the additional Type of data ('ThreadInitThenMultiStartData' or 'ThreadPoolingData') can be nested as is in its main Type, just above the declaration of its pointer.

'ThreadDispatching' versus 'ThreadPooling':
- The 'ThreadDispatching' Type allows to best and automatically distribute user tasks over a given number of secondary threads.
- But if the user wants complete control of the distribution per secondary thread, he can instead use a 'ThreadPooling' array with the desired number of secondary threads as the size.

'ThreadInitThenMultiStart' / 'ThreadPooling' / 'ThreadDispatching', and CPU time with pending secondary threads (waiting for user tasks):
Once a secondary thread is created and initialized (by creating an instance of 'ThreadInitThenMultiStart'+'ThreadInit' or 'ThreadPooling' or 'ThreadDispatching)', it no longer consumes CPU time as long as it is pending (waiting for a user task):
- This is because the thread code of 'ThreadInitThenMultiStart._Thread()' is in the 'MutexLock(pThis->_mutex1)' state and it will only wake after a 'MutexUnlock(This._mutex1)' triggered by a user task submission from 'ThreadInitThenMultiStart.ThreadStart()'.
- This is because the thread code of 'ThreadPooling._Thread()' is in the 'CondWait(pThis->_cond2, pThis->_mutex)' state and it will only wake after a 'CondSignal(This._cond2)' triggered by a user task submission from 'ThreadPooling.PoolingSubmit()'.

So the only interest of the 2nd optional parameter of the 'ThreadDispatching' constructor which allows to set the minimum number of secondary threads (0 by default) is only to start these secondary threads at the earliest at the time of the instance construction, in order to have greater responsiveness at the time of the first user task submissions.

    • Time wasted when running a user task either by procedure calling method, by elementary threading method, or by various thread pooling methods:
Creating a new thread is a costly act in terms of resources, both from a processor (CPU) and memory point of view.
Also, if a program requires the execution of many tasks, the creation and deletion of a thread for each of them would strongly penalize the performance of the application.
It would therefore be interesting to be able to share the creation of threads so that a thread that has finished executing a task is available for the execution of a future task.

The objective of thread pooling (ThreadInitThenMultiStart, ThreadPooling, ThreadDispatching methods) is to pool threads in order to avoid the untimely creation or deletion of threads, and thus allow their reuse.

Test code to evaluate the different times wasted depending on the feature used:
Type ThreadInitThenMultiStart
    Public:
        Declare Constructor()
        Declare Sub ThreadInit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
        Declare Sub ThreadStart()
        Declare Sub ThreadStart(ByVal p As Any Ptr)
        Declare Function ThreadWait() As String

        Declare Property ThreadState() As UByte

        Declare Destructor()
    Private:
        Dim As Function(ByVal p As Any Ptr) As String _pThread
        Dim As Any Ptr _p
        Dim As Any Ptr _mutex1
        Dim As Any Ptr _mutex2
        Dim As Any Ptr _mutex3
        Dim As Any Ptr _pt
        Dim As Byte _end
        Dim As String _returnF
        Dim As UByte _state
        Declare Static Sub _Thread(ByVal p As Any Ptr)
End Type

Constructor ThreadInitThenMultiStart()
    This._mutex1 = MutexCreate()
    MutexLock(This._mutex1)
    This._mutex2 = MutexCreate()
    MutexLock(This._mutex2)
    This._mutex3 = MutexCreate()
    MutexLock(This._mutex3)
End Constructor

Sub ThreadInitThenMultiStart.ThreadInit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
    This._pThread = pThread
    This._p = p
    If This._pt = 0 Then
        This._pt= ThreadCreate(@ThreadInitThenMultiStart._Thread, @This)
        MutexUnlock(This._mutex3)
        This._state = 1
    End If
End Sub

Sub ThreadInitThenMultiStart.ThreadStart()
    MutexLock(This._mutex3)
    MutexUnlock(This._mutex1)
End Sub

Sub ThreadInitThenMultiStart.ThreadStart(ByVal p As Any Ptr)
    MutexLock(This._mutex3)
    This._p = p
    MutexUnlock(This._mutex1)
End Sub

Function ThreadInitThenMultiStart.ThreadWait() As String
    MutexLock(This._mutex2)
    MutexUnlock(This._mutex3)
    This._state = 1
    Return This._returnF
End Function

Property ThreadInitThenMultiStart.ThreadState() As UByte
    Return This._state
End Property

Sub ThreadInitThenMultiStart._Thread(ByVal p As Any Ptr)
    Dim As ThreadInitThenMultiStart Ptr pThis = p
    Do
        MutexLock(pThis->_mutex1)
        If pThis->_end = 1 Then Exit Sub
        pThis->_state = 2
        pThis->_returnF = pThis->_pThread(pThis->_p)
        pThis->_state = 4
        MutexUnlock(pThis->_mutex2)
    Loop
End Sub

Destructor ThreadInitThenMultiStart()
    If This._pt > 0 Then
        This._end = 1
        MutexUnlock(This._mutex1)
        .ThreadWait(This._pt)
    End If
    MutexDestroy(This._mutex1)
    MutexDestroy(This._mutex2)
    MutexDestroy(This._mutex3)
End Destructor

'---------------------------------------------------

#include Once "crt/string.bi"
Type ThreadPooling
    Public:
        Declare Constructor()
        Declare Sub PoolingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
        Declare Sub PoolingWait()
        Declare Sub PoolingWait(values() As String)

        Declare Property PoolingState() As UByte

        Declare Destructor()
    Private:
        Dim As Function(ByVal p As Any Ptr) As String _pThread0
        Dim As Any Ptr _p0
        Dim As Function(ByVal p As Any Ptr) As String _pThread(Any)
        Dim As Any Ptr _p(Any)
        Dim As Any Ptr _mutex
        Dim As Any Ptr _cond1
        Dim As Any Ptr _cond2
        Dim As Any Ptr _pt
        Dim As Byte _end
        Dim As String _returnF(Any)
        Dim As UByte _state
        Declare Static Sub _Thread(ByVal p As Any Ptr)
End Type

Constructor ThreadPooling()
    ReDim This._pThread(0)
    ReDim This._p(0)
    ReDim This._returnF(0)
    This._mutex = MutexCreate()
    This._cond1 = CondCreate()
    This._cond2 = CondCreate()
    This._pt= ThreadCreate(@ThreadPooling._Thread, @This)
End Constructor

Sub ThreadPooling.PoolingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
    MutexLock(This._mutex)
    ReDim Preserve This._pThread(UBound(This._pThread) + 1)
    This._pThread(UBound(This._pThread)) = pThread
    ReDim Preserve This._p(UBound(This._p) + 1)
    This._p(UBound(This._p)) = p
    CondSignal(This._cond2)
    This._state = 1
    MutexUnlock(This._mutex)
End Sub

Sub ThreadPooling.PoolingWait()
    MutexLock(This._mutex)
    While (This._state And 11) > 0
        CondWait(This._Cond1, This._mutex)
    Wend
    ReDim This._returnF(0)
    This._state = 0
    MutexUnlock(This._mutex)
End Sub

Sub ThreadPooling.PoolingWait(values() As String)
    MutexLock(This._mutex)
    While (This._state And 11) > 0
        CondWait(This._Cond1, This._mutex)
    Wend
    If UBound(This._returnF) > 0 Then
        ReDim values(1 To UBound(This._returnF))
        For I As Integer = 1 To UBound(This._returnF)
            values(I) = This._returnF(I)
        Next I
        ReDim This._returnF(0)
    Else
        Erase values
    End If
    This._state = 0
    MutexUnlock(This._mutex)
End Sub

Property ThreadPooling.PoolingState() As UByte
    If UBound(This._p) > 0 Then
        Return 8 + This._state
    Else
        Return This._state
    End If
End Property

Sub ThreadPooling._Thread(ByVal p As Any Ptr)
    Dim As ThreadPooling Ptr pThis = p
    Do
        MutexLock(pThis->_mutex)
        If UBound(pThis->_pThread) = 0 Then
            pThis->_state = 4
            CondSignal(pThis->_cond1)
            While UBound(pThis->_pThread) = 0
                CondWait(pThis->_cond2, pThis->_mutex)
                If pThis->_end = 1 Then Exit Sub
            Wend
        End If
        pThis->_pThread0 = pThis->_pThread(1)
        pThis->_p0 = pThis->_p(1)
        If UBound(pThis->_pThread) > 1 Then
            memmove(@pThis->_pThread(1), @pThis->_pThread(2), (UBound(pThis->_pThread) - 1) * SizeOf(pThis->_pThread))
            memmove(@pThis->_p(1), @pThis->_p(2), (UBound(pThis->_p) - 1) * SizeOf(pThis->_p))
        End If
        'ReDim Preserve pThis->_pThread(UBound(pThis->_pThread) - 1)  '' bug in current fbc version 1.20
        With *pThis                                                   '' workaround for the bug
            ReDim Preserve ._pThread(UBound(pThis->_pThread) - 1)     '' workaround for the bug
        End With                                                      '' workaround for the bug
        ReDim Preserve pThis->_p(UBound(pThis->_p) - 1)
        MutexUnlock(pThis->_mutex)
        ReDim Preserve pThis->_ReturnF(UBound(pThis->_returnF) + 1)
        pThis->_state = 2
        pThis->_returnF(UBound(pThis->_returnF)) = pThis->_pThread0(pThis->_p0)
    Loop
End Sub

Destructor ThreadPooling()
    MutexLock(This._mutex)
    This._end = 1
    CondSignal(This._cond2)
    MutexUnlock(This._mutex)
    .ThreadWait(This._pt)
    MutexDestroy(This._mutex)
    CondDestroy(This._cond1)
    CondDestroy(This._cond2)
End Destructor

'---------------------------------------------------

Type ThreadDispatching
    Public:
        Declare Constructor(ByVal nbMaxSecondaryThread As Integer = 1, ByVal nbMinSecondaryThread As Integer = 0)
        Declare Sub DispatchingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
        Declare Sub DispatchingWait()
        Declare Sub DispatchingWait(values() As String)

        Declare Property DispatchingThread() As Integer
        Declare Sub DispatchingState(state() As UByte)

        Declare Destructor()
    Private:
        Dim As Integer _nbmst
        Dim As Integer _dstnb
        Dim As ThreadPooling Ptr _tp(Any)
End Type

Constructor ThreadDispatching(ByVal nbMaxSecondaryThread As Integer = 1, ByVal nbMinSecondaryThread As Integer = 0)
    This._nbmst = nbMaxSecondaryThread
    If nbMinSecondaryThread > nbMaxSecondaryThread Then
        nbMinSecondaryThread = nbMaxSecondaryThread
    End If
    If nbMinSecondaryThread > 0 Then
        ReDim This._tp(nbMinSecondaryThread - 1)
        For I As Integer = 0 To nbMinSecondaryThread - 1
            This._tp(I) = New ThreadPooling
        Next I
    End If
End Constructor

Sub ThreadDispatching.DispatchingSubmit(ByVal pThread As Function(ByVal As Any Ptr) As String, ByVal p As Any Ptr = 0)
    For I As Integer = 0 To UBound(This._tp)
        If (This._tp(I)->PoolingState And 11) = 0 Then
            This._tp(I)->PoolingSubmit(pThread, p)
            Exit Sub
        End If
    Next I
    If UBound(This._tp) < This._nbmst - 1 Then
        ReDim Preserve This._tp(UBound(This._tp) + 1)
        This._tp(UBound(This._tp)) = New ThreadPooling
        This._tp(UBound(This._tp))->PoolingSubmit(pThread, p)
    ElseIf UBound(This._tp) >= 0 Then
        This._tp(This._dstnb)->PoolingSubmit(pThread, p)
        This._dstnb = (This._dstnb + 1) Mod This._nbmst
    End If
End Sub

Sub ThreadDispatching.DispatchingWait()
    For I As Integer = 0 To UBound(This._tp)
        This._tp(I)->PoolingWait()
    Next I
End Sub

Sub ThreadDispatching.DispatchingWait(values() As String)
    Dim As String s()
    For I As Integer = 0 To UBound(This._tp)
        This._tp(I)->PoolingWait(s())
        If UBound(s) >= 1 Then
            If UBound(values) = -1 Then
                ReDim Preserve values(1 To UBound(values) + UBound(s) + 1)
            Else
                ReDim Preserve values(1 To UBound(values) + UBound(s))
            End If
            For I As Integer = 1 To UBound(s)
                values(UBound(values) - UBound(s) + I) = s(I)
            Next I
        End If
    Next I
End Sub

Property ThreadDispatching.DispatchingThread() As Integer
    Return UBound(This._tp) + 1
End Property

Sub ThreadDispatching.DispatchingState(state() As UByte)
    If UBound(This._tp) >= 0 Then
        ReDim state(1 To UBound(This._tp) + 1)
        For I As Integer = 0 To UBound(This._tp)
            state(I + 1) = This._tp(I)->PoolingState
        Next I
    End If
End Sub

Destructor ThreadDispatching()
    For I As Integer = 0 To UBound(This._tp)
        Delete This._tp(I)
    Next I
End Destructor

'---------------------------------------------------

Sub s(ByVal p As Any Ptr)
    '' user task
End Sub

Function f(ByVal p As Any Ptr) As String
    '' user task
    Return ""
End Function

'---------------------------------------------------
'Time wasted when running a user task either by procedure calling or by various threading methods
Print "Mean time wasted when running a user task :"
Print "   either by procedure calling method,"
Print "   or by various threading methods."
Print

Scope
    Dim As Double t = Timer
    For I As Integer = 1 To 1000000
        s(0)
    Next I
    t = Timer - t
    Print Using "      - Using procedure calling method        : ###.###### ms"; t / 1000
    Print
End Scope

Scope
    Dim As Any Ptr P
    Dim As Double t = Timer
    For I As Integer = 1 To 1000
        p = ThreadCreate(@s)
        ThreadWait(p)
    Next I
    t = Timer - t
    Print Using "      - Using elementary threading method     : ###.###### ms"; t
    Print
End Scope

Scope
    Dim As ThreadInitThenMultiStart ts
    Dim As Double t = Timer
    ts.ThreadInit(@f)
    For I As Integer = 1 To 10000
        ts.ThreadStart()
        ts.ThreadWait()
    Next I
    t = Timer - t
    Print Using "      - Using ThreadInitThenMultiStart method : ###.###### ms"; t / 10
End Scope

Scope
    Dim As ThreadPooling tp
    Dim As Double t = Timer
    For I As Integer = 1 To 10000
        tp.PoolingSubmit(@f)
    Next I
    tp.PoolingWait()
    t = Timer - t
    Print Using "      - Using ThreadPooling method            : ###.###### ms"; t / 10
End Scope

Scope
    Dim As ThreadDispatching td
    Dim As Double t = Timer
    For I As Integer = 1 To 10000
        td.DispatchingSubmit(@f)
    Next I
    td.DispatchingWait()
    t = Timer - t
    Print Using "      - Using ThreadDispatching method        : ###.###### ms"; t / 10
End Scope

Print
Sleep

Output:
Mean time wasted when running a user task :
   either by procedure calling method,
   or by various threading methods.

      - Using procedure calling method        :   0.000033 ms

      - Using elementary threading method     :   0.146337 ms

      - Using ThreadInitThenMultiStart method :   0.007382 ms
      - Using ThreadPooling method            :   0.006873 ms
      - Using ThreadDispatching method        :   0.007066 ms
The above results with my PC show that a thread pooling method allows to gain about 140 µs by user task compared to a elementary threading method, but it remains about 7 µs to compare to 0.03 µs for a simple calling method.

See also

  • Multi-Threading Overview
  • Threads
  • Mutual Exclusion
  • Conditional Variables
  • Critical Sections
  • Critical Sections FAQ


Community Tutorials
Tutorials submitted by the FreeBASIC community:
Getting Started
  • Getting Started with FreeBASIC by SJ Zero
  • Using libraries in FreeBASIC by SJ Zero
  • Using the Mouse in FreeBASIC by MystikShadows
  • Get Information into your program by TekRat
  • Using Dynamic Arrays in FreeBASIC by SephKnows
  • Beginners Guide to Types as Objects (Part 1) by YetiFoot
  • Beginners Guide to Types as Objects (Part 2) by YetiFoot
  • Introduction to Variable Scope by rdc
  • Introduction to Arrays by rdc
  • Introduction to the Type Def by rdc
  • New To Programming? by The FB Community
  • Compiling a BIG QB program by Antoni

Game Programming
  • How to Program a Game: Lesson 1 by Lachie Dazdarian
  • Managing A High Score Table by Lachie Dazdarian

Flow Control Statements
  • The IF Statement by rdc
  • The Select Case Statement by rdc

Pre Processor
  • Conditional Compilation And You by AetherFox

Memory Management
  • Introduction to Pointers by rdc
  • Pointers, Data Types and Memory by rdc
  • The Pointer Data Type by rdc
  • Using Linked Lists by Parker
  • Dynamic Arrays in Types by rdc

 Intermediate Techniques
  • Introduction to Function Overloading in FreeBASIC by :stylin:

Mathematics
  • Different ways angles are measured by RandyKeeling
  • A Brief Introduction To Trigonometry by RandyKeeling

Windows API
  • Introduction to Message-Based Programming by rdc

Libraries
  • Interfacing with C by UtenNavn
  • SDL_NET by Paragon
  • Using FreeBASIC Built Libraries with GCC by Jeff Marshall

Object Oriented Programming
  • Introduction to the Extended Type by rdc
  • Simulating Polymorphism by rdc
  • OOP in non-OOP languages by KevinWhitefoot
  • Const Qualifiers and You by notthecheatr

FBgfx
  • Creating and Understanding Your FBgfx Img and Font Buffer by The FB Community


Community Code Library
FreeBASIC Code, Games, and Libraries. Written in FreeBASIC, by FreeBASIC Community Members.

Code Editors & IDEs
FBEdit, an IDE for FB by KetilO (Win32)
Tiko Editor, an IDE for FB by Paul Squires (Win32,Win64)
VISG GUI Builder (WIN) by mrhx
List of FreeBASIC IDEs/Editors on the forum

Graphics Code
Demos
Animated Clouds by Zamaster
Island Generation by rdc
Plasma Generation by Zamaster
Tree Generation by Zamaster

Graphics Functions and Primitives
AntiAliased Bezier Curves by Acetoline
Antialiased Circles by Acetoline
Ellipse Renderer by Pritchard
Catmull-rom Splines by relsoft Bezier vs Catmull-rom by relsoft
Accurate Image Scaler by KristopherWindsor
Spline Curve by Zamaster
Rotozoom by Dr_D

Colors and Palettes
24bit to 16bit color width by Eternal_Pain
HSV Color Space by Antoni

Formats
fbpng library by yetifoot
FBImage static Win/Lin 32/64-bit by by D.J.Peters

Animation
ASCII Animation Example by Pritchard
Chain-Like Animation Tutorial by Lachie Dazdarian

Sound Code
Mic Input using FMod by mambazo
Using the PC Speaker by several
Wave synthesizer by Zamaster

Math Code
FBMath by jdebord
A* Pathfinding by dumbledore
Fraction Library by Zamaster
Big Number Wrapper by Yetifoot
BCD arithmetics by srvaldez
10Byte extended float by srvaldez, included in FB examples
CRC Calculation by Fragmeister

Physics simulation
Atom smash simulation by coderjeff
2d rigid body library by coderjeff
Irrlicht wrapper + Newton Intergrated by SiskinEDGE

Text/Parser Code
Expression Parser by yetifoot
Turing Machine by Zamaster
Roman Numeral to Integer Conversion by stylin
Unicode console calender, by zippy and voodooattack
FB source to highlighted HTML by Kristopher Windsor
Portable help (not .chm) viewer by coderjeff
Lisp interpreter by coderjeff

Cryptography
MARS encryption by Zamaster
AES Encryption/Decryption by Zamaster
DES/LUCIFER Encryption/Decryption by Zamaster
MD5 Calculator by DOS386
Tiger Hash by Mindless

FreeBASIC Games
FreeBASIC Games Directory by Lachie Dazdarian

Featured Games:
Kingdoms by Piptol
Lynn's Legacy by cha0s and Josiah Tobin
Relativity by Lithium
Star Cage by Lachie Dazdarian
100 Line Tetris by Deleter
Any PNG or JPEG as a Jigsaw Puzzle by Mysoft

GUI Code
In Game GUI by coderJeff
Zine GUI by VonGodric
WX GUI example by ciw1973
KwikGUI (WIN/LIN/DOS) by Vincent DeCampo
FB_GUI by BasicScience

Networking- Web Code
FB Web Server (Win) by parakeet
FB Server side scripting (uses the server above) by fishhf
ChiSock portable sockets library by cha0s

I/O Code
Text Input by Pritchard & sir_mud
ConLib Console library with PCopy by cha0s
Lock Mouse to Grid Positions by Pritchard

Serial Port
Drive a Parallax servo controller by phishguy
Modbus device finder by Antoni
Serial port terminal program by Antoni

OS Specific Code
Windows
ServiceFB (Win) by zerospeed
FBWinPrint 1.0 by vdecampo
In memory dialogs by MichaelW
Talking program usin Win Voice API, by coder guy
Using GfxLib in Windows API by MichaelW
Print a bitmap file by MichaelW
ShellExecute wrapper by RayBritton
FBWiki to chm format converter by coderjeff
FB ODBC library by KaraK
Get a file from an URL by Sisophon

Linux
Printing on Linux by coderJeff
Using GfxLib on Gtk by caseih

DOS
Detect system codepage by DrV
Calling an Interrupt requiring a pointer by DrV
Access BTRIEVE files by mjs
"GetDiskFreeSpaceEx" Check for disk total/free space on FAT32 by DOS386
DPMI host detection version/capabilities by DrV

Data structures and special-purpose UDTs
Boolean Type by Imortis
Safe FBstring Type by stylin
FreeBASIC Memory Leak Detector by DrV & Others
Auto-deallocating 'Smart' Pointers by stylin
UDTs for Properties by Pritchard

Miscellaneous Code
FreeBASIC Extended Library
FB CAD by owen
FBstd C++ Lib Port (W.I.P.) by stylin
Testly by zerospeed
Portable way to add a resource to a program by voodooattack
CPU Identification by MichaelW
Cpu Cycle counter for benchmarking of code by MichaelW
Use of the FBGfx built-in LZW routines by Lillo
Using FB dll's in RapidQ programs by JohnK

Community Websites/Links
This is a place to post worthy projects/code snippets for FreeBASIC, in their relative categories. To add a page, link to either its wiki page, website, or thread on the FreeBASIC Forums. State the project name and who it's by. Sections may be broken down into their own separate pages some time in the future. Note: Due to FB being in Beta stage of development, earlier coded projects may need to be reconfigured or recompiled to work on later versions of FreeBASIC.


External Library Bindings

FreeBASIC includes many .bi files that let BASIC programs call libraries written in C or other languages. These pages describe the bindings and the examples that use them.

A binding is not the same thing as a complete package. The include file can describe the API, but the target system still needs the matching library, import library, shared object, DLL, framework, package, or runtime asset. This matters more now because the compiler is being used on a much wider set of targets than the old desktop-only cases.

How to read this page

Usually practical | The binding is commonly usable when the normal operating system package is installed, or when this build already ships the needed pieces.
Optional external library | The examples are valid, but the library is not part of every FreeBASIC install. Install the development package for your operating system before expecting the example to link.
Legacy or special-case | The binding exists, but the upstream library is old, commercial, uncommon, or not packaged cleanly on every target.
Platform API | The binding is only meaningful on the platform that owns the API. A Windows API example is not expected to work on JavaScript, Android, Xbox, DOS, or Unix targets.

Platform reality from the example sweeps

The examples directory contains notes from current example sweeps. Those notes are a useful reality check for this table.

Windows win32 | The shipped example set links a large number of examples. Some old or commercial libraries still need extra runtime DLLs or import libraries before the examples are truly runnable.
Windows win64 | Many restored examples are now practical, including several database, image, helper, and math examples. The remaining rough edges are mostly older libraries such as Allegro 4, CGUI, GRX, wx-c, BASS/BASSMOD, FMOD 3, PDFlib, Newton, and old SpiderMonkey.
Cygwin | The core compiler, runtime, networking, procedure, and thread examples are healthy. Most external-library failures are plain missing-library failures, not compiler failures.
JavaScript, Android, and Xbox | These targets are useful compile targets for portable BASIC code, but most desktop external libraries and OS APIs are not expected to exist there. Treat those examples as target-specific unless the target package says otherwise.

Sound and media

sfxlib command set | Built-in BASIC-oriented sound commands. Start here for SOUND, PLAY, SFX, MUSIC, MIDI, waveform, envelope, channel, panning, and capture commands.
OpenAL | 3D audio API. Optional on most systems.
BASS and BASSMOD | Third-party audio libraries. Useful when you provide the vendor runtime, but not something every FreeBASIC package can ship.
FMOD | Old FMOD binding. Treat this as legacy unless you already have the matching SDK/runtime.
Flite | Speech synthesis library.
mpg123, Ogg, Vorbis, and sndfile | Audio decoding and file-format libraries.
PortAudio | Cross-platform audio I/O library.
VLC | Media playback through libVLC.
old sfx binding page | Historical page kept for compatibility. New code should normally read the sfxlib command pages instead.

Graphics, image, and display libraries

Allegro, SDL, TinyPTC, DUGL, and GRX | Game, framebuffer, and graphics libraries. Several are useful for old examples, but some are legacy on modern Windows packages.
OpenGL, GLUT, freeglut, and GLFW | OpenGL-related bindings. Good desktop targets, but not generally expected on JavaScript, Android, Xbox, or DOS package installs.
Cairo, caca, DISLIN, and PDFLib | Drawing, terminal graphics, plotting, and document-generation libraries.
DevIL, FreeImage, FreeType2, GD, GIFLIB, jpeglib, jpgalleg, and libpng | Image loading, image writing, font, and raster helpers. These are often practical when the OS package manager provides matching development libraries.

GUI and platform user-interface libraries

CGUI, GTK+, IUP, JAPI, and wx-c | GUI bindings. Some are useful, but they are among the bindings most likely to need extra runtime libraries.
Curses | Terminal UI support.
X11 and Windows API | Platform APIs. These are not portable abstractions.

Data, database, and web libraries

GDBM, MySQL, PostgreSQL, and SQLite | Database bindings. SQLite is usually the easiest dependency to satisfy; client/server databases need the matching client development package.
CGI utility, FastCGI, curl, and ZeroMQ | Web, HTTP, and messaging support. These are practical where the OS provides the libraries, but they are not automatically available on every cross target.
Expat, libxml, libxslt, and mxml | XML parsers and transformation libraries.
json-c | JSON support through json-c.

Development, language, and helper libraries

ASpell, BFD, CUnit, GDSL, gettext, MediaInfo, and UUID | Helper and developer libraries. These are useful, but mostly depend on optional OS packages.
libffi, libjit, JNI, Lua, and SpiderMonkey | Language runtimes and foreign-function interfaces. libjit and old SpiderMonkey are special-case bindings on current Windows package builds.

Math, physics, compression, regex, and system APIs

big_int, GMP, and GSL | Big-number and scientific/math libraries.
Chipmunk, Newton, and ODE | Physics libraries. Expect package availability to vary by platform.
cryptlib | Cryptography library binding. Treat licensing and redistributability as part of the dependency decision.
bzip2, LZMA, LZO, QuickLZ, Zip, and zlib | Compression libraries.
PCRE and TRE | Regular-expression libraries.
C runtime, DOS API, DispHelper, and GLib | System/runtime bindings. These should be read with the target platform in mind.

Practical advice

If an external-library example fails to build, first check whether the target system has the library development package installed. Missing -l errors usually mean the compiler is fine and the library is absent. Runtime DLL errors on Windows usually mean the program linked, but the needed DLL was not next to the executable or on PATH.

For new portable programs, prefer the built-in FreeBASIC runtime, gfxlib, sfxlib, sockets, and standard file/string functionality before reaching for a third-party binding. Use external libraries when they give you a real feature you need, and document that dependency in your own project.


CGUI
Library for making GUIs in a simple way.

Website: http://cgui.sourceforge.net/index.html, http://www.allegro.cc/resource/Libraries/GUI/CGUI
Platforms supported: Win32, Linux
Headers to include: cgui.bi
Header version: 2.0.4
Example Usage: yes, in examples/GUI/CGUI/


Curses
Standardized console user interface library

Website: http://pdcurses.sourceforge.net/ and http://www.gnu.org/software/ncurses/
Platforms supported: DOS, Win32, Linux
Headers to include: curses.bi
Header versions: pdcurses 3.4, ncurses 5.9
Note: On Win32 systems pdcurses is used, on Linux it uses the standard ncurses library.
Examples: yes, in examples/console/curses/

Example

#include Once "curses.bi"

initscr()
cbreak()
noecho()
start_color()

'' The default pair 0 will have the console's default colors

'' Set pair 1 to be white/blue
init_pair(1, COLOR_WHITE, COLOR_BLUE)

'' Select pair 1, so from now on output will be white text on blue background
attrset(COLOR_PAIR(1))

printw(!"Hello, world!\n")

'' Reset to pair 0
attrset(COLOR_PAIR(0))

'' Sleep
printw(!"Waiting for keypress...\n")
getch()

endwin()


GTK+, The GIMP ToolKit
Cross-platform Graphical User Interface library

Website: http://www.gtk.org
Platforms supported: Win32, Linux
Headers to include: gtk/gtk.bi
Example Usage: yes, in examples/GUI/GTK+/
Header version: 2.24.27, 3.14.10

By default, gtk/gtk.bi will use the GTK+ 2 API.
Define __USE_GTK3__ before including gtk/gtk.bi to use GTK+ 3.

Example

#include Once "gtk/gtk.bi"

Dim Shared As GtkWidget Ptr win

Private Sub on_clicked cdecl(ByVal button As GtkButton Ptr, ByVal userdata As gpointer)
    Static As Integer clickcount = 0
    clickcount += 1
    gtk_window_set_title(GTK_WINDOW(win), "clicked " & clickcount & " times")
End Sub

gtk_init(NULL, NULL)

win = gtk_window_new(GTK_WINDOW_TOPLEVEL)
gtk_window_set_title(GTK_WINDOW(win), "A small GTK+ example")
gtk_window_set_default_size(GTK_WINDOW(win), 300, 200)
gtk_container_set_border_width(GTK_CONTAINER(win), 20)

g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(@gtk_main_quit), NULL)

Dim As GtkWidget Ptr button = gtk_button_new_with_label("Click me!")
gtk_container_add(GTK_CONTAINER(win), button)

g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(@on_clicked), NULL)

gtk_widget_show_all(win)

gtk_main()


IUP
Portable toolkit for building graphical user interfaces.

Website: http://www.tecgraf.puc-rio.br/iup/
Platforms supported: Win32, Linux
Headers to include: IUP/iup.bi
Header version: 3.13
Example Usage: yes, in examples/GUI/IUP/


wx-c, C Interface for WxWidgets
Cross-platform Graphical User Interface library

Website: http://wxnet.sourceforge.net/
Platforms supported: Win32, Linux
Headers to include: wx-c/wx.bi
Header version: 0.9.0.2
Example Usage: yes, in examples/GUI/wx-c/


Windows API
Standard API for all Windows Systems, used for example for creating Windows GUIs (forms and controls), socket programming, inter-process communication, and so much more.

Website: http://msdn.microsoft.com/en-us/library/ee663300.aspx
Platforms supported: Win32, Linux (using WINE)
Headers to include: windows.bi
Examples: yes, in examples/win32/


X11
The X Windowing System is widely used on Linux as the layer that coordinates drawing to the screen by providing windows. It also delivers events such as mouse and keyboard input from the kernel to applications. It is designed to run as a server that can be contacted through a specific protocol. The client's side of the protocol is implemented by libraries such as the old Xlib or the more modern XCB. Applications can use these to create windows and draw to them. However, typically most developers will choose to use a GUI library such as GTK+ (which has an X11 backend) instead.

Website: http://www.x.org/, http://xcb.freedesktop.org/
Platforms supported: Linux
Headers to include: X11/*.bi


Allegro
Game programming library

Website: http://alleg.sourceforge.net/index.html
Platforms supported: Win32, Linux, DOS
Headers to include: allegro.bi (Allegro 4) or allegro5/allegro.bi (Allegro 5)
Header version: 4.4.2, 5.0.11
Example Usage: yes, in examples/graphics/Allegro/


DUGL
Graphics and game programming library

Website: http://dugl.50webs.com
Platforms supported: DOS
Headers to include: DUGL.BI (not yet included with FB, see link below)
Example of usage: see link below
Note: use DUGL 1.13 or newer (see link below), older version have a bug and do crash when used with FB

See forum thread: http://www.freebasic.net/forum/viewtopic.php?t=11046


caca
A colour ASCII art library.

Website: http://libcaca.zoy.org/
Platforms supported: Win32, Linux, DOS
Headers to include: caca.bi (new API) or caca0.bi (old API)
Header version: libcaca-0.99.beta19
Example Usage: yes, in examples/console/caca/


Cairo
2D graphics library with support for multiple output devices. It can be used to draw on multiple different surfaces, such as the FB graphics window, in-memory pixel buffers, a GTK+ widget or a Win32 window or device context.

Website: http://www.cairographics.org
Platforms supported: Win32, Linux
Headers to include: cairo/cairo.bi
Header version: 1.14.2
Examples: yes, in examples/graphics/cairo/

Example

'' Example showing cairo being used to draw into the FB graphics window
#include Once "cairo/cairo.bi"

Const SCREEN_W = 400
Const SCREEN_H = 300

ScreenRes SCREEN_W, SCREEN_H, 32

'' Create a cairo drawing context, using the FB screen as surface.
Var surface = cairo_image_surface_create_for_data(ScreenPtr(), CAIRO_FORMAT_ARGB32, SCREEN_W, SCREEN_H, SCREEN_W * SizeOf(ULong))

Var c = cairo_create(surface)

ScreenLock()

'' Draw the entire context white.
cairo_set_source_rgba(c, 1, 1, 1, 1)
cairo_paint(c)

'' Draw a red line
cairo_set_line_width(c, 1)
cairo_set_source_rgba(c, 1, 0, 0, 1)
cairo_move_to(c, 0, 0)
cairo_line_to(c, SCREEN_W - 1, SCREEN_H - 1)
cairo_stroke(c)

ScreenUnlock()

Sleep

'' Clean up the cairo context
cairo_destroy(c)

last reviewed: MrSwiss, 2018-05-05 -- changed: SizeOf(Integer) to SizeOf(ULong) reason: 32/64 compatibility


DisLin
Library of subroutines and functions that display data graphically.

Website: http://www.mps.mpg.de/dislin/
Platforms supported: Win32, Linux
Headers to include: dislin.bi
Header version: from 2005


Free alternative to the OpenGL Utility Toolkit
Just like GLUT, freeglut is a helper library that can be used to create OpenGL applications. It allows easy creation of windows with OpenGL drawing contexts and callback-based input event handling.

Website: http://freeglut.sourceforge.net/
Platforms supported: Win32, Linux
Headers to include: GL/freeglut.bi
Header version: 3.0.0


FreeImage
FreeImage is an Open Source library project for developers who would like to support popular graphics image formats like PNG, BMP, JPEG, TIFF and others as needed by today's multimedia applications. FreeImage is easy to use, fast, multithreading safe, compatible with all 32-bit versions of Windows, and cross-platform (works both with Linux and Mac OS X).

Website: http://freeimage.sourceforge.net/
Platforms supported: Win32, Linux
Headers to include: FreeImage.bi
Header version: 3.15.1
Example included: yes, in examples/files/FreeImage

Example

Here follows an example of using FreeImage in FreeBASIC. If using Windows you will require freeimage.dll which is available from the FreeImage site.
'' Code example for loading all common image types using FreeImage.
'' The example loads an image passed as a command line argument.

'' The function FI_Load returns a null pointer (0) if there was an error during
'' loading.  Otherwise it returns a 32-bit PUT compatible buffer.

#include "FreeImage.bi"
#include "crt.bi"
#include "fbgfx.bi"

Function FI_Load(filename As String) As Any Ptr
    If Len(filename) = 0 Then
        Return NULL
    End If

    '' Find out the image format
    Dim As FREE_IMAGE_FORMAT form = FreeImage_GetFileType(StrPtr(filename), 0)
    If form = FIF_UNKNOWN Then
        form = FreeImage_GetFIFFromFilename(StrPtr(filename))
    End If

    '' Exit if unknown
    If form = FIF_UNKNOWN Then
        Return NULL
    End If

    '' Always load jpegs accurately
    Dim As UInteger flags = 0
    If form = FIF_JPEG Then
        flags = JPEG_ACCURATE
    End If

    '' Load the image into memory
    Dim As FIBITMAP Ptr image = FreeImage_Load(form, StrPtr(filename), flags)
    If image = NULL Then
        '' FreeImage failed to read in the image
        Return NULL
    End If

    '' Flip the image so it matches FB's coordinate system
    FreeImage_FlipVertical(image)

    '' Convert to 32 bits per pixel
    Dim As FIBITMAP Ptr image32 = FreeImage_ConvertTo32Bits(image)

    '' Get the image's size
    Dim As UInteger w = FreeImage_GetWidth(image)
    Dim As UInteger h = FreeImage_GetHeight(image)

    '' Create an FB image of the same size
    Dim As fb.Image Ptr sprite = ImageCreate(w, h)

    Dim As Byte Ptr target = CPtr(Byte Ptr, sprite + 1)
    Dim As Integer target_pitch = sprite->pitch

    Dim As Any Ptr source = FreeImage_GetBits(image32)
    Dim As Integer source_pitch = FreeImage_GetPitch(image32)

    '' And copy over the pixels, row by row
    For y As Integer = 0 To (h - 1)
        memcpy(target + (y * target_pitch), _
               source + (y * source_pitch), _
               w * 4)
    Next

    FreeImage_Unload(image32)
    FreeImage_Unload(image)

    Return sprite
End Function

ScreenRes 640, 480, 32

Dim As String filename = Command(1)

Dim As Any Ptr image = FI_Load(filename)
If image <> 0 Then
    Put (0, 0), image
    ImageDestroy(image)
Else
    Print "Problem while loading file : " & filename
End If

Sleep


Freetype2
A Free, High-Quality, and Portable Font Engine

Website: http://www.freetype.org
Platforms supported: Win32, Linux
Headers to include: freetype2/freetype.bi
Header version: 2.5.5
Examples: yes, in examples/graphics/FreeType/

Example

'' Example of rendering a char using freetype

#include "freetype2/freetype.bi"

#ifdef __FB_LINUX__
Const TTF_FONT = "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"
#else
Const TTF_FONT = "Vera.ttf"
#endif

Dim As FT_Library library
If (FT_Init_FreeType(@library) <> 0) Then
    Print "FT_Init_FreeType() failed" : Sleep : End 1
End If

''
'' Load a font and render an '@' character on to a bitmap
''

Dim As FT_Face face
If (FT_New_Face(library, TTF_FONT, 0, @face) <> 0) Then
    Print "FT_New_Face() failed (font file '" & TTF_FONT & "' not found?)" : Sleep : End 1
End If

If (FT_Set_Pixel_Sizes(face, 0, 200) <> 0) Then
    Print "FT_Set_Pixel_Sizes() failed" : Sleep : End 1
End If

If (FT_Load_Char(face, Asc("@"), FT_LOAD_DEFAULT) <> 0) Then
    Print "FT_Load_Char() failed" : Sleep : End 1
End If

If (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL) <> 0) Then
    Print "FT_Render_Glyph() failed" : Sleep : End 1
End If

''
'' Draw the rendered bitmap
''

ScreenRes 320, 200, 32

Dim As FT_Bitmap Ptr bitmap = @face->glyph->bitmap

For y As Integer = 0 To (bitmap->rows - 1)
    For x As Integer = 0 To (bitmap->Width - 1)
        Dim As Integer col = bitmap->buffer[y * bitmap->pitch + x]
        PSet(x, y), RGB(col, col, col)
    Next
Next

Sleep


GD
Open source code library for the dynamic creation of images by programmers.

Website: http://www.libgd.org
Platforms supported: Win32, Linux
Headers to include: gd.bi
Header version: 2.1.0 development version
Examples: yes, in examples/files/GD/


GIFLIB
GIFLIB is a package of portable tools and library routines for working with GIF images

Website: http://giflib.sourceforge.net/intro.html
Platforms supported: Win32, Linux, DOS
Headers to include: gif_lib.bi
Header version: 4.2.1, 5.0.4 (#define __GIFLIB_VER__ to 4 or 5 if needed; default = 5)
Examples: yes, in examples/files/GIFLIB/


GLUT, the OpenGL Utility Toolkit
GLUT is a helper library that can be used to create OpenGL applications. It allows easy creation of a window with an OpenGL drawing context and also handles events such as mouse and keyboard input or timers. GLUT appears to be no longer maintained, however there is an active alternative: freeglut.

Website: http://www.opengl.org/resources/libraries/glut/
Platforms supported: Win32
Headers to include: GL/glut.bi
Header version: 3.7
Examples: in examples/graphics/OpenGL/


GLFW, an OpenGL library
GLFW is a helper library that can be used to create OpenGL applications. It allows the creation of a window with an OpenGL drawing context and input handling while still allowing the program to have its own main loop.

Website: http://www.glfw.org/
Platforms supported: Win32, Linux
Headers to include: GL/glfw.bi, GLFW/glfw3.bi
Header version: 2.7.9, 3.1.1
Examples: in examples/graphics/OpenGL/


GRX
2D graphics library

Website: http://grx.gnu.de/
Platforms supported: Win32, Linux, DOS
Headers to include: grx/grx20.bi
Header version: 2.4.6
Examples: in examples/graphics/grx/


DevIL
A full featured cross-platform image library.

Website: http://openil.sourceforge.net/
Platforms supported: Win32, Linux
Headers to include: IL/il.bi, IL/ilu.bi, IL/ilut.bi
Header version: 1.7.8
Examples: in examples/files/DevIL/

Example

'' DevIL example

#include Once "IL/il.bi"

'' Version check
If (ilGetInteger(IL_VERSION_NUM) < IL_VERSION) Then
    Print "DevIL version is different"
    End 1
End If

'' Good practice to explicitely initialize it
ilInit()

'' Load a bitmap
Dim As ILuint fblogo
ilGenImages(1, @fblogo)
ilBindImage(fblogo)

Print "Loading fblogo.bmp..."
ilLoadImage("fblogo.bmp")

'' Save a copy
Print "Saving a copy, fblogo-copy.bmp..."
ilEnable(IL_FILE_OVERWRITE)
ilSaveImage("fblogo-copy.bmp")

'' Clean up
ilDeleteImages(1, @fblogo)


Java Application Programming Interface
Open source free software GUI toolkit using Java's AWT Toolkit

Website: http://www.japi.de/
Platforms supported: Win32, Linux
Headers to include: japi.bi
Header version: from 2005


jpeglib
Cross-platform library for reading/writing jpeg images

Website: http://ijg.org/
Platforms supported: Win32, Linux, DOS
Headers to include: jpeglib.bi
Header version: 6.2, 7.0, 8.4, 9.0 (#define __JPEGLIB_VER__ to one of 6,7,8,9 if needed; default = 9)
Example Usage: yes, in examples/files/jpeglib


JPGAlleg
JPGalleg is a small addon for Allegro that adds JPG images handling capabilities to the library

Website: http://www.ecplusplus.com/index.php?page=projects&pid=1
Platforms supported: Win32, Linux
Headers to include: jpgalleg.bi
Header version: 2.5


libPNG
Allows reading and writing PNG images.

Website: http://www.libpng.org/pub/png/libpng.html
Platforms supported: Win32, Linux, DOS
Headers to include: png.bi
Header versions: 1.2.53, 1.4.16, 1.5.21, 1.6.16
Examples: in examples/files/libpng/

When #including png.bi, you can #define __LIBPNG_VERSION to one of 12, 14, 15, 16 in order to select the desired libpng version. The default is the latest version.

Overriding the default allows you to match the exact libpng version on your system (interesting for Linux distros which, for example, use the libpng 1.2 series instead of the latest version).


OpenGL, The Open Graphics Language
OpenGL is a standardized and widely used cross-platform 3D graphics library.

Usually OpenGL support comes as part of the system and the graphics drivers. There are many different projects providing a library that implements the main OpenGL API, and which one is used depends on the platform and system setup. For example, on Windows, the client API is implemented in Microsoft's opengl32.dll, while on Linux, there is for example the free Mesa3D project, which provides a libGL implementation. It depends on the used library or system setup which way the OpenGL API does its rendering, typically it uses OpenGL hardware drivers and is hardware-accelerated, but there also is software-rendered OpenGL (e.g. standalone Mesa3D). The system's graphics hardware drivers may provide additional OpenGL extensions, access to which is again system dependant.

Besides plain OpenGL, there are several utility, helper and wrapper libraries, such as GLUT, freeglut and GLFW, and even FreeBASIC's built-in graphics library has an OpenGL mode, see Screen (Graphics) And Fb.Gfx_Opengl.

Websites:
OpenGL standard: http://www.opengl.org
Mesa3D: http://mesa3d.org/
Windows OpenGL: http://msdn.microsoft.com/en-us/library/dd374278.aspx

Platforms supported: Win32, Linux
Headers to include: GL/gl.bi
Header version: Mesa-3D 10.5.1, MinGW-w64 3.3.0
Examples: yes, in examples/graphics/OpenGL/


PDFlib
Portable library for dynamically generating PDF documents

Website: http://www.pdflib.com
Platforms supported: Win32, Linux
Headers to include: pdflib.bi
Header version: 4.0.2
Examples: in examples/files/pdflib/


SDL, the Simple DirectMedia Layer
Cross-platform mulitmedia library

Website: http://www.libsdl.org
Platforms supported: Win32, Linux
Headers to include: SDL/SDL.bi or SDL2/SDL.bi
Header version: SDL 1.2.15, SDL2 2.0.3
Examples: yes, in examples/graphics/SDL/


TinyPTC
A small and easy to use framebuffer graphics library.

Website: http://sourceforge.net/projects/tinyptc/
Platforms supported: Win32, Linux, DOS
Headers to include: tinyptc.bi
Examples: in examples/graphics/tinyptc/


BASS
Audio library for use in Windows with a Beta Version for Linux.

Website: http://www.un4seen.com/bass.html
Platforms supported: Win32, Linux (beta)
Headers to include: bass.bi
Header version: 2.4.14
Examples: in examples/sound/BASS/

Example

#include Once "bass.bi"

'' this mod file should be available in the fbc package:
Const SOUND_FILE = "..\..\sound\data\dne_trtn.mod"

If (BASS_GetVersion() < MAKELONG(2,2)) Then
    Print "BASS version 2.2 or above required!"
    End 1
End If

If (BASS_Init(-1, 44100, 0, 0, 0) = 0) Then
    Print "Could not initialize BASS"
    End 1
End If

Dim As HMUSIC test = BASS_MusicLoad(False, @SOUND_FILE, 0, 0, BASS_MUSIC_LOOP, 0)
If (test = 0) Then
    Print "BASS could not load '" & SOUND_FILE & "'"
    BASS_Free()
    End 1
End If

BASS_ChannelPlay(test, False)

Print "Sound playing; waiting to keypress to stop and exit..."
Sleep

BASS_ChannelStop(test)
BASS_MusicFree(test)
BASS_Stop()
BASS_Free()


BASSMOD
BASSMOD is a MOD only (XM, IT, S3M, MOD, MTM, UMX) version of BASS - useful for demos, or anything else where you want to play some MOD music.

Website: http://www.un4seen.com/bassmod.html
Platforms supported: Win32, Linux
Headers to include: bassmod.bi
Header version: 2.0
Examples: in examples/sound/BASS/

Example

#include Once "bassmod.bi"

Const SOUND_FILE = "test.mod"

If (BASSMOD_GetVersion() < 2) Then
    Print "BASSMOD version 2 or above required!"
    End 1
End If

If (BASSMOD_Init(-1, 44100, 0) = 0) Then
    Print "Could not initialize BASSMOD"
    End 1
End If

If (BASSMOD_MusicLoad(False, SOUND_FILE, 0, 0, BASS_MUSIC_LOOP) = 0) Then
    Print "BASSMOD could not load '" & SOUND_FILE & "'"
    BASSMOD_Free()
    End 1
End If

BASSMOD_MusicPlay()

Print "Sound playing; waiting for keypress to stop and exit..."
Sleep

BASSMOD_MusicStop()
BASSMOD_MusicFree()
BASSMOD_Free()


Flite
Flite is a run-time speech synthesis engine.

Website: http://www.speech.cs.cmu.edu/flite/
Platforms supported: Win32, Linux
Headers to include: flite/flite.bi
Header version: 1.4, machine-translated only


FMOD
Audio library supporting just about any format.

Website: https://www.fmod.com/core
Platforms supported: Win32, Linux
Headers to include: fmod.bi
Header version: 3.75
Examples: in examples/sound/FMOD/

Example

#include Once "fmod.bi"

Const SOUND_FILE = "test.mod"

If (FSOUND_GetVersion() < FMOD_VERSION) Then
    Print "FMOD version mismatch"
    End 1
End If

If (FSOUND_Init(44100, 32, 0) = 0) Then
    Print "Could not initialize FMOD"
    End 1
End If

Dim As FMUSIC_MODULE Ptr song = FMUSIC_LoadSong(SOUND_FILE)
If (song = 0) Then
    Print "FMOD could not load '" & SOUND_FILE & "'"
    FSOUND_Close()
    End 1
End If

FMUSIC_PlaySong(song)

Print "Sound playing; waiting for keypress to stop and exit..."
Sleep

FMUSIC_FreeSong(song)
FSOUND_Close()

'' mp3 player based on FMOD

#include Once "fmod.bi"

Const SOUND_FILE = "test.mp3"

Sub print_all_tags(ByVal stream As FSOUND_STREAM Ptr)
    Dim As Integer count = 0
    FSOUND_Stream_GetNumTagFields(stream, @count)

    For i As Integer = 0 To (count - 1)
        Dim As Integer tagtype, taglen
        Dim As ZString Ptr tagname, tagvalue
        FSOUND_Stream_GetTagField(stream, i, @tagtype, @tagname, @tagvalue, @taglen)
        Print Left(*tagname, taglen)
    Next
End Sub

Function get_tag _
    ( _
        ByVal stream As FSOUND_STREAM Ptr, _
        ByVal tagv1 As ZString Ptr, _
        ByVal tagv2 As ZString Ptr _
    ) As String

    Dim tagname As ZString Ptr, taglen As Integer

    FSOUND_Stream_FindTagField(stream, FSOUND_TAGFIELD_ID3V1, tagv1, @tagname, @taglen)
    If (taglen = 0) Then
        FSOUND_Stream_FindTagField(stream, FSOUND_TAGFIELD_ID3V2, tagv2, @tagname, @taglen)
    End If

    Return Left(*tagname, taglen)
End Function

    If (FSOUND_GetVersion < FMOD_VERSION) Then
        Print "FMOD version " + Str(FMOD_VERSION) + " or greater required!"
        End 1
    End If

    If (FSOUND_Init(44100, 4, 0) = 0) Then
        Print "Could not initialize FMOD"
        End 1
    End If

    FSOUND_Stream_SetBufferSize(50)

    Dim As FSOUND_STREAM Ptr stream = FSOUND_Stream_Open(SOUND_FILE, FSOUND_MPEGACCURATE, 0, 0)
    If (stream = 0) Then
        Print "FMOD could not load '" & SOUND_FILE & "'"
        FSOUND_Close()
        End 1
    End If

    '' Read out mp3 tags to show some meta information
    Print "Title:", get_tag(stream, "TITLE", "TIT2")
    Print "Album:", get_tag(stream, "ALBUM", "TALB")
    Print "Artist:", get_tag(stream, "ARTIST", "TPE1")
    ''print_all_tags(stream)

    Print "Playing mp3, press a key to exit..."
    FSOUND_Stream_Play(FSOUND_FREE, stream)

    While (Inkey() = "")
        If (FSOUND_Stream_GetPosition(stream) >= FSOUND_Stream_GetLength(stream)) Then
            Exit While
        End If
        Sleep 50, 1
    Wend

    FSOUND_Stream_Stop(stream)
    FSOUND_Stream_Close(stream)
    FSOUND_Close()


MediaInfo
MediaInfo is a cross-platform library allowing you to read out technical and tag information from audio and video files in various formats.

Website: http://mediainfo.sourceforge.net/
Platforms supported: Win32, Linux
Headers to include: MediaInfo.bi
Header version: from October 2011


mpg123
libmpg123 is the decoder library used by the mpg123 MPEG player.

Website: http://mpg123.org/
Platforms supported: Win32, Linux
Headers to include: mpg123.bi
Header version: from 2010, machine-translated only


libogg
Ogg multimedia container format creation/decoder library

Website: http://www.xiph.org/ogg/
Platforms supported: Win32, Linux
Headers to include: ogg/ogg.bi
Header version: from 2007


OpenAL
OpenAL is a cross-platform 3D audio API appropriate for use with gaming applications and many other types of audio applications. ALUT is the OpenAL utility toolkit, a library providing additional functions to work with OpenAL.

Website: http://www.openal.org
Platforms supported: Win32, Linux
Headers to include: AL/al.bi, AL/alut.bi
Header version: openal-soft-1.16.0, freealut 1.1.0
Examples: in examples/sound/OpenAL/


PortAudio
PortAudio is a cross-platform audio I/O library that allows programs to access the system's audio devices to record or play sounds.

Website: http://www.portaudio.com/
Platforms supported: Win32, Linux
Headers to include: portaudio.bi
Header version: from 2010, machine-translated only


sfx
Freebasic sfx library is cross-platform and comes for Windows, Linux, Dos
Author: angros47
Download link: https://sourceforge.net/projects/freebasic-sfx-library/files/
Forum link: https://freebasic.net/forum/viewtopic.php?f=17&t=26256

Compile Library: all the necessary scripts are in the archive, you just need to substitute the correct path to the compiler in them

A brief description of the main (not all) functions:

1) SoundmidiSet - setting mode midi (needed for function PLAY)
2) SoundSet - setting mode PCM sound (needed for functions SOUND , PlayWave)
1) PLAY - command is for playing musical notes, octave *.
2) SOUND - command produces sound of a specific frequency for a specific duration
3) LoadMidi - load midi from file, returns a pointer MidiSequence ptr
4) PlayMidi - play midi, in parameters you need to pass pointer MidiSequence ptr
5) CreateMidi - creates MidiSequence ptr, then you can use the pointer using PLAY to write data , and then save in the file
6) SaveMidi - save data MidiSequence ptr in file, in format .mid
7) LoadWave - loads a file into memory WAV (supported only WAVE_FORMAT_PCM), returns a pointer WaveHeaderType ptr
8) PlayWave - play wav , in parameters you need to pass pointer WaveHeaderType ptr

* The command PLAY can play more than one note at time: it supports chords, by grouping notes inside curly brackets, and it allows to play up to 16 channels at the same time. For more information see also command QBASICs play.

Description of all functions is in the file readme.txt (in archive)

Example

Example Play
#include "sfx.bi"
#inclib "fbsfx"

SoundmidiSet ()
PLAY "a4e4g4a4g4e4c2f4f4f2d4d4d2"

Example Play wih thread
#include "sfx.bi"
#inclib "fbsfx"

Dim Shared As Any Ptr mutex
mutex = MutexCreate

Dim Shared As Long iMusicExit
Dim As Double dTimer

Sub procThread(p As Any Ptr)
   SoundmidiSet ()
   PLAY "a4e4g4a4g4e4c2f4f4f2d4d4d2"
   MutexLock(mutex)
   iMusicExit = 1
   MutexUnlock(mutex)
End Sub

dTimer = Timer
ThreadCreate(@procThread)

Do
   ? Timer - dTimer
   MutexLock(mutex)
   If iMusicExit Then
      Exit Do
   EndIf
   MutexUnlock(mutex)
Loop

Example CreateMidi, SaveMidi
#include "sfx.bi"
#inclib "fbsfx"

SoundmidiSet ()
Dim As Any Ptr Midi=CreateMidi()
PLAY Midi,"a4e4g4a4g4e4c2f4f4f2d4d4d2"
SaveMidi "music.mid", Midi

Example LoadMidi, PlayMidi
#include "sfx.bi"
#inclib "fbsfx"

SoundmidiSet ()
Dim As Any Ptr Midi=LoadMidi("music.mid")
PlayMidi(Midi, 1)
Sleep

Example Sound
#include "sfx.bi"
#inclib "fbsfx"

SoundSet (44100,1,16)
sound SineWave(2000), 1 ' sine 2 kHz
sound NoiseWave(), 1 ' noise
Sleep

Example LoadWave, PlayWave
#include "sfx.bi"
#inclib "fbsfx"

Dim As WaveHeaderType Ptr pWave
SoundSet (44100,2,16)
pWave = LoadWave("1.wav")
If pWave = 0 Then
   Print "pWave = 0" : End
End If
PlayWave(pWave)
Sleep


libsndfile
libsndfile is a library allowing programs to access or modify audio files in various formats, for example .wav files, and also convert between them.

Website: http://www.mega-nerd.com/libsndfile/
Platforms supported: Win32, Linux
Headers to include: sndfile.bi
Header version: 1.0.X


libVLC
Audio/video playback library from the VLC media player.

Website: http://www.videolan.org/, http://wiki.videolan.org/Libvlc
Platforms supported: Win32, Linux
Headers to include: vlc/*.bi
Header version: 1.1.x


libvorbis
Ogg Vorbis audio compression library

Website: http://xiph.org/vorbis/
Platforms supported: Win32, Linux
Headers to include: vorbis/vorbisenc.bi, vorbis/vorbisfile.bi
Header version: from 2007


GDBM, the GNU Database manager
Provides database functions using extensible hashing, primarily for storing key/data pairs to data files

Website: http://www.gnu.org.ua/software/gdbm/
Platforms supported: Win32, Linux
Headers to include: gdbm.bi
Header version: from 2010


MySQL
High-Quality, widely used database engine.

Website: http://www.mysql.org
Platforms supported: Win32, Linux
Headers to include: mysql/mysql.bi
Header version: 4.0.17
Examples: in examples/database/


PostgreSQL
Free software object-relational database management system

Website: https://www.postgresql.org/
Platforms supported: Win32, Linux
Headers to include: postgresql/postgres_ext.bi
Header version: 12.0
Example Usage: yes, in examples/database/


SQLite
Small C library that implements a self-contained, embeddable, zero-configuration SQL database engine.

Website: https://sqlite.org
Platforms supported: Win32, Linux, DOS
Headers to include: sqlite2.bi or sqlite3.bi
Header versions: 2.8.17, 3.30.0
Examples: in examples/database/


CUnit
Lightweight system for writing, administering, and running unit tests in C.

Website: http://cunit.sourceforge.net/
Platforms supported: Win32, Linux, DOS
Headers to include: CUnit/CUnit.bi
Header version: 2.1-3
Examples: in examples/misc/CUnit/


GDSL, The Generic Data Structures Library
The Generic Data Structures Library is a collection of routines for generic data structures.

Website: https://web.archive.org/web/20170502005430/http://home.gna.org:80/gdsl/ http://home.gna.org/gdsl/ (archived copy)
Platforms supported: Win32, Linux, DOS
Headers to include: gdsl/gdsl.bi
Header version: from 2005
Examples: in examples/misc/gdsl/


gettext
An internationalization library/toolchain

Website: http://www.gnu.org/software/gettext/gettext.html
Platforms supported: Win32, Linux, DOS
Headers to include: libintl.bi, gettext-po.bi
Header version: from 2010, 0.17


GNU Aspell
Free and Open Source spell checker.

Website: http://aspell.net/
Platforms supported: Win32, Linux
Headers to include: aspell.bi
Header version: 0.60.6.1

Example

'' GNU-ASspell example, converted from http://aspell.net/win32/

#include Once "aspell.bi"

Dim As AspellConfig Ptr spell_config = new_aspell_config()

'' Change this to suit the installed dictionary language if desired
aspell_config_replace(spell_config, "lang", "en_CA")

'' Create speller object
Dim As AspellCanHaveError Ptr possible_err = new_aspell_speller(spell_config)
If (aspell_error_number(possible_err) <> 0) Then
    Print *aspell_error_message(possible_err)
    End 1
End If
Dim As AspellSpeller Ptr speller = to_aspell_speller(possible_err)

Dim As String word
Do
    Print
    Input "Enter a word (blank to quit): ", word
    If (Len(word) = 0) Then
        Exit Do
    End If

    If (aspell_speller_check(speller, StrPtr(word), Len(word)) <> 0) Then
        Print "Word is Correct"
    Else
        Print "Suggestions:"
        Dim As AspellStringEnumeration Ptr elements = _
            aspell_word_list_elements(aspell_speller_suggest(speller, StrPtr(word), Len(word)))
        Do
            Dim As Const ZString Ptr w = aspell_string_enumeration_next(elements)
            If (w = 0) Then
                Exit Do
            End If
            Print "   "; *w
        Loop
        delete_aspell_string_enumeration(elements)
    End If

    ' - Report the replacement
    'aspell_speller_store_repl(speller, misspelled_word, size,
    '                          correctly_spelled_word, size);

    ' - Add to session or personal dictionary
    'aspell_speller_add_to_session|personal(speller, word, size)
Loop

delete_aspell_speller(speller)


BFD, the Binary File Descriptor Library
Provides an API to read and write object files in many different object file formats. libbfd is the core of the GNU binutils.

Website: http://sourceware.org/binutils/
Platforms supported: Win32, Linux, DOS
Headers to include: bfd.bi
Header version: binutils versions from 2.16 to 2.25

Define __BFD_VER__ to 216, 217, 218, ..., 225 to include the bfd header for the corresponding binutils version.

Example

#define __BFD_VER__ 217
#include "bfd.bi"


JNI, The Java Native Interface
Standard programming interface for writing Java native methods and embedding the Java virtual machine into native applications.

Website: http://download.oracle.com/javase/6/docs/technotes/guides/jni/index.html, http://java.sun.com/docs/books/jni/
Platforms supported: Win32, Linux
Headers to include: jni.bi
Header version: from 2006
Examples: in examples/other-languages/Java/

Example

Three files:

  • mylib.bas - A DLL writting in FreeBASIC

#include "jni.bi"

'' Note: The mangling must be "windows-ms" or the JRE won't find any function
Extern "windows-ms"
    Function Java_MyLib_add( env As JNIEnv Ptr, obj As jobject, l As jint, r As jint ) As jint Export
        Return l + r
    End Function
End Extern

  • Mylib.java - The Java class that represents the interface to the FreeBASIC code and ensures the FreeBASIC DLL is loaded

(cpp)
class MyLib {
    public native int add( int l, int r );
    static {
        System.loadLibrary( "mylib" );
    }
}

  • Test.java - The Java main() that uses the Mylib class

(cpp)
class Test {
    public static void main(String[] args) {
        MyLib lib = new MyLib();
        System.out.println( "2+2=" + lib.add( 2, 2 ) );
    }
}

Steps to test it:

  • Compile the FreeBASIC DLL: fbc mylib.bas -dll
  • Compile the two Java classes: javac Mylib.java Test.java
  • Run the Test class: java Test


json-c
A JSON implementation in C

Website: https://github.com/json-c/json-c
Platforms supported: Win32, Linux
Headers to include: json-c/json.bi
Header version: 0.9 (not sure)


libffi
LibFFI is a foreign function interface library allowing programs to arbitrarily call native function without pointers and to bind function pointers to generic functions which take variable arguments via closures. It is used to bind native code in modern scripting languages.

Website: https://sourceware.org/libffi/
Platforms supported: Windows, Linux, DOS
Headers to include: ffi.bi
Header version: 3.1

Example

Hello world:
#include "ffi.bi"

' Simple "puts" equivalent function
Function printer cdecl (ByVal s As ZString Ptr) As Integer
    Print *s
    Return 42
End Function

' Initialize the argument info vectors
Dim s As ZString Ptr
Dim args(0 To 0) As ffi_type Ptr = {@ffi_type_pointer}
Dim values(0 To 0) As Any Ptr = {@s}

' Initialize the cif
Dim cif As ffi_cif
Dim result As ffi_status
result = ffi_prep_cif( _
    @cif,              _ ' call interface object
    FFI_DEFAULT_ABI,   _ ' binary interface type
    1,                 _ ' number of arguments
    @ffi_type_uint,    _ ' return type
    @args(0)           _ ' arguments
)

' Call function
Dim return_value As Integer
If result = FFI_OK Then
    s = @"Hello world"
    ffi_call(@cif, FFI_FN(@printer), @return_value, @values(0))

    ' values holds a pointer to the function's arg, so to
    ' call puts() again all we need to do is change the
    ' value of s */
    s = @"This is cool!"
    ffi_call(@cif, FFI_FN(@printer), @return_value, @values(0))
    Print Using "Function returned &"; return_value
End If

Closures:
#include "ffi.bi"

' Acts like puts with the file given at time of enclosure.
Sub Printer cdecl(ByVal cif As ffi_cif Ptr, ByVal ret As Any Ptr, ByVal args As Any Ptr Ptr, ByVal File As Any Ptr)
    Write #*CPtr(Integer Ptr, file), **CPtr(ZString Ptr Ptr, args[0])
    *CPtr(UInteger Ptr, ret) = 42
End Sub

' Allocate the closure and function binding
Dim PrinterBinding As Function(ByVal s As ZString Ptr) As Integer
Dim closure As ffi_closure Ptr
closure = ffi_closure_alloc(SizeOf(ffi_closure), @PrinterBinding)

If closure <> 0 Then
    ' Initialize the argument info vector
    Dim args(0 To 0) As ffi_type Ptr = {@ffi_type_pointer}

    ' Initialize the call interface
    Dim cif As ffi_cif
    Dim prep_result As ffi_status = ffi_prep_cif( _
        @cif,            _ ' call interface object
        FFI_DEFAULT_ABI, _ ' binary interface type
        1,               _ ' number of arguments
        @ffi_type_uint,  _ ' return type
        @args(0)         _ ' arguments
    )
    If prep_result = FFI_OK Then
        ' Open console file to send to PrinterBinding as user data
        Dim ConsoleFile As Integer = FreeFile()
        Open Cons For Output As ConsoleFile

        ' Initialize the closure, setting user data to the console file
        prep_result = ffi_prep_closure_loc( _
            closure,         _ ' closure object
            @cif,            _ ' call interface object
            @Printer,        _ ' actual closure function
            @ConsoleFile,    _ ' user data, our console file #
            PrinterBinding   _ ' pointer to binding
        )
        If prep_result = FFI_OK Then
            ' Call binding as a natural function call
            Dim Result As Integer
            Result = PrinterBinding("Hello World!")
            Print Using "Returned &"; Result
        End If

        Close ConsoleFile
    End If
End If

' Clean up
ffi_closure_free(closure)


libjit
LibJIT is a fairly straightforward, lightweight library for runtime compilation with a simple and stable ABI.

Website: https://www.gnu.org/software/libjit/
Platforms supported: Windows, Linux, DOS
Headers to include: jit.bi
Header version: git a8293e141b79c28734a3633a81a43f92f29fc2d7

Example

'' Simple mul/add example

#include "jit.bi"

' initialize libjit
Dim context As jit_context_t = jit_context_create()
jit_context_build_start(context)

' define function mul_add(x, y, z)
Dim params(0 To 2) As jit_type_t = {jit_type_int, jit_type_int, jit_type_int}
Dim signature As jit_type_t = jit_type_create_signature( _
    jit_abi_cdecl,  _ ' C-style function
    jit_type_int,   _ ' Return type
    @params(0),     _ ' Parameter array
    3,              _ ' Number of components
    1               _ ' Count references?
)
Dim mul_add As jit_function_t = jit_function_create(context, signature)

' build function (result = (x*y)+z)
Dim As jit_value_t x, y, z, temp1, temp2
x = jit_value_get_param(mul_add, 0)
y = jit_value_get_param(mul_add, 1)
temp1 = jit_insn_mul(mul_add, x, y)
z = jit_value_get_param(mul_add, 2)
temp2 = jit_insn_add(mul_add, temp1, z)
jit_insn_return(mul_add, temp2)

' compile function function
jit_function_compile(mul_add)
jit_context_build_end(context)

' call function
Dim As Integer a=3, b=5, c=2, result
Dim args(0 To 2) As Integer Ptr = {@a, @b, @c}
jit_function_apply(mul_add, @args(0), @result)
Print Using "mul__add(&, &, &) = &"; a; b; c; result

' clean up libjit
jit_context_destroy(context)

'' GCD calculation example

#include "jit.bi"

' initialize libjit
Dim context As jit_context_t = jit_context_create()
jit_context_build_start(context)

' define function gcd(x as uinteger, y as uinteger) as uinteger
Dim params(0 To 1) As jit_type_t = {jit_type_uint, jit_type_uint}
Dim signature As jit_type_t = jit_type_create_signature( _
    jit_abi_cdecl,  _ ' C-style function
    jit_type_uint,  _ ' Return type
    @params(0),     _ ' Parameter array
    2,              _ ' Number of components
    1               _ ' Count references?
)
Dim gcd As jit_function_t = jit_function_create(context, signature)

' build function
' check x = y
Dim As jit_value_t x, y, x_eq_y
x = jit_value_get_param(gcd, 0)
y = jit_value_get_param(gcd, 1)
x_eq_y = jit_insn_eq(gcd, x, y)

' if x = y, return x
Dim label_x_ne_y As jit_label_t = jit_label_undefined
jit_insn_branch_if_not(gcd, x_eq_y, @label_x_ne_y)
jit_insn_return(gcd, x)

' else if...
jit_insn_label(gcd, @label_x_ne_y)

' check x < y
Dim As jit_value_t x_lt_y
Dim label_x_gte_y As jit_label_t = jit_label_undefined
x_lt_y = jit_insn_lt(gcd, x, y)
jit_insn_branch_if_not(gcd, x_lt_y, @label_x_gte_y)

' if x < y, return gcd(x, y-x)
Dim As jit_value_t gcd_args(0 To 2), gcd_result
gcd_args(0) = x
gcd_args(1) = jit_insn_sub(gcd, y, x)
gcd_result = jit_insn_call( _
    gcd,          _ ' where we are calling from
    "gcd",        _ ' function name
    gcd,          _ ' function reference
    0,            _ ' signature = auto
    @gcd_args(0), _ ' arguments
    2,            _ ' argument count
    0             _ ' flags = nothing special
)
jit_insn_return(gcd, gcd_result)

' else...
jit_insn_label(gcd, @label_x_gte_y)

' return gcd(x-y, y)
gcd_args(0) = jit_insn_sub(gcd, x, y)
gcd_args(1) = y
gcd_result = jit_insn_call( _
    gcd,          _ ' where we are calling from
    "gcd",        _ ' function name
    gcd,          _ ' function reference
    0,            _ ' signature = auto
    @gcd_args(0), _ ' arguments
    2,            _ ' argument count
    0             _ ' flags = nothing special
)
jit_insn_return(gcd, gcd_result)

' compile function
jit_function_compile(gcd)
jit_context_build_end(context)

' call function
Dim As jit_uint a=21, b=14, result
Dim As jit_uint Ptr args(0 To 1) = {@a, @b}
jit_function_apply(gcd, @args(0), @result)
Print Using "gcd(&, &) = &"; a; b; result

' clean up libjit
jit_context_destroy(context)


Lua
Lightweight, embeddable scripting engine using the Lua language.

Website: https://www.lua.org
Platforms supported: Win32, Linux, DOS
Headers to include: Lua/lua.bi
Header version: 5.2.3
Examples: in examples/other-languages/Lua/


SpiderMonkey
Embeddable javascript engine.

Website: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey
Platforms supported: Win32, Linux
Headers to include: spidermonkey/jsapi.bi
Header version: from 2006

Example

'' Evaluating javascript code
#include Once "spidermonkey/jsapi.bi"

Dim Shared As JSClass global_class = _
( _
    @"global", 0, _
    @JS_PropertyStub, @JS_PropertyStub, @JS_PropertyStub, @JS_PropertyStub, _
    @JS_EnumerateStub, @JS_ResolveStub, @JS_ConvertStub, @JS_FinalizeStub _
)

Dim As JSRuntime Ptr rt = JS_NewRuntime(1048576 /'memory limit'/)
Dim As JSContext Ptr cx = JS_NewContext(rt, 4096 /'stack size'/)
Dim As JSObject Ptr global = JS_NewObject(cx, @global_class, NULL, NULL)

JS_InitStandardClasses(cx, global)

'' This string could also be read in from a file or as part of HTTP data etc.
Const TEST_SCRIPT = _
    !"function fact(n)           \n" + _
    !"{                          \n" + _
    !"    if (n <= 1)            \n" + _
    !"        return 1;          \n" + _
    !"                           \n" + _
    !"    return n * fact(n - 1);\n" + _
    !"}                          \n" + _
    !"                           \n" + _
    !"    fact(5)                \n"

Dim As jsval rval
If (JS_EvaluateScript(cx, global, TEST_SCRIPT, Len(TEST_SCRIPT), "localhost", 1, @rval) = 0) Then
    Print "JS_EvaluateScript failed"
    Sleep
    End 1
End If

Print "result: " & *JS_GetStringBytes(JS_ValueToString(cx, rval))

JS_DestroyContext(cx)
JS_DestroyRuntime(rt)

'' Callback example: Functions that are used by the Javascript code,
'' but are implemented in FB.
#include Once "spidermonkey/jsapi.bi"

Dim Shared As JSClass global_class = _
( _
    @"global", 0, _
    @JS_PropertyStub, @JS_PropertyStub, @JS_PropertyStub, @JS_PropertyStub, _
    @JS_EnumerateStub, @JS_ResolveStub, @JS_ConvertStub, @JS_FinalizeStub _
)

Private Function print_callback cdecl _
    ( _
        ByVal cx As JSContext Ptr, _
        ByVal obj As JSObject Ptr, _
        ByVal argc As uintN, _
        ByVal argv As jsval Ptr, _
        ByVal rval As jsval Ptr _
    ) As JSBool

    If (argc < 1) Then
        Return 0
    End If

    Print *JS_GetStringBytes(JS_ValueToString(cx, argv[0]))

    Return 1
End Function

Private Function ucase_callback cdecl _
    ( _
        ByVal cx As JSContext Ptr, _
        ByVal obj As JSObject Ptr, _
        ByVal argc As uintN, _
        ByVal argv As jsval Ptr, _
        ByVal rval As jsval Ptr _
    ) As JSBool

    If (argc < 1) Then
        Return 0
    End If

    '' Get the first argument
    Dim As ZString Ptr arg1 = JS_GetStringBytes(JS_ValueToString(cx, argv[0]))

    '' Get a buffer for the result string
    Dim As ZString Ptr result = JS_malloc(cx, Len(*arg1) + 1)

    '' Do the work
    *result = UCase(*arg1)

    '' Return it in rval
    *rval = STRING_TO_JSVAL(JS_NewString(cx, result, Len(*result)))

    Return 1
End Function

    Dim As JSRuntime Ptr rt = JS_NewRuntime(1048576 /'memory limit'/)
    Dim As JSContext Ptr cx = JS_NewContext(rt, 4096 /'stack size'/)
    Dim As JSObject Ptr global = JS_NewObject(cx, @global_class, NULL, NULL)

    JS_InitStandardClasses(cx, global)

    JS_DefineFunction(cx, global, "print", @print_callback, 1, 0)
    JS_DefineFunction(cx, global, "ucase", @ucase_callback, 1, 0)

    Const TEST_SCRIPT = "print(ucase('hello'));"

    Dim As jsval rval
    If (JS_EvaluateScript(cx, global, TEST_SCRIPT, Len(TEST_SCRIPT), "localhost", 1, @rval) = 0) Then
        Print "JS_EvaluateScript failed"
        Sleep
        End 1
    End If

    JS_DestroyContext(cx)
    JS_DestroyRuntime(rt)


cryptlib
A powerful security toolkit which allows even inexperienced crypto programmers to easily add encryption and authentication services to their software.

Website: http://www.cs.auckland.ac.nz/~pgut001/cryptlib/
Platforms supported: Win32, Linux
Headers to include: cryptlib.bi
Header version: from 2005
Examples: in examples/math/cryptlib/

Example

#include Once "cryptlib.bi"

Function calc_hash( ByVal filename As String, ByVal algo As CRYPT_ALGO_TYPE ) As String
    Const BUFFER_SIZE = 8192
    Dim As Byte buffer( 0 To BUFFER_SIZE-1 )

    '' create a new context using the wanted algorithm
    Dim As CRYPT_CONTEXT ctx
    cryptCreateContext( @ctx, CRYPT_UNUSED, algo )

    '' open input file in binary mode
    Dim As Integer f = FreeFile()
    If( Open( filename For Binary Access Read As #f ) <> 0 ) Then
        Return ""
    End If

    '' read until end-of-file
    Do Until( EOF( f ) )
        Dim As Integer oldpos = Seek( f )
        Get #f, , buffer()
        Dim As Integer readlength = Seek( f ) - oldpos
        '' encrypt
        cryptEncrypt( ctx, @buffer(0), readlength )
    Loop

    '' close input file
    Close #f

    '' finalize
    cryptEncrypt( ctx, 0, 0 )

    '' get the hash result
    Dim As Long buffersize = BUFFER_SIZE
    cryptGetAttributeString( ctx, CRYPT_CTXINFO_HASHVALUE, @buffer(0), @buffersize )

    '' convert to hexadecimal
    Dim As String result = ""
    For i As Integer = 0 To buffersize-1
        result += Hex( buffer(i) )
    Next

    '' free the context
    cryptDestroyContext( ctx )

    Return result
End Function

    Dim As String filename = Trim( Command(1) )
    If( Len( filename ) = 0 ) Then
        Print "Usage: hash.exe filename"
        End -1
    End If

    '' init cryptlib
    cryptInit( )

    '' calculate hashes
    Print "md5:  "; calc_hash( filename, CRYPT_ALGO_MD5 )
    Print "sha1: "; calc_hash( filename, CRYPT_ALGO_SHA1 )

    '' shutdown cryptlib
    cryptEnd( )

    Sleep


UUID library
The UUID library can be used to generate universally unique identifiers. It's part of the e2fsprogs utilities.

Website: http://e2fsprogs.sourceforge.net/
Platforms supported: Win32, Linux
Headers to include: uuid.bi
Header version: from 2010


big_int
Library for using arbitrarily large integers. Note: This library seems to be dead, a possible alternative is gmp.

Website: https://web.archive.org/web/20060112164857/http://valyala.narod.ru/big_int/ http://valyala.narod.ru/big_int/ (archived copy)
Github: https://github.com/valyala/big_int
Platforms supported: Win32, Linux
Headers to include: big_int/big_int.bi
Header version: from 2005
Examples: in examples/math/big_int/

Example

#include Once "big_int/big_int_full.bi"

Sub print_num(ByVal num As big_int Ptr)
    Dim As big_int_str Ptr s = big_int_str_create(1)
    If (s = 0) Then
        Exit Sub
    End If

    If (big_int_to_str(num, 10, s) <> 0) Then
        Exit Sub
    End If

    Print *s->Str;

    big_int_str_destroy(s)
End Sub

    Dim As big_int Ptr bignum = big_int_create(1)

    big_int_from_int(2, bignum)
    big_int_pow(bignum, 65536, bignum)

    Print "2^65536 = ";
    print_num(bignum)
    Print

    big_int_destroy(bignum)


Chipmunk Physics
Chipmunk is a physics library designed specifically for 2D games.

Website: https://chipmunk-physics.net
Platforms supported: Win32, Linux
Headers to include: chipmunk/chipmunk.bi
Header version: 4.1.0


gmp, The GNU Multiple Precision Arithmetic Library
Free library for arbitrary precision arithmetic, operating on signed integers, rational numbers, and floating point numbers.

Website: https://www.gmplib.org
Platforms supported: Win32, Linux
Headers to include: gmp.bi
Header version: 4.1.4

Example

#include Once "gmp.bi"

Dim As mpz_ptr bignum = Allocate(SizeOf(__mpz_struct))
mpz_init_set_si(bignum, 2)
mpz_pow_ui(bignum, bignum, 65536)

Print "2^65536 = ";
Dim As ZString Ptr s = mpz_get_str(0, 10, bignum)
Print *s;
Deallocate(s)
Print

mpz_clear(bignum)
Deallocate(bignum)


gsl, The GNU Scientific Library
Provides a wide range of mathematical routines such as random number generators, special functions and least-squares fitting.

Website: https://www.gnu.org/software/gsl/, Windows port: http://gnuwin32.sourceforge.net/packages/gsl.htm
Platforms supported: Win32, Linux
Headers to include: gsl/*.bi
Header version: 1.6
Examples: in examples/math/GSL/

Example

'' Elementary math example
#include "gsl/gsl_math.bi"

'' Raise the value of 3.141 to the fourth power
? "3.141 ^ 4 = "; gsl_pow_4(3.141)
?

'' Find the hypotenuse of a right triangle with sides 3 and 4
? "The hypotenuse of a right triangle with sides of length 3 and 4 is"; gsl_hypot(3,4)
?

Sleep

'' Matrix example
#include "gsl/gsl_matrix.bi"

'' gsl uses the c-style row major order, unlike VB or Fortran
? "A 3x3 matrix"
Dim As gsl_matrix Ptr m = gsl_matrix_alloc(3, 3)
For i As Integer = 0 To 2
    For j As Integer = 0 To 2
        gsl_matrix_set (m, i, j, 0.23 + 100*i + j)
    Next
Next

For i As Integer = 0 To 2
    For j As Integer = 0 To 2
        Print "m(";i;",";j;") = "; gsl_matrix_get (m, i, j)
    Next
Next
?

gsl_matrix_transpose(m)

? "And its transpose"
For i As Integer = 0 To 2
    For j As Integer = 0 To 2
        Print "m(";i;",";j;") = "; gsl_matrix_get (m, i, j)
    Next
Next

Sleep


Newton
The Newton Physics Engine is an integrated solution for real time simulation of physics environments.

Website: http://newtondynamics.com/
Github: https://github.com/MADEAPPS/newton-dynamics/
Platforms supported: Win32, Linux
Headers to include: Newton.bi
Header version: from 2005
Examples: in examples/math/Newton/


ODE, the Open Dynamics Engine
Open source, high performance library for simulating rigid body dynamics.

Website: http://www.ode.org/
Platforms supported: Win32, Linux
Headers to include: ode/ode.bi
Header version: 0.11.1
Examples: in examples/math/ODE/


cgi-util
Small C library for creating CGI programs for Websites.

Website: http://www.newbreedsoftware.com/cgi-util/
Platforms supported: Win32, Linux
Headers to include: cgi-util.bi


curl
Free and easy-to-use client-side URL transfer library supporting almost every protocol.

Website: https://curl.haxx.se/libcurl/
Platforms supported: Win32, Linux, DOS
Headers to include: curl.bi
Header version: 7.44.0
Examples: in examples/network/curl/

Example

'' Curl HTTP Get example

#include Once "curl.bi"
#include Once "crt/string.bi"

'' this callback will be called when any data is received
Private Function write_callback cdecl _
    ( _
        ByVal buffer As Byte Ptr, _
        ByVal size As Integer, _
        ByVal nitems As Integer, _
        ByVal outstream As Any Ptr _
    ) As Integer

    Static As ZString Ptr zstr = 0
    Static As Integer maxbytes = 0

    Dim As Integer bytes = size * nitems

    '' current zstring buffer too small?
    If( maxbytes < bytes ) Then
        zstr = Reallocate( zstr, bytes + 1 )
        maxbytes = bytes
    End If

    '' "buffer" is not null-terminated, so we must dup it and add the null-term
    memcpy( zstr, buffer, bytes )
    zstr[bytes] = 0

    '' just print it..
    Print *zstr

    Return bytes
End Function

    '' init
    Dim As CURL Ptr curl = curl_easy_init( )
    If( curl = 0 ) Then
        End 1
    End If

    '' set url and callback
    curl_easy_setopt( curl, CURLOPT_URL, "freebasic.net" )
    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, @write_callback )

    '' execute..
    curl_easy_perform( curl )

    '' shutdown
    curl_easy_cleanup( curl )


FastCGI
Open extension to CGI that provides high performance without the limitations of server specific APIs.

Website: https://web.archive.org/web/20080213023105/http://www.fastcgi.com:80/ http://www.fastcgi.com (archived copy from 2006)
Github: https://github.com/FastCGI-Archives
See also Forum: https://freebasic.net/forum/viewtopic.php?f=14&t=28126
Platforms supported: Win32, Linux
Headers to include: fastcgi/fastcgi.bi, fastcgi/fcgiapp.bi, fastcgi/fcgi_stdio.bi
Header version: 2.4.1-SNAP-0311112127

Example

#include "fastcgi/fcgi_stdio.bi"

Dim As Integer count = 0
While (FCGI_Accept() >= 0)
    count += 1
    Print !"Content-type: text/html\r\n"
    Print !"\r\n"
    Print "<title>FastCGI Hello!</title>"
    Print "<h1>FastCGI Hello!</h1>"
    Print Using "Request number ### running on host <i>&</i>"; count; *getenv("SERVER_NAME");
Wend


ZeroMQ
High-performance asynchronous messaging library

Website: https://www.zeromq.org
Platforms supported: Win32, Linux
Headers to include: zmq/zmq.bi
Header version: 2.1.10


Expat
Stream oriented XML parser library with several useful features.

Website: https://libexpat.github.io
Platforms supported: Win32, Linux
Headers to include: expat.bi
Header version: 1.95.8
Examples: in examples/xml/

Example

'' XML file parser command line tool based on libexpat

'' Can use zstring or wstring (libexpat or libexpatw):
'#define XML_UNICODE

#include Once "expat.bi"
#include Once "crt/mem.bi"

#ifndef False
#define False 0
#endif
#define NULL 0

Const BUFFER_SIZE = 1024

Type Context
    As Integer nesting
    As XML_char * (BUFFER_SIZE+1) text
    As Integer textlength
End Type

Dim Shared As Context ctx

'' Callback called by libexpat when begin of XML tag is found
Sub elementBegin cdecl _
    ( _
        ByVal userdata As Any Ptr, _
        ByVal element As Const XML_char Ptr, _
        ByVal attributes As Const XML_char Ptr Ptr _
    )

    '' Show its name
    Print Space(ctx.nesting);*element;

    '' and its attributes (attributes are given as an array of XML_char pointers
    '' much like argv, for each attribute there will apparently be the one
    '' element representing the name and a second element representing the
    '' specified value)
    While (*attributes)
        Print " ";**attributes;
        attributes += 1
        Print "='";**attributes;"'";
        attributes += 1
    Wend
    Print

    ctx.nesting += 1
    ctx.text[0] = 0
    ctx.textlength = 0
End Sub

'' Callback called by libexpat when end of XML tag is found
Sub elementEnd cdecl(ByVal userdata As Any Ptr, ByVal element As Const XML_char Ptr)
    '' Show text collected in charData() callback below
    Print Space(ctx.nesting);ctx.text
    ctx.text[0] = 0
    ctx.textlength = 0
    ctx.nesting -= 1
End Sub

Sub charData cdecl _
    ( _
        ByVal userdata As Any Ptr, _
        ByVal chars As Const XML_char Ptr, _  '' Note: not null-terminated
        ByVal length As Integer _
    )

    '' This callback will apparently recieve every data between xml tags
    '' (really?), including newlines and space.

    '' Append to our buffer, if there still is free room, so we can print it out later
    If (length <= (BUFFER_SIZE - ctx.textlength)) Then
        memcpy(@ctx.text[ctx.textlength], @chars[0], length * SizeOf(XML_char))
        ctx.textlength += length
        ctx.text[ctx.textlength] = 0
    End If
End Sub

''
'' Main
''

    Dim As String filename = Command(1)
    If (Len(filename) = 0) Then
        Print "Usage: expat <xmlfilename>"
        End 1
    End If

    Dim As XML_Parser parser = XML_ParserCreate(NULL)
    If (parser = NULL) Then
        Print "XML_ParserCreate failed"
        End 1
    End If

    ''XML_SetUserData(parser, userdata_pointer)
    XML_SetElementHandler(parser, @elementBegin, @elementEnd)
    XML_SetCharacterDataHandler(parser, @charData)

    If (Open(filename, For Input, As #1)) Then
        Print "Could not open file: '";filename;"'"
        End 1
    End If

    Static As UByte buffer(0 To (BUFFER_SIZE-1))

    Dim As Integer reached_eof = False
    Do
        Dim As Integer size = BUFFER_SIZE
        Dim As Integer result = Get(#1, , buffer(0), size, size)
        If (result Or (size <= 0)) Then
            Print "File input error"
            End 1
        End If

        reached_eof = (EOF(1) <> False)

        If (XML_Parse(parser, @buffer(0), size, reached_eof) = False) Then
            Print filename & "(" & XML_GetCurrentLineNumber(parser) & "): Error from XML parser: "
            Print *XML_ErrorString(XML_GetErrorCode(parser))
            End 1
        End If
    Loop While (reached_eof = False)

    XML_ParserFree(parser)


libxml2
De-facto standard library for accessing xml files.

Website: http://xmlsoft.org/
Platforms supported: Win32, Linux
Headers to include: libxml/*.bi
Header version: 2.6.17
Examples: in examples/xml/

Example

#include Once "libxml/xmlreader.bi"
#define NULL 0

Dim As String filename = Command(1)
If( Len( filename ) = 0 ) Then
    Print "Usage: libxml filename"
    End 1
End If

Dim As xmlTextReaderPtr reader = xmlReaderForFile( filename, NULL, 0 )
If (reader = NULL) Then
    Print "Unable to open "; filename
    End 1
End If

Dim As Integer ret = xmlTextReaderRead( reader )
Do While( ret = 1 )
    Dim As Const ZString Ptr constname = xmlTextReaderConstName( reader )
    Dim As Const ZString Ptr value = xmlTextReaderConstValue( reader )

    Print xmlTextReaderDepth( reader ); _
        xmlTextReaderNodeType( reader ); _
        " "; *constname; _
        xmlTextReaderIsEmptyElement(reader); _
        xmlTextReaderHasValue( reader ); _
        *value

    ret = xmlTextReaderRead( reader )
Loop

xmlFreeTextReader( reader )

If( ret <> 0 ) Then
    Print "failed to parse: "; filename
End If

xmlCleanupParser( )
xmlMemoryDump()


libxslt
XSLT itself is a an XML language to define transformation for XML.

Website: http://xmlsoft.org/XSLT/
Platforms supported: Win32, Linux
Headers to include: libxslt/libxslt.bi
Header version: 1.1.13


mxml, Mini-XML
Mini-XML is a small XML parsing library that you can use to read XML and XML-like data files in your application without requiring large non-standard libraries.

Website: https://www.msweet.org/mxml/
Github: https://github.com/michaelrsweet/mxml
Platforms supported: Win32, Linux
Headers to include: mxml.bi
Header version: 2.7


PCRE, Perl Compatible Regular Expressions
Consists of a set of functions that implement regular expression pattern matching using the same syntax and semantics as Perl 5.

Website: https://www.pcre.org
Platforms supported: Win32, Linux
Headers to include: pcre.bi, pcre16.bi, prceposix.bi
Version: 8.31
Examples: in examples/regex/PCRE/


TRE (regex matching library)
Lightweight, robust, and efficient POSIX compliant regexp matching library

Website: https://laurikari.net/tre/
Github: https://github.com/laurikari/tre/
Platforms supported: Win32, Linux
Headers to include: tre/tre.bi, tre/regex.bi
Header version: 0.8.0
Examples: in examples/regex/TRE/


libbzip2
libbzip2 is the library implementing .bz2 file or in-memory compression and extraction, with interfaces similar to zlib.

Website: http://bzip.org/
Platforms supported: Win32, Linux, DOS
Headers to include: bzlib.bi
Header version: 1.0.6
Examples: in examples/compression/


libzip
Easy-to-use library for creating, reading out or modifying .zip archives.

Website: https://libzip.org
Github: https://github.com/nih-at/libzip
Platforms supported: Win32, Linux, DOS
Headers to include: zip.bi
Header version: 0.11.2
Examples: in examples/compression/

Example

'' .zip unpacking using libzip
#include Once "zip.bi"

Sub create_parent_dirs(ByVal file As ZString Ptr)
    '' Given a path like this:
    '' foo/bar/baz/file.ext
    '' Do these mkdir()'s:
    '' foo
    '' foo/bar
    '' foo/bar/baz
    Dim As UByte Ptr p = file
    Do
        Select Case (*p)
        Case Asc("/")
            *p = 0
            MkDir(*file)
            *p = Asc("/")
        Case 0
            Exit Do
        End Select
        p += 1
    Loop
End Sub

'' Asks libzip for information on file number 'i' in the .zip file,
'' and then extracts it, while creating directories as needed.
Private Sub unpack_zip_file(ByVal zip As Any Ptr, ByVal i As Integer)
    #define BUFFER_SIZE (1024 * 512)
    Static As UByte chunk(0 To (BUFFER_SIZE - 1))
    #define buffer (@chunk(0))

    '' Retrieve the filename.
    Dim As String filename = *zip_get_name(zip, i, 0)
    Print "file: " & filename & ", ";

    '' Retrieve the file size via a zip_stat().
    Dim As zip_stat stat
    If (zip_stat_index(zip, i, 0, @stat)) Then
        Print "zip_stat() failed"
        Return
    End If

    If ((stat.valid And ZIP_STAT_SIZE) = 0) Then
        Print "could not retrieve file size from zip_stat()"
        Return
    End If

    Print stat.size & " bytes"

    '' Create directories if needed
    create_parent_dirs(filename)

    '' Write out the file
    Dim As Integer fo = FreeFile()
    If (Open(filename, For Binary, Access Write, As #fo)) Then
        Print "could not open output file"
        Return
    End If

    '' Input for the file comes from libzip
    Dim As Any Ptr fi = zip_fopen_index(zip, i, 0)
    Do
        '' Write out the file content as returned by zip_fread(), which
        '' also does the decoding and everything.
        '' zip_fread() fills our buffer
        Dim As Integer bytes = _
            zip_fread(fi, buffer, BUFFER_SIZE)
        If (bytes < 0) Then
            Print "zip_fread() failed"
            Exit Do
        End If

        '' EOF?
        If (bytes = 0) Then
            Exit Do
        End If

        '' Write <bytes> amount of bytes of the file
        If (Put(#fo, , *buffer, bytes)) Then
            Print "file output failed"
            Exit Do
        End If
    Loop

    '' Done
    zip_fclose(fi)
    Close #fo
End Sub

Sub unpack_zip(ByRef archive As String)
    Dim As Any Ptr zip = zip_open(archive, ZIP_CHECKCONS, NULL)
    If (zip = NULL) Then
        Print "could not open input file " & archive
        Return
    End If

    '' For each file in the .zip... (really nice API, thanks libzip)
    For i As Integer = 0 To (zip_get_num_entries(zip, 0) - 1)
        unpack_zip_file(zip, i)
    Next

    zip_close(zip)
End Sub

    unpack_zip("test.zip")

'' .zip packing using libzip
#include "zip.bi"

Sub zipAddFileFromString(ByVal fname As ZString Ptr, ByVal id_d As ZString Ptr, ByVal myData As ZString Ptr)
    Dim As Any Ptr pzo
    Dim As Any Ptr pzsb

    pzo = zip_open(fname, ZIP_CREATE, 0)
    If pzo = 0 Then End

    pzsb = zip_source_buffer(pzo, myData, Len(*myData), 0)
    If pzsb = 0 Then End

    zip_file_add(pzo, id_d, pzsb, ZIP_FL_OVERWRITE)
    zip_close(pzo)

    Print "OK"
End Sub

Dim As String mytext, myzipfile, myfile

myText    = "<My text           >......6...3..2"
myZipFile = "26.zip"
myFile    = "file3.txt"

zipAddFileFromString(myZipFile, myFile, myText)

myFile    = "BIMBO/file3.txt"
zipAddFileFromString(myZipFile, myFile, myText)

Sleep


liblzma
Configurable compression library based around the LZMA algorithm with zlib-like API. liblzma is the heart of the xz-utils used to handle the .lzma and .xz file formats. It is based on 7-Zip's LZMA SDK.

Website: https://tukaani.org/xz/
Platforms supported: Win32, Linux, DOS
Headers to include: lzma.bi
Header version: 5.0.2
Examples: in examples/compression/


LZO
LZO is a compression library offering fast compression and very fast decompression.

Website: https://www.oberhumer.com/opensource/lzo/
Platforms supported: Win32, Linux, DOS
Headers to include: lzo/lzo.bi
Header version: 2.02

Example

#include "lzo/lzo1x.bi"

Dim inbuf As ZString Ptr = @"string to compress (or not, since it's so short)"
Dim inlen As Integer = Len(*inbuf) + 1
Dim complen As lzo_uint = 100
Dim compbuf As ZString Ptr = Allocate(complen)
Dim decomplen As lzo_uint = 100
Dim decompbuf As ZString Ptr = Allocate(decomplen)
Dim workmem As Any Ptr

Print "initializing LZO: ";
If lzo_init() = 0 Then
    Print "ok"
Else
    Print "failed!"
    End 1
End If

Print "compressing '" & *inbuf & "': ";

workmem = Allocate(LZO1X_1_15_MEM_COMPRESS)

If lzo1x_1_15_compress(inbuf, inlen, compbuf, @complen, workmem) = 0 Then
    Print "ok (" & inlen & " bytes in, " & complen & " bytes compressed)"
Else
    Print "failed!"
    End 1
End If

Deallocate(workmem)

Print "decompressing: ";

workmem = Allocate(LZO1X_MEM_DECOMPRESS)

If lzo1x_decompress(compbuf, complen, decompbuf, @decomplen, NULL) = 0 Then
    Print "ok: '" & *decompbuf & "' (" & complen & " bytes compressed, " & decomplen & " bytes decompressed)"
Else
    Print "failed!"
    End 1
End If

Deallocate(workmem)


QuickLZ
Cross-platform fast compression library

Website: https://www.quicklz.com
Platforms supported: Win32, Linux, DOS
Headers to include: quicklz.bi
Header version: 1.5.0
Example: examples/compression/QuickLZ.bas


zlib
Loss-less data compression library using the Deflate algorithm unencumbered by patents.

Website: http://www.zlib.net
Platforms supported: Win32, Linux, DOS
Headers to include: zlib.bi
Header version: 1.2.8
Examples: in examples/compression/

Example

zlib-based PNG save & load code: http://www.freebasic.net/forum/viewtopic.php?t=3936

In-memory compression example:
'' Zlib compress/decompress example, by yetifoot

#include Once "zlib.bi"

Dim As Integer errlev

'' This is the size of our test data in bytes.
Dim As Integer src_len = 100000

Print "ZLib test - Version " & *zlibVersion()
Print
Print "Test data size      : " & src_len & " bytes."

'' The size of the destination buffer for the compressed data is calculated by
'' the compressBound function.
Dim As Integer dest_len = compressBound(src_len)

'' Allocate our needed memory.
Dim As UByte Ptr src = Allocate(src_len)
Dim As UByte Ptr dest = Allocate(dest_len)

'' Fill the src buffer with random, yet still compressable data.
For i As Integer = 0 To src_len - 1
    src[i] = Rnd * 4
Next

'' Store the crc32 checksum of the input data, so we can check if the
'' uncompression has worked.
Dim As UInteger crc = crc32(0, src, src_len)

'' Perform the compression.  dest_len is passed as its address, because when
'' the function returns it will contain the size of the compressed data.
errlev = compress(dest, @dest_len, src, src_len)
If errlev <> 0 Then
    '' If the function returns a value other than 0 then an error occured.
    Print "**** Error during compress - code " & errlev & " ****"
End If
Print "Compressed to       : " & dest_len & " bytes."

'' NOTE: in normal use in a program, you would store the src_len, in order to
'' be able to tell uncompress the output size.  However in this example we can
'' just leave it in src_len.  The same goes for dest_len, which is the compressed
'' datas size.

'' Wipe the src buffer before we uncompress to it, so that we can check if the
'' decompression has worked.
For i As Integer = 0 To src_len - 1
    src[i] = 0
Next

'' Perform a decompression.  This time we uncompress the data back to src.
'' src_len is passed as its address, because when
'' the function returns it will contain the size of the uncompressed data.
errlev = uncompress(src, @src_len, dest, dest_len)
If errlev <> 0 Then
    '' If the function returns a value other than 0 then an error occured.
    Print "**** Error during uncompress - code " & errlev & " ****"
End If
Print "Uncompressed to     : " & src_len & " bytes."

'' Make sure the checksum of the uncompressed data matches our original data.
If crc <> crc32(0, src, src_len) Then
    Print "crc32 checksum      : FAILED"
Else
    Print "crc32 checksum      : PASSED"
End If

'' Free the buffers used in the test.
Deallocate(src)
Deallocate(dest)

Print
Print "Press any key to end . . . "
Sleep


CRT, the C Runtime Library
Standard C language functions. On Windows, this is implemented in msvcrt.dll (however, there also are version-specific msvcrXX.dlls, the Microsoft Visual C++ runtimes). On Linux, the C runtime is typically implemented by glibc. For DOS, FreeBASIC uses DJGPP, which provides a libc library.

Websites: http://msdn.microsoft.com/en-us/library/59ey50w6.aspx, https://www.gnu.org/software/libc/, http://www.delorie.com/djgpp/
Platforms supported: Win32, Linux, DOS
Headers to include: crt.bi
Function reference: C Runtime Functions
MSDN function reference: http://msdn.microsoft.com/en-us/library/634ca0c2.aspx


DOS API
Provides access to low-level BIOS and DOS calls.

Website: http://freedos.org
Platforms supported: DOS
Headers to include: dos/dos.bi
Examples: in examples/DOS/


disphelper
Disphelper is a COM helper library that can be used in plain C. No MFC or ATL is required. It allows you to call COM objects with an easy printf style syntax.

Website: http://disphelper.sourceforge.net/
Platforms supported: Win32, Linux (using WINE)
Headers to include: disphelper/disphelper.bi
Header version: from 2005

Example

'' HTTP GET example, using MSXML2

#define UNICODE
#include "disphelper/disphelper.bi"

DISPATCH_OBJ(objHTTP)

dhInitialize(True)
dhToggleExceptions(True)

dhCreateObject("MSXML2.XMLHTTP.4.0", NULL, @objHTTP)

dhCallMethod(objHTTP, ".Open(%s, %s, %b)", "GET", "http://sourceforge.net", False)
dhCallMethod(objHTTP, ".Send")

Dim As ZString Ptr szResponse
dhGetValue("%s", @szResponse, objHTTP, ".ResponseText")

Print "Response: "; *szResponse
dhFreeString(szResponse)

SAFE_RELEASE(objHTTP)
dhUninitialize(True)

'' IExplorer example

#define UNICODE
#include "disphelper/disphelper.bi"

Sub navigate(ByRef url As String)
    DISPATCH_OBJ(ieApp)
    dhInitialize(True)
    dhToggleExceptions(True)

    dhCreateObject("InternetExplorer.Application", NULL, @ieApp)
    dhPutValue(ieApp, "Visible = %b", True)
    dhCallMethod(ieApp, ".Navigate(%s)", url)

    SAFE_RELEASE(ieApp)
    dhUninitialize(True)
End Sub

    navigate("www.freebasic.net")

'' VB Script example

#define UNICODE
#include "disphelper/disphelper.bi"

'' This function runs a script using the MSScriptControl.
'' Optionally returns a result.
Sub RunScript _
    ( _
        ByVal result_identifier As LPWSTR, _
        ByVal result As LPVOID, _
        ByVal script As LPWSTR, _
        ByVal language As LPWSTR _
    )

    DISPATCH_OBJ(control)
    If (SUCCEEDED(dhCreateObject("MSScriptControl.ScriptControl", NULL, @control))) Then
        If (SUCCEEDED(dhPutValue(control, ".Language = %T", language))) Then
            dhPutValue(control, ".AllowUI = %b", True)
            dhPutValue(control, ".UseSafeSubset = %b", False)

            If (result) Then
                dhGetValue(result_identifier, result, control, ".Eval(%T)", script)
            Else
                dhCallMethod(control, ".Eval(%T)", script)
            End If
        End If
    End If

    SAFE_RELEASE(control)
End Sub

    dhInitialize(True)
    dhToggleExceptions(True)

    '' VBScript sample
    RunScript(NULL, NULL, !"MsgBox(\"This Is a VBScript test.\" & vbcrlf & \"It worked!\",64 Or 3)", "VBScript")

    '' JScript sample
    Dim As Integer result
    RunScript("%d", @result, "Math.round(Math.pow(5, 2) * Math.PI)", "JScript")
    Print "Result ="; result

    Print "Press any key to exit..."
    Sleep

    dhUninitialize(True)


GLib
Universal utility library most commonly used with GTK+ and GNOME. Provides a main loop implementation for event-based programming, portable multi-threading, portable file/pipe I/O, many utilities such as command line parsing, timers, XML parsing, regular expressions, Unicode manipulation, and also general-purpose data structures.

Website: http://developer.gnome.org/glib/
Platforms supported: Linux, Win32
Headers to include: glib.bi
Version: 2.42.2
Examples: in examples/misc/glib/


Installing
Installing FreeBASIC, any additionally needed packages, and perhaps a text editor or IDE.

Note: If the user specifies directory names during the installation of the different components, it is recommended in the chosen names to use only alphanumeric ASCII characters without accent and preferably not any space, otherwise some paths might not work as expected.

Windows 32bit

  • Download the latest FreeBASIC-x.xx.x-win32.zip package, or the latest FreeBASIC-x.xx.x-win32.exe installer (not recommended for Windows 7 and later versions).
  • Depending on the chosen method:
    • Zip package: Extract it where you like, for example at ...\FreeBASIC (no further installation required to use fbc).
    • Installer (not recommended for Windows 7 and later versions): Run it and click through it. The installer will install FreeBASIC at C:\%ProgramFiles%\FreeBASIC, or if you chose a different installation directory, in your chosen directory. Start Menu shortcuts to the website will be installed as well.
  • Unless you already have a source code editor or IDE, you should install one too, as FreeBASIC itself does not include one. An IDE can be used to write and save .bas files and to launch the FreeBASIC Compiler to compile them. The following IDEs are known to explicitly support FreeBASIC:
    • FBIDE
    • FBEdit
    • WinFBE Editor and Visual Designer
To uninstall FreeBASIC:
      • If previously installed using zip package: simply deleted the directory where you extracted it.
      • If previously installed using installer: remove it from the system's list of installed software (Add/remove programs, Uninstall or change a program).

Windows 64bit

  • Download the latest FreeBASIC-x.xx.x-win64.zip package.
  • Extract it where you like, for example at ...\FreeBASIC (no further installation required to use fbc).
  • You may want to install a source code editor or IDE; also see the Windows 32bit section.
To uninstall FreeBASIC, simply deleted the directory where you extracted it.

Linux

  • Download the latest FreeBASIC-x.xx.x-linux-x86.tar.gz (32bit) or FreeBASIC-x.xx.x-linux-x86_64.tar.gz (64bit) package
  • Extract the archive, for example by doing right-click -> Extract Here, or manually in a terminal:
$ cd Downloads
$ tar xzf FreeBASIC-x.xx.x-linux-x86.tar.gz

  • The FreeBASIC compiler can be used from where it was extracted. Usually it is installed into the /usr/local system directory though, so that the fbc program is available through-out the whole system. To do that, run the included installation script:
$ cd FreeBASIC-x.xx.x-linux-x86
$ sudo ./install.sh -i

The install.sh script can also be given a path as in ./install.sh -i /usr if you prefer to install into a directory other than the default /usr/local. This default is a good choice though, as it avoids mixing with the content of /usr which is usually managed by the distribution's packaging tool.

  • FreeBASIC requires several additional packages to be installed before it can be used to compile executables. In general, these are:
      • binutils
      • libc development files (installing gcc will typically install these too)
      • gcc
      • libncurses development files
      • libtinfo development files (if not already installed by libncurses)
      • X11 development files (for FB graphics programs)
      • libffi development files (for the ThreadCall keyword)
      • gpm (general purpose mouse) daemon and libgpm (only needed for GetMouse support in the Linux console)
The actual package names to install vary depending on the GNU/Linux distribution.

For native development (32bit FB on 32bit system, or 64bit FB on 64bit system):
      • Debian/Ubuntu:
        • gcc
        • libncurses5-dev
        • libtinfo5 (if not already installed by libncurses5)
        • libffi-dev
        • libgl1-mesa-dev
        • libx11-dev libxext-dev libxrender-dev libxrandr-dev libxpm-dev
      • Fedora:
        • gcc
        • ncurses-devel
        • libffi-devel
        • mesa-libGL-devel
        • libX11-devel libXext-devel libXrender-devel libXrandr-devel libXpm-devel
      • OpenSUSE:
        • gcc
        • ncurses-devel
        • libncurses5
        • libffi-devel
        • xorg-x11-devel

For 32bit development on a 64bit system:
      • Debian/Ubuntu:
        • gcc-multilib
        • lib32ncurses5-dev
        • libx11-dev:i386 libxext-dev:i386 libxrender-dev:i386 libxrandr-dev:i386 libxpm-dev:i386
        • (See comment below re Ubuntu 10.04 LTS)
      • OpenSUSE:
        • gcc-32bit
        • ncurses-devel-32bit
        • xorg-x11-devel-32bit
        • xorg-x11-libX11-devel-32bit
        • xorg-x11-libXext-devel-32bit
        • xorg-x11-libXrender-devel-32bit
        • xorg-x11-libXpm-devel-32bit
        • libffi-devel-32bit

  • Unless you already have a text editor or IDE, you should install one too, as FreeBASIC itself does not include one. An IDE can be used to write and save .bas files and to launch the FreeBASIC Compiler to compile them. The following IDEs are known to explicitly support FreeBASIC:
    • Geany

To uninstall FreeBASIC from /usr/local, you can run the install.sh script again, but with the -u option: sudo ./install.sh -u

DOS

  • Download the latest FreeBASIC-x.xx.x-dos.zip archive
  • Find a place for FreeBASIC with at least 13 MiB free space.
  • Unpack the ZIP archive, making sure that the directory structure as used inside the archive is preserved ("PKUNZIP -d" for example).
  • The top-level directory is named FreeBASIC-x.xx.x-dos (will be truncated to "FREEBASI" in DOS without full LFN support), so you might want to rename it then to a convenient DOS-compliant name not longer than 8 characters and containing no white-spaces, like "FB".
  • All the important files used by the compiler (includes, libs) inside the archive do have DOS-compliant names, therefore DOSLFN is not required to use FreeBASIC, however, some examples and texts do have longer names and will be truncated when extracted without full LFN support.
(Note: you can install the DOS version "over" the Windows one or vice-versa, or "merge" those installations later, but rename the FBC.EXE file of the previous installation to FBCW.EXE , FBCD.EXE or such, or it will be overwritten by the new one. Other platform specific files are placed in subdirectories making sure that they won't conflict.)

Compiling under Ubuntu 10.04 LTS, 64-bit:
This comment applies to FB 1.01.0, and may apply to other builds also.
Install all of the Libraries listed above; some of the entries ending in ":i386" may throw "not found" errors.
To verify that you're using a 64-bit build, use: "uname -a" or "uname -m" (it'll show x86_64 for 64-bit, i386 for 32-bit).
Then, when running FBC, an error may appear: "error while loading shared libraries: libtinfo.so.5: cannot open shared object file: No such file or directory".

"libtinfo.so.5" is available as a separate library in Ubuntu 11.10+, but it is built into "ncurses.so.5" in 10.04 LTS. So, we need to re-direct the libtinfo references into the ncurses.so.5 libraries:
  • Issue: find / -name 'libtinfo.so.5' - just to verify that there are no confusing references to these libraries anywhere. Any references should be checked, and probably deleted?
  • Change to the folder containing the FBC executable (perhaps "/usr/local/bin/").
  • Issue: ldd fbc - it will list the various library folder(s) being searched (probably "/lib32" in most cases).
  • Issue: sudo ln -s /lib32/libncurses.so.5 /lib32/libtinfo.so.5 (assuming "/lib32" was emitted in the previous step).
  • Issue: sudo ln -s /lib32/libtinfo.so.5 /lib32/libtinfo.so (assuming "/lib32"...)
  • Retry!
  • [Unrelated point: if "private" Libraries are needed for compiles, they were expected to be in /usr/local/lib/freebasic/. Now, they may have to be in /usr/local/lib/freebasic/linux-x86/].
  • [Mike Kennedy, Jan, 2015. (This note was not acceptable as a standard "comment" - I don't know why?)].

See also

  • Invoking the Compiler
  • Compiler Command Line Options


Requirements

Windows version
  • The FreeBASIC compiler (fbc.exe) and the executables generated by it, need at least Windows 98 to run.
  • The msvcrt.dll (the Microsoft's C runtime library) must be present (note: it wasn't shipped with Windows 95, but it's installed by many applications and can be also downloaded at: Microsoft).
  • The gfx routines will use Direct2D if found on the host system, otherwise they'll fall back on DirectX, and as last resort on standard Win32 GDI which will work on any Windows system.
  • Unicode wide strings (WSTRING's) only work in Windows NT/2000/XP/2003/Vista or above. Applications that depend on wide-strings will run in Windows 98/Me, but no input/output will work if the character set isn't Latin-based, because those platforms don't support Unicode strings. Windows 95 has most Unicode API functions missing; applications using wide strings won't even be loaded by this specific OS.

Linux version
  • The FreeBASIC compiler (fbc) and the executable generated by it depend on libc, libm, libpthread, libdl and libncurses. These are all standard Linux libraries and should be available by default on all modern distros.
  • When using the gfx routines, the dependencies will increase. FreeBASIC gfx programs will also need libX11, libXext, libXpm, libXrender and libXrandr to be installed on the host system to be executed. This is usually not a problem as long as there's a recent X11 server installed in the system (at least XFree86 4.3.0 or any X.org version).
  • If having a working X11 installation is enough to run FreeBASIC gfx programs, it may be not enough to compile them; you may need to install the X11 development libraries from your Linux packages repository.
  • Unicode wide-strings (WSTRING's) with non-ASCII character sets can only be displayed in console if the locale is set to an UTF-8 version - most modern distros come with support that and char sets other than latin may work only in xterm.

DOS version
  • Official requirement: A DPMI (DOS Protected-Mode Interface) server must be present to run fbc.exe and any executable generated by it. This is not as bad as it looks. It simply means, that the "CWSDPMI.EXE" file (cca 20 KiB) must be present in the same directory or a place where the PATH environment variable points to. CWSDPMI package: homer.rice.edu...cwsdpmi (note: FreeDOS comes with it already installed). Further, there is a possibility to bypass this problem, and to use alternatively HDPMI, for details see DOS related FAQ .
  • You need a 80386 or newer CPU and cca 4 MiB of RAM. For compiling of large programs or libraries, you will need more. Similar applies to executables generated by FBC, those using FB's graphics library however will need a better/faster CPU (200 MHz (?), work in progress, code not yet fully optimized, and exact minimum not known by now). FBC and executables generated by it need an FPU (80387, 80487, always built-in since Pentium). This requirement can by bypassed using "EMU387" (auto-loaded if needed, but not included in FB packages, see delorie.com/djgpp/... ), or by avoiding floats and (non-trivial) removing float-related startup code.
  • The DOS version should run in any DOS, like FreeDOS, [Enhanced-]DR-DOS (do not use the DR-EMM386's included DPMI, use CWSDPMI or HDPMI), or MS-DOS. It also works properly under a number of "DOS box" environments that emulate a DOS system, such as the Windows NTVDM; however, some of these environments are not implemented faithfully and contain bugs, so caution should be exercised.
  • Long filenames are supported under systems that supply the long filename API defined by Windows 95, including DOS with an LFN TSR (for example DOSLFN (1) (2)). Long filename support is not required to use the compiler; however, care must be taken in unpacking the distribution, for example, with a Windows program which creates short names with numeric tails (FREEBA~1) instead of truncating to 8 characters (FREEBASI). The filenames of all files in the distribution should be truncated to 8.3 if the compiler is to be used without long filename support.
  • There are a few limitations, see DOS related FAQ .

See also

  • Installing FreeBASIC
  • Compiler Command Line Options
and
  • Compiler FAQ
  • Win32 related FAQ
  • DOS related FAQ
  • LINUX related FAQ


Running
Invoking the compiler after installation.

Windows
The compiler can be manually invoked from the command-line, or automatically by your IDE/Code Editor. If you're using an IDE, you will usually have to tell it where the compiler was installed, so it can find it. How exactly to do that depends on the IDE.

To compile manually, you should append the FreeBASIC installation directory to your PATH environment variable, separating it from previous entries using a semi-colon. Now you can simply use "fbc" from the command prompt, instead of always having to type in the full path (e.g. "C:\FreeBASIC\fbc.exe").

Then, open a console/command prompt/MS DOS prompt, in the same directory as your program. To compile your program, you can use:

C:\mystuff\myprogram\> fbc myprogram.bas

and myprogram.exe will be created in the same directory.

A console can be launched in a specific directory from Explorer by using Microsoft's "Open Command Window Here" PowerToy on Windows XP. On Windows Vista & above you can SHIFT+RightClick on a folder in Explorer to see the 'Open Command Window Here' option. As a last resort, you can also select Start -> Run, type "cmd" and hit Enter, and use the "cd" command to change the current directory.

Note: You can in fact invoke the compiler from any directory you like, but you have to specifiy the correct path to your program, so the compiler can find it, for example:

C:\> fbc mystuff\myprogram\myprogram.bas

The resulting executable will still be put in the same directory as the program.

Linux
If the install.sh script was successfully executed with enough priviledges, the compiler binary should have been copied /usr/local/bin/fbc, allowing any user access to the compiler from any directory.

From the prompt, type,

fbc

to see a list of options. To compile the "Hello, world!" example program, navigate to the directory where the FreeBASIC examples were installed (/usr/local/share/freebasic), and type,

fbc examples/misc/hello.bas

and a ./hello executable file will be created in the examples/misc directory.

Linux (standalone)
If the install script install-standalone.sh was successfully executed with enough privileges, a link to the compiler binary should have been created at /usr/bin/fbc, allowing any user access to the compiler from any directory. If it was not possible to create the link, you may want to alter your PATH environmental variable to be able to invoke the compiler from any directory. Navigate to the directory where FreeBASIC was installed.

From the prompt, type,

fbc

to see a list of options. To compile the "Hello, world!" example program type,

fbc examples/misc/hello.bas

and a ./hello executable file will be created in the examples/misc directory.

DOS
Navigate to the directory where FreeBASIC was installed. For example, if FreeBASIC is installed in the directory C:\FB, type,

C:
CD FB

Some DOSes accept "CDD C:\FB" as well. You can also add the FreeBASIC directory to your PATH environment variable (usually something like "SET PATH=C:\FB\;%PATH%") so you can invoke the compiler from any directory.

At the prompt, type,

fbc

to see a list of options. To compile the "Hello, world!" example program type,

fbc examples\misc\hello.bas

and a hello.exe executable file will be created in the examples\misc directory.

See also

  • Installing FreeBASIC
  • Compiler Command Line Options
  • Compiler FAQ


fbc command-line
Using the fbc command-line.

The official FreeBASIC distribution comes with fbc, FreeBASIC's flagship compiler. fbc is a command line compiler, and can be launched from the console - from DOS, the Windows command prompt or a Linux shell. Running fbc from the console without any arguments displays a list of available options, or command-line switches, that can be used to adjust the behavior of the compiler.

At its simplest, fbc takes a source file as a command-line argument and produces an executable file. It does this by compiling the source file (.bas) into an assembly (.asm) file, then compiling this into an object file (.o) using GAS and finally linking using LD this object file to other object files and libraries it needs to run, producing the final executable file. The assembly and compiled object files are deleted at this point by default. For example, the following command,

fbc foo.bas
produces the executable foo.exe in DOS and Windows, and ./foo in Linux. fbc can accept multiple source files at once, compile and link them all into one executable. For example, the following command,

fbc foo.bas bar.bas baz.bas
produces the executable foo.exe in DOS and Windows, and ./foo in Linux. Since foo.bas was listed first, it will be the main entry point into the executable, and also provide its name. To specify a different entry point or executable name, use the "-m" and "-x" switches, respectively. To have, for example, baz.bas provide the main entry point into an executable called foobar.exe, you would use

fbc -x foobar.exe -m baz foo.bas bar.bas baz.bas
The "-x" switch names the executable verbatim, so in Linux, the executable produced from the above command would be called ./foobar.exe.

Syntax

fbc [ options ] [ input_list ]

Where input_list is a list of filenames. Accepted files are:

File extension | Description
.bas | FreeBASIC source file
.a | Library
.o | Object file
.rc | Resource script (Windows only)
.res | Compiled resource (Windows only)
.xpm | X icon pixmap (Linux only)

Source code
-b < name >
Add a source file to compilation
-i < name >
Add a path to search for include files
-include < name >
Include a header file on each source compiled
-d < name=val >
Add a preprocessor's define
-lang < name >
Select language mode: fb, fblite, qb, deprecated
-forcelang < name >
Select language mode: fb, fblite, qb, deprecated (overrides statements in code)

Code generation
-target < platform >
Set the target platform for cross compilation
-gen < backend >
Sets the compiler backend (default is 'gas' for x86 and 'gcc' for x86_64)
-asm < format >
Sets the assembler format for Asm block
-arch < type >
Set target architecture
-O < level >
Set the optimization level (-gen gcc)
-vec < level >
Set level of vector optimizations enabled by the compiler (default: 0)
-fpu < type >
Set the floating point arithmetics unit (default: X87)
-fpmode < type >
Select between fast and accurate floating-point operations (default: PRECISE)
-z < value >
Sets miscellaneous or experimental options
-pic
Generate position-independent code (non-x86 Unix shared libs)

Compilation
-m < name >
Define main file (without extension), the entry point (default is the first .bas file on the command line)
-entry < name >
Override public exported name of implicit user main function
-g
Add debug info
-profgen < profiler >
Enable function profiling by selecting a specific profiler
-profile
Enable function profiling for gmon/gprof
-e
Add error checking
-ex
Add error checking with RESUME support
-exx
Same as -ex, plus array bounds and dimensions, null-pointer, and error location reporting
-eunwind
Enable stack unwind information
-Wa < opt >
Pass options to GAS (separated by commas)
-Wc < opt >
Pass options to GCC (separated by commas)
-o < name >
Set object file path/name (must be passed after the .bas file)
-earray
Enable array bounds checking
-earraydims
Enable array dimensions checking
-eassert
Enable assert() and assertwarn() checking
-edebug
Enable __FB_DEBUG__
-edebuginfo
Add debug information
-elocation
Enable full error location reporting
-enullptr
Enable null-pointer checking

Linking
-a < name >
Add an object file to linker's list
-l < name >
Add a library file to linker's list
-p < name >
Add a path to search for libraries
-mt
Link with thread-safe runtime library
-nodeflibs
Do not include the default libraries
-nolib < name(s) >
Do not include specific library(s)
-static
Prefer static libraries over dynamic ones when linking
-map < name >
Save the linking map to file name
-Wl < opt >
Pass options to LD (separated by commas)
-export
Export symbols for dynamic linkage
-lib
Create a static library
-dylib
Create a DLL, including the import library
-dll
Create a DLL, including the import library. (Same as -dylib)
-x < name >
Set executable/library path/name
-nostrip
Do not strip symbol information from the output file
-strip
Omit all symbol information from the output file
-sysroot < path >
Tell the linker where to find libraries (needed by some toolchains)

Behaviour
-prefix < path >
Set the compiler prefix path
-buildprefix < name >
Set the internal buildprefix option
-version
Show compiler version on the command line, do not compile or link
-v
Be verbose
-print < option >
Display certain information (fblibdir, host, target, x)
-pp
Emit the preprocessed input file only, do not compile
-r
Compile into intermediate file(s) only, do not assemble or link
-rr
Compile into asm file(s) only, do not assemble or link
-c
Compile and assemble source file only, do not link
-R
Do not delete the intermediate file(s)
-RR
Do not delete the asm file(s)
-C
Do not delete the object file(s)
-w < value >
Set min warning level: all, none, param, escape, pedantic, next, funcptr, constness, suffix, error, upcast or a value
-maxerr < val >
Only stop parsing if <val> errors occurred
-noerrline
Do not show source line where error occurred
-noobjinfo
Do not read/write compile-time info from/to .o and .a files
-showincludes
Display a tree of file names of #included files

Target specific
-s < name >
Set subsystem (gui, console)
-t < value >
Set stack size in kbytes (default: 1 MB or 2 MB)

Meta
@< file >
Read (additional) command-line options from a file

Example

fbc myfile.bas
(With DOS version of FBC, compile and link a DOS executable MYFILE.EXE.)

fbc -s gui myfile.bas
(With Windows version of FBC, compile and link a Windows executable myfile.exe. Running the program will not show the console window ("MS-DOS Prompt"))

fbc -lib module1.bas module2.bas module3.bas -x libmylib.a
(Compile and link a static library libmylib.a from the three source files)

fbc -m main_module -c main_module.bas
(Compile an object file main_module.o and mark it as an entry point)

fbc -c sub_module.bas
(Compile an object file sub_module.o)

fbc -x application.exe main_module.o sub_module.o
(Link an executable application.exe)

Note: How to include an icon in a FB executable program
There is a simple command line option to compile a FB program into an executable with an Icon:
- Create a *.rc file, for example appicon.rc, with this info:
FB_PROGRAM_ICON ICON "appicon.ico"
(where appicon.ico is the name of icon)
- Then when compiling program, add appicon.rc in the list of files to compile.

See also

  • Compiler Options
  • Installing FreeBASIC
  • Invoking the FreeBASIC compiler
  • #Cmdline


Compiler Options
Command line compiler options for the fbc compiler:
@< file >
    • Read (additional) command-line options from the file
-a < name >
    • Add an object file to linker's list
-arch < type >
    • Set target architecture
-asm < format >
    • Sets the assembler format for Asm block
-b < name >
    • Add a source file to compilation
-buildprefix < name >
    • Set the internal buildprefix option
-c
    • Compile only, do not link
-C
    • Do not delete the object file(s)
-d < name=val >
    • Add a preprocessor's define
-dll
    • Create a DLL, including the import library. (Same as -dylib)
-dylib
    • Create a DLL, including the import library
-e
    • Add error checking
-earray
    • Enable array bounds checking
-earraydims
    • Enable array dimensions checking
-eassert
    • Enable assert() and assertwarn() checking
-edebug
    • Enable __FB_DEBUG__
-edebuginfo
    • Add debug information
-elocation
    • Enable full error location reporting
-entry < name >
    • Override public exported name of implicit user main function
-enullptr
    • Enable null-pointer checking
-eunwind
    • Enable stack unwind information
-ex
    • Add error checking with RESUME support
-exx
    • Same as -ex, plus array bounds and dimensions, null-pointer, and error location reporting
-export
    • Export symbols for dynamic linkage
-forcelang <name>
    • Select language compatibility, overriding #lang/$lang in code
-fpmode < type >
    • Select between fast and accurate floating-point operations (default: PRECISE)
-fpu < type >
    • Set the floating point arithmetics unit (default: X87)
-g
    • Add debug info, enable##__FB_DEBUG__##, and enable asserts
-gen < backend >
    • Sets the compiler backend (default is 'gas' for x86 and 'gcc' for x86_64)
-i < name >
    • Add a path to search for include files
-include < name >
    • Include a header file on each source compiled
-l < name >
    • Add a library file to linker's list
-lang < name >
    • Select language compatibility: fb, fblite, qb, deprecated
-lib
    • Create a static library
-m < name >
    • Define main file (without extension), the entry point (default is the first .bas file on the command line)
-map < name >
    • Save the linking map to file name
-maxerr < val >
    • Only stop parsing if <val> errors occurred
-mt
    • Link with thread-safe runtime library
-nodeflibs
    • Do not include the default libraries
-nolib < name(s) >
    • Do not include specific library(s)
-noerrline
    • Do not show source line where error occurred
-noobjinfo
    • Do not read/write compile-time info from/to .o and .a files
-nostrip
    • Do not strip symbol information from the output file
-o < name >
    • Set object file path/name (must be passed after the .bas file)
-O < level >
    • Set the optimization level (-gen gcc)
-p < name >
    • Add a path to search for libraries
-pic
    • Generate position-independent code (non-x86 Unix shared libs)
-pp
    • Emit the preprocessed input file only, do not compile
-prefix < path >
    • Set the compiler prefix path
-print < option >
    • Let the compiler display certain information (fblibdir, host, target, x)
-profgen < profiler >
    • Enable function profiling by selecting a specific profiler
-profile
    • Enable function profiling for gmon/gprof
-r
    • Compile into *.asm / *.c / *.ll file(s) only, do not assemble or link
-R
    • Preserve intermediate *.asm / *.c / *.ll file(s) generated by compilation
-rr
    • Compile into *.asm file(s) only, do not assemble or link
-RR
    • Preserve intermediate *.asm files generated by compilation
-s < name >
    • Set subsystem (gui, console)
-showincludes
    • Display a tree of file names of #included files
-static
    • Prefer static libraries over dynamic ones when linking
-strip
    • Omit all symbol information from the output file
-sysroot < path >
    • Tell the linker where to find libraries (needed by some toolchains)
-t < value >
    • Set stack size in kbytes (default: 1 MB or 2 MB)
-target < platform >
    • Set the target platform for cross compilation
-v
    • Be verbose
-vec < level >
    • Set level of vector optimizations enabled by the compiler (default: 0)
-version
    • Show compiler version
-w < value >
    • Set min warning level: all, none, param, escape, pedantic, next, funcptr, constness, suffix, error, upcast or a value
-Wa < opt >
    • Pass options to GAS (separated by commas)
-Wc < opt >
    • Pass options to GCC (separated by commas)
-Wl < opt >
    • Pass options to LD (separated by commas)
-x < name >
    • Set executable/library path/name
-z < value >
    • Sets miscellaneous or experimental options

See also

  • Using the Command Line
  • #Cmdline


Compiler Option: @file
Read (additional) command-line options from the file

Syntax

@file

Parameters

file
Name of a text file containing command line options. It's possible to use multiple lines in the file. The options may be separated by spaces or line breaks, and support double-quoted strings to allow spaces in parameters (like the real command line). This file can itself contain additional @file options.

Description

The @file compiler option tells the compiler to parse the specified file to find more command line options. The options found in the file are treated as if they were found on the command line. This can be useful to pass very long command lines to the compiler, for example on DOS, where command lines are limited in length.

Example

options.txt:
-d TEST=123

opts.bas:
Print "TEST=" & TEST

Compile with:

fbc @options.txt opts.bas

Output:
TEST=123
See also

  • Using the Command Line


Compiler Option: -a
Add an object file to the linker's list

Syntax

[ -a ] < object file >

Parameters

object file
Name of the object file with extension.

Description

The -a compiler option adds a compiled object file to the linker's list. The "-a" is optional if the object file name has a ".o" file extension.

See also

  • Compiler Option: -b
  • Using the Command Line


Compiler Option: -arch
Set target architecture for improved/restricted code generation or cross-compiling

Syntax

-arch < architecture >

Parameters

architecture
The target architecture. Recognized values:

      • Related to 32bit x86:
        • 386
        • 486
        • 586 (default for x86 on DOS)
        • 686 (default for x86 except on DOS)
        • athlon
        • athlon-xp
        • athlon-fx
        • k8-sse3
        • pentium-mmx
        • pentium2
        • pentium3
        • pentium4
        • pentium4-sse3
        • x86
      • Related to 64bit x86_64:
        • x86_64, x86-64, amd64
      • Related to 32bit ARM:
        • armeabi
        • armeabi-v7a (default for arm-android)
        • armv5 (alias for armv5te)
        • armv5te (default for armeabi)
        • armv6
        • armv7 (alias for armv7-a)
        • armv7a (alias for armv7-a)
        • armv7-a (default for ARM, but the toolchain may interpret as armv7-a+fp)
        • armv7-a+fp
        • There are variations of arm architecture in the wild and interpretations will vary across toolchains and time
      • Related to 64bit ARM (AArch64):
        • aarch64
      • Related to 32bit PowerPC:
        • powerpc (big endian)
      • Related to 64bit PowerPC:
        • powerpc64 (big endian)
        • powerpc64le (little endian)
      • Others:
        • native: For compiling to the architecture which the compiler is running on.
        • 32, 64: For quick cross-compiling to the 32bit or 64bit version of the default architecture.

Description

The -arch compiler option sets the target CPU architecture. This can be used for multiple purposes:
    • Improving code generation; for example: You can use -arch pentium4-sse3 to override the default -arch 686, and the compiler will generate faster code in some cases, by using certain instructions which were not available on i686.
    • Restricting code generation; for example: You can use -arch 386 to limit the compiler to using only i386-compatible instructions.
    • Cross-compiling; for example: You can use -arch x86_64 on 32bit x86 systems to cross-compile to 64bit x86_64.

The exact impact which the -arch setting has on code generation depends on the code generation backend that is being used. The x86 ASM backend (-gen gas) handles the -arch setting and adjusts code generation accordingly in some cases. When using the GCC backend (-gen gcc), the specified architecture will be passed on to gcc via gcc -march=<...>, causing gcc to generate code for the specified architecture.

However, -arch only affects newly generated code, but not pre-compiled code such as the FreeBASIC runtime libraries, or any other library from the lib/ directory. For example, using -arch 386 is not necessarily enough to get a pure i386 executable -- it also depends on how all the libraries that will be linked in were compiled.

The -arch 32 and -arch 64 shortcuts are similar to gcc's -m32/-m64 options. On 32bit architectures, -arch 64 is an abbreviation for cross-compiling to the default 64bit version of the architecture (e.g. from 32bit x86 to 64bit x86_64, or 32bit ARM to 64bit AArch64, or 32bit PowerPC to 64bit PowerPC), and -arch 32 does nothing. On 64bit systems, it is the other way round: -arch 32 cross-compiles to the default 32bit architecture, while -arch 64 does nothing.

The -arch native shortcut is similar to gcc's -march=native option. On x86, it causes fbc to try and detect the host CPU automatically based on the cpuid instruction and its availability or results. On other architectures, this will currently simply use the architecture which the compiler itself was built for. Under -gen gcc this will use gcc -march=native.

Specifying an -arch setting incompatible to the native architecture will trigger cross-compilation, just like the -target option, except that only the target architecture, but not the target operating system, is changed.

Version

  • Before fbc 1.10.0, default was 486 for 32bit x86.

See also

  • Using the Command Line
  • -target
  • FB and cross-compiling


Compiler Option: -asm
Set assembler format for inline assembly under -gen gcc

Syntax

-asm < format >

Parameters

format
The assembler format: intel or att

Description

The -asm compiler option sets the assembler format for inline Asm blocks when using -gen gcc.

    • -gen gcc -asm intel: FB inline assembly blocks must use FB's usual Intel syntax format. Under -gen gcc, fbc will try to translate it to gcc's format automatically. For example:
Dim a As Long = 1
Print a
Asm
    inc dword Ptr [a]
End Asm
Print a

    • -gen gcc -asm att: FB inline assembly blocks must use gcc's format. For example:
Dim a As Long = 1
Print a
Asm
    "incl %0\n" : "+m" (a) : :
End Asm
Print a

The x86 ASM backend (-gen gas) currently only supports -asm intel and using -asm att results in an error.

See also

  • __FB_ASM__
  • Using the Command Line


Compiler Option: -b
Add a source file to compilation

Syntax

[ -b ] < source file >

Parameters

source file
The name with extension, and optionally a path, of the basic source file.

Description

The -b option adds a source file to the compilation list. In general, this option is redundant since source files with a .bas extension can be specified without it, but is useful if a source file does not have a .bas extension, or if exists in an other directory.

To compile and link the source files file1.bas, file2.bas and file3.fb into an executable (file1.exe in DOS/Windows, file1 in Linux), type,

fbc -b file1.bas file2.bas -b file3.fb

Note that the -b option was not needed for file1.bas or file2.bas.

See also

  • Compiler Option: -a
  • Using the Command Line


Compiler Option: -buildprefix
Set the internal buildprefix option.

Syntax

-buildprefix < name >

Parameters

name
name to override the internal buildprefix option.

Description

The -buildprefix command line option overrides the internal buildprefix option.

Detailed behavior if using -buildprefix, -prefix, -target command line options
fbc's default options should work for native builds.
When building for other platforms or developing and building with multiple tool chains, these command line options can help ease the needs of various non-native or uncommon compilations:
      • Default prefix:
- prefix may have been compiled in when fbc itself was compiled, in which case the default prefix is determined by fbc's build/makefile option 'ENABLE_PREFIX'
If fbc was not compiled with 'ENABLE_PREFIX':
- standalone: prefix = <fbc path>/
- normal : prefix = <fbc path>/../
      • Override default prefix:
- if the '-prefix <path>' command line option given, overrides the default prefix
      • Default target:
- determined by target build for fbc itself by fbc's makefile
      • Override default target:
- if the '-target <target>' command line option given, overrides the default target
      • Default buildprefix:
- always empty
      • Override default buildprefix:
- if the '-buildprefix <name>' command line option given, overrides the default build prefix
      • Default binary, include, and library paths:
- binary path: where tools are located (AS, LD, AR, etc)
- include path: location of fbc's '*.bi' files
- library path: where fbc's libraries are located (static libraries and import libraries)
- toolname: the name of a specific tool, which may include the path for a specific location, or the name of the tool only if relying on PATH environment variable
standalone fbc:
- binary path = <prefix>/bin/<target>/
- toolname = <binary path> + <buildprefix> + <tool name>[.exe]
- include path = <prefix>/inc
- library path = <prefix>/lib/<target>
normal fbc:
- default binary path:
- binary path = <prefix>/bin/<target->
- or if '-buildprefix' given:
- binary path = <prefix>/bin/<buildprefix>
- toolname = <binary path> + <tool name>[.exe]
- If binary tool not found, then rely on PATH environment variable to find:
- toolname = <target-|buildprefix> + <tool name>
- include path = prefix/include/freebasic
- library path = prefix/lib[64]/freebasic/target
      • Environment variables override tool name and path completely:
- environment variables can override the tool name and path completely
- 'AS' overrides gnu assembler tool
- 'AR' overrides gnu archiver tool
- 'LD' overrides gnu linker
- 'GCC' overrides gnu C compiler
- 'LLC' overrides LLVM compiler
- 'CLANG' overrides clang C compiler (currently never used)
- 'DLLTOOL' overrides gnu dlltool (GAS)
- 'GORC' overrides resource tool (if fbc was compiled with ENABLE_GORC)
- 'WINDRES' overrides resource tool (if fbc was compiled without ENABLE_GORC)
- 'CXBE' overrides xbox xbe generator tool
- 'DXE3GEN' overrides djgpp DOS dll tool
- 'EMAR' overrides emscripten archiver ool
- 'EMCC' overrides emscripten assember, compiler, and linker tools

The tool name prefix (stored in the binary path) is set by default with 'target' + '-'.
'-buildprefix <name>' overrides the tool name prefix.

When 'binary path' is set (internally in the fbc compiler), it includes the prefix of the tool name.

For examples:
fbc -prefix /pppp/ -target tttt source.bas
gnu-AS invoked is: /pppp/bin/tttt-as

fbc -prefix /pppp/ -target tttt -buildprefix bbbbX source.bas
gnu-AS invoked is: /pppp/bin/bbbbXas

'-prefix' command line option controls top level path for fbc.
'-target' command line option controls code generation and many related options including the default tool name prefix.
'-buildprefix' command line option overrides the default tool name prefix.

Version

  • Since fbc 1.10.0

See also

  • -prefix
  • -target
  • Using the Command Line


Compiler Option: -c
Compile and assemble source file only, do not link

Syntax

-c

Description

The -c option specifies that any source files listed are to be compiled and assembled into object files, and not linked into an executable (the default behavior). When using the "-c" switch, "-m" must be specified when compiling a main source file.

See also

  • Compiler Option: -C
  • Compiler Option: -r
  • Compiler Option: -m
  • Compiler Option: -o
  • Using the Command Line


Compiler Option: -C
Do not delete the object file(s)

Syntax

-C

Description

The -C compiler option causes the object file(s) that are generated during the compile process to not be deleted.

See also

  • Compiler Option: -c
  • Compiler Option: -R
  • Using the Command Line


Compiler Option: -d
Add a preprocessor definition

Syntax

-d < name=value >
-d < name >

Parameters

name
Name of the preprocessor macro to define. No parameters are allowed.
value
Value to give to the macro. If omitted, it will be defined as 1

Description

The -d compiler option adds a preprocessor macro to all source files. The same as using the preprocessor directive #define or #macro.

See also

  • #define
  • #macro
  • Intrinsic Defines
  • Using the Command Line


Compiler Option: -dll
Create a DLL and import library

Syntax

-dll

Description

The -dll compiler option creates a dynamic link library. This creates a DLL under Windows (including the import library), creates a .so under Linux, and a DXE under DOS.

Note: Same as -dylib

The intrinsic macro __FB_OUT_DLL__ is set to non-zero (-1) if the -dll option was specified, and set to zero (0) otherwise.

Platform Differences

  • On windows, dynamic link library is created with ".dll" file name extension and import library created as "lib<name>.dll.a" file name
  • On Linux, dynamic link library is created with ".so" file name extension
  • On DOS, dynamic link library is created with ".dxe" file name extension and import library created as "lib<name>_il.a" file name

See also

  • __FB_OUT_DLL__
  • Shared Libraries
  • Using the Command Line


Compiler Option: -dylib
Create a DLL and import library

Syntax

-dylib

Description

The -dylib compiler option creates a dynamic link library. This creates a DLL under Windows (including the import library), creates a .so under Linux, and a DXE under DOS.

Note: Same as -dll.

The intrinsic macro __FB_OUT_DLL__ is set to non-zero (-1) if the -dll option was specified, and set to zero (0) otherwise.

Platform Differences

  • See Compiler Option: -dll

See also

  • __FB_OUT_DLL__
  • Shared Libraries
  • Using the Command Line
  • Compiler Option: -dll


Compiler Option: -e
Add error checking

Syntax

-e

Description

Adds QuickBASIC-like error checking.

See also

  • __FB_ERR__
  • Compiler Option: -ex
  • Compiler Option: -exx
  • Error Handling
  • Using the Command Line


Compiler Option: -earray
Enable array bounds checking

Syntax

-earray

Description

Add array bounds error checking in generated run-time code. A run-time error is generated if an array index attempts to access an array element that is outside the bounds of the array.

-earray is automatically implied by the -exx option.

__FB_ERR__ can be used to determine in user source code if the -earray option was specified or implied on the command line.

Version

  • Since fbc 1.07.0

See also

  • __FB_ERR__
  • Compiler Option: -exx
  • Error Handling
  • Using the Command Line


Compiler Option: -earraydims
Enable array dimensions checking.

Syntax

-earraydims

Description

The -earraydims compiler option enables the array dimensions checking. Enabled by default with '-exx' command line option.

Version

  • Since fbc 1.20.0

See also

  • __FB_ERR__
  • Using the Command Line


Compiler Option: -eassert
Enable assert() and assertwarn() checking

Syntax

-eassert

Description

The -eassert compiler option enables Assert and AssertWarn code generation.

-eassert is automatically implied by the -g option.

__FB_ERR__ can be used to determine in user source code if the -eassert option was specified or implied on the command line.

Version

  • Since fbc 1.07.0

See also

  • __FB_ERR__
  • Assert
  • AssertWarn
  • Compiler Option: -g
  • Debugging
  • Using the Command Line


Compiler Option: -edebug
Enable __FB_DEBUG__

Syntax

-edebug

Description

The -edebug compiler option defines the intrinsic macro __FB_DEBUG__ to enable user debug code.

The intrinsic macro __FB_DEBUG__ is set to non-zero (-1) if the option was specified, and set to zero (0) otherwise.

-edebug is automatically implied by the -g option.

__FB_ERR__ can be used to determine in user source code if the -edebug option was specified or implied on the command line.

Version

  • Since fbc 1.07.0

See also

  • __FB_DEBUG__
  • __FB_ERR__
  • Compiler Option: -eassert
  • Compiler Option: -debuginfo
  • Compiler Option: -g
  • Debugging
  • Using the Command Line


Compiler Option: -edebuginfo
Add debug information

Syntax

-edebuginfo

Description

The -edebuginfo compiler inserts debugging symbols into output files, to use with GDB-compatible debuggers.

-edebuginfo is automatically implied by the -g option.

__FB_ERR__ can be used to determine in user source code if the -edebugingo option was specified or implied on the command line.

Version

  • Since fbc 1.07.0

See also

  • __FB_ERR__
  • Compiler Option: -g
  • Debugging
  • Using the Command Line


Compiler Option: -elocation
Enable full error location reporting

Syntax

-elocation

Description

The -elocation compiler option enables full location reporting in run-time code generation.

-elocation is automatically implied by the -exx option.

__FB_ERR__ can be used to determine in user source code if the -elocation option was specified or implied on the command line.

Note: the intent of this option is for a future fbc feature to optimize out some of the error tracking code associated with error location reporting. When implemented, only -exx or -elocation will enable the full error location reporting and will be optimized away otherwise.

Version

  • Since fbc 1.07.0

See also

  • __FB_ERR__
  • Compiler Option: -exx
  • Error Handling
  • Using the Command Line


Compiler Option: -entry
Override the public exported name of the implicit user main function

Syntax

-entry < name >

Parameters

name
The custom name that overrides the public exported name of the implicit user main function

Description

The -entry compiler option changes the name of the publicly export symbol of the implicit user main function (automatically generated by fbc as start-up code).
It affects the start-up code and implicit user main, which is also affected by the module being the main module or not.

By default, fbc defines a publicly exported 'main' symbol in the main module as required by the run time start-up system code.
There is no other way currently to override fbc's use of the 'main' symbol other than to always compile and link separately.

Proceedings:
- The custom main function name must only be declared with the following signature (same parameter types, same return type, same calling convention) to be compatible with the (basic) main function:
'' internally the implicit main function is now named "custom_main"
Declare Function custom_main cdecl Alias "custom_main" ( ByVal argc As Long, ByVal argv As ZString Ptr Ptr ) As Long

- The (basic) main function must also be provided to satisfy the linker and start-up code. This might be defined in another libray or framework, in C, or ASM, or fb language as shown by the following minimum code example:
Function main cdecl Alias "main" ( ByVal argc As Long, ByVal argv As ZString Ptr Ptr ) As Long
    '' .....
    '' call the custom main
    Return custom_main( argc, argv )
End Function

- Then compile with the -entry custom_main option.

Note:
This feature is being developed and may change in future (see Compiler Option: -entry in Documentation forum).
There will maybe a better way to deal with this: a compiler option that lets to outright specify an explicit "main".

Example

Example of program (option_entry.bas) trying to show the minimal code for fb language only:
'' option_entry.bas:

'' - demonstrate alternate named main function
''   as an alternate entry point for the implicit user main
'' - we also can compile and link in separate steps

'' compile: $ fbc -c -m option_entry option_entry.bas -entry custom_main
'' compile: $ fbc option_entry.o
'' OR
'' compile: $ fbc option_entry.bas -entry custom_main

'' internally the implicit main function is now named "custom_main"
Declare Function custom_main cdecl Alias "custom_main" ( ByVal argc As Long, ByVal argv As ZString Ptr Ptr ) As Long

'' But we still need a main() function to satisfy the linker and start-up code
'' - this might be defined in another libray or framework
'' - it's not so let's define it here for the demonstration
Function main cdecl Alias "main" ( ByVal argc As Long, ByVal argv As ZString Ptr Ptr ) As Long
    '' just call our custom main for demonstration
    Return custom_main( argc, argv )
End Function

'' ---------------------------------
'' START OF USER'S IMPLICIT MAIN
'' internally this is named "custom_main" and will automatically be
'' called by our custom frame work

Print "hello"
Sleep

'' END OF USER'S IMPLICIT MAIN
'' ---------------------------------

Version

  • Since fbc 1.09.0

See also

  • Compiler Option: -m
  • Executables


Compiler Option: -enullptr
Enable null-pointer checking

Syntax

-enullptr

Description

The -enullptr compiler option enables null-pointer error checking in run-time code generation. A run-time error is generated if an attempt is made to dereference a null-pointer to a value, or call a null function pointer.

-enullptr is automatically implied by the -exx option.

__FB_ERR__ can be used to determine in user source code if the -enullptr option was specified or implied on the command line.

Version

  • Since fbc 1.07.0

See also

  • __FB_ERR__
  • Compiler Option: -exx
  • Error Handling
  • Using the Command Line


Compiler Option: -eunwind
Enable stack unwind information.

Syntax

-eunwind

Description

The -eunwind compiler option turns on the code generation and link options to enable stack unwind information.

Version

  • Since fbc 1.10.0

See also

  • __FB_ERR__
  • Using the Command Line


Compiler Option: -ex
Add error checking with Resume support

Syntax

-ex

Description

The -ex compiler option adds error handling as with the -e option plus support for Resume.

See also

  • __FB_ERR__
  • Compiler Option: -e
  • Compiler Option: -exx
  • Error Handling
  • Using the Command Line


Compiler Option: -exx
Add error checking with Resume support and array bounds/dimensions and null-pointer checking, plus stack overflow checking with gas64 backend

Syntax

-exx

Description

The -exx compiler option adds error checking with Resume support, array bounds and dimensions, and null-pointer checking (including the procedure pointers), plus stack overflow checking with gas64 backend.

Use of the -exx option automatically implies -e, -ex, -earray, -earraydims, -elocation, and -enullptr, error checking and code generation options.

Version

  • Before fbc 1.20.0, array dimensions were never checked.
  • Before fbc 1.10.0, stack overflow was never checked.

See also

  • __FB_ERR__
  • Compiler Option: -e
  • Compiler Option: -earray
  • Compiler Option: -earraydims
  • Compiler Option: -elocation
  • Compiler Option: -enullptr
  • Compiler Option: -ex
  • Error Handling
  • Using the Command Line


Compiler Option: -export
Export symbols for dynamic linkage

Syntax

-export

Description

The -export compiler option exports symbols for dynamic linkage.

See also

  • Export
  • Shared Libraries
  • Using the Command Line


Compiler Option: -forcelang
Provides QuickBASIC or backward compatibility

Syntax

-forcelang dialect

Parameters

dialect
The dialect to use in compilation, one of fb (default), fblite, qb or deprecated.

Description

The -forcelang compiler option changes the way source code is interpreted, and is meant as a tool to users wanting traditional QuickBASIC-like behavior, or behavior deprecated from previous versions of FreeBASIC. It overrides any #lang statements within the code.

The intrinsic macro __FB_LANG__ is set to the string name of the dialect specified on the command line, or "fb" by default.

To learn more about the differences between each of these language dialects, see Compiler Dialects.

fb

This is the default dialect, and allows compilation of source code adhering to the most recent version of the FreeBASIC language.

fblite

This dialect provides support for FreeBASIC syntax and functionality, but with a more traditional QuickBASIC programming style.

qb

This dialect provides the best support for older QuickBASIC code.

deprecated

This dialect is for backward compatibility with some previous versions of FreeBASIC, however, this dialect may not exist in future versions. Programmers should consider using the "fblite" dialect instead.

See also

  • #lang
  • __FB_LANG__
  • Compiler Option: -lang
  • Compiler Dialects
  • Using the Command Line


Compiler Option: -fpmode
Selects faster, less accurate or slower, more precise floating-point math.

Syntax

-fpmode < mode >

Parameters

mode
The floating point mode: FAST | PRECISE.

Description

The -fpmode compiler option specifies whether speed or precision is more important for floating point math. If this option is not specified, the default is -fpmode PRECISE.

-fpmode FAST will generate faster, less accurate instructions for certain floating point operations.

-fpmode PRECISE will generate standard floating point instructions that operate at the default speed and accuracy of the selected floating point unit.

Currently, the only floating point operations that behave differently when using -fpmode FAST are: Sin(), Cos(), reciprocal, and reciprocal Square Root, all of which must operate on Single precision values.

A number of changes were made to normalize handling of NaN's (Not a Number) to get consistent results across toolchains and platforms, but that costs extra code, so intention was that -fpmode FAST could allow user to skip that extra code generation if they wanted (and they assume responsibility how things work or do not work across platforms).

Note:
- for x86 and x86_64, default is -fpu x87 -fpmode PRECISE
- -fpmode FAST|PRECISE controls code generation by fbc only
- for optimizing with '-gen gcc' backend, use '-Wc -ffast-math'

Version

  • Before fbc-1.20.0, an error message would be displayed if -fpmode FAST was with anything but -fpu SSE.

See also

  • Using the Command Line
  • Compiler Option: -fpu
  • __FB_FPMODE__


Compiler Option: -fpu
Sets the math unit to be used for floating point arithmetic.

Syntax

-fpu < type >

Parameters

type
The floating point unit: X87 | SSE | NEON.

Description

The -fpu compiler option sets the math unit to be used for floating point arithmetic. If this option is not specified, the default is -fpu X87.

-fpu X87 will generate floating point instructions for the 387.

-fpu SSE will generate floating point instructions for SSE and SSE2 with some math support still done by the 387.

-fpu NEON will generate floating point instructions for ARMv7-A Advanced SIMD (Neon) extension. Specifying this option will automatically set the arch to at least armv7-a if not already set to a later processor.

Functions normally return a floating point value (Single or Double) in the st(0) register. Sometimes, this may be optimized by returning the value in the xmm0 register instead. This can be specified with Option("Sse") after the return type in a function's declaration or definition. Option("Sse") is ignored unless the source is compiled with the -fpu SSE command line option.

Version

  • -fpu NEON added in fbc-1.20.0

See also

  • Using the Command Line
  • Option()
  • __FB_FPU__


Compiler Option: -g
Add debug information, define __FB_DEBUG__, and enable asserts

Syntax

-g

Description

The -g compiler option inserts debugging symbols into output files, to use with GDB-compatible debuggers.

The intrinsic macro __FB_DEBUG__ is set to non-zero (-1) if the option was specified, and set to zero (0) otherwise.

Assert and AssertWarn macros are enabled.

Use of -g automatically implies -edebug, -edebuginfo, and -eassert, command line options.

__FB_ERR__ can be used to determine in user source code if the -g option was specified or implied on the command line.

See also

  • __FB_DEBUG__
  • __FB_ERR__
  • Assert
  • AssertWarn
  • Compiler Option: -eassert
  • Compiler Option: -debug
  • Compiler Option: -debuginfo
  • Debugging
  • Using the Command Line


Compiler Option: -gen
Sets the backend code emitter.

Syntax

-gen < backend >

Parameters

backend
gas for x86 GAS assembly, gcc for GNU C, gas64 for x86_64 GAS assembly, clang for clang, llvm for LLVM IR.

Description

The -gen compiler option sets the backend code emitter and assembler. If this option is not specified, the default is -gen gas for x86 and -gen gcc for x86_64.

-gen gas
The compiler will emit GAS assembler code to a .asm file which will then be compiled to an object file using 'as'. This is fbc's original x86 code generation backend.

-gen gas64
The compiler will emit GAS assembler code (64-bit) to a .asm file which will then be compiled to an object file using 'as'.

-gen gcc
The compiler will emit C code to a .c file which will then be compiled to an .asm file using 'gcc' as a high level assembler. The C backend is intended to make FB portable to more platforms than just x86. This requires gcc to be installed so that fbc can invoke it to compile the C code, also see Installing gcc for -gen gcc.

-gen clang
The compiler will emit C code to a .c file which will then be compiled to an .asm file using 'clang' as a high level assembler.

-gen llvm
The compiler will emit LLVM IR code to a .ll file which will then be compiled to an .asm file using 'llc'. The LLVM backend is still a work in progress. It is intended for the same purpose as the C backend, and could theoretically solve some of the C backend's problems, such as debugging meta data support.

Version

  • -gen clang added in fbc 1.20.0

See also

  • Tools used by fbc
  • Using the Command Line


Compiler Option: -i
Add a path to search for include files

Syntax

-i < include path >

Parameters

include path
The directory path, relative or absolute, of where to search for include files.

Description

The -i option allows an additional directory to be used when searching for header files. By default, fbc searches in the current directory, and prefix/inc--in that order--where, prefix is the location where FreeBASIC is installed. A directory specified with the -i option will be searched before these default directories, and when the -i option is used multiple times, fbc will search the directories in the order they are listed on the command-line.

To search in the subdirectory includes first for header files while compiling the source file file.bas, type,

fbc -i includes file.bas

See also

  • #include
  • Compiler Option: -include
  • Header Files
  • Using the Command Line


Compiler Option: -include
Include a header file on each source compiled

Syntax

-include < include file >

Parameters

include file
The header file name with extension, and optionally a path, to include.

Description

The -include option has the effect of adding an #include preprocessor directive to the very beginning of each source file before processing it. When used multiple times, the files will be included in the order they are listed on the command-line.

To include the file header.bi when processing file1.bas and file2.bas, type,

fbc -include header.bi file1.bas file2.bas

See also

  • #include
  • Compiler Option: -i
  • Header Files
  • Using the Command Line


Compiler Option: -l
Add a library file to the linker's list.

Syntax

-l < libname >

Parameters

libname
Name of the library to link in. The library file name's extension should not be included. For example, when using -l something, the linker will look for the files:
      • Libsomething.a
      • Libsomething.dll.a (Windows)
      • something.dll (Windows)
      • Libsomething.so (Linux)

Description

The -l compiler option adds a library file to the linker's list, to be linked into the final executable or library if needed to satisfy dependencies.

See also

  • #inclib
  • Compiler Option: -p
  • Using the Command Line


Compiler Option: -lang
Provides QuickBASIC or backward compatibility

Syntax

-lang dialect

Parameters

dialect
The dialect to use in compilation, one of fb (default), fblite, qb or deprecated.

Description

The -lang compiler option changes the way source code is interpreted, and is meant as a tool to users wanting traditional QuickBASIC-like behavior, or behavior deprecated from previous versions of FreeBASIC.

The intrinsic macro __FB_LANG__ is set to the string name of the dialect specified on the command line, or "fb" by default.

To learn more about the differences between each of these language dialects, see Compiler Dialects.

fb

This is the default dialect, and allows compilation of source code adhering to the most recent version of the FreeBASIC language.

fblite

This dialect provides support for FreeBASIC syntax and functionality, but with a more traditional QuickBASIC programming style.

qb

This dialect provides the best support for older QuickBASIC code.

deprecated

This dialect is for backward compatibility with some previous versions of FreeBASIC, however, this dialect may not exist in future versions. Programmers should consider using the "fblite" dialect instead.
Note: this command-line option can be overridden by any #lang statements used in the code.

See also

  • #lang
  • __FB_LANG__
  • Compiler Option: -forcelang
  • Compiler Dialects
  • Using the Command Line


Compiler Option: -lib
Create a static library

Syntax

-lib

Description

The -lib compiler option creates a static library.

The intrinsic macro __FB_OUT_LIB__ is set to non-zero (-1) if the -lib option was specified, and set to zero (0) otherwise.

See also

  • __FB_OUT_LIB__
  • Static Libraries
  • Using the Command Line


Compiler Option: -m
Main file without extension to indicate the main module

Syntax

-m < source file >

Parameters

source file
The name without extension of the main module source file

Description

The -m compiler option specifies a main entry point for a source file; the argument is the name of a source file minus its extension.
So the main module file must be called twice in the command line:
- after compile option "-m", but without specified extension,
- and also like any module to compile, but there with its specified extension.

WARNING: the spelling of < source file > is case-sensitive! (unexpected ERRORS may otherwise occure)
If "-m" is not specified, the first source file listed is given a main entry point.

When using the "-c" or "-r" switch, "-m" must be specified when compiling a main source file.

The intrinsic macro __FB_MAIN__ is defined in the main module and not defined in other modules.

See also

  • __FB_MAIN__
  • Compiler Option: -c
  • Compiler Option: -r
  • Using the Command Line


Compiler Option: -map
Save the linking map to file name

Syntax

-map < map file >

Parameters

map file
Name of the map file to save generated during linking.

Description

The -map compiler option saves the a map file of the executable made.

See also

  • Using the Command Line


Compiler Option: -maxerr
Set maximum number of errors to report before aborting compilation

Syntax

-maxerr < value | "inf" >

Parameters

value | "inf"
Specifies the maximum number of errors or no maximum if "inf" is given instead of a value.

Description

The -maxerr compiler option sets the maximum number of errors the compiler must find before stopping. The default is 10. If inf, for infinite, is specified the compiler continues until it finds the end of the source. Useful if an IDE is parsing the error messages.
-maxerr only applies to compiler errors (therefore excluding compiler warnings, linker/assembler errors and other third party errors).

See also

  • Using the Command Line


Compiler Option: -mt
Link with thread-safe runtime library

Syntax

-mt

Description

The -mt compiler option forces linking with thread-safe runtime library for multithreaded applications. The thread-safe version is always used automatically if the FreeBASIC built-in threading functions are used, so you only need to specify this option if using your own threading routines.

The intrinsic macro __FB_MT__ is set to non-zero (-1) if the -mt option was specified, or whether one of the Threadtcreate or ThreadCall keywords is used more above in the source code. Otherwise, it is set to zero (0).

See also

  • __FB_MT__
  • Using the Command Line


Compiler Option: -nodeflibs
Do not include the default libraries

Syntax

-nodeflibs

Description

The -nodeflibs compiler option causes default libraries not to be used when linking. The libraries which are normally linked by default can still be used, but only if they are explicitly specified.

See also

  • Using the Command Line


Compiler Option: -nolib
Do not include specific library(s).

Syntax

-nolib < libname1 [, libname2 [, ... ] ] >

Parameters

libname1, libname2, ...
Name (without name's extension) of each library to exclude.
fbrt0.o
fbrt0pic.o
Exclude the fb runtime library start-up code module

Description

The -nolib compiler option allows excluding specific libraries when linking, no matter where it came from (default lib, #inclib, -l, objinfo...).
(more fine-grained control than -nodeflibs, and not only for default libraries)

This allows selectively blacklisting libraries and can be used to:
- remove unused dependencies when linking against shared libraries (when linking against static libraries via -l, the linker automatically skips unused libs, but not for shared libraries),
- override unwanted #inclibs (although arguably this is not the ideal solution for that, but just a last resort),
- disable only some default libs, instead of all as done by -nodeflibs:
For example, this could be used to specifically omit libtinfo.
Using -nodeflibs would omit a lot more (including fbrt0.o) which would have to be re-added manually again.
- remove only the fb runtime library start-up code module allowing replacement with a custom start-up module without affect any other libraries named in the link

Version

  • Since fbc 1.20.0: -nolib fbrt0.o, -nolib fbrt0pic.o
  • Since fbc 1.10.0.

See also

  • Compiler Option: -nodeflibs
  • Using the Command Line


Compiler Option: -noerrline
Do not show source line where error occurred

Syntax

-noerrline

Description

The -noerrline compiler option causes reported errors to not show the place in source where error occurred. Useful if an IDE is parsing the error messages.

See also

  • Using the Command Line


Compiler Option: -noobjinfo
Do not read/write compile-time info from/to .o and .a files

Syntax

-noobjinfo

Description

The -noobjinfo compiler option disables the writing/reading of compile-time library and other linking options from/to .o and .a files. This also disables the use of fbextra.x (the supplemental linker script) for discarding the .fbctinf sections, which is useful when using the gold linker that doesn't support this kind of linker script.

Version

  • Since fbc 1.06.0

See also

  • Using the Command Line


Compiler Option: -nostrip
Do not strip symbol information from the output file

Syntax

-nostrip

Description

The -nostrip compiler option explicitly prevents fbc from passing the option to strip (remove) all symbols to the linker. Object modules and libraries will retain any symbols present after linking.

Version

  • Since fbc 1.07.0

Platform Differences

  • fbc strips all symbols by default on Dos and Windows platforms only

See also

  • -strip
  • Using the Command Line


Compiler Option: -o
Set object file path/name

Syntax

-o < output file >

Parameters

output file
The name, with optional path, of the object file to create.

Description

The -o option can be used to specify the file name for the object file created while compiling an input file. By default, the name for the object file (and other temporaries like assembly files) is based on the name of the corresponding input file, but with an .o extension. This option is useful for example in combination with -c, or to force the compiler to create temporary object files in other directories (if, for example, the source code directory is or should be treated as read-only).

Given -o options are only assigned to input files that need to be compiled, namely *.bas, *.rc, *.res and *.xpm.

Note: -o options can appear in front of or behind the input file they correspond to, but there cannot be multiple -o options for one input file. For example, these are all accepted:
fbc 1.bas -o 1.o
fbc -o 1.o 1.bas
fbc 1.bas -o 1.o 2.bas -o 2.o
fbc 1.bas -o 1.o -o 2.o 2.bas
However, this is an error:
fbc 1.bas 2.bas -o 1.o -o 2.o

The -v option makes the compiler show the actual file names that it uses.

See also

  • Compiler Option: -b
  • Compiler Option: -c
  • Using the Command Line


Compiler Option: -O
Set the optimization level for GCC

Syntax

-O < level >

Parameters

level
The optimization level: 0, 1, 2, 3 or max (3).

Description

Specifies the optimization level to be passed to GCC when using -gen gcc.

See also

  • Compiler Option: -gen gcc
  • Using the Command Line


Compiler Option: -p
Add a path to search for libraries

Syntax

-p < library path >

Parameters

library path
The directory path, relative or absolute, of where to search for library files.

Description

The -p compiler option adds a path to search for libraries. By default, libraries are looked for in the system FreeBASIC libraries directory and in the current directory.

See also

  • #libpath
  • Using the Command Line


Compiler Option: -pic
Generate position-independent code (non-x86 Unix shared libs)

Syntax

-pic

Description

The -pic compiler option tells the compiler to generate position-independent code. This is needed for creating shared libraries on x86_64 or ARM Linux/BSD platforms except Win64 (and also not on 32bit x86). This option should not be used when creating executables (as opposed to shared libraries) though.

By default, -pic is enabled when using -dll or -dylib, and disabled for all other compilation modes. Usually you only have to specify -pic if you are using -c or -lib and want to link them into shared libraries later.

-pic is implemented by passing -fPIC to gcc (when using the -gen gcc backend). The -gen gas backend does not support position-independent code since it only supports 32bit x86 and there is no special position-independent code needed for shared libraries on 32bit x86.

See also

  • Using the Command Line


Compiler Option: -pp
Emit the preprocessed input file only, do not compile

Syntax

-pp

Description

The -pp compiler option enables the preprocessor-only mode. The code is parsed & checked as usual, but is not compiled. For every input file <source>.bas passed to the compiler, a pre-processed version named <source>.pp.bas is generated.
Specifically, preprocessor commands such as conditional compilation (#if, #ifdef, #ifndef, etc.), text replacement (#define, #macro, etc.) and file inclusion (#include) are processed recursively, and the resulting expanded code is written to the pp.bas file.

See also

  • Using the Command Line
  • Preprocessor commands


Compiler Option: -prefix
Set the compiler prefix path

Syntax

-prefix < path >

Parameters

path
The directory, relative or absolute to where fbc is located.

Description

The -prefix compiler option sets the compiler prefix (where the compiler finds the bin, lib, and inc directories); and defaults to the path where fbc resides, if this can be determined.

For more details, see on page -buildprefix, the paragraph: "Detailed behavior if using -buildprefix, -prefix, -target command line options".

See also

  • -buildprefix
  • -target
  • Using the Command Line


Compiler Option: -print
Print out information

Syntax

-print option

Description

The -print option can be used to query the compiler for certain information which may be useful especially for build scripts. It does not prevent compilation of input files given besides the -print option, but the compiler also can be invoked with only a -print option and no input files, in which case it will not compile anything but only respond to the -print option.

Currently, the following -print options are recognized:

option | effect
fblibdir | Prints the compiler's lib/ path
host | Prints the host system on which fbc is running
target | Prints the target system for which fbc is compiling (can be affected by the -target option)
x | Prints the file name of the output executable or library that fbc will or would generate (named after the -x option), depending on other command line options
sha-1 | Prints the source code revision sha-1 used to build the compiler if defined at fbc build time, or an empty string if it was not defined
fork-id | Prints the custom defined project fork identifier name if defined at fbc build time, or an empty string if it was not defined

Example

A makefile could use target := $(shell $(FBC) -print target) to find out the compilation target, which would even work when cross-compiling, with FBC set to something like fbc -target foo.

fbc -print x alone will print out the executable file extension for the target system.
fbc -print x -dll on the other hand will print out the dynamic library file name format.
fbc -print x -m foo will print out the executable file name that would be used when compiling a module called foo.bas.
fbc 1.bas 2.bas -lib -print x will compile 1.bas and 2.bas into a library, whose file name will be displayed.

See also

  • -x
  • -target
  • Using the Command Line
  • __FB_BUILD_SHA1__
  • __FB_BUILD_FORK_ID__


Compiler Option: -profgen
Enable function profiling by selecting a specific profiler

Syntax

-profgen < profiler >

Description

The -profgen compiler option enables function profiling by selecting a specific profiler among different profiler code generation backends.

-profgen fb
Enable function call profiling using fb's profiler. After running an executable compiled with this option, a <filename[.exe]>.prf file containing the profiling report will be created in the program directory.

-profgen cycles
Enable cycle count profiling on some targets using fb's profiler. After running an executable compiled with this option, it can generate a basic report of cycle counts.
(currently a work in progress on -gen gas64 only)

-profgen gmon
Enable profiling code generation for gmon/gprof. After running an executable compiled with this option, a gmon.out file will be created in the program directory, allowing use of GPROF for analysis of the program's execution.

Version

  • Since fbc 1.20.0

See also

  • Compiler Option: -profile
  • Profiling with fb's profiler
  • Profiling for gmon/gprof
  • Using the Command Line


Compiler Option: -profile
Enable function profiling for gmon/gprof

Syntax

-profile

Description

The -profile compiler option enables profiling code generation for gmon/gprof. After running an executable compiled with this option, a gmon.out file will be created in the program directory, allowing use of GPROF for analysis of the program's execution.

See also

  • Compiler Option: -profgen
  • Profiling for gmon/gprof
  • Using the Command Line


Compiler Option: -r
Compile into *.asm/*.c/*.ll file(s) only, do not assemble or link

Syntax

-r

Description

The -r option specifies that any source files listed are to be compiled to *.asm/*.c/*.ll files, depending on the used code generation backend, and not compiled or linked into an executable.

When using the -r option, -m must be specified when compiling the main module.

Use the -R option to preserve intermediate files without affecting compilation/assembling/linking.
Use the -rr option to compile input source files to *.asm regardless of the code generation backend.

See also

  • Compiler Option: -c
  • Compiler Option: -R
  • Compiler Option: -m
  • Compiler Option: -gen
  • Compiler Option: -rr
  • Using the Command Line


Compiler Option: -R
Preserve intermediate *.asm/*.c/*.ll file(s) generated by compilation

Syntax

-R

Description

The -R compiler option causes the intermediate *.asm/*.c/*.ll file(s) that are generated during the compile process to be preserved. Other than that, compilation is performed as usual. Which files are generated exactly depends on the used code generation backend and compilation target.

When compiling a Windows DLL, -R also preserves the intermediate *.def file used for generating the import library for the DLL.

See also

  • Compiler Option: -C
  • Compiler Option: -r
  • Compiler Option: -gen
  • Using the Command Line


Compiler Option: -rr
Compile into *.asm file(s) only, do not assemble or link

Syntax

-rr

Description

The -rr option specifies that any source files listed are to be compiled to *.asm files, and not compiled or linked into an executable. Unlike with the -r option, this works regardless of the used code generation backend.

When using the -rr option, -m must be specified when compiling a main source file.

Use the -RR option to preserve the generated *.asm files without affecting compilation/assembling/linking.

See also

  • Compiler Option: -c
  • Compiler Option: -r
  • Compiler Option: -RR
  • Compiler Option: -m
  • Compiler Option: -gen
  • Using the Command Line


Compiler Option: -RR
Preserve intermediate *.asm files generated by compilation

Syntax

-RR

Description

The -RR compiler option causes the intermediate *.asm file(s) that are generated during the compile process to be preserved. Other than that, compilation is performed as usual.

See also

  • Compiler Option: -C
  • Compiler Option: -rr
  • Compiler Option: -R
  • Using the Command Line


Compiler Option: -s
Sets the executable subsystem

Syntax

-s < subsystem >

Parameters

subsystem
The executable subsystem: gui or console.

Description

The -s compiler option specifies the executable subsystem. Allowed subsystems are gui and console (by default, console is used). Specifying a gui subsystem prevents the console window from appearing behind the program window.

The intrinsic macro __FB_GUI__ is set to non-zero (-1) if GUI subsystem mode is active, and set to zero (0) otherwise. #Pragma Gui and #Pragma Gui = 0 provide the source-level form of the same setting.

Platform Differences

  • On Windows and Cygwin, this controls the linker subsystem.
  • Other targets may use the same mode value for target-specific startup or runtime behavior.

See also

  • __FB_GUI__
  • #pragma
  • Using the Command Line


Compiler Option: -showincludes
Display a tree of file names of #included files

Syntax

-showincludes

Description

Tells the compiler to display the file names of loaded source code files, in form of a tree structure with indentation. This includes the *.bas files at the toplevel, aswell as the names of #included files as they are being #included. This is intended to be used for debugging #include dependencies, etc.

Version

  • Since fbc 1.02.0

See also

  • Using the Command Line


Compiler Option: -static
Prefer static libraries over dynamic ones when linking

Syntax

-static

Description

When creating an executable or a shared library/DLL, the -static compiler option can be used to tell the compiler to prefer linking against static libraries rather than shared libraries/DLLs. That way, if the linker finds both static and shared versions of a library, it will use the static version, rather than defaulting to the shared version.

Installing the proper static libraries and then using -static can be used to avoid some or all dependencies on shared libraries.

Platform Differences

  • On Linux & co it is possible to create purely statically linked executables, because static versions of the system libraries used by FreeBASIC are available.
  • On Windows, there are no static versions of the system libraries, but -static can still be useful to decide between static library or DLL versions of other libraries, if both are installed.

See also

  • Using the Command Line


Compiler Option: -strip
Omit all symbol information from the output file

Syntax

-strip

Description

The -strip compiler option passes an option to the linker to strip (remove) all symbols.

Version

  • Since fbc 1.07.0

Platform Differences

  • fbc strips all symbols by default on Dos and Windows platforms only

See also

  • -nostrip
  • Using the Command Line


Compiler Option: -sysroot
Add a path to search for libraries

Syntax

-sysroot < sysroot path >

Parameters

sysroot path
The directory path of the sysroot for the toolchain used.

Description

The -sysroot compiler option passes this directory location to the linker to indicate where the linker should look for default object modules and libraries.

This passes on some responsibility of finding needed resources to the toolchain. Typically only needed when cross compiling where resources are expected to be in a single directory structure and expected to be found at known locations relative to the sysroot.

To explicitly locate libraries use #libpath or Compiler Option: -p.

Version

  • Since fbc 1.20.0

See also

  • #libpath
  • Using the Command Line


Compiler Option: -t
Set stack size in kilobytes

Syntax

-t < stack size >

Parameters

stack size
Stack size in kilobytes.

Description

The -t compiler option sets the stack size in kilobytes (defaults to 1024 KBytes for 32-bit targets, and 2048 KBytes for 64-bit targets). The local arrays are created in the stack, so 1 MB or 2 MB of stack is not always enough.

Version

  • Before fbc 1.08.0, the default stack size for 64-bit targets was also 1024 KBytes.

Platform Differences

  • Supported on Windows, Cygwin and DOS only.

See also

  • Using the Command Line


-target

Syntax

-target <target>

Description

Selects the target platform used by the compiler.

The target controls the predefined platform symbols, default object format,
library search paths, runtime startup code, and platform-specific compiler
behavior.

Common target names include:
dos
win32
win64
linux
haiku
freebsd
dragonfly
netbsd
openbsd
darwin
solaris
illumos
android
js-asmjs
xbox
wii

Some targets may also be selected through a GNU-style target triplet such as
linux-x86_64, linux-arm, android-aarch64, or
freebsd-x86_64.

For some cross targets, helper commands may be installed alongside the
compiler. Those helpers are usually the best entry point:
fbc-js
fbc-android
fbc-wii
fbc-xbox

Those commands supply the SDK, linker, runtime, and packaging options expected
by their platform.

Example

fbc -target linux hello.bas
fbc -target haiku hello.bas
fbc -target android hello.bas
fbc -target js-asmjs hello.bas
fbc -target xbox hello.bas
fbc -target wii hello.bas

Predefined target symbols

The compiler defines __FB_<TARGET>__ for the selected target. For example:
-target android defines __FB_ANDROID__
-target haiku defines __FB_HAIKU__
-target dragonfly defines __FB_DRAGONFLY__
-target solaris defines __FB_SOLARIS__
-target illumos defines __FB_ILLUMOS__
-target js-asmjs defines __FB_JS__
-target xbox defines __FB_XBOX__
-target wii defines __FB_WII__

Portable programs should use these predefined symbols when platform-specific
code is required.
Compiler Options

See also
Compiling FB for JavaScript
Compiling FB for Android
Compiling FB for Wii
Compiling FB for Xbox

Compiler Meta-Commands


Compiler Option: -v
Be verbose

Syntax

-v

Description

The -v compiler option activates verbose mode. In this mode the compiler shows its actions step by step

See also

  • Using the Command Line


Compiler Option: -vec
Enables vector optimizations by the compiler.

Syntax

-vec < level >

Parameters

level
The level of vectorization: (0 | NONE) | 1 | 2.

Description

The -vec compiler option enables multiple levels of optimizations by searching for multiple scalar expressions that can be merged into a single vector expression. If this option is not specified, the default is -vec 0.

-vec 0 | none will disable vector optimizations.

-vec 1 will enable complete expression merging vectorization.

-vec 2 includes -vec 1 but also enables intra-expression vectorization.

This option is dependent on the -fpu SSE command line option. Attempting to enable vector optimizations without using -fpu SSE will generate an error.

See also

  • Using the Command Line
  • Compiler option -fpu


Compiler Option: -version
Show compiler version

Syntax

-version

Description

The -version compiler option makes FBC show the compiler version and exit. Any other command-line options are ignored, and no compilation will be performed.

See also

  • Using the Command Line


Compiler Option: -w
Set minimum warning level.

Syntax

-w level | all | none | param | Escape | pedantic | Next | funcptr | constness | suffix | Error | upcast

Parameters

level
Warning messages only with a level equal or greater to this value will be output.
all
Equivalent to specifying a level of zero (0).
none
Suppresses all warnings.
param
Warn when procedure parameters aren't specified with either ByVal or ByRef.
Escape
Warn when string literals contain any number of escape characters (\).
pedantic
Equivalent to specifying the param and Escape arguments, plus length checking of parameters passed ByVal and of any CPtr converting to pointer only, plus warning on useless suffixes in '-lang fb'.
Next
Warn when Next is followed by an identifier.
funcptr
Warn on mismatched procedure pointers, including conversions in Cast and CPtr expressions. Implies '-w all'. Experimental. Behaviour may change in future versions.
constness
Warn when Const (Qualifier) is discarded in an assignment. Implies '-w funcptr' and '-w all'. Behaviour may change in future versions.
suffix
Warn on useless suffixes in '-lang fb'.
Error
Report warnings as errors. Warnings cause the error count to increase, warning messages are displayed as "error warning...", compilation / assembly / linking aborts, and fbc exits with an error code.
upcast
Warn when up-casting discards initializers.

Description

The -w compiler option determines which compiler warnings, if any, are output. Each possible warning is associated with a warning level, starting from zero (0) and increasing with the potential problems that may occur.

The param, Escape, pedantic, Next, funcptr, constness, suffix, Error, and upcast arguments provide additional warnings not ordinarily output, even by default.

The default, if the -w option is not specified, is as if -w 1 was used. The -w option can be specified multiple times. Warning messages having a level of 0 are not shown by default.

-w none, or a significantly high level value will have the effect of suppressing all warning messages.

fbc does not issue warning or error if the warning name itself is not valid. For example -w flarg, is not a recognized name for a warning. The compiler silently ignores this to allow command lines with warnings added in new versions of fbc to also be used with older versions of fbc without complaint.

Version

  • Since fbc 1.10.0: '-w upcast' option
  • Since fbc 1.09.0: '-w error' option.
  • Before fbc 1.08.0, default warning level was 0 and some warnings had level of -1.

See also

  • Using the Command Line


Compiler Option: -Wa
Pass options to the assembler when using the assembly emitter (-gen gas).

Syntax

-Wa < options >

Parameters

options
Additional options to pass to the assembler.

Description

The -Wa compiler option passes additional options to GAS, the assembler. Options must be separated by commas only. The -Wa compiler option may be specified multiple times on the command line.

For example:
fbc -Wa -o,output.o,--verbose
fbc -Wa -o -Wa output.o -Wa --verbose

See also

  • Compiler Option: -gen
  • Compiler Option: -Wc
  • Compiler Option: -Wl
  • Using the Command Line


Compiler Option: -Wc
Pass options to the C compiler when using the C emitter (-gen gcc).

Syntax

-Wc < options >

Parameters

options
Additional options to pass to the C compiler.

Description

The -Wc compiler option passes additional options to GCC, the C compiler. Options must be separated by commas only. The -Wc compiler option may be specified multiple times on the command line.

For example:
fbc -gen gcc -Wc -m32,--verbose,-include,some-header.h
fbc -gen gcc -Wc -m32 -Wc --verbose -Wc -include -Wc some-header.h

See also

  • Compiler Option: -gen
  • Compiler Option: -Wa
  • Compiler Option: -Wl
  • Using the Command Line


Compiler Option: -Wl
Pass options to linker

Syntax

-Wl < options >

Parameters

options
Additional options to pass to the linker.

Description

The -Wl compiler option passes additional options to LD, the linker. Options must be separated by commas only. The -Wl compiler option may be specified multiple times on the command line.

See also

  • Compiler Option: -Wa
  • Compiler Option: -Wc
  • Using the Command Line


Compiler Option: -x
Set executable/library path/name

Syntax

-x < name >

Parameters

name
Name of the executable or library file.

Description

The -x compiler option set the executable or library name, with extension. Defaults to the name of the first source file passed on the command line. When compiling libraries, be sure to add the "lib" prefix to the file name, otherwise the linker will not be able to find it. If compiling and linking separately, this option must be set only in the linker.

See also

  • Using the Command Line


Compiler Option: -z
Sets miscellaneous or experimental compiler options.

Syntax

-z < value >

Parameters

value
Miscellaneous compiler option.

Description

The -z compiler option sets miscellaneous, obscure, temporary, or experimental options used by the developers. There is no guarantee that these options will be supported in future versions of the compiler.

-z gosub-setjmp
Specifies that the setjmp/longjmp implementation of GoSub should be used even when the GAS backend is used. By default, GoSub will be supported in -gen gas using CALL/RET assembly instructions and in -gen gcc using setjmp/longjmp C runtime functions.

-z valist-as-ptr
Specifies that the implementation of Cva_List variable argument lists and macros for variadic procedures should use a normal pointer type if the target supports it. By default, Cva_List data types are mapped to gcc's __builtin_va_list data type when using -gen gcc.

-z no-thiscall
Instructs fbc to ignore __Thiscall directives in source files.

-z no-fastcall
Instructs fbc to ignore __Fastcall directives in source files.

-z fbrt
Instructs fbc to link with the libfbrt[mt].a library instead of the libfb[mt].a library rather than having to copy or move files around or have multiple development directories.

-z nocmdline
Instructs fbc to ignore all Cmdline directives in source file and take control from the real compiler command line only.

-z retinflts
Instructs fbc to enable returning some structures (types) in floating point registers.

-z nobuiltins
Instructs fbc to disable all builtin declarations and definitions except those that are strictly necessary to initialize the compiler to a working state when it can begin to parse user code. The user code must then supply all of the required declarations and definitions needed by fbc internals to allow compilation of user code to succeed. Note well that using this option requires knowledge of the internal workings of the compiler. For experimentation and development this allows declaring and defining nearly all fbc builtins in fb user code. The actual user code declarations required will depend on the contents of the user code to be compiled. fbc is expected to throw an error if the required supporting declarations or definitions have not been made before they are required by fbc itself while parsing the user source code (fbc may also crash instead).

-z optabstract
Instructs fbc not to emit unused pure abstract types.

Version

  • Since fbc 1.20.0: optabstract compiler option
  • Since fbc 1.20.0: nobuiltins compiler option
  • Since fbc 1.10.0: retinflts compiler option
  • Since fbc 1.10.0: no-fastcall compiler option.
  • Since fbc 1.09.0: nocmdline compiler option
  • Since fbc 1.09.0: fbrt compiler option.
  • Since fbc 1.09.0: no-thiscall compiler option.
  • Since fbc 1.08.1: valist-as-ptr compiler option.
  • Since fbc 0.20.0: gosub-setjmp compiler option.

See also

  • Using the Command Line


Debugging

The debugger is in the bin\win32 or bin\dos directories (the GDB.EXE file), for the Windows and DOS versions respectively. It usually comes already installed in most Linux distros.

(Note: all commands should be typed without quotes and then [return] must be pressed.)

  • Compile the source code for your program in debug mode
    • use the -g command-line option to add debugging support, e.g. fbc -g myapp.bas.

  • Load your compiled program in GDB
    • For example, in Windows/DOS: gdb myapp.exe
    • Note: under windows if the GDB command prompt seems not to work well (like backspace not working), try set TERM=#win32con before launching GDB.

  • Set any arguments you want to send to your application
    • For example: set args arg1 arg2 argn.
    • You can also run GDB and pass the arguments directly to the application been debugged: gdb --args myapp.exe arg1 arg2 arg3.

  • Ensure GDB can see your program's source code directory
    • If the executable isn't in the same directory of the source files where it was compiled, type: dir path/to/my/application/sources.

  • Set a breakpoint in your program
    • Place a breakpoint in the first line using: b main.
    • To place a breakpoint in a function called func use: b FUNC.
    • Note: fbc exports variable/function names to UPPERCASE. GDB is case sensitive by default, but you can use the set language pascal command to change GDB to case-insensitive mode.

  • Use GDB shortcuts to run your code or to step through it
    • Type r to start running the application.
    • Type n to step to the next line, stepping over function calls.
    • Keep pressing [return] to step forward to the next line.
    • Type s to step into function calls.
    • As above, keep pressing [return] to step through.
    • Type c to continue execution until the next breakpoint.

  • Use GDB shortcuts to inspect variables
    • Use print VAR_NAME to show the contents of the variable called var_name.
      • GDB supports pointer/pointer field dereferencing, indexing and arithmetics too, so print *MYPOINTER will also work.
      • (note: undeclared variables or the ones with suffixes like % & ! # $ can't be printed).
    • Use disp VAR_NAME to display the contents of a variable called var_name.
    • Use watch VAR_NAME to stop each time a variable called var_name is changed.

  • Additional commands:
    • Use r again to restart the application when it finishes.
    • Type q to quit.
    • Type help to see a list of commands, there are many others.


Compiler Error Messages
During the program compilation three types of errors can arise:

Compiler Warnings:
The warnings don't stop the compilation, just alert the user some non-recommended and error-prone operation is attempted in the code.
Sometimes one of these operations is coded deliberately to achieve a result, in this case the warnings can be disabled by setting the -w none option at the command line.

  • 1(2) Passing scalar as pointer
  • 2(2) Passing pointer to scalar
  • 3(2) Passing different pointer types
  • 4(2) Suspicious pointer assignment
  • 5(1) Implicit conversion
  • 6(2) Cannot export symbol without -export option
  • 7(2) Identifier's name too big, truncated
  • 8(2) Literal number too big, truncated
  • 9(2) Literal string too big, truncated
  • 10(1) UDT with pointer, var-len string, or var-len array fields
  • 11(1) Implicit variable allocation
  • 12(1) Missing closing quote in literal string
  • 13(1) Function result was not explicitly set
  • 14(2) Branch crossing local variable definition
  • 15(1) No explicit BYREF or BYVAL
  • 16(1) Possible escape sequence found in
  • 17(1) The type length is too large, consider passing BYREF
  • 18(2) The length of the parameters list is too large, consider passing UDT's BYREF
  • 19(2) The ANY initializer has no effect on UDT's with default constructors
  • 20(3) Object files or libraries with mixed multithreading (-mt) options
  • 21(3) Object files or libraries with mixed language (-lang) options
  • 22(1) Deleting ANY pointers is undefined
  • 23(3) Array too large for stack, consider making it var-len or SHARED
  • 24(3) Variable too large for stack, consider making it SHARED
  • 25(1) Overflow in constant conversion
  • 26(1) Variable following NEXT is meaningless
  • 27(1) Cast to non-pointer
  • 28(1) Return method mismatch
  • 29(1) Passing Pointer
  • 30(1) Command line option overrides directive
  • 31(1) Directive ignored after first pass
  • 32(1) 'IF' statement found directly after multi-line 'ELSE'
  • 33(1) Shift value greater than or equal to number of bits in data type
  • 34(1) '=' parsed as equality operator in function argument, not assignment to BYREF function result
  • 35(1) Mixing signed/unsigned operands
  • 36(1) Mismatching parameter initializer
  • 37(2) Ambiguous LEN or SIZEOF
  • 38(1) Suspicious logic operation, mixed boolean and non-boolean operands
  • 39(1) Redefinition of intrinsic
  • 40(0) CONST qualifier discarded
  • 41(0) Return type mismatch
  • 42(0) Calling convention mismatch
  • 43(0) Argument count mismatch
  • 44(1) Suffix ignored
  • 45(1) FOR counter variable is unable to exceed limit value
  • 46(1) #cmdline ignored
  • 47(1) Use of reserved global or backend symbol
  • 48(1) Expected digit
  • 49(1) Up-casting discards initializer(s)
  • 50(2) Suspicious address expression passed to BYREF parameter
  • 51(1) GCC/Clang didn't provide a proper sysroot. You probably have to pass fbc an argument of the form '-sysroot $NDK/platforms/android-$API/arch-$ARCH'
  • 52(2) Invalid or truncated unicode escape sequence
  • 53(1) Surrogate or surrogate pair in unicode literal

Compiler Error messages:
The error messages stop the compilation after 10 errors (see the -maxerr command-line option to change that default value) or a fatal error occurred, and require a correction by the user before the compilation can be continued. The compiler signals the lines where the errors have been found, so the correction can be done quickly. In a few cases the place pointed at by the error messages is not where the errors can be found, it's the place where the compiler has given up in waiting for something that should be somewhere.

  • 1 Argument count mismatch
  • 2 Expected End-of-File
  • 3 Expected End-of-Line
  • 4 Duplicated definition
  • 5 Expected 'AS'
  • 6 Expected '('
  • 7 Expected ')'
  • 8 Undefined symbol
  • 9 Expected expression
  • 10 Expected '='
  • 11 Expected constant
  • 12 Expected 'TO'
  • 13 Expected 'NEXT'
  • 14 Expected identifier
  • 15 Expected '-'
  • 16 Expected ','
  • 17 Syntax error
  • 18 Element not defined
  • 19 Expected 'END TYPE' or 'END UNION'
  • 20 Type mismatch
  • 21 Internal!
  • 22 Parameter type mismatch
  • 23 File not found
  • 24 Invalid data types
  • 25 Invalid character
  • 26 File access error
  • 27 Recursion level too deep
  • 28 Expected pointer
  • 29 Expected 'LOOP'
  • 30 Expected 'WEND or END WHILE'
  • 31 Expected 'THEN'
  • 32 Expected 'END IF'
  • 33 Illegal 'END'
  • 34 Expected 'CASE'
  • 35 Expected 'END SELECT'
  • 36 Wrong number of dimensions
  • 37 Array boundaries do not match the original EXTERN declaration
  • 38 'SUB' or 'FUNCTION' without 'END SUB' or 'END FUNCTION'
  • 39 Expected 'END SUB' or 'END FUNCTION'
  • 40 Return type here does not match DECLARE prototype
  • 41 Calling convention does not match DECLARE prototype
  • 42 Variable not declared
  • 43 Variable required
  • 44 Illegal outside a compound statement
  • 45 Expected 'END ASM'
  • 46 Function not declared
  • 47 Expected ';'
  • 48 Undefined label
  • 49 Too many array dimensions
  • 50 Array too big
  • 51 User Defined Type too big
  • 52 Expected scalar counter
  • 53 Illegal outside a CONSTRUCTOR, DESTRUCTOR, FUNCTION, OPERATOR, PROPERTY or SUB block
  • 54 Expected var-len array
  • 55 Fixed-len strings cannot be returned from functions
  • 56 Array already dimensioned
  • 57 Illegal without the -ex option
  • 58 Type mismatch
  • 59 Illegal specification
  • 60 Expected 'END WITH'
  • 61 Illegal inside functions
  • 62 Statement in between SELECT and first CASE
  • 63 Expected array
  • 64 Expected '{'
  • 65 Expected '}'
  • 66 Expected ']'
  • 67 Too many expressions
  • 68 Expected explicit result type
  • 69 Range too large
  • 70 Forward references not allowed
  • 71 Incomplete type
  • 72 Array not dimensioned
  • 73 Array access, index expected
  • 74 Expected 'END ENUM'
  • 75 Var-len arrays cannot be initialized
  • 76 '...' ellipsis upper bound given for dynamic array (this is not supported)
  • 77 '...' ellipsis upper bound given for array field (this is not supported)
  • 78 Invalid bitfield
  • 79 Too many parameters
  • 80 Macro text too long
  • 81 Invalid command-line option
  • 82 Selected non-x86 CPU when compiling for DOS
  • 83 Selected -gen gas|gas64 ASM backend is incompatible with CPU
  • 84 -asm att used for -gen gas, but -gen gas only supports -asm intel
  • 85 -pic is not supported with -gen gas
  • 86 -pic used, but not supported by target system (only works for Unixes)
  • 87 Var-len strings cannot be initialized
  • 88 Recursive TYPE or UNION not allowed
  • 89 Recursive DEFINE not allowed
  • 90 Identifier cannot include periods
  • 91 Executable not found
  • 92 Array out-of-bounds
  • 93 Missing command-line option for
  • 94 Expected 'ANY'
  • 95 Expected 'END SCOPE'
  • 96 Illegal inside a compound statement or scoped block
  • 97 UDT function results cannot be passed by reference
  • 98 Ambiguous call to overloaded function
  • 99 No matching overloaded function
  • 100 Division by zero
  • 101 Cannot pop stack, underflow
  • 102 UDT's containing var-len string fields cannot be initialized
  • 103 Branching to scope block containing local variables
  • 104 Branching to other functions or to module-level
  • 105 Branch crossing local array, var-len string or object definition
  • 106 LOOP without DO
  • 107 NEXT without FOR
  • 108 WEND or END WHILE without WHILE
  • 109 END WITH without WITH
  • 110 END IF without IF
  • 111 END SELECT without SELECT
  • 112 END SUB or FUNCTION without SUB or FUNCTION
  • 113 END SCOPE without SCOPE
  • 114 END NAMESPACE without NAMESPACE
  • 115 END EXTERN without EXTERN
  • 116 ELSEIF without IF
  • 117 ELSE without IF
  • 118 CASE without SELECT
  • 119 Cannot modify a constant
  • 120 Expected period ('.')
  • 121 Expected 'END NAMESPACE'
  • 122 Illegal inside a NAMESPACE block
  • 123 Symbols defined inside namespaces cannot be removed
  • 124 Expected 'END EXTERN'
  • 125 Expected 'END SUB'
  • 126 Expected 'END FUNCTION'
  • 127 Expected 'END CONSTRUCTOR'
  • 128 Expected 'END DESTRUCTOR'
  • 129 Expected 'END OPERATOR'
  • 130 Expected 'END PROPERTY'
  • 131 Declaration outside the original namespace
  • 132 No end of multi-line comment, expected "'/"
  • 133 Too many errors, exiting
  • 134 Expected 'ENDMACRO'
  • 135 EXTERN or COMMON variables cannot be initialized
  • 136 EXTERN or COMMON dynamic arrays cannot have initial bounds
  • 137 At least one parameter must be a user-defined type
  • 138 Parameter or result must be a user-defined type
  • 139 Both parameters can't be of the same type
  • 140 Parameter and result can't be of the same type
  • 141 Invalid result type for this operator
  • 142 Invalid parameter type, it must be the same as the parent TYPE/CLASS
  • 143 Vararg parameters are not allowed in overloaded functions
  • 144 Illegal outside an OPERATOR block
  • 145 Parameter cannot be optional
  • 146 Only valid in -lang
  • 147 Default types or suffixes are only valid in -lang
  • 148 Suffixes are only valid in -lang
  • 149 Implicit variables are only valid in -lang
  • 150 Auto variables are only valid in -lang
  • 151 Invalid array index
  • 152 Operator must be a member function
  • 153 Operator cannot be a member function
  • 154 Method declared in anonymous UDT
  • 155 Constant declared in anonymous UDT
  • 156 Static variable declared in anonymous UDT
  • 157 Expected operator
  • 158 Declaration outside the original namespace or class
  • 159 A destructor should not have any parameters
  • 160 Expected class or UDT identifier
  • 161 Var-len strings cannot be part of UNION's or nested TYPE's
  • 162 Dynamic arrays cannot be part of UNION's or nested TYPE's
  • 163 Fields with constructors cannot be part of UNION's or nested TYPE's
  • 164 Fields with destructors cannot be part of UNION's or nested TYPE's
  • 165 Illegal outside a CONSTRUCTOR block
  • 166 Illegal outside a DESTRUCTOR block
  • 167 UDT's with methods must have unique names
  • 168 Parent is not a class or UDT
  • 169 CONSTRUCTOR() chain call not at top of constructor
  • 170 BASE() initializer not at top of constructor
  • 171 REDIM on UDT with non-CDECL constructor
  • 172 REDIM on UDT with non-CDECL destructor
  • 173 REDIM on UDT with non-parameterless default constructor
  • 174 ERASE on UDT with non-CDECL constructor
  • 175 ERASE on UDT with non-CDECL destructor
  • 176 ERASE on UDT with non-parameterless default constructor
  • 177 This symbol cannot be undefined
  • 178 RETURN mixed with 'FUNCTION =' or EXIT FUNCTION (using both styles together is unsupported when returning objects with constructors)
  • 179 'FUNCTION =' or EXIT FUNCTION mixed with RETURN (using both styles together is unsupported when returning objects with constructors)
  • 180 Missing RETURN to copy-construct function result
  • 181 Invalid assignment/conversion
  • 182 Invalid array subscript
  • 183 TYPE or CLASS has no default constructor
  • 184 Function result TYPE has no default constructor
  • 185 Missing BASE() initializer (base UDT without default constructor requires manual initialization)
  • 186 Missing default constructor implementation (base UDT without default constructor requires manual initialization)
  • 187 Missing UDT.constructor(byref as UDT) implementation (base UDT without default constructor requires manual initialization)
  • 188 Missing UDT.constructor(byref as const UDT) implementation (base UDT without default constructor requires manual initialization)
  • 189 Invalid priority attribute
  • 190 PROPERTY GET should have no parameter, or just one if indexed
  • 191 PROPERTY SET should have one parameter, or just two if indexed
  • 192 Expected 'PROPERTY'
  • 193 Illegal outside a PROPERTY block
  • 194 PROPERTY has no GET method/accessor
  • 195 PROPERTY has no SET method/accessor
  • 196 PROPERTY has no indexed GET method/accessor
  • 197 PROPERTY has no indexed SET method/accessor
  • 198 Missing overloaded operator:
  • 199 The NEW[] operator does not allow explicit calls to constructors
  • 200 The NEW[] operator only supports the { ANY } initialization
  • 201 The NEW operator cannot be used with fixed-length strings
  • 202 Illegal member access
  • 203 Expected ':'
  • 204 The default constructor has no public access
  • 205 Constructor has no public access
  • 206 Destructor has no public access
  • 207 Accessing base UDT's private default constructor
  • 208 Accessing base UDT's private destructor
  • 209 Illegal non-static member access
  • 210 Constructor declared ABSTRACT
  • 211 Constructor declared VIRTUAL
  • 212 Destructor declared ABSTRACT
  • 213 Member cannot be static
  • 214 Member isn't static
  • 215 Only static members can be accessed from static functions and parameter initializers
  • 216 The PRIVATE and PUBLIC attributes are not allowed with REDIM, COMMON or EXTERN
  • 217 STATIC used here, but not the in the DECLARE statement
  • 218 CONST used here, but not the in the DECLARE statement
  • 219 VIRTUAL used here, but not the in the DECLARE statement
  • 220 ABSTRACT used here, but not the in the DECLARE statement
  • 221 Method declared VIRTUAL, but UDT does not extend OBJECT
  • 222 Method declared ABSTRACT, but UDT does not extend OBJECT
  • 223 Not overriding any virtual method
  • 224 Implemented body for an ABSTRACT method
  • 225 Override has different return type than overridden method
  • 226 Override has different calling convention than overridden method
  • 227 Implicit destructor override would have different calling convention
  • 228 Implicit LET operator override would have different calling convention
  • 229 Override is not a CONST member like the overridden method
  • 230 Override is a CONST member, but the overridden method is not
  • 231 Override has different parameters than overridden method
  • 232 This operator cannot be STATIC
  • 233 This operator is implicitly STATIC and cannot be VIRTUAL or ABSTRACT
  • 234 This operator is implicitly STATIC and cannot be CONST
  • 235 Parameter must be an integer
  • 236 Parameter must be a pointer
  • 237 Expected initializer
  • 238 Fields cannot be named as keywords in TYPE's that contain member functions or in CLASS'es
  • 239 Illegal outside a FOR compound statement
  • 240 Illegal outside a DO compound statement
  • 241 Illegal outside a WHILE compound statement
  • 242 Illegal outside a SELECT compound statement
  • 243 Expected 'FOR'
  • 244 Expected 'DO'
  • 245 Expected 'WHILE'
  • 246 Expected 'SELECT'
  • 247 No outer FOR compound statement found
  • 248 No outer DO compound statement found
  • 249 No outer WHILE compound statement found
  • 250 No outer SELECT compound statement found
  • 251 Expected 'CONSTRUCTOR', 'DESTRUCTOR', 'DO', 'FOR', 'FUNCTION', 'OPERATOR', 'PROPERTY', 'SELECT', 'SUB' or 'WHILE'
  • 252 Expected 'DO', 'FOR' or 'WHILE'
  • 253 Illegal outside a SUB block
  • 254 Illegal outside a FUNCTION block
  • 255 Ambiguous symbol access, explicit scope resolution required
  • 256 An ENUM, TYPE or UNION cannot be empty
  • 257 ENUM's declared inside EXTERN .. END EXTERN blocks don't open new scopes
  • 258 STATIC used on non-member procedure
  • 259 CONST used on non-member procedure
  • 260 ABSTRACT used on non-member procedure
  • 261 VIRTUAL used on non-member procedure
  • 262 Invalid initializer
  • 263 Objects with default [con|de]structors or methods are only allowed in the module level
  • 264 Static member variable in nested UDT (only allowed in toplevel UDTs)
  • 265 Symbol not a CLASS, ENUM, TYPE or UNION type
  • 266 Too many elements
  • 267 Only data members supported
  • 268 UNIONs are not allowed
  • 269 Arrays are not allowed
  • 270 COMMON variables cannot be object instances of CLASS/TYPE's with cons/destructors
  • 271 Cloning operators (LET, Copy constructors) can't take a byval arg of the parent's type
  • 272 Local symbols can't be referenced
  • 273 Expected 'PTR' or 'POINTER'
  • 274 Too many levels of pointer indirection
  • 275 Dynamic arrays can't be const
  • 276 Const UDT cannot invoke non-const method
  • 277 Elements must be empty for strings and arrays
  • 278 GOSUB disabled, use 'OPTION GOSUB' to enable
  • 279 Invalid -lang
  • 280 Can't use ANY as initializer in array with ellipsis bound
  • 281 Must have initializer with array with ellipsis bound
  • 282 Can't use ... as lower bound
  • 283 FOR/NEXT variable name mismatch
  • 284 Selected option requires an SSE FPU mode
  • 285 Expected relational operator ( =, >, <, <>, <=, >= )
  • 286 Unsupported statement in -gen gcc/clang mode
  • 287 Too many labels
  • 288 Unsupported function
  • 289 Expected sub
  • 290 Expected '#ENDIF'
  • 291 Resource file given for target system that does not support them
  • 292 -o <file> option without corresponding input file
  • 293 Not extending a TYPE/UNION (a TYPE/UNION can only extend other TYPEs/UNIONs)
  • 294 Illegal outside a CLASS, TYPE or UNION method
  • 295 CLASS, TYPE or UNION not derived
  • 296 CLASS, TYPE or UNION has no constructor
  • 297 Symbol type has no Run-Time Type Info (RTTI)
  • 298 Types have no hierarchical relation
  • 299 Expected a CLASS, TYPE or UNION symbol type
  • 300 Casting derived UDT pointer from incompatible pointer type
  • 301 Casting derived UDT pointer from unrelated UDT pointer type
  • 302 Casting derived UDT pointer to incompatible pointer type
  • 303 Casting derived UDT pointer to unrelated UDT pointer type
  • 304 ALIAS name string is empty
  • 305 LIB name string is empty
  • 306 UDT has unimplemented abstract methods
  • 307 Non-virtual call to ABSTRACT method
  • 308 #ASSERT condition failed
  • 309 Expected '>'
  • 310 Invalid size
  • 311 ALIAS name here does not match ALIAS given in DECLARE prototype
  • 312 vararg parameters are only allowed in CDECL procedures
  • 313 the first parameter in a procedure may not be vararg
  • 314 CONST used on constructor (not needed)
  • 315 CONST used on destructor (not needed)
  • 316 Byref function result not set
  • 317 Function result assignment outside of the function
  • 318 Type mismatch in byref function result assignment
  • 319 -asm att|intel option given, but not supported for this target (only x86 or x86_64)
  • 320 Reference not initialized
  • 321 Incompatible reference initializer
  • 322 Array of references - not supported yet
  • 323 Invalid CASE range, start value is greater than the end value
  • 324 Fixed-length string combined with BYREF (not supported)
  • 325 Illegal use of reserved symbol
  • 326 Expected ',' or ';'
  • 327 Expected file number expression
  • 328 Malformed SOURCE_DATE_EPOCH environment variable
  • 329 Graphics routines were used, but the gfxlib has not been ported to this target
  • 330 -fpu sse option can only be used on x86 and x86_64 architectures
  • 331 -fpu neon option can only be used on arm architectures
  • 332 Undefined built-in symbol
  • 333 Illegal redefinition of rtlib command
  • 334 Illegal redefinition of gfxlib command. If gfxlib is not used, define FB_NO_GFXLIB before using this name
  • 335 Illegal redefinition of sfxlib command. If sfxlib is not used, define FB_NO_SFXLIB before using this name

Third party programs errors
These errors occur after the source has been compiled into assembler, they come from the auxiliary programs FB requires to compile a source into an executable: the linker, the assembler and (for Windows programs) the resource compiler.

If an IDE or a make utility are been used, additional errors can arise. These errors are outside the scope of this help.


Tools used by fbc
External tools the FreeBASIC compiler (fbc) may invoke during the compilation process.

Description

FreeBASIC uses several tools for compiling source code in addition to the fbc compiler. The exact tools used by fbc and how they are invoked depends on how fbc was configured, the host platform (where fbc is running), the target platform (where the produced executable will be run), and other options (like environment variables and command line options).

FreeBASIC (fbc) may have been configured in one of two ways: either as standalone or prefixed. The standalone version searches directories relative to where the executable is located. The prefixed version has a hardcoded path configured in to the compiler indicating where it expects to find additional tools and libraries. For more information on configuring FreeBASIC, see the INSTALL text file located in the src/compiler directory of the FreeBASIC sources.

You can check if your installed version of fbc is "standalone" or "prefixed" by invoking fbc with the -version command line option.

Standalone
If fbc was configured as "standalone", it will search for files relative to where the fbc executable is located. fbc is at the "top" of the directory tree and searches sub-directories below it. The "top" directory (which defaults to the location where fbc is located) can be overridden with the -prefix command line option. "topdir" shown in the directories below represents the directory where the fbc executable is located, or the directory specified with the -prefix command line option (if it was given). "<target>" refers to the target platform having the same name as specified by the -target option.

If not cross compiling, fbc looks in these locations:
    • /topdir/inc
    • /topdir/lib/<target>
    • /topdir/bin/<target>
    • gcc is queried for missing libraries (currently on linux/freebsd only)
If cross compiling, fbc looks in the these locations:
    • /topdir/inc
    • /topdir/lib/<target>
    • /topdir/bin/<target>
    • gcc is not queried (only target library directory is used)

Prefixed
If fbc was configured as "prefixed", it will search for files relative to the configured prefix (hardcoded in the fbc executable). "prefix" shown in the directories below represents the configured prefix, or the directory specified with the -prefix command line option (if it was given). "<target>" refers to the target platform having the same name as specified by the -target option.

If not cross compiling, fbc looks in these locations:
    • /prefix/include/freebasic
    • /prefix/lib/freebasic/<target>
    • /prefix/bin/freebasic/<target>
    • gcc is queried for missing libraries (currently on linux/freebsd only)
If cross compiling, fbc looks in the these locations:
    • /prefix/include/freebasic
    • /prefix/lib/freebasic/<target>
    • /prefix/bin/freebasic/<target>
    • gcc is not queried (only target library directory is used)

GCC Queries
If fbc is unable to locate a file, it may invoke gcc -print-file-name=<file> to query the location of the file. The following are files that may be located using gcc:
    • crt1.o
    • crtbegin.o
    • crtend.o
    • crti.o
    • crtn.o
    • gcrt1.o
    • libgcc.a
    • libsupc++.a
    • libc.so (Linux only)

Finding Binaries
fbc will invoke additional tools (binary executables) as part of the compiling and linking process. The following is a list of tools (executables) that may be invoked by fbc depending on the host platform, target, or type of executable or library to be produced:
    • as
    • ar
    • ld
    • gcc
    • GoRC
    • dlltool
    • pexports
    • cxbe
fbc will search for these tools in the following manner:
    • If an environment variable (having same name as the tool without any extension, all in uppercase) has been set, it explicitly indicates the path and name of the executable to be invoked.
    • If the file (or a symlink) exists in prefix/bin/freebasic/<target>, or ./bin/<target> for the standalone version, then use it.
    • On Linux, if the tool could not be found in prefix/bin/freebasic/<target>, or ./bin/<target> for the standalone version, fbc tries to invoke it anyway as it may be installed on the system and located on the PATH.
"<target>" refers to the target platform having the same name as specified by the -target option.

See also

  • Running FreeBASIC
  • Compiler Command Line Options
  • Compiler FAQ


Installing gcc for -gen gcc

Windows 32bit

If you are using the FreeBASIC-x.xx.x-win32 package, you can use our pre-made gcc package. Download gcc-x.x.x-for-FB-win32-gengcc.zip from the Binaries - Windows/More/ directory at the fbc downloads area, and extract it into the FreeBASIC installation directory (where fbc.exe is), such that gcc.exe and cc1.exe will be placed in these locations:

  • bin\win32\gcc.exe
  • bin\libexec\gcc\i686-w64-mingw32\x.x.x\cc1.exe

You can also download Win32 versions of gcc directly from the MinGW.org or MinGW-w64 projects.

Windows 64bit

The FreeBASIC-x.xx.x-win64 package already comes with gcc included, and uses -gen gcc by default (because -gen gas does not support 64bit).

DOS

It requires a (minimal) DJGPP installation. DJGPP can be downloaded from the DJGPP website. At least the djdev*.zip and gcc*b.zip are needed. In order to run the DJGPP gcc, the DJGPP environment variable must be set to point to the djgpp.env file.

To use the DJGPP gcc with the FreeBASIC-x.xx.x-dos package, copy gcc.exe and cc1.exe into the FreeBASIC installation directory, such that they will be placed in these locations:

  • bin\dos\gcc.exe
  • bin\libexec\gcc\djgpp\x.xx\cc1.exe

Linux

Typically the gcc package is already installed, or it can be installed by doing something like:
sudo apt-get install gcc
(the exact command depends on your GNU/Linux distribution)

Non-standalone fbc installed into DJGPP/MinGW toolchains

If you are using a non-standalone version of fbc (e.g. from one of the fbc-x.xx.x-win32 packages), and have it installed inside a DJGPP or MinGW toolchain, then -gen gcc should already work, as the DJGPP or MinGW toolchains provide gcc.

As long as gcc.exe is in the same directory as fbc.exe (typically C:\DJGPP\bin\ or C:\MinGW\bin\), or available in the PATH environment variable, fbc.exe should be able to find and use it.

See also

  • -gen <backend>


FreeBASIC and QBasic

FreeBASIC the Successor
FreeBASIC is designed as an official successor of sorts to a high level compiler for MS-DOS titled "QuickBASIC", which compiled BASIC code, an easy-to-read programming language created in 1964 by John Kemeny and Thomas Kurtz. "QB" was packaged with a user-friendly IDE and interpreter that made it very easy to write custom applications. This line of products is officially continued today in the form of "Visual Basic", part of Microsoft's Visual Studio .NET programming suite.

Microsoft and BASIC Products
Microsoft and BASIC extend far prior to QuickBASIC. In fact, Microsoft's first product was a small BASIC interpreter for Altair computers released in 1975, and until the early 1980s Microsoft was known only as a language vendor. They ported their BASIC software to several different personal computers at the time and made decent business doing it.

In August of 1981 Microsoft released the next major step in its BASIC line, "Advanced BASIC", as part of a commission for IBM's PC-DOS, and is more often called by its executable name, BASICA.EXE. For Microsoft's new MS-DOS, they released GW-BASIC, which was, for the most part, a port of BASICA that did not require IBM's Basic ROM included with its systems.

BASICA and GW-BASIC are interpreters. Interpreters read source code and "interpret" it into computer code as it is read. This is useful, but slow. Microsoft, in 1983, released BASCOM for MS-DOS. BASCOM compiled BASIC code into native machine code, which ran much faster than interpreted code. This was repackaged with an IDE and released as QuickBASIC in 1985.

QuickBASIC
From 1985 to 1992, QuickBASIC was the primary BASIC product, released by Microsoft and using BASCOM, and later the Microsoft BASIC Compiler. In 1991, a slimmed down interpreter often thought to be the missing "QuickBASIC 5.0" was packaged with MS-DOS 5.0 and released as "QBasic 1.1".

QuickBASIC as a BASIC dialect provides a loose standard for modern BASIC compilers. It abolishes the need for line numbers as a used in previous BASIC interpreters, is case sensitive and has keywords that are in plain English. QuickBASIC also featured a runtime library, a library compiled by default and usable in source code, with many useful commands.

In 1991, Microsoft combined a drag-and-drop GUI designer made in 1988 called 'Ruby' with QuickBASIC. This product was called "Visual Basic", and marks the beginning of the end of QuickBASIC. Microsoft released one last version of QuickBASIC called "Visual Basic for DOS" in 1992, and discontinued the product forever.

The Internet and QBasic's Second Wind
Because the "QBasic 1.1" interpreter was packaged with MS-DOS, it was released with every copy of DOS until its dying days, Windows 3.1, and even Windows 95, 98 and ME. With the wild success of Windows, QBasic became the most widely available programming tool available for Microsoft operating systems.

When the World Wide Web became popular in the mid-90s, many hobbyist programmers made websites dedicated to QuickBASIC not as an application tool, but as a platform for their demos and games. Many assembly libraries were created for it after Microsoft dropped support, and as these demos and games became more elaborate, so did the "QB Community". From the mid-90s, through the new millennium to today, QuickBASIC has enjoyed a small but present cult following.

Andre Victor, FreeBASIC's creator, was first known over the internet as the author of several extensions to QuickBASIC in the form of libraries. He created routines to improve the speed of floating point operations, access the internet, use SVGA graphics, and provide powerful QBasic language programming features. In the late summer of 2004, he began work on a 32-bit compiler using Visual Basic for DOS.

FreeBASIC is Born
FreeBASIC was first programmed in VB-DOS, with the goal of compiling itself. Because of this, both its syntax and runtime library are designed to emulate QB's syntax and runtime as far as it is practical in a 32-bit Windows environment. For the most part, the two dialects are extremely similar, and most code can be ported with little or no modification, though in some cases routines reliant on 16-bit DOS must be rewritten. The resulting compiler shares a greater similarity to QB than any compiler on the market, including Visual Basic.

Because of its open source, its well-written code and its similarity to QB, FreeBASIC has become popular among the "QB Community" and its boundaries continue to grow as it receives more attention and gathers more features that promise to move BASIC into the future.


Differences from QB
FreeBASIC introduced a -lang command-line option, that is used to change the language compatibility mode. Use the -lang qb option when compiling to select the most QB compatible parser. All differences listed below assume that -lang qb was used.

Architecture/Platform incompatibilities
  • FreeBASIC is written for 32-bit operating systems and a 32 bit DOS extender, and cannot utilize code which depends on 16-bit DOS, 16 bit assembly or memory model (segment & offset, XMS/EMS, ...).
  • DEF SEG is no longer necessary and will not work - any code which POKEs to video memory this way will no longer function, however, for DOS it can be easily rewritten using DPMI features.
  • CALL INTERRUPT no longer functions, as it relies on 16-bit DOS. DOS interrupts can be called in the DOS32 version by using the DPMI library, but they might work slowly because of the 32bit-16bit-32bit mode changes the processor will have to perform.

Changed due to ambiguity
  • A scalar variable and an array with the same name and suffix can no longer share the same name.
  • SHARED can't be used inside a SUB or FUNCTION as it resulted in shared variables not defined in the main program. A proper DIM SHARED in the main program must be used.
  • COMMON declarations do not depend on the order they are made, variables are matched by name and for that reason named COMMON is no longer supported. All COMMON arrays are variable-length arrays in FB.
  • If a single line If has an (unnecessary) colon directly after the THEN, it has to be terminated by an End If in FB. If that unneeded colon is removed, FB will behave as QB.

Design differences
  • Graphics support was internally redesigned, see GfxLib overview
  • CLEAR is no longer used to reset all variables and set the stack. Variables must be reset one by one, and stack size can be changed in the compiler command line. The keyword CLEAR is used to do memory fills in FB.
  • String DATA items must be enclosed in quotes in FB, in QB this was optional
  • All functions must have been declared, even with a CALL in FreeBASIC. With CALL it was possible to invoke prototype-less functions in QuickBASIC. (to be supported in future with -lang qb)
  • In FreeBASIC all arrays must be explicitly declared. (Interpreted QuickBASIC arrays are automatically created with up to 10 indices.)
  • Strings, except fixed length strings of STRING*N type since fbc version 1.20.0, use a null (char 0) terminator to be compatible with C libraries and the Windows API, so such fixed-length strings can't contain chr$(0) chars.
  • When INKEY[$] reads an extended key (Num Pad, Arrows...) it returns a two character string. In FB the first character is CHR[$](255), while in QB this first char is CHR$(0).
  • With fixed length strings, except fixed length strings of STRING*N type since fbc version 1.20.0, FreeBASIC gives the real length as Len plus one (null-char), even on Type fields.
  • In FreeBASIC, unused characters of fixed-length strings are set to 0, regardless of what "-lang" compiler option is used. In QB, unused characters are set to 32 (space, or " ", in ASCII).
  • When a fixed-length string, except fixed length strings of STRING*N type since fbc version 1.20.0, is declared, but still not initialized, all characters are set to 0, both in FreeBASIC and QB. Since fbc version 1.20.0, when a fixed-length string of STRING*N type is declared, but still not initialized, all characters are set to space.
  • The arrays are stored in row-major order in FB, they were stored in column-major order in QB by default. Row major order: data with consecutive last index are contiguous. Column-major order: data with consecutive first index are contiguous. For example, if you have DIM A(1 TO 6, 1 TO 8), in row-major order the elements are stored such that A(3,5) is followed in memory by A(3,6); in column-major order A(3,5) is followed in memory by A(4,5).
  • Programs don't stop anymore on runtime errors unless -e, -ex or -exx option is used in the command line. Using these options allow the use of QB style error handling (ON ERROR, RESUME...).
  • Octal numbers are written &o..., whereas in QB they could be written as &o... or &....
  • In FB FOR loops in subs/functions do not accept arguments received byref as counters. A local variable must be used.
  • FB's Locate does not respect the fourth and fifth arguments for cursor shape.
  • FB's Screen (Graphics) does not allow switching the visible or the work-page. Use ScreenSet instead.
  • FB's Color does not allow a third argument for border color.
  • FB's Timer returns the number of seconds since the computer started, while QB's TIMER returns the number of seconds since midnight. (Win32 and Linux only: No more wrapping at midnight! :))
  • In QB a chr$(13) inside a string did a CR+LF when PRINTed. In FB the CHR(13) prints just at what it is, a CR.
  • EOF can no longer be used to detect an empty comms buffer. Empty buffer should be tested comparing Loc with 0 in FB. Also, for files opened in RANDOM or BINARY mode, EOF returns non-zero already after reading exactly the file size, see EOF.
  • Integer variables do not signal overflow errors in FB, even with the -ex or -exx option on. Any QB code relying in catching integer overflow errors will not work in FB.

Archaic commands
  • BSAVE and BLOAD can be used in FB only to save and retrieve screens or graphic buffers. They will work only if gfxlib is linked, this is, if a graphics screen mode is requested somewhere in the program. The console can't be saved with BSAVE or retrieved with BLOAD. The other use of BSAVE-BLOAD, saving and loading full arrays, can be achieved easily with GET and PUT.
  • FIELD statement (for record definition at runtime) has been left aside. The keyword FIELD is used in FB to specify field alignment in Type variables.
  • PC Speaker commands no longer function: Any references to SOUND or PLAY statements will result in an error message. There is a third party library available to emulate this functionality, but it's not included with FreeBASIC.
  • Fake event-driven programming (ON KEY, ON PEN, ON STRIG, ON TIMER) no longer works. They could be emulated by a separate library.
  • MKSMBF$ and all the MKxMBF$ commands supporting the pre-QB4.0 Microsoft proprietary floating point format (MBF) are not implemented.
  • The use of parenthesis in the arguments passed to a function to emulate by-value passing is not permitted. The CALL quirk resulting in all arguments being passed by value, no longer works. The proper ByVal and ByRef keywords must be used.
  • FILES is not implemented. Instead, PDS 7.1-compatible Dir[$] can be used.
  • IOCTL, ERRDEV and ERRDEV$, low level functions to access hardware are not implemented as they were OS-dependent.
  • CALL ABSOLUTE to run inline machine code is no longer supported. Instead you can use Asm...END ASM blocks to insert inline assembler commands. Or use the ASM ... one line command.


FreeBASIC Dialects

FreeBASIC version 0.17b introduces a -lang command-line option, used to change the language compatibility mode for different dialects of the basic language.

Starting with version 0.18.3b the -lang qb dialect has been further restricted to only allow what would have been allowed in QuickBASIC.

In version 0.18.4b the -lang fblite dialect was added, intended to replace -lang deprecated in future.

In version 0.20.0b the #lang directive and $Lang metacommand were added to specify a dialect from source.

-lang option | description
fb | FreeBASIC compatibility (default)
qb | qbasic compatibility
fblite | FreeBASIC language compatibility, with a more QBASIC-compatible coding style
deprecated | compatibility with FB 0.16

The -lang option was needed to allow FreeBASIC to support object-orientation and other features in the future, without crippling the QuickBASIC support or breaking compatibility with old FreeBASIC sources, and without making FreeBASIC difficult to maintain with many different versions of very similar packages. The QuickBASIC support can continue to be improved, if needed, without breaking the sources written specifically for FreeBASIC.

To compile old GW-BASIC or QuickBASIC/QBasic sources without too many changes, use the -lang qb option on the command-line when running fbc. This option will evolve into a better compatibility with QuickBASIC/QBasic code.

To compile FreeBASIC sources from 0.16b, use the -lang deprecated option. This option is maintained for compatibility and will not evolve in the future, and it's likely to disappear when FreeBASIC reaches a non-beta release.

For programmers who want to access some of FreeBASIC's newer features, but want to retain a more QBASIC-friendly programming style, use the -lang fblite option. This dialect will not undergo significant changes in the future, but will continue to be maintained and supported.
This option is also the most compatible with sources that were made using older versions of FreeBASIC.

It is recommended to use -lang fb for new projects, as new features (object classes, inheritance..) will be added exclusively to this dialect.

-lang fb (the default mode)

Not supported:

1) implicit variable declaration
    • All variables must be explicitly declared, using Dim, ReDim, Var, Const, Extern or Common.

2) type suffixes (!, #, $, %, &)
    • They are only allowed for numeric literals, but it's recommended to use Cast or the f (single), d (double), ll (longint), ul (ulong), ull (ulongint) numeric literal suffixes to resolve overloading.

3) DefByte, DefUByte, DefShort, DefUShort, DefInt, DefUInt, DefLng, DefLongInt, Defulongint, DefSng, DefDbl, DefStr
    • An explicit type ("As T") is needed when declaring variables using Dim, ReDim, Extern or Common. Variables declared using Var or Const have their types inferred from an initialization value (an explicit type is optional using Const).

4) all parameters passed by reference by default
    • By default, all intrinsic scalar types - numeric and pointer types - are passed by value (ByVal). Any other type - String or user-defined type - is passed by reference (ByRef).
    • Use the -w pedantic command-line option to have parameters without explicit ByVal or ByRef reported.

5) OPTIONs of any kind (no context-sensitivity)
    • Instead of Option NoKeyword, use #undef.
    • Instead of Option Escape, use: !"some esc seq \n\r" (notice the '!' char) and pass -w pedantic to check for possible escape sequences.
    • Option Explicit isn't needed, see item 1.
    • Instead of Option Dynamic, declare variable-length arrays using ReDim. Dim can also be used to declare variable-length arrays using variable or no subscripts.
    • Instead of Option Base, use explicit lower-bound subscripts in arrays declarations.
    • Instead of Option Private, use Private to declare procedures with internal linkage.
    • Instead of Option Gosub and Option Nogosub, use procedures with Sub or Function.

6) periods in symbol names
    • Use namespaces instead.

7) GoSub or Return (From Gosub)
    • Nested procedures may be allowed in future.

8) On Gosub
    • Use Select Case As Const Integer_Expression instead.

9) Resume
    • Most runtime and graphics library procedures now return an error code, like: IF OPEN( "text" FOR INPUT AS #1 ) <> 0 THEN error...

10) '$DYNAMIC, '$STATIC, '$INCLUDE meta-commands embedded in comments
    • See item 5 about Option Dynamic.
    • Use #include "filename" instead of '$include.

11) Call or Let
    • Just remove them.

12) numeric labels
    • Named labels can be used instead, e.g. label_name: / Goto label_name.

13) global symbols with the same name as keywords
    • Declare them inside a namespace.

-lang deprecated

Supported: Anything allowed in version 0.16b, but:

1) GOSUB/RETURN and ON ... GOSUB (even at module-level)
    • so the GOSUB implementation could be thread-unsafe in the -lang qb mode, allowing fast execution (-lang qb doesn't support multi-threading, while -lang deprecated does).

Not supported:

1) Classes
    • Periods allowed in symbol names make it too difficult and/or ambiguous.

2) Operator overloading
    • Periods allowed in symbol names make it too difficult and/or ambiguous.

3) Constructors, destructors and methods in TYPEs.
    • Periods allowed in symbol names make it too difficult and/or ambiguous.

-lang fblite

Supported: Anything allowed in the -lang deprecated dialect, plus..

1) GOSUB/RETURN
    • Use Option Gosub to enable. This will disable RETURN from exiting a procedure, due to ambiguity.

Not supported:

1) Scope blocks
    • All variables are given procedure scope. Explicit Scope blocks may be added later.

-lang qb

Supported: Everything not supported/allowed in the -lang fb dialect, plus..

1) Call can be used with forward-referenced functions.

2) Shared can be used inside functions. (W.I.P.)

3) All variables, implicitly or explicitly declared, are always allocated in the procedure scope, like in QuickBASIC.

4) The Data statement won't look up symbols, every token is assumed to be a literal string even without quotes, like in QuickBASIC.

Not supported:

1) Multi-threading
    • None of the threading procedures may be used.

2) Classes and Namespaces

3) Procedure and operator overloading

4) Constructors, destructors and other member procedures in Type definitions.

5) Scope blocks

6) Extern blocks

7) Variable initialization
    • All variables are moved to the procedure scope (like in QuickBASIC), making initializing local variables too difficult to support.


Frequently Asked Questions

FreeBASIC questions:

- What is FreeBASIC?
- Who is responsible for FreeBASIC?
- Why should I use FreeBASIC rather than QBasic?
- Why should I use FreeBASIC rather than some other newer BASIC ?
- How fast is FreeBASIC?
- How compatible is FreeBASIC with QuickBASIC?
- How compatible is FreeBASIC with Windows? DOS? Linux?
- Does FreeBASIC support Object Oriented Programming?
- What are the future plans with FB / ToDo list ?
- Can I program GUI applications in FB ?
- Is FB suitable for complex / big applications?
- Can I use a non-latin charset in my FreeBASIC applications?
- Can I use Serial/COM and Hardware/CPU ports in FB?

Getting Started with FreeBASIC questions
- Where can I find more information about FreeBASIC?
- Why doesn't the QB GUI open when I start FreeBASIC?
- Can I have an offline version of the documentation?
- What's the idea behind the FB dialects?
- Why does my program crash when I define an array larger than xx?
- Why does my program fail to compile with the message 'cannot find -llibname'?

Advanced FreeBASIC

- How do I link to C libraries?
- Can I use a debugger?
- What's the goal of the AR.EXE, AS.EXE and LD.EXE files included with FB ?
- Is there a limit on how big my source files can be?
- Can I write an OS in FreeBASIC ?
- I'm developing an OS, can FreeBASIC be ported to my OS ?
- Does FreeBASIC support returning reference from Functions, like in C++?

See also

FreeBASIC questions

What is FreeBASIC?
FreeBASIC is a free, BASIC compiler for Windows (32-bit and 64-bit), 32 bit protected-mode DOS (COFF executables, like DJGPP), and Linux (x86, x86_64, and ARM). It began as an attempt to create a code-compatible, free alternative to Microsoft QuickBASIC, but has quickly grown into a powerful development tool, already including support for libraries such as Allegro, SDL, OpenGL, and many others with its default installation.

Aside from having a syntax mostly compatible with QuickBASIC, FreeBASIC introduces several new features to the aged language, including pointers to variables and functions, and unsigned data types.

FreeBASIC compiler is self-hosting - written in FreeBASIC, the libraries however are written in C.

Back to top

Who is responsible for FreeBASIC?
The first versions of FreeBASIC were developed exclusively by V1ctor. Later versions gained contributions from many people, including Lillo, who developed the Linux port and the graphics library, and DrV, who developed the DOS port.

See the FreeBASIC Credits page.

Back to top

Why should I use FreeBASIC rather than QBasic?
FreeBASIC has innumerable advantages over QBasic, QuickBASIC, PDS, and Visual Basic for DOS.
    • It supports 32-bit and 64-bit processors, where QBasic is designed for 16-bit CPU's.
    • It supports modern OSes. It has ports to Windows, Linux, and 32-bit DOS.
    • It supports modern APIs such as SDL, Direct2D or DirectX, Win32, and OpenGL.
    • It is distributed under the GPL, meaning it's free and legal to use, unlike most copies of QuickBASIC / other BASICs.
    • The library is distributed under the LGPL with additional exception, meaning you may do whatever you want with your compiled programs, including selling them.
    • FreeBASIC is many times faster than QuickBASIC / other BASICs.
    • FreeBASIC supports many features, such as pointers and inline Assembly, which are not available in QuickBASIC / other BASICs.
    • QuickBASIC only supports DOS. Windows support for DOS emulation (and thus QuickBASIC) is becoming thinner with every new version. Vista does not support graphics or fullscreen text for DOS applications.

Back to top

Why should I use FreeBASIC rather than some other newer BASIC ?
FreeBASIC has many traits which make it more desirable than most other BASIC language implementations:
    • FreeBASIC adheres closely to the standard BASIC syntax, making it easier to use.
    • FreeBASIC is compiled to actual programs (executables), not bytecode.
    • FreeBASIC has a large, dedicated community which has actively participated in the development of FreeBASIC.
    • FreeBASIC utilizes standard methods of accessing common C libraries. SDL, for example, is standard C SDL, not a new set of intrinsic commands.
    • FreeBASIC has ports to Windows, Linux, and 32-bit DOS. It retains consistent syntax between the three ports.

Back to top

How fast is FreeBASIC?
Most tests run by the community have shown FreeBASIC is significantly faster than QuickBASIC, faster than most other GPL or commercial BASICs, and often approaching GCC in terms of speed.
The Computer Languages Benchmark Game, an independent test team, give FreeBASIC for Linux a speed 1.8 times slower than GNU g++. Tests are about calculation, memory and disk access speed in console programs, no graphics capabilities were tested. This is not a bad result considering FreeBASIC is not yet an optimizing compiler.
One area where there is a notable speed deficiency is in 32-bit console modes. While FreeBASIC is consistently on-par with other 32-bit console mode applications, 32-bit console mode operations are significantly slower than 16-bit console operations, as seen in QuickBASIC. In DOS version, some I/O operations can slow down after porting from a 16-bit BASIC to FB - optimizing the code brings the speed back.

Back to top

How compatible is FreeBASIC with QuickBASIC?
The FreeBASIC built in graphics library emulates the most used QB graphics modes (modes 7,12,13) and implements all the drawing primitives featured in QB.
Most compatibility problems arise from the use of 8086-DOS-hardware specific low-level techniques in the old QB programs. VGA port programming, DOS interrupts, memory segment switching, poking to the screen memory or music playing using the PC speaker are not directly supported, even if they can be supported/emulated by external libraries.
Other issues in porting old QB programs, like variable name clashes with new FB keywords, variables with the name of a QB keyword plus a type suffix, default integer size being 32 bits in FB, are addressed by running FreeBASIC with the commandline switch -lang qb .

See Differences between FreeBASIC and QuickBASIC.

Back to top

How compatible is FreeBASIC with Windows? DOS? Linux?
FreeBASIC is fully compatible with Windows, MS-DOS, FreeDOS and Linux. When planning to create a program for all three platforms, however, keep API availability in mind -- code utilizing OpenGL will work in Windows and Linux, for example, but won't in DOS, because OpenGL is not available for DOS.

Back to top

Does FreeBASIC support Object Oriented Programming?
FreeBASIC supports classes (user-defined types) with member functions (methods), static methods, static member variables, constructors, destructors, properties, operator overloading, single inheritance, virtual and abstract methods (polymorphism) and Run-Time Type Information (RTTI). Future plans regarding OOP functionality include adding support for multiple inheritance and/or interfaces. For more information see: A Beginners Guide to Types as Objects.

Back to top

What are the future plans with FB / ToDo list ?

You can find out what's planned for the future releases by directly looking at fbc's todo.txt.

Back to top

Can I program GUI applications in FB ?
Yes, you can. Headers allowing you to call the GUI API of Windows and Linux are supplied with the respective versions, but the programs made this way are not portable.
There are some API wrappers and experimental RAD applications that create non-portable GUI code for Windows.
For portable programming a multiplatform GUI wrapper library as GTK or wx-Widgets may be used. GTK headers are provided with FB, but the OOP functionality currently available in FB prevents the use of wx-Widgets. The programs created with these libraries may require the user to install the wrapper libraries in their systems.
For games and small graphics applications there are some FB-specific libraries that draw and manage simple controls as buttons and edit boxes inside the graphics screen, programs made with those libs are entirely portable.

Back to top

Is FB suitable for complex / big applications?
The FB compiler is self-hosting, it is programmed itself in FB. That means more than 120 000 lines of code at the moment, a fairly complex application.

Back to top

Can I use a non-latin charset in my FreeBASIC applications?

FreeBASIC has the Unicode support provided by the C runtime library for the given platform. This means FB DOS won't help you with Unicode. On other platforms you can use Wstrings to support any charset you need. The File OPEN keyword has an additional Encoding parameter allowing for different encodings. As FreeBASIC is coded itself in FB, this means you can code your source in an Unicode editor so the comments and string literals can be in any character set (keywords, labels and names for variables and procedures must be kept inside the ASCII set..).
For the output to screen the support is different from console to graphics. In console mode wstring printing in non latin charsets is supported if the console font supports them. Graphics mode uses an internal CP437 charset (the old DOS charset) font so non-latin output requires a custom made raster font and the use of the Draw String keyword. Third party tools exist to grab an external font and convert it to the DRAW STRING format.

Back to top

Can I use Serial/COM and Hardware/CPU ports in FB?
Yes, FB has built in functions to access the serial/COM port and hardware/CPU ports with no need of external libraries. See the OS specific FAQ's for details for your OS, and Open Com, Inp and Out .

Back to top

Getting Started with FreeBASIC questions

Where can I find more information about FreeBASIC?
The FreeBASIC Wiki is the most up-to-date manual for using FreeBASIC, available here.

Active FreeBASIC related forums, besides the official one, can be found at qbasicnews, Pete's QB Site , the FB Games directory or freebasic-portal.de (in German).

Active magazines which regularly have FreeBASIC related articles are QB Express and QBXL Magazine. These magazines are always looking for new articles, so if you think you've got a good idea for an article about FreeBASIC, submit it!

Back to top

Why doesn't the QB GUI open when I start FreeBASIC?
QB had an Integrated Development Environment (IDE). FreeBASIC does not.
FreeBASIC is only a compiler, not a complete QuickBASIC clone. It is a console mode application. It will accept a BAS file on the command line, and spit out an EXE file.
You can create the BAS file with the simplest plain text editor in your OS (Notepad, EDIT, nano,...), then run the compiler.
If you can't live without syntax coloring, error highlighting, multiple file managing, integrated debugger, context help or other features, you need an IDE. See the OS specific FAQ's for the IDE's and editors available.

Back to top

Can I have an offline version of the documentation?
This online Wiki is the official documentation for FB. Usually it is up-to-date with the latest improvements found in the development version of FB.
Offline versions of this wiki (in CHM, HTML and other formats) are available from the Documentation directory at fbc's downloads site on SourceForge.

Back to top

What's the idea behind the FB dialects?
The idea is to allow improvements in the language while maintaining backwards compatibility with QB code. The quirks of the QB syntax are not compatible with the more rigid style required by OOP. The new FB keywords often clashed with variable names in old QB programs. QB allowed to use freely dots in variable names and procedures not being UDT's.
The three dialects (-lang fb, -lang qb, -lang fblite) allow to combine the best of two worlds.
  • lang fb provides the framework required for OOP programming . Other dialects don't give access to OOP.
  • lang qb will allow the developers to keep increasing the compatibility with qb programs. Newer keywords in FB can be used by preceding them with two underscores. For example, Getmouse can be called by using __Getmouse
  • lang fblite offers FreeBASIC language compatibility, with a more QBASIC-compatible coding style.
See Compiler Dialects for details.

Back to top

Why does my program crash when I define an array larger than xx ?

This generally happens because you made an automatic fixed-length array too large, and it is corrupting the program stack. You have a couple of options:

  • if possible, reduce the size of the automatic array
  • create a variable-length array, by
    • defining the array with an empty subscript list (using Dim), or
    • defining the array with variable subscripts instead of numeric literals, Constants or Enums (using Dim), or
    • defining the array with ReDim
  • reserve more memory for the program stack by using the -t command-line option when compiling. The default is -t 1024 (kilobytes) or -t 2048 (kilobytes). Note: it's a bad idea to use very large values here.
  • create a static array by defining the array with Static rather than Dim (only locally visible, but globally preserved)
  • define the array with Shared access using Dim (this makes the array fully global)
  • use Pointers and Memory Functions like Allocate and Deallocate to manage memory yourself - this is the preferred way for storing big buffers, but not for beginners.
Static and variable-length arrays don't use the program stack for their element data, so do not have the problem associated with automatic fixed-length arrays. See Storage Classes for more information. Note that storing huge buffers as static or increasing the stack size far above the default is not a very good idea, since it increases the fixed amount of memory needed to load and start you program, even if most of it is not used later, and can result in performance degrade, or even refusing your program to load at all.

Back to top

Why does my program fail to compile with the message 'cannot find -llibname'"?
This is an error raised by the linker. The program is supposed to link to an external library, designated in the program code with #inclib or on the compiler command line with -l. However, the linker has been unable to find a matching file in any of the library paths. Check the homepage of the library you want to compile with to find out how to download it, or check ExtLibTOC to see if information about the library can be found there. For general information on libraries and how to use them see Using Prebuilt Libraries and the related pages listed there.

Back to top

Advanced FreeBASIC

How do I link to C libraries?
C libraries are set up in much the same way in FreeBASIC as they are in C. Every library included with FreeBASIC has a basic include file named "library name.bi" which uses the #inclib metacommand to include the library, and the Declare Statement to declare the functions within the library. FreeBASIC includes hundreds of BI files, see full list of library headers here.

Back to top

Can I use a debugger?
FreeBASIC can use preferably a debugger compatible with GNU GDB.
    • Win32: Insight is an user friendly wrapper for GDB, see Win32 related FAQ.
    • DOS: Be warned that DOS also has product named "Insight", but it's a real mode debugger not usable with FreeBASIC, use GDB or some DPMI32 debugger at least.
    • Linux: use GDB.
See the OS specific FAQ's for details for your OS.

Back to top

What's the goal of the AR.EXE, AS.EXE and LD.EXE files included with FB ?
AS.EXE is GAS, the "GNU assembler". It is always involved in compilation. LD.EXE is the "GNU linker", involved in creation of executables. AR.EXE is the "GNU archiver", in fact a librarian, creating .A libraries.

Back to top

Is there a limit on how big my source files can be?
Yes, since FreeBASIC is a fully 32-bit compiler it may operate on source files up to theoretically 4GB or 4294967296 bytes, however your RAM capacity should be significantly above the size of your source, otherwise the compilation won't finish or will be very slow at least.

Back to top

Can I write an OS in FreeBASIC ?

YES and NO. If you really insist to write an OS and involve FB, the answer is YES. If the question is, whether it is a good idea that you, even more if a beginner, should start coding an OS using FB now, the answer is NO. Several pitfalls apply:
    • OS development is hard, see http://www.osdev.org/wiki/Getting_Started .
    • FB won't help you to bypass the need to deal with assembly, also C might be almost impossible to avoid.
    • You won't be able to use most of the trusted FB features, like graphics, file I/O, threads, memory management, even console I/O ... just control flow, math and logic. If you need those library functions, you will have to reimplement them.
FreeBASIC relies on GCC, and available informations about developing an OS in C apply to FreeBASIC as well. FB will help you neither more nor less than GCC.

Back to top

I'm developing an OS, can FreeBASIC be ported to my OS ?

Depends. If your OS at least egalizes the functionality of DOS with DPMI32 (console I/O (seeking, multiple files open, ...), file I/O, memory management) and has a port of GCC, then the answer is YES. If you have at least an other somewhat compliant C compiler with libraries, it might be possible. You can't reasonably port FB for example to an OS allowing to load or save a file in one block only, or a 16-bit OS.

Back to top

Does FreeBASIC support returning references from Functions, like in C++?

Yes, this functionality exists. Procedures can now return references using ByRef as datatype for the return type.

Back to top

See also

  • Win32 related FAQ
  • DOS related FAQ
  • Linux related FAQ
and
  • FB Runtime Library FAQ
  • Frequently Asked FreeBASIC Graphics Library Questions


Frequently Asked FreeBASIC Graphics Library Questions

FreeBASIC Graphics Library questions:
- How can I link/use Gfxlib?
- What about the fbgfx.bi header file?
- How are Get/Put arrays managed?
- Why is Bsave/Bload crashing?
- How can I get the red, green, blue, or alpha component of a colour?
- How can I make the 'x' button in the window header close my application?
- Can't run programs using Screen 13 or 14 in fullscreen !
- Why does Imagecreate return a NULL pointer?

FreeBASIC Graphics Library questions

How can I link/use Gfxlib?
Gfxlib is "built in" into the language, it is not necessary to include any .bi file or to link any library explicitly. FreeBASIC detects you want to use Gfxlib when you use the Screen (Graphics) or ScreenRes statements. So to use Gfxlib, just start a graphics screen mode and use the graphics commands.

Back to top

What about the fbgfx.bi header file?
The fbgfx.bi header file is available for inclusion by your program, and contains constant and type definitions that may be helpful to the programmer when using Gfxlib. You do not have to explicitly include this file to use Gfxlib however; the header is only available as an aid. It contains the constants for the mode flags that can be passed to Screen (Graphics) and ScreenRes, and also definitions of Keyboard scancodes and the fb.Image buffer structure.

Back to top

How are Get/Put arrays managed?
In FreeBASIC, images can be used as arrays (as in QB) or as pointers. Either way, the image data is contained in one continuous chunk. The chunk consists of an header followed by the image data. The header can be of two types (old-style and new-style) and determines the format of the following image data, for details see GfxInternalFormats .

Back to top

Why is Bsave/Bload crashing?
Bsave/Bload can only be used to load and save graphics screens in FreeBASIC. It can't be used to save a text mode screen. To load and save an array check this snippet using file Get/Put .

Back to top

How can I get the red, green, blue, or alpha component of a color?

Each byte in a color attribute corresponds with the red, green, blue, and alpha components. The following example shows how to extract the component values from a 16, 24, or 32 bit color attribute.

#define rgb_a(x) ((x) Shr 24)
#define rgb_r(x) ((x) Shr 16 And 255)
#define rgb_g(x) ((x) Shr 8 And 255)
#define rgb_b(x) ((x) And 255)

Dim As UInteger c
Dim As Integer x, y
Dim As UByte red, green, blue, Alpha

'' Assume a 16, 24, or 32 bit screen mode has been set
c = Point(x, y)
red = rgb_r(c)
green = rgb_g(c)
blue = rgb_b(c)
Alpha = rgb_a(c)

Back to top

How can I make the 'x' button in the window header close my application?
In windowed graphics mode you can test for the press of the window's X (close) button with Inkey, checking for the value Chr( 255 ) + "k" (which is also the code returned for Alt+F4). This applies to Win32 and Linux, in DOS there is no "X" button.

Here is a small example:

'' "X" close button example , Win32 and Linux only
Dim As String key
Screen 13
Do
  Print "Click the 'x' to close this app."
  Sleep
  key = Inkey
Loop Until key = Chr(27) Or key = Chr(255, 107) 'escape or x-button

Back to top

Can't run programs using Screen 13 or 14 in full-screen!
It's a hardware/driver limitation (Win32 and Linux only?). Video cards don't implement those low resolution graphic modes nowadays. If full-screen is required you should rewrite it using at least Screen 17 or 18, or a resolution of 640x480 or higher to be sure modern hardware can handle it.

Back to top

Why does Imagecreate return a NULL pointer?
ImageCreate needs to create an image buffer that fits the current screen's pixel format, and it cannot do so if there is no screen mode setup yet, so it returns NULL, very likely resulting in a NULL pointer access later on that crashes the program.

This is known to happen when Imagecreate is called before the graphics library was initialized with a call to Screen (Graphics) or ScreenRes, as may happen when Imagecreate is called in a global constructor that is invoked before the Screen or Screenres call in the main part of the program. In such a case it is necessary to move the screen initialization into a constructor too, and have it execute before the image-creating constructors.

Back to top

See also

  • Compiler FAQ
  • FB Runtime Library FAQ
  • Frequently Asked FreeBASIC Graphics Library Questions


Frequently Asked FB Runtime Library Questions

FreeBASIC Runtime Library questions:
- How do I play sound?
- How do I access the serial ports?
- How do I print?
- How do I access the hardware ports?

FreeBASIC Runtime Library questions

How do I play sound?
Of the QB's sound keywords only BEEP is implemented in FB.
If PC speaker sound is required, it should be programmed using IN and OUT. See the example in the OUT keyword for a replacement of SOUND.
There is a library called QBSound that allows to emulate qb's ability to PLAY in the background tunes encoded in strings, it uses the soundcard's synthesizer.
If what's required is to play .wav or .mp3 files thru a soundcard, external libraries as FMOD or BASS can be used in Linux and Windows. For DOS see the DOS FAQ section.

Back to top

How do I access the serial ports?

DOS
See DOS FAQ section.

Windows and Linux
See Open Com.

Back to top

How do I print?
FB supports character output to printer.
To print graphics two approaches are possible:
  • Preprocess the graphics data, program the printer, and send the data to it (see wikipedia.org/wiki/ESC/P). This is OS-portable but depends on the printer model. The only way for DOS, see also DOS FAQ section.
  • In Windows and Linux there are specific API calls. This is not OS-portable but the OS's printer driver makes it printer-independent.

Back to top

How do I access the hardware ports?

INP, OUT and WAIT known from QB are implemented in FB.
The GfxLib intercepts the calls to some VGA ports to emulate the widely used QB's palette manipulation and vsync methods. So ports &H3DA, &H3C7, &H3C8 and &H3C9 can't be accessed it GfxLib is used. All other ports are accessible.
No further tricks are required to use INP and OUT in Linux or DOS. For the Windows version the required device driver is installed each first time the program is run in a windows session; this requires Administrator rights for this first run or the program will end with an error. Note that accessing hardware ports by applications is not common practice in Windows and Linux.

Back to top

See also

  • Compiler FAQ
  • Frequently Asked FreeBASIC Graphics Library Questions
and
  • Win32 related FAQ
  • DOS related FAQ


Xbox port FAQ

This page describes the FreeBASIC target for the original Xbox.

The Xbox target is for the 2001 Xbox. It is not for Xbox 360, Xbox One, Xbox
Series X/S, or normal Windows programs.

Xbox port questions:
- Is this target usable?
- Does it need the Microsoft XDK?
- How do I build a program?
- Where can I run it?

Is this target usable?

Yes. The fbc-xbox helper driver builds programs and produces .xbe
executables. The fbc-xbox-xiso helper can make Xbox ISO images with
optional asset directories.
Does it need the Microsoft XDK?

No. The target is built around nxdk and other open tools. That keeps the
toolchain redistributable and avoids depending on the old proprietary
Microsoft XDK.
How do I build a program?

fbc-xbox program.bas -x program.xbe

To create an ISO:
fbc-xbox-xiso program.xbe program.iso

To include assets:
fbc-xbox-xiso program.xbe program.iso --assets game-assets

Where can I run it?

Use real modified Xbox hardware or an emulator that can run homebrew XBE
files. The source tree includes build and test scripts for emulator-based
smoke tests where they are available.
FAQ Table of Contents

See also
Compiling FB for Xbox
__FB_XBOX__

FAQ Pages


DOS related FAQ

DOS

The FreeBASIC port to DOS is based on the DJGPP port of the GNU toolchain to 32-bit protected-mode DOS.

The current maintainer of this port is DrV.

To be written: platform-specific information, differences from Win32/Linux, differences from QB?, tutorials, etc.

WANTED TESTERS

The DOS version/target of FreeBASIC needs more testers. If you are interested in using FreeBASIC on DOS, please don't wait for future releases, give it a try now. Tests from running in DOS on both old and new PC's are welcome (graphics, file I/O, serial port, ...). If something doesn't work, please place a detailed bug report into the forum or bug Tracker. If all works well, you can write about your success as well. Make sure to test a recent version of FB (reports from FB older than 0.90 will be probably considered as obsolete and useless), and check this document before complaining about anything.

Limitations

The DOS target is fairly well working and supported by FreeBASIC, and up-to-date. A few differences compared to other platforms exist, however. The features missing are mostly those not supported by the operating system or DOS extender or C runtime:
  • Cross-compiling to an other target
  • Multithreading (see FAQ 23)
  • Graphics in windowed mode or using OpenGL
  • Setting ScreenRes to a size not matching any resolution supported by the graphics card
  • Unicode isn't supported in DOS, WString will be the same as ZString, character sets other than latin aren't supported. (do it yourself)
  • Shared libraries (DLL's) can't be created/used (at least not "easily"), amount of available static external libraries usable with DOS is limited

FreeBASIC DOS related questions:
- 1. FB is a 32-bit compiler - do I need a 32-bit DOS?
- 2. What about FreeDOS-32? Does/will FB work, is/will there be a version?
- 3. When running FreeBASIC in DOS, I get a 'Error: No DPMI' message!
- 4. Is there a possibility how to get rid of this CWSDPMI.EXE and CWSDPMI.SWP?
- 5. Can I use other DOS extenders, like DOS/4GW, Causeway, DOS/32A?
- 6. Where is the nice blue screen with all the ... / where is the IDE?
- 7. How can I view the documentation in CHM or PDF format in DOS?
- 8. How can I write/edit my source code?
- 9. How can I play sound in DOS?
- 10. How can I use USB in DOS?
- 11. How can I use graphics in DOS?
- 12. DEF SEG is missing in FB! How can I workaround this in my code?
- 13. How can I rewrite QB's CALL INTERRUPT / access the DOS and BIOS interrupts?
- 14. How can I rewrite QB's XMS/EMS handling?
- 15. FBC gives me a 'cannot find lsupcxx' error!
- 16. How can I use the serial or parallel port?
- 17. How can I use a printer?
- 18. How can I make a screenshot of a FreeBASIC program running in DOS?
- 19. Graphics mode doesn't work (freeze / black screen / garbage output)!
- 20. Mouse trouble! Mouse doesn't work at all in DOS / arrow 'jumps' / etc. ...
- 21. What about the 64 KiB and 640 KiB problems / how much memory is supported by FB in DOS?
- 22. My program crashes when I try to use more than cca 1 MiB RAM! Is this a bug in FreeBASIC?
- 23. Threading functions are disallowed in DOS? Help!
- 24. Executables made with FB DOS are bloated!
- 25. Compilation is very slow with FB!
- 26. SLEEP doesn't work! How can I cause a delay?
- 27. The performance is very bad in DOS!
- 28. Can I access disk sectors with FB?
- 29. Can I use inline ASM with advanced instructions like SSE in DOS ?

See also

FreeBASIC DOS related questions

1. FB is a 32-bit compiler - do I need a 32-bit DOS?
No, the DOS version of FreeBASIC uses a DOS extender, allowing you to execute 32-bit code on top of a 16 bit DOS kernel. You can use FreeDOS (16-bit), Enhanced-Dr-DOS, old closed Dr-DOS, or even MS-DOS down to version cca 4. You need at least 80386 CPU, see also Requirements.

2. What about FreeDOS-32? Does/will FB work, is/will there be a version?
FreeDOS-32 is experimental at time of writing, but it should execute FreeBASIC and applications generated by it with no change. While FB DOS support already works on FreeDOS (16), it should be ready for FreeDOS-32 as well.

3. When running FreeBASIC in DOS, I get a 'Error: No DPMI' message!
You need a DPMI host (DPMI kernel, DPMI server), means the file "CWSDPMI.EXE" (cca 20 KiB) or HDPMI32.EXE (cca 34 KiB). See requirements, and FAQ 4 for more details.

4. Is there a possibility how to get rid of this CWSDPMI.EXE and CWSDPMI.SWP?
Yes, 2 possibilities. To get rid of CWSDPMI.EXE and create a standalone DOS executable embedding CWSDPMI, you need the CWSDPMI package and the "EXE2COFF.EXE" file. Using EXE2COFF, you remove the CWSDPMI.EXE loader (file loses 2 KiB of size, resulting in a "COFF" file without extension), and then glue the file "CWSDSTUB.EXE" before this COFF. The new executable is cca 21 KiB bigger than the original one, but it is standalone, no additional files are needed. To get rid of CWSDPMI.SWP, you can then edit your executable with CWSPARAM.EXE, and disable the swapping (occasionally also - incorrectly - referred as paging). Note, however, that this will limit the memory that can be allocated to the amount of physical memory that is installed in a system. This work can be done both with the FBC.EXE file and all executables created by FBC. The method is also described in the CWSDPMI docs in the package. Alternatively, you can use the WDOSX or D3X extender. They don't swap and create standalone executables. Since they run your executable in Ring 0, the crash handling of them is not very good and can cause freezers or reboots on bugs where other hosts exit the "civil" way with a register dump. Also, spawning might not work well / at all with WDOSX or D3X. Finally, you can use HDPMI . Download the "HXRT.ZIP" file (here: japheth.de/HX.html alternative links), extract "HDPMI32.EXE" (cca 34 KiB) and "HDPMI.TXT" (not required by the code, just for your information), and include it to your DOS startup ("HDPMI32 -r"). This will make HDPMI resident and prevent all FreeBASIC (also FreePASCAL and DJGPP) programs from both crying about missing DPMI and swapping. HDPMI can not (easily / yet) be included into your executables. Running an executable containing D3X, CWSDPMI or some DPMI host inside under HDPMI or other external host is fine - the built-in host will be simply skipped. Using DPMI is definitely required for FreeBASIC, since it can't generate 16-bit real mode code, and there is no other good way to execute 32-bit code in DOS.

5. Can I use other DOS extenders, like DOS/4GW, Causeway, DOS/32A?
Not any extender around. So-called WATCOM-like extenders can't be used because of important differences in memory management and executable structure. WDOSX and D3X do work, since they are a multi-standard extenders, not only WATCOM-like. You also can use PMODE/DJ (not "original" Tran's PMODE, nor PMODE/W (!), saves cca 5 KiB compared to CWSDPMI, can be included into the EXE, but might affect stability or performance) or, as aforementioned, HDPMI.

6. Where is the nice blue screen with all the ... / where is the IDE?
The FreeBASIC project focuses on the compiler, generating the executables from your BAS sources. It looks unspectacular, but is most important for the quality of software developed by you. The project does not include an IDE. There are several external IDEs for FreeBASIC, but probably none does have a DOS version by now. If you really need one, you could try Rhide, but note that it is complicated and buggy, so use it at your own risk. See also FAQ 7 and 8.

7. How can I view the documentation in CHM or PDF format in DOS?
There is no good way to view CHM or PDF files in DOS by now. But you can view the FreeBASIC documentation nevertheless. One of the FreeBASIC developers, coderJeff provides a FreeBASIC documentation viewer with the docs included in a special format, and having also a DOS version. It looks similar the QB's built-in help viewer, but does not contain an editor or IDE. Download here: http://www.execulink.com/~coder/FreeBASIC/docs.html

8. How can I write/edit my source code?
There are many editors for DOS around, but only few of them are good - some possibilities are FreeDOS EDIT (use version 0.7d (!!) or 0.9, 64 KiB limit, suboptimal stability (save your work regularly) ), SETEDIT, INFOPAD (comes with CC386 compiler, can edit big texts also, has syntax highlighting for C and ASM, but not for BASIC).

9. How can I play sound in DOS?
There are 2 ways how to play sound in DOS: either the ("archaic") PC speaker, famous for beeping if something goes wrong, or a soundcard. The speaker is easy to control, allows more than one might think, even to play audio files (WAV, with decompression code also OGG Vorbis, MP3 etc.), you can re-use most of existing QB code easily (example: o-bizz.de/qb...speaker.zip) or ASM code via inline ASM, but provides one channel and 6 bits only, and of course significantly worse quality than a soundcard, and, on some newest (P4) PC's the speaker quality is very bad or there is no speaker at all. For old ISA soundcards, there is much example code around, a newer PCI soundcard can be accessed (supposing bare DOS in this category) either using a ( "emulation" SB16 compatible) driver, if it is available for your card (unfortunately, this is becoming more and more a problem, the DOS drivers are poor or even inexistent), or access the card directly (this is low-level programming, hardware-related, assembler is also needed, and you need technical docs about the card). There are a few sources of inspiration like the DOS audio player MPXPLAY (written in C with some ASM), supporting both methods (native + "emu" drivers), see an up-to-date list here: drdos.org/...wiki...SoundCardChip. Support of sound in DOS is not business FB DOS port, actually FB doesn't "support" sound on Win32 and Linux either - the games "connect to the API" rather than use FreeBASIC commands or libraries. To play compressed files (MP3, OGG Vorbis, FLAC, ...) , you additionally need the decompressing code, existing DJGPP ports of those libraries should be usable for this.

10. How can I use USB in DOS?
Again, not business of FB, you need a driver, FB doesn't "support" USB on Win32 or Linux either, see other Wiki: drdos.org/...wiki...USB about possibilities of USB usage in DOS.

11. How can I use graphics in DOS?
GUI or graphics in DOS is definitely possible, there are several approaches:
    • Use the FB graphics library. It uses VESA (preferably linear, but also supports banked) to access the video card and supports any resolution reported by the card's VESA VBE driver, in addition to standard VGA modes.
    • VGA mode 320x200x8bpp: very simple, maximum reliability and compatibility, but low resolution and 256 colours only, see example.
    • VGA "ModeX" 320x240x8bpp: similar to above, less easy, good reliability and compatibility, but low resolution and 256 colours only, see example.
    • VGA "planed" mode 640x480x4bpp: difficult to set pixels, maximum reliability and compatibility, but low resolution and 16 colours only, no public example yet (?).
    • Some other "odd" VGA "ModeX" modes (like 360x240x8bpp): possible, but for freaks only ;-)
    • Write your own VESA code: More difficult, good compatibility, high-res and true color possible, there might be reliability problems if not implemented carefully.
    • Use an external library (DUGL, Allegro, MGL, WxWidgets): Allows to create "expensive" graphics & GUI's, bloats EXE size, need to respect library license, potential loss of reliability.
Note that some graphic cards report limited features through VESA, most notably less memory (for example 8 MiB instead of 64 MiB) or less modes (for example only 24 bpp modes visible while 32 bpp hidden, only lower resolutions visible (up to cca 1280x1024) while higher hidden, only "4:3" modes visible while "wide" modes hidden). This is a problem of the card, not of DOS or FreeBASIC. You will see the additional features in systems other than DOS, or in DOS only using hardware detection tools going to the lowest level bypassing VESA.

12. DEF SEG is missing in FB! How can I workaround this in my code?
DEF SEG is related to 16-bit RM addressing and was removed because of this. "direct" access to VGA or other low memory areas is not possible, because FreeBASIC's memory model (same as DJGPP's) is not zero-based. For accessing low DOS memory, use DOSMEMGET and DOSMEMPUT , see "vga13h.bas" example, or "_dos_ds" selector for inline ASM, see example:

'' DOS only example of inline ASM accessing low memory
'' Run in text mode 80x25 only

'' Including dos/go32.bi will define "_dos_ds"
'' "pointing" into GO32 block

#include "dos/go32.bi"

Dim As UInteger DDS

DDS=_dos_ds

? : ? "Hello world !"
? "_dos_ds=$";Hex$(DDS)
? "This is just a tEst - abcd ABCD XYZ xyz @[`{ - press any key ..."

Do
  Sleep 1000
  If Inkey$<>"" Then Exit Do
  Asm
    mov  eax,[DDS] '' Directly using "_dos_ds" won't work here !!!
    push eax
    pop  gs        '' Just to get sure, it is usually set anyway
    Xor  ebx,ebx
    aa3:
    mov  al,[gs:0xB8000+2*ebx]
    cmp  al,65  '' "a"
    jb   aa1
    cmp  al,122 '' "z"
    ja   aa1
    cmp  al,90  '' "Z"
    jbe  aa2
    cmp  al,97  '' "a"
    jb   aa1
    aa2:
    Xor  al,32  '' Swap case
    aa1:
    mov  [gs:0xB8000+2*ebx],al
    inc  ebx
    cmp  ebx,2000
    jne  aa3
  End Asm
Loop
? : ? "Bye"
End

13. How can I rewrite QB's CALL INTERRUPT / access the DOS and BIOS interrupts?
Those interrupts can be accessed only using the DOS version/target of FB.

The access to interrupts is slower than in QB: with FB the DPMI host will have to do 2 context switches, going to real-mode and coming back. All of that will eat hundreds of clocks in raw DOS and thousands of clocks if emm386 is loaded or if inside a Windows' DOS box. The slow down might be negligible or relevant, it depends. You should try to minimize the number of such calls, and process more data per call - at least several KiB, not just one byte or a few bytes.

Use DJGPP's DPMI wrapper:

#include "dos/dpmi.bi"

Type RegTypeX As __dpmi_regs

#define INTERRUPTX(v,r) __dpmi_int( v, @r )

Alternatively you can call INT's via inline ASM, 2 important things you have to care about are the fact that FB's memory model is not zero-based (see also FAQ 12, "DEF SEG" issues), and additionally "direct" passing of addresses (like DS:[E]DX) to an INT will not work except you have a DPMI host with "DOS API translation".

14. How can I rewrite QB's XMS/EMS handling?
Depends why original code uses it. If it's just to bypass low memory limits, simply remove it and use "ordinary" FB's data types / memory handling features instead. If it is used for (sound) DMA, you are out of luck and have to redesign the code completely, about sound see FAQ 9. For DMA use preferably the low memory (should be no big problem, since the application code and most buffers are in DPMI memory instead), DMA in DPMI memory is possible but more difficult.

15. FBC gives me a 'cannot find lsupcxx' error!
The source of this problem is the libsupcxx.a file in LIB\DOS\ directory, having 9 characters in the name. Your fault is to have extracted the ZIP with long file names enabled, usually in Windows, and then using FB in DOS with no LFN support, resulting in this file looks LIBSUP~1.A and can't be found. Rename the file in LIBSUPCX.A (one X only) or extract the ZIP again in DOS. Note: changes in FB 0.18, retest needed.

16. How can I use the serial or parallel port?
The DOS INT14 is not very useful/efficient as it sends/reads a single char in each call. So it's better to use an external DOS32 comms library. /* does someone know a good one ? */ FB up to 0.18.2 doesn't support OPEN COM on DOS target, coderJeff has an experimental library/driver available, included with FB since 0.18.3.

17. How can I use a printer?
DOS kernel won't help you here, so you have to prepare the text (trivial) or pixel data (acceptably easy for printers compatible with the "ESC/P" standard) yourself and send in to the printer via the parallel port or USB using an additional driver (see FAQ 10). So-called "GDI" or "Windows" printers can't be made working in DOS with reasonable effort.

18. How can I make a screenshot of a FreeBASIC program running in DOS?
Ideally include this feature into your own code. DOS TSR based screenshooters like SNARF mostly will work with text based screens, but probably none of them with FreeBASIC's GFX library. It's not really a bug on one or other side, it's a problem "by design".

19. Graphics mode doesn't work (freeze / black screen / garbage output)!
Place a bug report into the forum. To make it as useful and productive as possible, please beware of the following, proceed given steps and provide all related information:
    • Check the limitations listed on the page GfxLib
    • The graphics might not work well / at all on very old PC's. If your CPU has less than cca 500 MHz, provide exact info about it, if you don't know, use RayeR's CPUID or similar program to test.
    • Exact info about your graphics card is needed. Test on DOS using DrV's VBEDIAG (reports info only) and RayeR's VESATEST (also tries to set mode, allows visual inspection of the result). Find out what "useful" modes (640x480, 800x600) are supported and with what bitdepths (8, 16, 24, 32 bpp), and whether they can be set and look correctly.
    • Find out and describe exactly what's wrong ("mode works with VESATEST but not with FB", "no graphics but no error either", "black screen and freezer", "graphics is messy/incomplete", ...).
    • If some sophisticated program doesn't work, try also a minimal test like placing a circle in middle of the screen.
    • Try without a mouse driver (this reduces the CPU "cost").
    • Find out what modes are affected. If a mode doesn't work, reduce the resolution or bitdeph, make sure to test the "cheapest"/safest modes 640x480 with 32/24/16/8 bpp, 640x480 with 4 bpp, and 320x200 with 8bpp.
    • For some old cards there are VESA drivers available (S3VBE/UVIVBE). Test both with and without, and include this info into your report.
    • Remove potentially problematic content (memory managers, drivers) from DOS startup files. Nothing of such is required for FB, except a DPMI host (see also FAQ 4.).
    • Post info about your graphics card, CPU (if old), DOS type and version, bug symptoms, and a simple example code.
RayeR's VESATEST and CPUID can be downloaded here: rayer.ic.cz/programm/programe.htm , VBEDIAG here drv.nu/vbediag/.

20. Mouse trouble! Mouse doesn't work at all in DOS / arrow 'jumps' / etc. ...
To use a mouse in DOS, you need a compatible driver, recognizing your mouse, and recognized by FreeBASIC library. For optimal results, you need a good driver and a suitable mouse.

Mouse: the optimal choice, and pretty well available nowadays, is a PS/2 mouse. The old type would be a serial mouse, also this one should work. The newest is USB mouse - but is not very suitable for use in DOS, since it would need a compatible (INT33) high quality native USB mouse driver (none available by now, only some experimental), or rely on BIOS emulation (not always available, or "unprecise").

Driver: the preferred choice is CTMOUSE from FreeDOS project. There are versions 1.9a1, 2.0a4, and 2.1b4 from 2008-July available. It is included with (but not limited to) FreeDOS, or download a version from here: ibiblio.org/pub/...mouse . None of them is perfect, but still they are well usable and better than most competitors. 1.9xx and 2.1xx will cooperate with BIOS, allowing USB emulation, 2.0xx bypasses BIOS and thus USB emulation will NOT work. Also Logitech mouse drivers usually do a good job, download from here: uwe-sieber.de/util_e.html - version 6.50 is a good start. Known for problems are DRMOUSE and some (old ?) versions of MSMOUSE.

If the mouse does not work at all, then most likely the driver is not loaded, doesn't recognize the mouse (see driver messages), or is not compatible with the INT33 "standard". For USB mouse, activating the "USB mouse emulation" in BIOS settings can help.

If the mouse control is "unprecise", the arrow "jumps" , then you either have a bad driver - use a better one, or the BIOS emulation is bad - the solution is to buy a PS/2 mouse then.

21. What about the 64 KiB and 640 KiB problems / how much memory is supported by FB in DOS?
Memory management is business of the DPMI host, rather than the compiler. FreeBASIC and executables generated by it do not suffer from this problem, since they use 32-bit DPMI code, rather than real mode. You can use almost all the memory of your PC, with some limitations, but they are far above 64 or 640 KiB. CWSDPMI r5 is verified to work well up to 512 MiB only, additional memory does not crash it (unlike some older versions), but is silently ignored. HDPMI is supposed to support more: up to 4 GiB (the limit of 32-bit addressing), but there was not much testing on such huge machines - verified up to cca 1.5 GiB. FreeBASIC and code generated by it do not require classical DOS based memory managers (HIMEM/XMS and EMM386/EMS), but are supposed to coexist with them if they are present. All this of course applies to true DOS only, things like "Dos Box" will keep the control over the memory management and provide only a small piece of memory (depends, up to cca 64 MiB) to your DOS code.

22. My program crashes when I try to use more than cca 1 MiB RAM! Is this a bug in FreeBASIC?
No, it's not a bug in FreeBASIC and it's not really DOS specific, see also Compiler FAQ. For a beginner, the easy solution is to use Shared for arrays. More advanced users could consider using memory management functions like Allocate. This is even more important in DOS, since it allows the application to run on (old) PCs with little memory (and still edit at least small texts for example), as well as to use all huge RAM if available (and edit huge texts for example).

23. Threading functions are disallowed in DOS? Help!
The Threading Support Functions are not supported for DOS target, and most likely won't be soon/ever. The reason is simple: neither the DOS kernel, nor the DPMI host/standard, nor "GO32" DOS Extender support threading, unlike the Win32 or Linux kernel. However nothing is impossible in DOS: you can set up your threading on the top of DPMI. There are multiple possibilities, two of which are:
    • Set up an ISR, see "ISR_TIMER.BAS" example. This is not a "full" replacement, but sufficient in some cases.
    • There is a pthreads library for DJGPP allowing to "emulate" Linux-like threading to some degree. It works acceptably for [P]7-ZIP DJGPP port (written in C++), no tests with FB yet.
    • See forum t=21274

24. Executables made with FB DOS are bloated!
This is true but there is no easy/fast way to fix. FB is a 32-bit HLL compiler, and most of the size is imported from DJGPP. !writeme! (see forum: t=11757)

25. Compilation is very slow with FB!
Problem: "FBC takes 10 seconds to compile a "Hello world" program ! TurboBASIC / QBASIC / VBDOS / PowerBASIC do take < 1 second for the same job ..."

True, but this is "by design": FB compiles your sources in 3 steps, saving the intermediate files, as described in CompilerCmdLine, while many older compilers do just 1 pass in memory. This is related mostly to file I/O performance, see FAQ 27 below about possibilities of improvements, additionally a small improvement can be achieved here by making the DPMI host resident (HDPMI32 -r or CWSDPMI -p , see FAQ 4 above). Note that the delay is mostly "additive" , so it won't hurt too much with bigger projects.

26. SLEEP doesn't work! How can I cause a delay?
Sleep does work ... but has a resolution of cca 55ms = 1/18s only, thus "SLEEP 500" is fine, while for example using "SLEEP 2" for 2 milliseconds won't work. !writeme! / !fixme!
    • PIT / BIOS timer (runs at 18.2 Hz by default), peek the BIOS timer or set your own, see "ISR_TIMER.BAS" example, raise PIT frequency (use with care)
    • Poll the BIOS timer + PIT counter, method from TIMERHLP.ASM from DKRNL32, allows to enhance precision of above without raising the PIT frequency
    • RDTSC instruction (Pentium and newer)
    • RTC clock
    • Delay loops

27. The performance is very bad in DOS!
Problem: "The performance in DOS is poor compared to Win32 / Linux binary compiled from the very same source !" or "Even worse, the very same DOS binary runs much faster in NTVDM than in DOS !"

Both indeed can happen, nevertheless, DOS is no way predestined to be slow, the inefficiencies can be fixed. First you have to identify the area where you code looses performance.

File I/O: DOS by default uses very little memory for its buffers, while other systems use much more and are "aggressive" with file caching. When dealing with many small files, this results in serious performance degrade. The solution is to install a filecache, for example LBACache, or you can install a RAMDISK (a good one: SRDISK ) and copy the "offending" files (for example FreeBASIC installation) there in and work there (make sure to backup your work to a more durable media regularly). Both will need an XMS host (use HIMEMX ). Also DOS by default uses BIOS to access the hard drives, while other systems try hard to find and use DMA. Test util: IDECHECK by Japheth (Download: japheth.de/Download/IDECheck.zip) - run it in "I13" and "DMA" modes and compare results. If "DMA" is much faster (can be 1...10 times, depends from PC model), then installing a DOS DMA driver (for example XDMA 3.1 is worth to try) can bring a big speedup on large files. Also make sure to read and write data in large pieces (16 KiB at least), not just single bytes. Other OSes are more forgiving here, but on DOS every single file I/O call causes a small "additive" delay, thus an efficient code design with good buffering is crucial.

Graphics: Pentium 2 and newer CPU's have a cache related feature called "MTRR" to speed up writes to video RAM. Drivers of other OSes usually do enable it. DOS doesn't (since it doesn't deal with graphics at all), neither does FB GFX. Use "VESAMTRR" tool by Japheth (contained in "HXGUI.ZIP" package), it will enable the speedup, surviving also mode switches and most "non-fatal" application crashes, up to a reboot. The possible speedup factor varies much depending from the PC model, up to cca 20 times. Also the mouse handling eats some (too much) CPU performance on DOS, this is a known weak point (the design of DOS FB GFX is not "very bad", it's just the common "standard" - which is not very good), fixing is theoretically possible but not easy, you just can try several mouse drivers (see FAQ 20).

28. Can I access disk sectors with FB?
You can ... but FreeBASIC won't help you too much here: no "portable" solution, use OS specific low level way. For DOS 3 methods are possible
    • Use logical disk access features of DOS for sector access bypassing the filesystem, see example in the forum: freebasic.net/forum/viewtopic.php?t=11830
    • Use physical disk BIOS INT 13, bypassing DOS
    • Use CPU ports, lowest level, bypassing both DOS and BIOS, see forum freebasic.net/forum/viewtopic.php?t=16196, source of IDECHECK from FAQ 27 above, FASM forum or some OS development resources
Note that such experiments are a bit "dangerous" - you can easily lose data or make your PC unbootable if something goes wrong.

29. Can I use inline ASM with advanced instructions like SSE in DOS ?
You can ... but SSE2 and above need to get enabled before. This is usually considered as business of the DPMI host, HDPMI32 and CWSDPMI 7 will do that, most other hosts won't. Make sure to properly CPUID for such instructions before using them. It's a good idea to provide a code branch compatible with older CPU's (early Pentium, 80386) besides supporting latest instructions, and to avoid CMOV in those too.

See also

  • Compiler FAQ.
  • FB Runtime Library FAQ.
  • Frequently Asked FreeBASIC Graphics Library Questions


Windows Related FAQ

Windows:
- Which IDEs are available for Windows?
- Can I get rid of the console / 'DOS' screen in a graphics application?
- My GUI program does nothing when run / The program compiles but I get a permission denied error in the linker
- How can I debug my program?
- Why Windows refuses to run my code using OUT and/or INP?
- I get the error 'Cannot start blah.exe because xxxx.dll was not found.' or similar. What is missing?
- Does FreeBASIC work with Windows Vista/7?
- Where can I find some tutorials on programming the Windows GUI?
- Are there Windows GUI code builders for FB?

FreeBASIC Windows questions

Which IDEs are available for Windows?

At the moment three full featured IDEs have been developed specifically for FB: FBIde (not being updated, avoid using of old versions of FBC bundled with it), FbEdit and WinFBE Editor. These IDEs require a minimum configuration -as path to the compiler- to work.
You can also download FBIde and FbEdit as bundles (Editor + Compiler) that install in a single operation. But the bundled version of the compiler may be out of date.
WinFBE Editor (WinFBE_Suite) comes packaged with the latest FB Compiler. It is an all-in-one solution.
Commercial "general use" IDEs can be used with FreeBASIC but may require an extensive setup. They are handy for multi language programming, as they provide a unified user interface.
Instructions for installing WinFBE, FBIde, and FbEdit can be found here:
- IDE Installation guide for Windows

Back to top

Can I get rid of the console / 'DOS' screen in a graphics application?
Yes. You have to give FreeBASIC the right command for it when you compile your program.
  • If you compile from a command prompt, simply add "-s gui" to the end, like "fbc myprg.bas -s gui"
  • If you compile in a specific IDE, you have to edit the "Compiler Defaults".
    • In WinFBE, its "Options->Build Configurations". Add "-s gui" (NO QUOTES) for the build of your choice.
    • In FbEdit select Windows GUI in the targets dropdown list in the right of the tool bar.

Back to top

My GUI program does nothing when run / The program compiles but I get a permission denied error in the linker
The problem may be related with the previous question. If a program tries to PRINT and it was compiled with "-s gui" it will freeze because no console is available. If the PRINT is issued before the first window is registered/opened, nothing will show in the screen or in the taskbar. The running program can only be seen in (and killed from) the task manager's processes tab. If a new compilation is tried before killing the process it will give a "Permission denied" error when the compiler tries to modify a still running .exe.
In Windows GUI programs do not use console commands. Use MessageBox or print to a log file to issue any error message to the user. Be sure any PRINT to console you used for debugging is not compiled in the final version.

Back to top

How can I debug my program?
FreeBASIC can use any debugger compatible with GNU GDB. Insight Win32 debugger is an user friendly wrapper for GDB.
  • Get Insight from Dev-C++
  • Rename the file to Insight.tar.bz2, and decompress it to an empty folder
  • Compile your program with the -g switch
  • Run <Your_Insight_Dir>\bin\usr\bin\Insight.exe
  • Do File>Open to load your program into Insight
  • From there you can watch, set breakpoints, step, examine memory and registers. Check Insight's help

Back to top

Why Windows refuses to run my code using OUT and/or INP?
Windows requires a driver to be installed to access the hardware ports. FB-Win32 programs using INP and OUT include a built-in driver that installs temporarily for a session. Windows allows only users with Admin rights to run driver installations. This means if you usually run your windows sessions without Admin rights, you will have to use the windows command line command RUNAS to run your program for the first time in each session so Windows allows it to install the driver.
If this behavior is not acceptable you can use an external library as PortIO32 that installs a permanent port driver.

Back to top

I get the error 'Cannot start blah.exe because xxxx.dll was not found.' or similar. What is missing?
You are trying to run a program using a third party library that resides in a dll not installed in your system.
FreeBASIC comes with headers and wrappers required to code for a lot of third party libraries but does not provide the actual runtime dll files.
You have to download and install these from their home page. Find in the Links thread in the Libraries subforum the URL's of the home pages of the libraries provided. You need the binaries for Win32 of the libraries. If you want to develop programs with the libs you will need the documentation too.
When releasing compiled code it is good etiquette to provide the third party dll's required to run it.

Back to top

Does FreeBASIC work with Windows Vista/7?
Yes. (Write me!!!)

Back to top

Where can I find some tutorials on programming the Windows GUI?
See the answers to this question in this thread in the forum
More advanced use requires a frequent consultation of the reference at the Microsoft Developers Network. A local install of the API reference is possible, search Microsoft for the Platform SDK (a huge download) and install just the documentation.

Back to top

Are there Windows GUI code builders for FB?
Yes there are some 3rd party developments generating Windows API code from a windows designer à la Visual Basic:
WinFBE Editor (open source) full visual designer similar to Visual Basic.
Jerry Fielden' Ezeegui (freeware) uses a "graphical" textmode interface to let you build your code.
mrhx Software's VISG (GPL) has a more classical user interface.
Less helpful may be the graphical resource editors generating scripts for the resource compiler. Any editor generating scripts compatible with GoRC can be used, as the one included with FbEdit. Graphical resource editors are a great help in designing dialogs and menus, but they leave to you the task of writing the window procedures required to make them active.

Back to top

See also


Linux Related FAQ

FreeBASIC Linux questions:
- FreeBASIC gives me an error 'ld: can't find -lX11' or something similar!
- How do I install FreeBASIC in Ubuntu?

FreeBASIC Linux questions

FreeBASIC gives me an error 'ld: can't find -lX11' or something similar!
FreeBASIC uses ld to link its files under linux. This program requires that any libraries you use have the '-dev' versions installed. For example, for the above error message, you'd want to install xlib-dev for your distribution. Other common errors are for glibc, which requires glibc-dev, and sdl, which requires sdl-dev. Most distributions make these easily available on your install media.

Back to top

How do I install FreeBASIC in Ubuntu?"
See This thread in the FB forums

Back to top

See also


Obsolete Keywords
Along the way FB has had a few keywords changed. Here is the list of those no longer supported. Old code must be updated if recompiled.

OPEN "CON:"
Use Open Cons
OPEN "ERR:"
Use Open Err
OPEN "PIPE:"
Use Open Pipe
POKEI
Use Poke (Integer,Address,N)
POKES
Use Poke (Short,Address,N)
SCREENINFO (Function returning a pointer to a structure)
Use Screeninfo, Sub Returning Values In Its Arguments
VAL64
Use Vallng()
GOSUB
Do not use GoSub in SUBs or FUNCTIONs anymore; allowed in -lang qb mode.


Glossary
Brief definitions and explanations for words and phrases used in the FreeBASIC manual.

Index: A - B - C - D - E - F - G - H - I - J - K - L - M - N - O - P - Q - R - S - T - U - V - W - X - Y - Z

A

abstract member function
A member function without body that must be overridden by a member function of a type more derived than the type it was declared in. See Abstract (Member).

access rights
The level of access associated with Type or Class members. Public members are accessible to any code; protected members are accessible to member functions and any derived Type or Class member functions; private members are accessible only to member functions of that Type or Class. By default, Type members have public access rights, while Class members are private.

any pointer
A variable or expression that points to a memory address where it is not known, at least from the compiler's point of view, what type of data is stored at that address. In C this would be the same as a void pointer or (void *). See Ptr.

archive
An archive is a group or files or a single file packed into a container format and usually compressed before or afterward. Typical container formats are GNU Tar and Zip. Typical compression formats are Gzip and Zip.

argument
Data that is passed to a procedure. The procedure refers to this data using the parameter(s) in its parameter list.

argument passing convention
The method in which arguments are passed to procedures, being either By Reference or By Value. See Passing Arguments to Procedures.

array (container)
A collection of data whose elements are stored contiguously in memory (one after the other, in increasing order). Because of this, an array offers random-access to its elements (any element can be accessed at any time). Insertion or removal of elements anywhere but at the back of the container requires that those elements that follow be relocated, so a linked-list is typically preferred when insertion or removal needs to be efficient.

assembler
A component in the tool chain for translating source code in to executable programs. The assembler converts the low level assembly instruction mnemonics emitted by the compiler to object code.

assignment
Assignment is one of the fundamental operations of computing. All it means is copying a value into the memory location pointed at by a variable. The value might be a literal, another variable, or the result of some expression. For an instance of a Type or Class, this involves calling one of its assignment operators. Not to be confused with initialization.

automatic storage
Refers to storage on the call stack. Local procedure variables, objects and arrays with automatic storage are allocated when the procedure is called, initialized when defined, destroyed (in the case of objects) when leaving the scope they're declared in and deallocated when returning from the procedure.

automatic variable/object/array
A variable, object or array with automatic storage.

Back to top

B

byref
ByRef specifies passing arguments to procedures by reference. Arguments passed by reference can be modified by the procedure and the changes seen by the caller.

byval
ByVal specifies passing arguments to procedures by value. Procedures receive a copy of the argument passed. With Type or Class instances, this involves instantiating temporary objects by calling their copy constructor. These temporaries are destroyed upon procedure exit.

binaries
Binaries are the end result of source code. Binaries include executable files (.exe on windows), static library files (.a), dynamic library files (.dll on windows, .so on Linux), and relocatable object files. (.o)

.BSS section
The part of the executable program that will contain zero bytes only when the program starts. Since all of the bytes are zero, the final size of the executable can often be reduced by placing uninitialized data, or zero initialized data in this section.

buffer
A region of memory that allows data to be saved or manipulated before being copied somewhere else. In a communications device this may hold incoming or outgoing data yet to be processed. In graphics, a buffer may contain an image before being copied to the screen.

Back to top

C

call back
A control mechanism where a caller lets a procedure call another procedure (the call back) provided by the caller typically through a function pointer.

call stack
A chunk of memory reserved for a process or thread that is used as a stack for storing various information needed by procedures when they are called. Among the information stored on the call stack are all of the local automatic variables, objects and array data and usually whatever parameters are passed to the procedure. These items are allocated (pushed onto the call stack) when the procedure is called and deallocated (popped from the call stack) when the procedure returns, either by the caller or the callee, depending on the calling convention used. The initial and maximum sizes of this reserved memory vary by platform.

caller
A misnomer used to refer to the point in code in which a procedure is called.

cast
A cast operation changes one data type to another using specified rules. A Type structure can implement a custom Cast for any intrinsic data type, and/or other TYPEs, See Cast.

code block
Several lines of source code grouped together all sharing at least one common scope. For example a procedure's code block will be all the lines of code between Sub and End Sub.

com port
A short name for serial communications port. A program can communicate with an external device, such as modem or another computer through a com port (nowadays the good old com ports are deprecated in favor of USB). See Open Com.

compiler
A compiler is a computer program which takes source code and transforms it into machine or object code.

compiler directives
These are instructions included in the text of the program that affect the way the compiler behaves. For instance the compiler might be directed to include one section of code or another of depending on the target operating system.

compound statement
A statement composed one or more additional statements. Typically, a compound statement has a beginning (opening statement), a middle (a statement block) and an end (closing or ending statement), while some have additional parts. Examples of compound statements would be If and Function.

constant
A symbol that retains a consistent value throughout the execution of the program. See Const.

constructor (module)
A special type of module-level procedure that is automatically called prior to the module-level code flow. See Constructor (Module).

constructor (TYPE or CLASS)
A special member function of a Type or Class that is called when an object is instantiated.

contravariance (TYPE or CLASS)
A typing rule for Type or Class that allows you to use a more generic (less derived) type than the one originally specified.

covariance (TYPE or CLASS)
A typing rule for Type or Class that allows you to use a less generic (more derived) type than the one originally specified.

CVS
Concurrent Versions System. The file manager implemented at Sourceforge where sources are stored, it keeps the history of the changes introduced by the developers. Used by FB in the past. (see also SVN and GIT)

Back to top

D

.DATA section
The part of the executable program that will data that can be changed while to program is running.

debugger
A program that allows controlled execution of compiled code. The values of variables can be tracked, execution can be paused, stepped or accelerated, etc. A debugger is typically used to help find the source of programmer errors in source code, called 'bugs'.

declaration
A source code statement that introduces a symbol, constant, variable, procedure, data type, or similar, to the compiler but not necessarily allocate any space for it. See Dim, Declare, Extern, Type.

definition
A source code statement (or statements) that allocates space for data or code. For example, Sub defines a procedure by allocating space for the program code it will contain. Some statements can be both a declaration and a definition. For example, Dim both declares and defines a variable.

dereference
The act of accessing (in read and in write) a variable stored at a given address. See Operator * (Valueof), Pointers.

descriptor
Refers to the internal data structure used by the compiler and runtime library for managing variable length strings and arrays.

destroy (TYPE or CLASS)
The act of deconstructing and deallocating memory for an object instance. When an object is destroyed, its destructor is called. This happens automatically when an object goes out of scope, or when Delete (Statement) is called with a pointer to an object.

destructor (module)
A special type of module-level procedure that is automatically called at program termination. See Destructor (Module).

destructor (TYPE or CLASS)
A special member function of a Type or Class that is called when an object is destroyed.

dll
Shorthand for dynamically linked library.

DPMI
A method / standard allowing to execute protected mode code (mostly also 32-bit) on a 16-bit real mode DOS kernel. Affects only DOS version of FreeBASIC. See also DOS related FAQ

DJGPP
A complete 32-bit C/C++ development system for Intel 80386 (and higher) PCs running DOS and includes ports of many GNU development utilities.

dynamically linked library
A file containing executable code that is loaded by another application when it is started. Also referred to as a dll or shared library. See Shared Libraries (DLLs).

Back to top

E

enum
A data type restricted to a sequence of named values given in a particular order. See Enum.

executable
A binary file that can be run. It consists of libraries and object files bound together by the linker.

exit sub/function
When called inside a procedure, leaves the procedure and returns control to the calling program.

expression
An instruction to execute a statement that will evaluate/return a value.

Back to top

F

field
Commonly refers to a data member in a Type or Class.

file number
An integer associated with an open file or device as given in Open. All subsequent operations on the opened file or device must use the same file number.

format string
A sequence of characters that controls how data should be presented. See Format, Print Using.

function
A procedure defined using Function, optionally taking parameters and returning a value.

function pointer
A variable containing the address of a function. The address (function) to which the variable points can be changed while the program is running allowing for dynamic program flow, such as call back functions.

Back to top

G

get/put buffer
See: Image Buffer. An image buffer in FreeBASIC's native format.

GIT
The file manager implemented at Sourceforge where sources are stored, it keeps the history of the changes introduced by the developers. Used by FB now. (see also CVS , SVN and GIT).

global variable
A variable that is visible to all procedures within a module, across multiple modules, or both. See Common and Extern.

GNU
A mass collaboration project with the primary goal to provide a free and non-proprietary Unix-like operating system.

GPL
Short hand for GNU General Public License: a license for software and other kinds of works. Open source, obligates the user to keep the project open source and under the GPL.

graphics primitive
A graphics primitive is another term for common shapes like circles and rectangles.

Back to top

H

hash table
A data structure that associates keys with values allowing for efficient look-up of values based on a given key.

header
When talking about a collection of data, this is generally the first part of that data that describes the rest. When talking about (header) files, this refers to an include file. In FreeBASIC the file extension '.bi' is usually used.

heap
The area of memory (free store) provided by the runtime library (and operating system) from which the program can dynamically allocate memory. See Allocate.

Back to top

I

image buffer
A collection of data used to describe an image, containing such information as width, height, color depth and pixel data.

include file
A kind of source file that typically contains type definitions and declarations for variables and procedures that one or more other source files refer to. In general, these files provide a public interface to some module or modules, although a file that is #included can contain any text whatsoever.

inheritance (TYPE or CLASS)
Inheritance is when deriving a type from a base type, by using the Extends declaration. Members of the base type become members of the derived type. Private members of a base type are never directly accessible from a derived type, but can be accessed through calls to the Public and Protected members of the base type.

initialization
The act of giving a variable a value at the point of its creation. For object instances, this involves calling one of its constructors. Not to be confused with assignment, which gives an already existing variable another value.

instance
An instantiated object of a Type or Class.

instantiate
The act of creating an object of a Type or Class, either directly with Dim, or indirectly by, for example, passing an object to a procedure by value.

invariance (TYPE or CLASS)
A typing rule for Type or Class that requires you to use exactly the same type as the one originally specified.

Back to top

J

Back to top

K

Back to top

L

library
Compiled code stored in a single file that can be used when making other programs. A library typically has one or more headers (or include files) to provide all the needed declarations for using the library.

linked list (container)
A collection of data whose elements are typically stored on the heap. The linked list's elements store the addresses of their adjacent elements, and so only sequential access (an element is accessed by following the links from adjacent elements) is possible. This scheme does provide constant-time insertion of elements anywhere into the container, however, and because of this is often preferred over the array.

linker
A program which combines multiple modules and libraries into a single executable which can be loaded into the computer's memory and followed by the computer. FreeBASIC uses the LD linker. Linkers are the most common, but not the only way to produce executables.

LGPL
Shorthand for GNU Lesser General Public License. Like the GNU GPL, but more permissive allowing non-(L)GPL'd works to be statically linked to the LGPL'd work, provided that the new work can have the LGPL'd portion relinked or replaced.

LHS
Acronym for "Left Hand Side".

local variable
A variable that is visible only within the scope in which it is declared, and that is destroyed when program execution leaves that scope.

lock
A synchronization mechanism such that only one thread or process can have access to a shared object, for example a global variable, a device, or a file.

Back to top

M

member
A data field, procedure, enumeration, type alias or anything else declared within a Type or Class definition.

member data
Variables associated with a Type or Class. Member data can be static or non-static.

member function
A procedure associated with a Type or Class. Member functions have full access rights to the members of its type or class, and can be static or non-static.

method
See member function.

module
A source file in its entirety, including any include files that may be present as well. Typically, a module is a logical unit of code, containing parts of a program that relate to one another. For example, if making a game, one may separate the procedures needed for error logging from the procedures that control graphics into their own modules.

Back to top

N

non-static member data
Member data that each instance of a Type or Class gets their own copy of.

non-static member function
A member function that has an implicit This reference as an argument.

null
A constant usually associated with pointers denoting a 'nothing' value. This value is typically an integer '0' (zero) - the 'NULL terminator' appended to zstrings is chr(0), or asc(!"\0") - but can also be defined as a pointer type, like Cast(any ptr, 0).

Back to top

O

object (built-in TYPE or CLASS)
Object is a built-in type which provides Run-Time Type Information (RTTI) for all types derived from it using the Extends declaration, allowing them to be used with Operator Is, and to support Virtual and Abstract member functions.

object code
Code in machine-readable form that can be executed by your computer's CPU and operating system, usually linked with libraries to create an executable file.

operand
One of the arguments passed to an operator. For example, in the expression a = b + c, the operands are a, b and c, while the operators are = and +.

operator
A function taking one or more operands (arguments) and returning a value. Operators can work on built-in data types, or can be overloaded to work on user defined types. See Operators.

overload
To declare a procedure having the same name as another, but with different parameters. Free functions, or module-level functions, can be overloaded using the Overload keyword. Type or Class member functions can be overloaded by default.

override
Attribute to specify that a member function must override a Virtual or Abstract member function of a type less derived than the type it was declared in. Using the Override attribute, the compiler will show an error if the member function does not override anything.

Back to top

P

page buffer
A buffer used for holding the contents of the screen before being displayed on screen. Where multiple page buffers are allowed, one page will be visible to the users while all others are hidden. Also the active page (the one to which changes are made) need not be the visible one allowing changes to one page while showing another.

parameter
The name used by a procedure that corresponds to the argument that is passed to it.

parameter list
The parenthesized comma-separated list of parameters in a procedure declaration or definition.

PDS
Professional Development System. Sometimes referred to as QB7.1.

pitch
The number of bytes per row, in an image or screen buffer. If there is no padding between rows, then this can be calculated by width * bytes_per_pixel, but this is not necessarily safe to assume. The screen's pitch can be found using ScreenInfo, and an image buffer's pitch can be found by checking the pitch value in the image's header.

pointer
A data type used to hold addresses. The kind of pointer determines how the data at the address is interpreted when the pointer is dereferenced, or when used with Operator -> (Pointer To Member Access). See Pointers.

polymorphism (TYPE or CLASS)
Polymorphism is the ability of an object to provide different behaviors (use different implementations) depending on its own nature, specifically depending on position of its real type in the inheritance hierarchy. Polymorphism is achieved by overriding Virtual or Abstract member functions of the base type.

preprocessor
The FreeBASIC preprocessor is responsible for expanding Macros and replacing Defined values with their values.

procedure
A generic name for any block of code that can be called from somewhere else in a program. See Sub, Function.

property
A property is a special sort of type/class members, intermediate between a field (or data member) and a method. See Property.

ptr
Shorthand for pointer. See pointer.

Back to top

Q

queue (container)
A collection of data that offers first-in first-out (FIFO) storage and retrieval. Typically, elements can only be inserted at the back and removed from the front but can be accessed from either end.

Back to top

R

ragged array (container)
A ragged array is an array having rows of differing lengths.

real number
Any positive or negative number including fractions, irrational and transcendental numbers (like pi or e) and zero. Variables containing a real number have a limited range and precision depending on the number of bits used to represent the number. See: Single and Double.

reference
A reference is an entity that is a way to access (in read and in write) data at memory location. A reference can be thought of as a pointer having as value the memory location, and which is implicitly dereferenced. See: Byref (Variables).

registers
Places inside the CPU for data storage. 80386 and compatible 32-bit models have EAX, EBX, ECX, EDX, ESI, EDI, EBP and ESP, plus some special (control/test/debug) registers. NOT related to "Windows registry".

RHS
Acronym for "Right Hand Side".

RTTI
Acronym for "Run-Time Type Information". The Object built-in type provides the RTTI capacity for all types derived from it using the Extends declaration, allowing them to be used with Operator Is, and to support Virtual and Abstract member functions.

Back to top

S

scope
Refers to the life-time and visibility of some component of the program, like a variable or a procedure. For example, a variable defined inside a procedure would have procedure scope: it is visible throughout the procedure, but not outside the procedure's code block. When the procedure ends, the variable goes out of scope and no longer exists.

scope block
A code block where all the lines of source have the same scope. An explicit scope block can be indicated with the Scope statement. Scope blocks may also be implicit with the usage of If..Then, For..Next, and other compound statements.

shared library
A library that exists once on a system that multiple executables can link to at runtime. See Shared Libraries (DLLs).

source code
Code written by the programmer, in a human-readable form, not yet compiled.

stack (container)
A collection of data that offers last-in first-out (LIFO) storage and retrieval. Typically, elements can only be inserted, accessed and removed from the top of the stack.

statement block
One or more lines of code bookended by a compound statement.

static library
A library that is linked into a program at link time. There is one copy of the library for each executable that links to it. All data is executable specific. See Static Libraries.

static member data
Member data that each instance of a Type or Class shares. This data is defined outside of any Type or Class, and takes up no space in the resulting object instance.

static member function
A member function without an implicit this reference as an argument. Static member functions can be called normally through a variable, or directly using the type's name and the scope resolution operator See Static (Member).

static storage
Refers to storage in the .BSS or .DATA sections of an executable. Variables, objects and arrays with static storage are allocated and initialized at compile-time and destroyed (in the case of objects) and deallocated at program-termination. Explicitly initialized variables, objects and arrays are allocated in the .DATA section.

static variable/object/array
A variable, object or array with static storage.
Note: Some times, we talk about 'static array/string' (as opposed to 'dynamic array/string'), but here, the 'static' term applies on the size of the array/string (fixed length as opposed to 'dynamic' term for a variable length) .
Thus this 'static/dynamic' term does not apply on the storage type for data.

sub
A procedure defined using Sub, optionally taking parameters and not returning a value.

SVN
Subversion. A version control system that allows users to keep track of changes made to sources and documents. Used by FB in the past. (see also CVS and GIT)

SWIG
A tool that automatically translates C headers to FreeBASIC (although not always perfectly).

symbol
Used to refer to variables, labels, functions, methods, procedures, or other programmatic constructs in a program.

Back to top

T

.TEXT section
The part of the executable program that will contain program instructions and constant data.

this reference
A reference to an instance of a Type or Class that is passed as a hidden argument to non-static member functions of that type or class. Throughout the member function, this instance is referred to using the this keyword, See This.

thread
A thread of execution within a process (running program) that shares execution time with other threads in the same process. See Threading.

trace
To follow the execution of a program step-by-step either manually by examining the source code, or more practically with a debugger.

Back to top

U

union
A structure that can be used to store different types of variables, such as integers, doubles and fixed-length strings in the same location, but only one at a time. See Union.

user defined data type
A Type, Union, Enum, or Class data type.

Back to top

V

variable
A symbol representing data in memory.

VBDOS
Visual BASIC for DOS, a historical BASIC compiler by M$ from 1992, following after QBASIC. DOS platform dropped very soon, VBDOS never became popular.

vector
A series of data items in memory that can be accessed by an index number. Similar to an array except that vector elements are not necessarily all contained within a single block of memory.

virtual member function
A member function that can be overridden by a member function of a type more derived than the type it was declared in. See Virtual (Member).

Back to top

W

warning
A message displayed by the compiler during compilation that suggests there may be potential problems with the current code.

wiki
An on-line system that provides a set of pages containing information that can be viewed and modified by the public. In this context, it is typically used to refer to the FreeBASIC on line documentation.

Back to top

X

x86
Refers to the instruction set compatible with the 8086 (and later) CPU architecture, FreeBASIC only supports 80386 and later.

Back to top

Y

Back to top

Z

zstring
A zstring is in essence a standard C style string terminated by a null character. This data type is provided for greater compatibility with C libraries.

Back to top


Miscellaneous Keywords

Data
  • Data
  • Read
  • Restore

Debugging
  • Assert
  • AssertWarn
  • Stop

Hardware Access
  • Inp
  • LPrint
  • LPos
  • Out
  • Wait

Operating System
  • Beep
  • Sleep
  • End (Statement)

Stub Pages
  • As
  • For
  • To
  • Is
  • Step
Control Flow
  • Do
  • End If
  • IIf
  • Loop
  • Next
  • Then
  • Until
  • Wend
  • While

Uncategorized
  • End (Block)
  • OffsetOf
  • SizeOf
  • TypeOf
  • Let
  • Rem
  • Option()


Runtime Error Codes
Runtime error codes and messages used by the runtime library.

Description

Freebasic returns the following runtime error codes:
0 | No error
1 | Illegal function call
2 | File not found signal
3 | File I/O error
4 | Out of memory
5 | Illegal resume
6 | Out of bounds array access
7 | Null Pointer Access
8 | No privileges
9 | interrupted signal
10 | illegal instruction signal
11 | floating point error signal
12 | segmentation violation signal
13 | Termination request signal
14 | abnormal termination signal
15 | quit request signal
16 | return without gosub
17 | end of file

No user error code range is defined. If Error is used to set an error code it is wise to use high values to avoid collisions with the list of built-in error codes. (This built-in list may be expanded later.)

See also

  • Err
  • Error
  • On Error
  • Error Handling


Comparison of C/C++ and FreeBASIC

C/C++ FreeBASIC

variable declaration
int a;
int a, b, c; 		dim a as long
dim as long a, b, c

uninitialized variable
int a; dim a as long = any

zero-initialized variable
int a = 0; dim a as long

initialized variable
int a = 123; dim a as long = 123

array
int a[4];
a[0] = 1; 		dim a(0 to 3) as long
a(0) = 1

pointer
int a;
int *p;
p = &a;
*p = 123; 		dim a as long
dim p as long ptr
p = @a
*p = 123

structure, user-defined type
struct UDT {
int myfield;
} 		type UDT
myfield as long
end type

typedef, type alias
typedef int myint; type myint as long

struct pointer
struct UDT x;
struct UDT *p;
p = &x;
p->myfield = 123; 		dim x as UDT
dim p as UDT ptr
p = @x
p->myfield = 123

function declaration
int foo( void ); declare function foo( ) as long

function body
int foo( void ) {
return 123;
} 		function foo( ) as long
return 123
end function

sub declaration
void foo( void ); declare sub foo( )

sub body
void foo( void ) {
} 		sub foo( )
end sub

byval parameters
void foo( int param );
foo( a ); 		declare sub foo( byval param as long )
foo( a )

byval pointers to parameters
void foo( int *param );
foo( &a ); 		declare sub foo( byval param as long ptr )
foo( @a )

byref parameters
void foo( int& param );
foo( a ); 		declare sub foo( byref param as long )
foo( a )

statement separator
;

:
<end-of-line>

for loop
for (int i = 0; i < 10; i++) {
...
} 		for i as long = 0 to 9
...
next

while loop
while (condition) {
...
} 		while condition
...
wend

do-while loop
do {
...
} while (condition); 		do
...
loop while condition

if block
if (condition) {
...
} else if (condition) {
...
} else {
...
} 		if condition then
...
elseif condition then
...
else
...
end if

switch, select
switch (a) {
case 1:
...
break;
case 2:
case 3:
...
break;
default:
...
break;
} 		select case a
case 1
...

case 2, 3
...

case else
...

end select

string literals, zstrings
char *s = "Hello!";
char s[] = "Hello!"; 		dim s as zstring ptr = @"Hello!"
dim s as zstring * 6+1 = "Hello!"

hello world
#include <stdio.h>
int main() {
printf("Hello!\n");
return 0;
} 		print "Hello!"

comments
// foo
/* foo */ 		' foo
/' foo '/

compile-time checks
#if a
#elif b
#else
#endif 		#if a
#elseif b
#else
#endif

compile-time target system checks
#ifdef _WIN32 #ifdef __FB_WIN32__

module/header file names
foo.c, foo.h foo.bas, foo.bi

typical compiler command to create an executable
gcc foo.c -o foo fbc foo.bas

Last reviewed by MrSwiss on April 6, 2018 Note: changed Integer to Long (where needed)


Comparison of FreeBASIC and C integer data types
This page is a guide for writing FreeBASIC declarations for C libraries. It is not a promise that every C type name has one universal FreeBASIC spelling.

C has two different concerns that are easy to mix up:
  • ABI compatibility: the argument has the same size, signedness, alignment, and calling convention.
  • C spelling compatibility: the generated declaration uses the same C type spelling, which can matter for C++ name mangling, GCC/Clang builtin prototype checks, and compiler diagnostics.
For ordinary Extern "C" calls, ABI compatibility is often enough. For C++ bindings and compiler builtins, exact C spelling can matter.

The most important rule is that Long in FreeBASIC is always 32-bit. It is not the same thing as C long on all targets. Integer is pointer-sized: 32-bit on 32-bit targets and 64-bit on 64-bit targets.

Common C data models

C data model | Example targets in this tree | C int | C long | C long long | Pointer size
ILP32 | DOS, Win32, 32-bit Linux/BSD/Android, Wii, Xbox, WebAssembly/JavaScript | 32 bits | 32 bits | 64 bits | 32 bits
LLP64 | Win64, Windows ARM64 | 32 bits | 32 bits | 64 bits | 64 bits
LP64 | 64-bit Linux/BSD/Android/macOS/Solaris/illumos/Haiku/Cygwin | 32 bits | 64 bits | 64 bits | 64 bits

All targets supported by this tree use a 32-bit C int. This is a statement about the targets supported here, not about every possible C implementation.

FreeBASIC integer sizes

FreeBASIC type | Signedness | Size | Good ABI match for
Byte | signed | 8 bits | signed char
UByte | unsigned | 8 bits | unsigned char or raw bytes
Short | signed | 16 bits | short
UShort | unsigned | 16 bits | unsigned short
Long | signed | 32 bits | int, int32_t on most supported targets
ULong | unsigned | 32 bits | unsigned int, uint32_t on most supported targets
LongInt | signed | 64 bits | long long
ULongInt | unsigned | 64 bits | unsigned long long
Integer | signed | pointer size | intptr_t or pointer-sized indexes
UInteger | unsigned | pointer size | uintptr_t, size/count values when the target typedef agrees
Any Ptr | pointer | pointer size | void * or opaque handles

The table above is about ABI-sized values. It is not enough when the exact C spelling matters. For example, on LP64 targets C long and C long long are both 64-bit, but they are still different C types.

Recommended binding choices

C type or typedef | Use in FreeBASIC | Why | Notes
signed char | Byte | exact 8-bit signed storage | Plain C char is a different C spelling
unsigned char | UByte | exact 8-bit unsigned storage | Also useful for raw byte buffers
char | ZString Ptr for C strings, otherwise a deliberate Byte/UByte based alias | C char signedness is target dependent | Plain char is distinct from signed char and unsigned char in C++
short | Short | exact 16-bit signed storage |
unsigned short | UShort | exact 16-bit unsigned storage |
int | Long | C int is 32-bit on supported targets |
unsigned int | ULong | C unsigned int is 32-bit on supported targets |
long | target c_long alias | C long is 32-bit on ILP32/LLP64 and 64-bit on LP64 | Do not blindly use FB Long
unsigned long | target c_ulong alias | same data model issue as C long | Do not blindly use FB ULong
long long | LongInt | natural 64-bit signed long long spelling | Not the exact spelling of LP64 C long
unsigned long long | ULongInt | natural 64-bit unsigned long long spelling | Not the exact spelling of LP64 unsigned long
int32_t, uint32_t | crt/stdint.bi typedefs | fixed-width C typedefs are already translated | Use Long/ULong only when the actual header spelling does not matter
int64_t, uint64_t | crt/stdint.bi typedefs | fixed-width 64-bit typedef spelling can differ by target | Do not assume every int64_t is C long long
intptr_t, uintptr_t | crt/stdint.bi typedefs | pointer-sized integer typedefs | Integer/UInteger are the underlying FB-sized types
ptrdiff_t, size_t, ssize_t | crt/stddef.bi typedefs | spelling differs between LP64, LLP64 and some 32-bit targets | Avoid hand-written mappings unless needed
wchar_t, wint_t | crt/stddef.bi typedefs | width and signedness vary by target | Never assume wchar_t is always 16-bit
enum | usually Long for ordinary C enums | ordinary C enum ABI is normally int-sized on these targets | verify packed enums, compiler flags, and C++ scoped enums

For target-specific C long, define a small alias once and use it in the binding:

' C long follows the target C data model.
#if defined( __FB_64BIT__ ) and defined( __FB_UNIX__ )
    type c_long  as integer  alias "long"
    type c_ulong as uinteger alias "long"
#else
    type c_long  as long  alias "long"
    type c_ulong as ulong alias "long"
#endif

extern "C"
    declare function c_function( byval value as c_long ) as c_long
end extern

For standard C typedefs, prefer the translated CRT headers instead of spelling the mapping by hand:

#include once "crt/stddef.bi"   ' size_t, ptrdiff_t, wchar_t
#include once "crt/stdint.bi"   ' int32_t, uint64_t, intptr_t

Common mistakes

  • Do not translate C long as FreeBASIC Long without checking the target data model.
  • Do not translate LP64 C long as LongInt when exact C spelling matters. The size matches, but the C type is long, not long long.
  • Do not use Integer as a universal replacement for C long. Integer is pointer-sized, while C long is ABI-model-sized.
  • Do not assume size_t is always the same C spelling as unsigned long or unsigned long long. Use the target typedef.
  • Do not assume plain C char has the same signedness on every target. Use signed char or unsigned char in the C API when signedness matters.
  • Do not assume standard typedefs such as time_t, wchar_t, or ssize_t are the same across all operating systems.
See also

  • Creating FB bindings for C libraries - How to translate C data types to FB
  • Standard Data Type Limits
  • Integer
  • Long
  • LongInt
  • Type (Alias)


Developer pages
This chapter collects the pages used by developers working on the compiler, runtime libraries, build system, package scripts, and platform ports.

Building FreeBASIC

  • Building FreeBASIC from source
  • Getting the source code
  • Building for DOS
  • Building on Linux and package distros
  • Building on Windows/MSYS2
  • Building for JavaScript
  • Building for Android
  • Building for Wii
  • Building for Xbox
  • Getting source code updates and rebuilding FreeBASIC
  • Debugging FreeBASIC
  • FreeBASIC build configuration
  • The builtin.bi include
  • Build troubleshooting notes
  • Toolchain choice notes
  • The FreeBASIC test suite
  • Normal vs standalone compilers
  • Developer glossary
  • Making releases
  • Cross-compiling FreeBASIC
  • Bootstrapping FreeBASIC

Headers, bindings, and libraries

  • Creating bindings
  • Translating C headers
  • Header style guide
  • External library bindings

Compiler internals

  • Compiler platform modules
  • Object information
  • Compiler memory management
  • Lexer
  • Parser
  • Top-level parser
  • Symbol parser
  • Compiler type system
  • Array implementation notes
  • SELECT CASE implementation notes
  • Profiling with fb profiler
  • Profiling with gprof

Runtime and graphics internals

  • Structure layout
  • INKEY runtime notes
  • Graphics library overview
  • Graphics internal formats


builtin.bi compiler builtin declarations

builtin.bi exposes a selected set of GCC and clang __builtin_* entry points to FreeBASIC code.

It is a low-level include for code that is already depending on the C backend. It is not a general replacement for normal FreeBASIC statements, the runtime library, or the C runtime headers.

Backend requirement

builtin.bi requires the GCC or clang backend:

#include once "builtin.bi"

Compile with one of the C backends:

fbc -gen gcc program.bas
fbc -gen clang program.bas

If the active backend is not gcc or clang, the include stops with an error. That is intentional. The declarations name compiler builtins, not portable runtime functions, and non-C backends do not have the same builtin lowering rules.

Why this header exists

GCC and clang recognize many __builtin_* names specially. Some behave like C library calls that can be optimized or inlined. Others expose compiler knowledge such as object size, checked arithmetic, branch prediction hints, byte swapping, or bit counting.

FreeBASIC can call those entry points through normal DECLARE statements, but the declarations must match the target C ABI. That is the main job of builtin.bi.

The header defines private helper types such as __fb_builtin_size_t, __fb_builtin_clong, and __fb_builtin_culong so declarations follow the target C data model. For example, LP64 Unix targets use C long for size_t-related declarations, while Win64 keeps C long at 32 bits.

What is included

The header intentionally exposes a core useful set, not every builtin supported by GCC or clang.

Currently included families:
  • Memory and string builtins: __builtin_memcpy, __builtin_memmove, __builtin_memset, __builtin_strlen, __builtin_strcmp, and related routines.
  • Bit operations: __builtin_ffs, __builtin_clz, __builtin_ctz, __builtin_clrsb, __builtin_popcount, and __builtin_parity with int, long, and long long variants.
  • Byte swaps: __builtin_bswap16, __builtin_bswap32, and __builtin_bswap64.
  • Object-size queries: __builtin_object_size and, on targets where it lowers correctly, __builtin_dynamic_object_size.
  • Checked arithmetic: fixed-type signed and unsigned add, subtract, and multiply overflow helpers.
  • Code-generation hints: __builtin_expect, __builtin_expect_with_probability, __builtin_prefetch, __builtin_trap, and __builtin_unreachable.
The header intentionally does not include target-specific builtins, language-specific builtins, broad SIMD families, every C library builtin, or portable fallback implementations.

Examples

Checked addition:

#include once "builtin.bi"

dim as long a = &h7fffffff
dim as long b = 1
dim as long result

if __builtin_sadd_overflow( a, b, @result ) then
    print "overflow"
else
    print result
end if

Byte swapping:

#include once "builtin.bi"

dim as ulong value = &h12345678
print hex( __builtin_bswap32( value ) )

Using an unlikely branch hint:

#include once "builtin.bi"

dim as long failed = any
failed = 0

if __builtin_expect( failed, 0 ) then
    print "rare failure path"
end if

Important caveats

These calls follow the C compiler builtin semantics. That means some familiar-looking routines still carry C rules:
  • __builtin_memcpy requires non-overlapping source and destination ranges. Use __builtin_memmove when overlap is possible.
  • __builtin_clz and __builtin_ctz families have undefined results when the input is zero.
  • __builtin_unreachable tells the optimizer that execution cannot reach that point. If execution does reach it, program behavior is undefined.
  • __builtin_trap is an intentional abnormal stop, not normal error handling.
  • Object-size builtins report what the compiler can prove, not a general run-time allocation size.
  • Builtins may be optimized away, lowered inline, or emitted as calls depending on target, optimization level, and compiler version.
When to use it

Use builtin.bi when the program or library is intentionally tied to the GCC/clang backend and needs a specific compiler builtin.

Good uses include low-level runtime code, checked arithmetic helpers, fortify-style buffer checks, endian conversion helpers, and small hot paths where the C compiler builtin is exactly the interface desired.

For ordinary application code, prefer normal FreeBASIC features, the runtime library, or documented C runtime declarations unless a builtin is specifically needed.

See also

  • FreeBASIC build configuration
  • Getting source code updates and rebuilding FreeBASIC
  • C/C++ vs. FreeBASIC integer data type comparison

FreeBASIC Developer Information

Table of Contents


Antoni
antonigual [at] eic [dot] ictnet [dot] es


About
About the FreeBASIC project.

The FreeBASIC project is a set of cross-platform development tools initially created by Andre Victor, consisting of a compiler, GNU-based assembler, linker and archiver, and supporting runtime libraries, including a software-based graphics library. The compiler, fbc, currently supports building for i386-based architectures on the DOS, Linux, Windows and Xbox platforms. The project also contains thin bindings (header files) to some popular 3rd party libraries such as the C runtime library, Allegro, SDL, OpenGL, GTK+, the Windows API and many others, as well as example programs for many of these libraries.

FreeBASIC is a high-level programming language supporting procedural, object-oriented and meta-programming paradigms, with a syntax compatible to Microsoft QuickBASIC. In fact, the FreeBASIC project originally began as an attempt to create a code-compatible, free alternative to QuickBASIC, but it has since grown into a powerful development tool. FreeBASIC can be seen to extend the capabilities of QuickBASIC in a number of ways, supporting more data types, language constructs, programming styles, and modern platforms and APIs.


Credits (in alphabetical order)

Project Members

  • Andre Victor T. Vicentini (av1ctor[at]yahoo.com.br):
Founder, main compiler developer, author of many parts of the runtime, FB headers (FBSWIG)
  • Angelo Mottola (a.mottola[at]libero.it):
Author of the FB graphics library, built-in threads, thread-safe, runtime, ports I/O, dynamic library loading, Linux port.
  • Bryan Stoeberl (b_stoeberl[at]yahoo.com):
SSE/SSE2 floating point math, AST vectorization.
  • Daniel C. Klauer (daniel.c.klauer[at]web.de):
FB releases since 0.21, C & LLVM backends, 64bit port, dynamic arrays in UDTs, virtual methods, preprocessor-only mode, miscellaneous fixes and improvements.
  • Daniel R. Verkamp (i_am_drv[at]yahoo.com):
DOS, XBox, Darwin, *BSD ports, DLL and static library automation, VB-compatible runtime functions, compiler optimizations, miscellaneous fixes and improvements.
  • Ebben Feagan (sir_mud[at]users.sourceforge.net):
FB headers, C emitter
  • Jeff Marshall (coder[at]execulink.com):
FB releases since 0.17, FB documentation (wiki maintenance, fbdocs, offline-docs generator), Gosub/Return, profiling support, dialect, specifics, DOS serial driver, miscellaneous fixes and improvements.
  • Mark Junker (mjscod[at]gmx.de):
Author of huge parts of the runtime (printing support, date/time, functions, SCR/LPTx/COM/console/keyboard I/O), Cygwin port, first FB installer scripts.
  • Matthew Fearnley (matthew.w.fearnley[at]gmail.com):
Print Using & Co, ImageInfo, and others, dialect specifics, optimization improvements in the compiler, many fixes and improvements.
  • Ruben Rodriguez (rubentbstk[at]gmail.com):
Var keyword, const specifier, placement new, operator overloading and, other OOP-related work, C BFD wrapper, many fixes and improvements.
  • Simon Nash:
AndAlso/OrElse operators, ellipsis for array initializers, miscellaneous fixes and improvements.

Contributors

  • 1000101:
gfxlib2 patches, e.g. image buffer alignment
  • Abdullah Ali (voodooattack[at]hotmail.com):
Windows NT DDK headers & examples
  • AGS:
gdbm, zlib, Mini-XML, PCRE headers
  • Claudio Tinivella (tinycla[at]yahoo.it):
Gtk tutorials
  • Chris Davies (c.g.davies[at]gmail.com):
OpenAL headers & examples
  • Dinosaur:
CGUI headers
  • D.J.Peters:
ARM port, ODE headers & examples, Win32 API header fixes
  • Dumbledore:
wx-c headers & examples
  • dr0p (dr0p[at]perfectbg.com):
PostgreSQL headers & examples
  • Edmond Leung (leung.edmond[at]gmail.com):
SDL headers & examples
  • Eric Lope (vic_viperph[at]yahoo.com):
OpenGL & GLU headers & examples, examples/gfx/rel-*.bas demos
  • Florent Heyworth (florent.heyworth[at]swissonline.ch):
Win32 API sql/obdc headers
  • fsw (fsw.fb[at]comcast.net):
Win32 API headers, Gtk/Glade/wx-c examples
  • Garvan O'Keeffe (sisophon2001[at]yahoo.com):
FB ports of many NeHe OpenGL lessons, PDFlib examples
  • Hans L. Nemeschkal (Hans.Leo.Nemeschkal[at]univie.ac.at):
DISLIN headers
  • Jofers (spam[at]betterwebber.com):
ThreadCall keyword, libffi/libjit headers, FreeType examples
  • Jose Manuel Postigo (postigo[at]uma.es):
Linux serial devices support
  • Laanan Fisher (laananfisher[at]gmail.com):
FB test suite using CUnit
  • Matthew Riley (pestery):
OpenGL, GLFW, glext, FreeGLUT, cryptlib headers
  • Matthias Faust (matthias_faust[at]web.de):
SDL_ttf headers & examples
  • Marzec:
SDL headers, SDL_bassgl, SDL_opengl and SDL_key examples, First file routines for FB's rtlib
  • MJK:
big_int header fixes
  • MOD:
wx-c, BASS headers; -lang qb support for built-in macros, "real" Rnd() algorithm
  • Nek (dave[at]nodtveidt.net):
Win32 API headers
  • Plasma:
FMOD and BASS headers & examples
  • Randy Keeling (randy[at]keeling.com):
GSL matrix example
  • Saga Musix (Jojo):
BASS examples with sounds
  • Sisophon2001:
gfxlib2 fixes, Nehe OpenGL lesson ports
  • Sterling Christensen (sterling[at]engineer.com):
Ex-project-member, author of FB's initial QB-like graphics library
  • TeeEmCee:
gfxlib2 fixes
  • TJF (Thomas.Freiherr[at]gmx.net):
ARM port, GTK+, glib, Cairo, Pango headers & examples, SQLiteExtensions headers
  • zydon:
Win32 API examples

Greetings

  • Plasma:
Owner of the freebasic.net domain and main site hoster, many thanks to, him.
  • VonGodric:
Author of the first FreeBASIC IDE: FBIDE.
  • Everybody that helped writing the documentation (and in special Nexinarus who started it):
https://www.freebasic.net/wiki/wikka.php?wakka=ContributorList
  • All users that reported bugs, requested features and as such helped improving the compiler, language and run-time libraries.


Most Important Features

BASIC compatibility
  • FreeBASIC is not a "new" BASIC language. You don't need to learn much new if you are familiar with any Microsoft-BASIC variant. You can use either "-lang qb" for compatibility, or (default) "-lang fb" for some of the new features, but it also brings some restrictions and some similarity with the "C" programming language. See also CompilerDialects.
  • FreeBASIC is case-insensitive; explicit "main" procedure is not required; most of the graphic and console statements and procedures found in Microsoft QuickBASIC are implemented, et cetera.
  • Only with "-lang qb": scalar variables don't need to be dimensioned and suffixes can be used; line numbers are supported; GoSub supported.

Clean syntax
  • Only a small number of keywords have been added. All procedures are implemented as libraries, so for the most part, there are no new intrinsic routines, and therefore there is a low chance of having name duplication with old code.

Thin bindings (header files) to existing C libraries and APIs
  • No wrappers or helpers are necessary, just a ported header file, making usage of external C libraries very easy
  • The official distribution comes with several bindings to existing C libraries already, see External Libraries TOC for a complete up-to-date list

Multi-platform
  • FreeBASIC currently runs on 32-bit Windows, Linux, and DOS (a 16-bit DOS is good enough, although FreeBASIC itself and compiler output are 32-bit) and also creates applications for the Xbox console. More platforms to come.
  • The runtime library was written with portability in mind. All third-party tools used exist on most operating systems already as they are from the GNU binutils. The compiler is written in 100% FreeBASIC code (that is, FreeBASIC compiles itself.), which makes it simple to be bootstrapped as it doesn't depend on non-portable tools.

Unicode support
  • Besides ASCII files with Unicode escape sequences (\u), FreeBASIC can parse UTF-8, UTF-16LE, UTF-16BE, UTF-32LE and UTF-32BE source (.bas) or header (.bi) files, they can be freely mixed with other sources/headers in the same project (also with other ASCII files).
  • Literal strings can be typed in the original non-Latin alphabet, just use a text-editor that supports some of the Unicode formats listed above.
  • The WString type holds wide-characters, all string procedures (like Left, Trim, etc) will work with wide-strings too.
  • Open was extended to support UTF-8, UTF-16LE and UTF-32LE files with the Encoding specifier. Input # and Line Input #, as well as Print # and Write # can be used normally, and any conversion between Unicode to ASCII is done automatically if necessary.
  • Print also supports Unicode output (see Requirements).

A large number of built-in data types
  • Integer: Byte, UByte, Short, UShort, Integer, UInteger, Long, ULong, LongInt, ULongInt
  • Floating-point: Single, Double
  • String: fixed, variable-length or null-terminated (ZString), up to 2GB long
  • Unicode strings (WString), like ZString, but with support for wide characters. Use the Windows Unicode API procedures directly, etc.

User-defined types (UDTs)
  • Unlimited nesting.
  • BASIC's Type statement is supported, along with the new Union statement (including anonymous nested unions).
  • Array fields utilizing up to eight dimensions can be used.
  • Procedure pointer fields.
  • Bit fields.

Enumerations (enums)
  • Easily declare a list of constants with sequential values with Enum.

Arrays
  • Fixed- and variable- length arrays are supported, up to 2 GB in size.
  • Up to eight dimensions, including arrays with unknown dimensions.
  • Any lower and upper boundaries.
  • Element data can be preserved during a re-size of variable-length arrays with ReDim using the new Preserve specifier.

Pointers
  • Pointers to any of the data types listed above, including string characters, array elements and UDT's.
  • Uses the same syntax as C.
  • Unlimited indirection levels (e.g., pointer to pointer to ...).
  • Procedure pointers.
  • Indexing []'s (including string indexing).
  • Type casting.

Variable, object and array initialization
  • For static, module-level or local variables, arrays and UDT's.

Default procedure parameter values
  • For numeric, string and UDT parameter types.

Procedure overloading
  • Including procedures with default parameter values.

In-line assembly
  • Intel syntax.
  • Reference variables directly by name; no "trick code" needed.

Traditional preprocessor support
  • Same syntax as in C.
  • Single-line macros supported with the #define command, including parameters.
  • Multi-line macros supported with the #macro command.

Type aliases
  • Supporting forward referencing as in C, including UDT and procedure pointer types.

C-like escape sequences for string literals
  • Same as in C (except numbers are interpreted as decimal, not octal).

Debugging support
  • Full debugging support with GDB (the GNU debugger) or Insight (a GDB GUI frontend).
  • Array bounds checking (only enabled by the -exx command-line option).
  • Null pointer checking (same as above).

Create OBJ's, LIB's, DLL's, and console or GUI EXE's
  • You are in no way locked to an IDE or editor of any kind.
  • You can create static and dynamic/shared libraries adding just one command-line option (-lib or -dylib/-dll).

As a 32-bit application
  • FreeBASIC can compile source code files up to 2 GB in size.
  • The number of symbols (variables, constants, et cetera) is only limited by the total memory available during compile time. (You can, for example, include OpenGL, GTK/SDL, BASS, simultaneously in your source code.)

Optimized code generation
  • While FreeBASIC is not an optimizing compiler, it does many kinds of general optimizations to generate the fastest possible code on x86 CPU's, not losing to other BASIC alternatives, including the commercial ones.

Completely free
  • All third-party tools are also free. No piece of abandoned or copyrighted software is used (except GoRC on Win32). The assembler, linker, librarian/archiver, and other command-line applications come from the GNU binutils programming tools.


Data Types
  • Standard Data Types
  • User Defined Types


Arrays
An array in fbc is a collection of elements where each element has the same type and is accessed with an index in to the array.

Example:
    '' one dimensional array (1 index)
    Dim a(1 To 10) As Integer
    Print a(1)  '' first element (integer)
    Print a(10) '' last element (integer)

    '' two dimensional array (2 indexes)
    Dim b(1 To 2, 1 To 5) As Integer
    Print b(1,1) '' first element (integer)
    Print b(2,5) '' last element (integer)

Array Dimensions and Bounds

The number of dimensions refers to the number of indexes that are required to be given to access an element of an array. The number of dimensions may or may not be part of the declaration. If the number of dimensions are known at compile time within the scope that the array is used, fbc can check and error if the wrong number of indexes are specified.

The bounds of an array are the allowable minimum and maximum index values for each dimension. Accessing an array element with an index or indexes that are outside the array bounds of a dimension is undefined behaviour.

fbc can check and error if an index or indexes (access) are outside the bounds of the array when compiled with '-exx' or '-earray' compile options. If the array bounds are compile-time constant, and the array access is compile-time constant, fbc can check if an array access is outside the bounds of the array at compile time. Otherwise, the array bounds check must occur at run-time if either the bounds or the access is non-constant.

Fixed dimension versus unknown dimension

The number of dimensions may be fixed or unknown. fbc will attempt to determine the number of dimensions an array is expected to have based on declarations for the array. If fbc cannot determine the number of dimensions at compile time, the number of dimensions will become fixed on first redimension of the array at run time.

Example: fixed 2 dimension, dynamic bounds
Dim a(Any, Any) As Integer
ReDim a(1 To 2, 1 To 5)

Example: Dynamic dimension, dynamic bounds
Dim a() As Integer

'' then only one of on first time use ...
ReDim a(1 To 10)
ReDim a(1 To 2, 1 To 5)
ReDim a(1 To 2, 1 To 5, 1 To 3)

Once number of dimensions are known to fbc, within the scope of the array, fbc will error if any access to the error has wrong number of dimensions. Or if still unknown at compile time, as in the case of an array passed as argument to a procedure and resized, the number of dimensions become fixed at run time.

Fixed bounds versus Dynamic bounds

Fixed length arrays have array bounds that are known at compile-time. Dynamic (or variable length) arrays have array bounds that can be altered and resized at run-time, and may be considered unknown at compile time.

Example: fixed (constant) bounds and constant access
Dim a(1 To 10) As Integer
Print a(11) '' compile time array out-of-bounds

Example: fixed bounds and non-constant access
Dim a(1 To 10) As Integer
Dim i As Integer
Print a(i) '' run time array out-of-bounds

Example: dynamic bounds
Dim a(Any) As Integer '' 1 dimensional, empty
ReDim a(1 To 10)      '' resized to 10 elements
Print a(11) '' run time array out-of-bounds
Print a(i)  '' run time array out-of-bounds

Static Array versus Dynamic Array

Arrays may have static or dynamic memory allocation. The descriptor may be static or dynamic, and memory space for the data may be static or dynamic. The terms static and dynamic may be overused and so may lose meaning when describing an array. In this context static versus dynamic should not be confused with fixed-length or variable-length. In this context we are referring to how and where the array descriptor and it's associated data are allocated in memory, and to some extent the life time of the variable.

For an array descriptor to be valid, it must be initialized. An uninitialized array descriptor will almost certainly lead to undefined behaviour at run time.

The array descriptor itself may be allocated in .bss, .data, on stack or on heap, depending on the declaration of the array. Though typically not in .bss section because an array descriptor usually must be initialized to some non-zero default values to be usable.

The array's data may be located in .bss section, .data section, on stack or on heap, depending on the declaration of the array. In fbc's current implementation, the array data for variable-length arrays is always allocated on the heap (i.e. malloc()).

Array Descriptor

See Fbarray (Array Descriptor Structure And Access).


Creating FB bindings for C libraries

This page aims to document the problems and solutions commonly encountered when creating FB bindings for C libraries.

In general, FB and C/C++ are very similar. FB follows the same ABI as GCC where applicable, in order to be binary-compatible as much as possible. The language syntax is also similar to C/C++. As a result, a lot of type and procedure declarations can be translated directly 1:1 between C and FB. However, there also are constructs which cannot be translated directly, for example: typedefs declaring function types. FB has function pointer types, but not plain function types.
  • The good news: We have tools (fbfrog and h_2_bi) which can do most of the translation automatically.
  • The bad news: There always are some problems which cannot be solved automatically and thus need to be fixed manually.

Data types

C/C++ type | Size in bytes (GCC on Linux/Windows) | Corresponding FreeBASIC type (on 32bit or 64bit)
char | 1 | Byte
short [int] | 2 | Short
int | 4 | Long
enum (underlying type int) | 4 | Long
long long [int] | 8 | LongInt
float | 4 | Single
double | 8 | Double
long double | 12 on 32bit, 16 on 64bit | CLongDouble from crt/longdouble.bi
_Bool/bool | 1 | Byte / Boolean
* (pointer) | 4 on 32bit, 8 on 64bit | Ptr/Pointer
ssize_t, intptr_t | 4 on 32bit, 8 on 64bit | Integer
size_t, uintptr_t | 4 on 32bit, 8 on 64bit | UInteger
long [int] | 4 on 32bit systems and Win64 (!), 8 on 64bit Linux/BSD | CLong from crt/long.bi

  • Caveat: int/long is not Integer/Long. In FB, Integer corresponds to Pointer - it's 32bit on 32bit and 64bit on 64bit (on all operating systems). Long stays 32bit everywhere. In C, int stays 32bit everywhere, and long only corresponds to pointers on Linux/BSD systems, but not on Win64, where long is still 32bit. On Win64, long long is the only 64bit integer type in C. Thus, neither C's int nor C's long are compatible to FB's Integer.
  • Caveat: long int is not LongInt. FB's LongInt corresponds to C's long long, not C's long.
  • int can be translated to Long, as both are 32bit consistently.
  • ssize_t or intptr_t can be translated to Integer because they typically have the same size as pointers.
  • long cannot be translated directly, but we have crt/long.bi which provides the target-specific clong and culong type aliases.
  • long double cannot be translated directly, but we have crt/longdouble.bi which provides the target-specific clongdouble type.
  • enum is a special case. Typically their underlying type is int (32bit), but in FB Enum uses Integer (32bit/64bit) and it does not allow changing that. Thus enums (used as data type in declarations) cannot be translated as Enums.
For example:

Enum MyEnum {
    A,
    B
}

has to be translated as:

Type MyEnum As Long
Enum
    A
    B
End Enum

  • BOOL from windows.h is just a typedef for int, and should not be confused with the C _Bool or C++ bool types.

Symbol name conflicts

  • C/C++ is case-sensitive, with ~50 keywords
  • FreeBASIC is case-insensitive, with ~400 keywords
  • C code sometimes uses FB keywords as symbol identifiers, for example INT, string, open. Such symbols must be renamed.
  • C code often contains identifiers which differ only in case, for example GET_VERSION and get_version. This is not allowed in FB; one of the symbols must be renamed.
  • In C, a macro can have the same identifier as a function. This is not allowed in FB; one of the symbols must be renamed.
Examples

C code using FB keywords as identifiers:
typedef Int Int;
void Open(void);

Type INT_ As Long
Declare Sub open_ cdecl Alias "open"()

C code relying on case-sensitivity:
void foo(void);
void Foo(void);
void FOO(void);

'' Wrong translation:
Extern "C"
    Declare Sub foo()
    Declare Sub Foo()  '' error: duplicate definition
    Declare Sub FOO()  '' error: duplicate definition
End Extern

'' Correct translation:
Extern "C"
    Declare Sub foo()
    Declare Sub Foo_ Alias "Foo"()
    Declare Sub FOO__ Alias "FOO"()
End Extern

Another classic example where this kind of conflict happens:
#define GET_VERSION_NUMBER 123
Int get_version_number(void);

Extern "C"
    #define GET_VERSION_NUMBER_ 123  '' renamed to avoid conflict
    Declare Function get_version_number() As Long
End Extern

Conflict between procedure and macro:
void f(Int);
#define f(i) f(i + 1)

Extern "C"
    Declare Sub f(ByVal As Long)
    #define f_(i) f(i + 1)  '' renamed to avoid conflict
End Extern

Solutions

    • Symbols should be renamed by appending _ underscores. This way we solve the conflicts and still stay close to the original API.
    • Renaming a symbol should not cause further renames (for example, if foo must be renamed, but foo_ already exists, then foo should be renamed to foo__ instead)
    • A list of renamed symbols should be available in the binding or in the binding's documentation such that users can identify such differences to the original API.
    • Fields inside structures do not need to be renamed just because they match an FB keyword. By using the "As DataType Name" syntax they can be use FB keywords as identifiers. This only works as long as it is a simple structure (plain old data), not a class.
Type UdtWithKeywordFields
    As ZString Ptr String  '' Field "String" of type ZString Ptr
    As Long Type  '' Field "Type" of type "Long"
    As Long As  '' Field "As" of type "Long"
End Type

Function types

In C it's possible to have typedefs with function types. Dereferencing a function pointer type results in a function type. FB only has function pointer types, but not function types.

// A Function typedef (Function result = void, no parameters)
typedef void F(void);

// Using it To Declare a Function called f1
F f1;

// Usually f1 would be declared like This (use of Function typedefs Is pretty rare):
void f1(void);

// A more Common use For Function typedefs Is To Declare pointers To them (Function pointers):
Extern F *pf1;

Since FB does not have function types, such typedefs have to be solved out, or turned into a function pointer:

Extern "C"

Type F As Sub()  '' Function pointer type

'' Declaring procedures is only possible with Declare in FB
Declare Sub f1()

'' But at least FB has function pointer types.
'' Since F already is the function pointer in the FB translation, there is no extra PTR here
Extern pf1 As F

End Extern


Bootstrapping FreeBASIC

FreeBASIC can rebuild itself, but the path depends on what compiler and bootstrap sources are already available. The current make system keeps bootstrap logic in mk/bootstrap/ and exposes named targets for the common states.

Common bootstrap targets

make prereqs-fbc
make bootstrap-minimal
make bootstrap-seed-peer
make bootstrap-emit
make bootstrap-dist-target

The right target depends on whether the host already has a usable fbc, a system package compiler, or target-specific generated bootstrap sources.

How to debug bootstrap problems

Start with make print-config and make prereqs-fbc. Bootstrap failures are usually easier to understand after confirming the selected host, target, bootstrap compiler, backend, binary names, and output directories.

If a new platform cannot even run a previous FreeBASIC compiler, generate or stage bootstrap sources from a platform that can. Avoid changing parser or code generation behavior just to make a bootstrap step pass until the failure has been traced to the real portability problem.


Building FreeBASIC from source

FreeBASIC is built through the top-level GNUmakefile and the modular make files in mk/. Older notes may refer to a single monolithic makefile or to manual platform setup. The current tree is intentionally split into platform, CPU, toolchain, feature policy, bootstrap, packaging, and install-layout modules.

Normal local build

A typical native build starts with these commands:

make print-config
make prereqs-fbc
make -j4

Use a job count that fits the machine. The important point is that print-config shows what the make system thinks the host, target, compiler, layout, and tools are before a long build starts.

The default build includes the compiler stage and the core libraries. The library group includes the runtime library, graphics library, sound library, and other shipped libraries used by installed FreeBASIC programs.

Build scripts

The build_scripts/ directory contains the repeatable build, package, and smoke-test entry points used for platform work. Prefer those scripts when building release packages or exercising a target-specific port. Direct make commands are still useful while developing a focused compiler or runtime change.

Useful follow-up pages

  • Build configuration
  • Getting updates and rebuilding
  • The test suite
  • Cross-compiling
  • Bootstrapping


Building for Android

The Android port builds FreeBASIC programs into Android application packages using the Android SDK/NDK tools. The current tree keeps the Android application template under share/freebasic-android/template and drives package creation through the build scripts.

Build scripts

./build_scripts/msys2-build-freebasic-android.sh
./build_scripts/debianubuntu-test-freebasic-android.sh

Those scripts check for the Android toolchain pieces that matter in practice, including the NDK compiler, android.jar, aapt, d8, signing tools, and the runtime package layout.

What to test

Android changes should be tested with normal command-line programs and with application-style smoke tests. Graphics, touch input, audio, lifecycle exit, and asset packaging are all Android-specific enough to deserve explicit tests.


FreeBASIC build configuration

The FreeBASIC build has two layers of configuration:
  • GNUmakefile and the modules under mk/ build the compiler, runtime libraries, support libraries, bootstrap sources, install tree, and tests.
  • build_scripts/ contains the platform and release workflows that call the make system with the right target, staging, dependency, package, VM, emulator, or installer settings.
For a normal developer rebuild, call make directly. For release packages, cross builds, Windows/MSYS2 installers, Android, JavaScript, Xbox, Wii, BSD VM builds, Solaris/illumos builds, or package validation, start with the appropriate script in build_scripts/.

The old approach of compiling source files by hand is not a normal build path. It can still be useful while bringing up a new platform, but then you are responsible for matching the compiler, runtime, headers, libraries, layout, and feature policy yourself.

Where configuration lives

Configuration is intentionally split by responsibility:
  • config.mk and make command-line variables hold local build choices.
  • mk/platform.mk resolves host and target identity.
  • mk/cpu.mk normalizes CPU family details.
  • mk/platform-features.mk and mk/feature-policy.mk decide which features are enabled for the target.
  • mk/compiler-config.mk, mk/toolchain-flags.mk, mk/host-tools.mk, and mk/os-flags.mk choose tools and flags.
  • mk/layout.mk and mk/build-layout.mk define install-tree and in-tree paths.
  • mk/source-graph.mk and mk/archives.mk decide what gets built.
  • mk/build/*.mk contains the compile, archive, dependency, target, and clean rules.
  • mk/bootstrap/*.mk owns compiler recovery and bootstrap source emission.
  • mk/tests*.mk and mk/tests/**/*.mk own build-system tests.
  • mk/inst_uninst.mk owns install, uninstall, and simple package payload layout.
For a detailed map of those modules, read mk/README.md and mk/File-Map.md in the source tree.

Inspecting the resolved configuration

Before guessing which variable is wrong, ask the build what it resolved:

make print-config
make prereqs-fbc

make print-config shows the selected host, target, tools, feature policy, runtime layout, and install layout. make prereqs-fbc checks whether the build has a usable compiler or bootstrap path.

Use verbose output when you need the exact compiler and linker command lines:

make V=1

Setting variables

You can pass variables on the make command line:

make -j4 CFLAGS='-O2 -g'

Or put persistent local choices in config.mk beside GNUmakefile:

CFLAGS = -O2 -g
FBFLAGS = -maxerr 1 -g
prefix = /usr/local

Do not commit machine-local config.mk files unless the tree specifically needs a documented example. Release and package settings should usually live in build_scripts/ instead.

Common make targets

Useful high-level targets include:
  • all builds the normal compiler and libraries. This is the default target.
  • libs builds the runtime/support libraries.
  • compiler-stage builds the compiler after the libraries it needs are available.
  • compiler, rtlib, fbrt, gfxlib2, and sfxlib build individual pieces.
  • install and uninstall install or remove the configured tree.
  • clean removes normal build outputs.
  • bootstrap-minimal builds a minimal compiler from current-target bootstrap sources.
  • bootstrap-seed-peer is a manual recovery path for bring-up work, not a normal default step.
  • sanity, quick-test, compiler-smoke, and bootstrap-emit-test run useful build-system checks.
Some non-GNU systems use gmake instead of make. The platform scripts choose the right command where practical.

Important variables

The most useful variables are grouped by purpose below. Most of these can be set on the make command line or in config.mk.

Target and toolchain identity:
  • TARGET_TRIPLET selects the toolchain triplet used by the build identity layer.
  • FBC_TARGET selects the FreeBASIC runtime target name, such as linux-x86_64, win64, or dos.
  • TARGET is kept as a compatibility alias for older command lines. New build code should prefer TARGET_TRIPLET.
  • FBC can name the FreeBASIC compiler used by the build when a system or local compiler is being supplied directly.
  • CC, CXX, AR, LD, and related tool variables override the C/C++ and binutils programs.
Build flags:
  • FBFLAGS adds options used when compiling FreeBASIC compiler sources.
  • CFLAGS and CXXFLAGS add C/C++ compile flags.
  • CPPFLAGS adds preprocessor flags.
  • LDFLAGS adds linker flags.
  • V=1 prints full command lines.
Layout and installation:
  • prefix selects the install prefix. It defaults to a platform-appropriate location such as /usr/local on many Unix-like targets.
  • DESTDIR stages installation under a packaging root without changing the final configured prefix.
  • ENABLE_STANDALONE=1 selects the standalone layout and compiler behavior.
  • ENABLE_PREFIX=1 hard-codes prefix into the compiler. Use this only when the compiler should not be relocatable.
  • ENABLE_SUFFIX=name appends a suffix to installed compiler/runtime names so multiple builds can share a prefix.
  • ENABLE_LIB64=1 selects the lib64 runtime layout where that policy applies.
  • MULTILIB applies a multilib mode to the FreeBASIC and C toolchains.
Feature policy and hardening:
  • ENABLE_PIC and ENABLE_NONPIC control which runtime library variants are built when the target supports that distinction.
  • DISABLE_MT disables multithreaded runtime variants.
  • ENABLE_PIE controls position-independent executable policy.
  • Hardening toggles such as ENABLE_STACK_PROTECTOR, ENABLE_FORTIFY, ENABLE_RELRO, ENABLE_NOW, ENABLE_NOEXECSTACK, ENABLE_SEPARATE_CODE, ENABLE_CET, ENABLE_NO_PLT, and ENABLE_AUTO_VAR_INIT are translated by mk/toolchain-flags.mk where supported.
  • ENABLE_REPRODUCIBLE asks the build to avoid avoidable timestamp and path noise where the target/toolchain supports it.
Optional library and platform features:
  • DISABLE_X11 disables X11-dependent graphics and input pieces.
  • DISABLE_XPM disables XPM-dependent support.
  • DISABLE_OPENGL disables OpenGL graphics support.
  • DISABLE_FBDEV disables the Linux framebuffer driver.
  • DISABLE_D3D10 disables the Direct3D 10/D2D Windows graphics path.
  • DISABLE_TCP disables TCP support in targets where that switch is honored.
  • DISABLE_FFI disables libffi-dependent runtime support such as ThreadCall.
  • DISABLE_NCURSES disables ncurses/libtinfo-dependent console support.
Compiler identity defaults:
  • FBFORKID=name sets the value reported by __FB_BUILD_FORK_ID__.
  • FBSHA1=value sets the value reported by __FB_BUILD_SHA1__.
  • DEFAULT_CPUTYPE_X86 and DEFAULT_CPUTYPE_ARM can override the compiler's default CPU choice for those families.
  • DISABLE_GAS64_DEBUG disables gas64 debug comments in generated assembly even when debug info is enabled.
  • DISABLE_STDCXX_PATH prevents the compiler from adding some target-specific C++ runtime library search paths.
Build scripts

The scripts under build_scripts/ are part of the build system now. They are not just examples.

Use them for work that involves dependency setup, package layout, cross builds, release artifacts, installers, VM images, emulators, browser tests, or target-specific smoke tests.

Examples:

./build_scripts/debianubuntu-build-freebasic.sh --help
./build_scripts/msys2-build-freebasic.sh --help
./build_scripts/msys2-build-freebasic-android.sh --help
./build_scripts/msys2-build-freebasic-js.sh --help
./build_scripts/freebsd-vm-build-freebasic.sh --help
./build_scripts/illumos-vm-build-freebasic.sh --help

Many scripts accept environment variables such as JOBS and OUT. The exact options are script-specific, so use --help before assuming two platform scripts take the same arguments.

Cross-build notes

For direct make use, prefer the explicit target variables:

make print-config TARGET_TRIPLET=x86_64-w64-mingw32 FBC_TARGET=win64
make TARGET_TRIPLET=x86_64-w64-mingw32 FBC_TARGET=win64

For release packages and tested target bundles, prefer the matching script instead. The scripts know the expected bootstrap archive names, staging directories, package names, and post-build smoke checks.

Low-level source defines

The compiler and runtime still contain low-level conditional code controlled by defines such as ENABLE_STANDALONE, DISABLE_FFI, or DISABLE_WCHAR. In normal builds, those should be driven through the make modules or platform scripts so the compiler, runtime libraries, installed layout, and package metadata agree with each other.

Only pass raw -d... or -D... options directly when you are doing bring-up work and have checked the matching make policy. A compiler built with one idea of the layout or feature set can easily fail against runtime libraries built with another.

See also

  • Getting source code updates and rebuilding FreeBASIC
  • FreeBASIC Build Options
  • Normal vs. standalone builds


Building for DOS

The DOS target is still a supported FreeBASIC target, but it is not shaped like a modern hosted operating system. It normally uses a DJGPP-style toolchain and has platform limits that the compiler and runtime must respect.

Current build path

Use the DOS build scripts when they are available in the checkout. They encode the current toolchain layout and package expectations better than the old manual DJGPP notes did.

./build_scripts/msdos-build-freebasic.sh
./build_scripts/msdos-test-freebasic.sh

For direct make work, prefer a target triplet rather than old short target names:

make print-config TARGET_TRIPLET=i586-pc-msdosdjgpp
make TARGET_TRIPLET=i586-pc-msdosdjgpp

Known DOS limits

Most normal compiler and runtime work is usable on DOS, but DOS-specific limits remain important. The DOS runtime does not provide thread support, TCP support is not available there, and audio playback is blocking compared with the more modern hosted ports.


Building for JavaScript

The JavaScript target is built through the Emscripten toolchain. In this tree, the target is normally exercised through the package and test scripts rather than by manually assembling an Emscripten command line.

Build scripts

./build_scripts/msys2-build-freebasic-js.sh
./build_scripts/debianubuntu-test-freebasic-js.sh

Use the matching script for the host environment. The scripts set the expected compiler target, runtime layout, Emscripten tools, and package names.

Runtime limits

JavaScript output is useful for many programs, but it is not identical to a native hosted target. Thread support and TCP support are not available in the same way as on the main desktop/server targets, so programs depending on those features need a different target or a target-specific design.


Build troubleshooting notes

Most build failures become easier to diagnose if the selected configuration is checked before rebuilding.

make print-config
make prereqs-fbc
Common causes

  • Missing bootstrap compiler or wrong FBC path.
  • Wrong host shell for the script being run.
  • MSYS2, Cygwin, and Windows paths mixed in one command line.
  • Target triplet does not match the installed cross toolchain.
  • Old generated objects remain after a platform, backend, or bootstrap change.
  • Android SDK/NDK tools are installed but not on the path expected by the script.
  • Installer tests pass in-tree but fail from an installed layout because a file was not packaged.

First fixes to try

Use the build script for the platform when one exists. If working directly with make, clean only the affected output directory first. Save full clean rebuilds for cases where platform identity, bootstrap output, or generated headers have changed.


Building on Linux and package distros

Linux builds can be done directly with make or through the distro scripts in build_scripts/. Direct builds are best while developing a compiler or runtime change. Distro scripts are best when producing installable packages or checking that a target still works from a clean system image.

Direct native build

make print-config
make prereqs-fbc
make -j4

The prereqs-fbc target checks for the compiler and tools needed by the build that the make system has selected.

Distro scripts

The tree contains scripts for Debian, Ubuntu, Raspbian, Alpine, Arch, RPM-based distros, Slackware, and related test images. Use the matching script for the package family being tested. The scripts also document the package names needed by that distro family.

Cross and package builds

For Linux-to-other-target work, start with Cross-compiling and the target-specific build page. The package scripts generally know more about install layout, naming, and archive contents than a short manual command line does.


FreeBASIC Build Options
Building FreeBASIC from sources uses a makefile that is intended to be used on a host to build the run time library, graphics library, and the FreeBASIC compiler itself. The makefile accepts a number of configuration options or variables.

Environment Variables
SOURCE_DATE_EPOCH
Set the build date for the compilation. See SOURCE_DATE_EPOCH Specification. The SOURCE_DATE_EPOCH environment variable specifies a build time constant (a decimal unsigned integer in unix time format) to be used during the build process. Setting this variable sets the values to be returned by __DATE__, __TIME__, and __DATE_ISO__ built-in macros when a source is compiled. This in turn affects the values returned by __FB_BUILD_DATE__ and __FB_BUILD_DATE_ISO__ when fbc compiler itself is built.

Build Options
FB[C|L]FLAGS
to set -g -exx etc. for the compiler build and/or link
CFLAGS
same for the rtlib and gfxlib2 build
prefix
install/uninstall directory, default: /usr/local
TARGET
GNU triplet for cross-compiling
MULTILIB
"32", "64" or empty for cross-compiling using a gcc multilib toolchain
FBC, CC, AR
fbc, gcc, ar programs (TARGET may be prefixed to CC/AR)
V=1
to see full command lines
ENABLE_STANDALONE=1
build source tree into self-contained FB installation
ENABLE_PREFIX=1
use "-d ENABLE_PREFIX=$(prefix)" to hard-code the prefix into fbc
ENABLE_SUFFIX=-0.24
append a string like "-0.24" to fbc/FB dir names, and use "-d ENABLE_SUFFIX=$(ENABLE_SUFFIX)" (non-standalone only)
ENABLE_LIB64=1
use prefix/lib64/ instead of prefix/lib/ for 64bit libs (non-standalone only)
ENABLE_STRIPALL=1
use "-d ENABLE_STRIPALL" with all targets
ENABLE_STRIPALL=0
disable "-d ENABLE_STRIPALL" with all targets
FBSHA1=1
determine the sha-1 of the current commit in repo and store it in the compiler
FBSHA1=some-sha-1
explicitly indicate the sha-1 to store in the compiler
FBPACKAGE
bindist: The package/archive file name without path or extension
FBPACKSUFFIX
bindist: Allows adding a custom suffix to the normal package name (and the toplevel dir in the archive)
FBMANIFEST
bindist: The manifest file name without path or extension
FBVERSION
bindist/gitdist: FB version number
DISABLE_DOCS
bindist: Don't package readme/changelog/manpage/examples
BUILD_PREFIX
automatically set depending on the target but can override for special builds where the build tools have different file naming than the target to build (i.e. cross compiling)
DISABLE_GAS64_DEBUG
use "-d DISABLE_GAS64_DEBUG" (see below)
DISABLE_STDCXX_PATH=1
Tells fbc to not search for some libstdc/libc depending on target platform
DEFAULT_CPUTYPE_X86=<FB_CPUTYPE>
Sets default x86 cpu type to one of FB_CPU_TYPE
For example make compiler DEFAULT_CPUTYPE_X86=FB_CPUTYPE_386
DEFAULT_CPUTYPE_ARM=<FB_CPUTYPE>
Sets default arm cpu type to one of FB_CPUTYPE
make compiler DEFAULT_CPUTYPE_ARM=FB_CPUTYPE_ARM7A_FP
FBFORKID=name
Tells fbc to set a custom value for __FB_BUILD_FORK_ID__

compiler source code configuration (FBCFLAGS, FBLFLAGS)
-d ENABLE_STANDALONE
build for a self-contained installation
-d ENABLE_SUFFIX=-0.24
assume FB's lib dir uses the given suffix (non-standalone only)
-d ENABLE_PREFIX=/some/path
hard-code specific $(prefix) into fbc
-d ENABLE_LIB64
use prefix/lib64/ instead of prefix/lib/ for 64bit libs (non-standalone only)
-d ENABLE_STRIPALL
configure fbc to pass down '--strip-all' to linker by default
-d FBSHA1=some-sha-1
store 'some-sha-1' in the compiler for version information as built in define __FB_BUILD_SHA1__
-d DISABLE_GAS64_DEBUG
disable gas64 debugging comments in asm files even if __FB_DEBUG__ is defined (-g)
-d DISABLE_STDCXX_PATH
tells fbc to not search for some libstdc/libc depending on target platform
-d BUILD_FB_DEFAULT_CPUTYPE_X86=<FB_CPUTYPE>
This make the compiler set default x86 cpu type to one of enum FB_CPUTYPE as defined in fbc sources
-d BUILD_FB_DEFAULT_CPUTYPE_ARM=<FB_CPUTYPE>
This make the compiler set default arm cpu type to one of enum FB_CPUTYPE as defined in fbc sources
-d FBFORKID="name"
This makes the compiler set a custom value for __FB_BUILD_FORK_ID__

internal makefile configuration (but can override)
libsubdir
override the library directory - default is set depending on TARGET
objsubdir
override object file directory - default is set depending on TARGET
fbcobjdir
override compiler object directory - default is set depending on TARGET

fbrt source code configuration (FBRTCFLAGS, FBRTLFLAGS)
FBRTCFLAGS
override the compiler flags when building fbrt library
FBRTLFLAGS
override the linker flags then building fbrt library

rtlib/gfxlib2 source code configuration (CFLAGS)
-DDISABLE_X11
build without X11 headers (disables X11 gfx driver)
-DDISABLE_GPM
build without gpm.h (disables GetMouse in the Linux terminal (TERM=linux), although the TERM=xterm variant keeps working)
-DDISABLE_FFI
build without ffi.h (disables ThreadCall)
-DDISABLE_OPENGL
build without OpenGL headers (disables OpenGL gfx drivers)
-DDISABLE_FBDEV
build without Linux framebuffer device headers (disables Linux fbdev gfx driver)
-DDISABLE_D3D10
build without DirectX 10 driver (disable D2D driver in windows)
-DDISABLE_NCURSES
build without libtinfo or ncurses (disables console commands)
-DDISABLE_LANGINFO
build without locale info (affects Unix only; makes no difference unless you call setlocale() manually). Does not remove setlocale(LC_CTYPE, "") call.
-DDISABLE_WCHAR
build without wchar_t type or functions. wstring becomes ASCII only (fbc needs to match this).


Building for Wii

The Wii port is a cross-target port. The build and installer scripts should be used instead of hand-copying compiler pieces, because the Wii package layout has target-specific library, include, and tool paths.

Build and installer test

./build_scripts/msys2-build-freebasic-wii.sh
./build_scripts/msys2-test-freebasic-installer.sh --kind wii

The package scripts use the same version metadata as the rest of the tree and the install/uninstall layout rules in mk/inst_uninst.mk.

Runtime testing

Wii graphics, sound, input, and program startup should be tested with emulator or hardware smoke tests when the toolchain is available. This target is close enough to the graphics and sound internals that backend changes can affect it even when desktop tests pass.


Building on Windows/MSYS2

The main Windows development environment for this tree is MSYS2. The Windows build scripts handle the details that are easy to get wrong by hand: shell path conversion, package naming, compiler architecture, installer layout, and NSIS lookup.

Native Windows builds

Use the MSYS2 scripts for repeatable package builds:

./build_scripts/msys2-build-freebasic.sh
./build_scripts/msys2-test-freebasic.sh

The tree has support for the usual Win32 and Win64 x86 targets and for Windows ARM64 work where the required toolchain is installed.

Cross-target Windows packages

Several non-Windows targets are packaged from Windows because the required tools are easiest to stage there or because the release artifact is a Windows-hosted compiler for another platform. Examples include Android, JavaScript, Wii, and Xbox package scripts.

./build_scripts/msys2-build-freebasic-android.sh
./build_scripts/msys2-build-freebasic-js.sh
./build_scripts/msys2-build-freebasic-wii.sh
./build_scripts/msys2-build-freebasic-xbox.sh

Keep MSYS2, Cygwin, and plain Windows command prompts separate when debugging a build. Many Windows failures come from crossing those path and shell models in one command line.


Building for Xbox

This page is about the original 2001 Xbox target. It is not for Xbox 360, Xbox One, Xbox Series X/S, or ordinary Windows programs.

The modern Xbox package builds programs with fbc-xbox and produces .xbe executables. It also includes fbc-xbox-xiso for creating ISO images with an optional asset directory.

Build scripts

./build_scripts/msys2-build-freebasic-xbox.sh
./build_scripts/debianubuntu-build-freebasic-xbox.sh

The package is built around open tools such as nxdk rather than the old proprietary Microsoft XDK.

Compiling a program

fbc-xbox program.bas -x program.xbe
fbc-xbox-xiso program.xbe program.iso
fbc-xbox-xiso program.xbe program.iso --assets game-assets

Use real modified Xbox hardware or an emulator that can run homebrew XBE files.


Cross-compiling FreeBASIC

Cross-compiling is controlled by the same modular make system as native builds. The preferred identity knob is TARGET_TRIPLET. Old short names still exist in places, but new build and package work should prefer the triplet form when a real target identity is needed.

Basic direct build

make print-config TARGET_TRIPLET=x86_64-pc-linux-gnu
make TARGET_TRIPLET=x86_64-pc-linux-gnu

Always inspect print-config before a long cross build. If host, target, CPU, binary names, library directories, or tool names look wrong there, fix the configuration before building.

Important variables

  • TARGET_TRIPLET selects the target identity.
  • BUILD_FBC_TARGET selects the compiler used to build generated tools when needed.
  • FBTARGET_DIR_OVERRIDE can override the installed target directory for package layouts.
  • FBC can point to the bootstrap compiler used by the build.

When to use scripts

Use the target package scripts for Android, JavaScript, Wii, Xbox, and distro packages. Those scripts set more than a target CPU. They also set external SDK paths, package layout, archive names, installer behavior, and smoke-test expectations.


Debugging FB

For debugging and development it's a good idea to build the compiler with -g and -exx to enable assertions, plus array boundary and null-pointer checks. For the rtlib/gfxlib2 code, -DDEBUG enables the assertions. Just update config.mk and (re)build. Example config.mk settings:

FBFLAGS := -g -exx
CFLAGS := -g -O0 -DDEBUG

Running fbc inside gdb typically looks like this:

gdb --args fbc foo.bas

Running fbc inside valgrind typically looks like this:

valgrind fbc foo.bas

Also note that fbc can be tested right from inside the build tree, without having to be "installed" somewhere else, which also is a great debugging and development help.


Lexer & preprocessor

lex*.bas: File input, tokenization, macro expansion buffer, token queue, #include contexts.
pp*.bas: Preprocessor directive parsing, macro expansion text construction.
The lexer reads the source code from the .bas files and translates it into a series of tokens, so the FB parser sees this:

dim as integer i = 5
print i

as:

(Top-level parser retrieves the first token:)
DIM     keyword         (Go to variable declaration parser)
AS      keyword         (Go to datatype parser)
INTEGER keyword         (Data type)
"i"     symbol          (Back to variable declaration, variable identifier)
"="     operator        (Go to initializer parser)
"5"     number literal  (Expression)
EOL     statement end   (Variable declaration parser is done,
             the variable is added to the AST,
             back to toplevel parser)
(Next line, next statement)
PRINT   keyword         (Go to QB print quirk function call parser)
"i"     symbol          (Expression, lookup "i" symbol, it's an integer variable,
             create a CALL to fb_PrintInt(), the expression is the argument)
EOL                     (Print parser is done, back to toplevel)
EOF                     (Top-level parser is done)

The lexer is an abstraction hiding the ugly details of user input (indentation, comments, keyword capitalization, #includes) from the parser. Additionally it does preprocessing, consisting of macro expansion and preprocessor directive parsing. The general idea is to handle all preprocessing in the lexer, so the parser does not get to see it. The parser never calls preprocessor functions, the lexer functions do that.

Tokens
Macro storage and expansion
Preprocessor directive parsing
File contexts
Quick overview of the call graph


Quick overview of the call graph

Showing the recursion between the FB parser, the PP parser, and the lexer:

                +------------------> lexGetLookAhead() --------+
                |                                              |
                |                                              v
     (begin)        (FB parsing)                (PP parsing)         (lexing)
    fbCompile()  ->  cProgram()  ------------>  lexSkipToken()  ->  lexNextToken()
        |               ^  |                       |     ^              |    ^
        v               |  |                       v     |              v    |
       fbPreIncludes()  |  |('$include)       ppCheck()  |           ppDefineLoad()
              |     |  |                       |     |          (macro expansion)
              v     |  v                       v     |
             fbIncludeFile()  <--------------  ppParse()
                    (#include)    (directives)


Directive parsing

Preprocessor directives (#if, #define, #include, etc.) are parsed during lex.bas:lexSkipToken() by calling pp.bas:ppCheck(). After moving to the next token (or loading a new token), ppCheck() will check whether the new current token is a '#'. If so it will also check whether the previous token was an EOL. If so, it found a '#' at line begin, and directly parses the PP directive, using the same lexGetToken()/lexSkipToken() interface used by the parser. This is necessary because some PP directives result in parser functions being called, for example parser-identifier.bas:cIdentifier() is used by the #ifdef parser, to recognize variables etc.:
dim as integer i
#ifdef i
#print yes, the variable will be recognized
#endif

So, lexSkipToken() is recursive because of the PP. ppCheck() will only call pp.bas:ppParse() for toplevel calls to lexSkipToken(), but not if it was called recursively from the PP. This lets the PP parse multi-line directives like #macro ... #endmacro or skip #if ... #endif blocks without "executing" the directives those structures may contain. Note that unlike C, FB allows macros to contain PP directives.

As a result, every time the FB parser skips an EOL, lexSkipToken() might detect a '#' at line begin and handle then call the PP to let it parse that directive. It may "silently" parse more lines, and the parser stays fully unaware that the PP directives are even there. The PP parsing launched from lexSkipToken() might even encounter an #include and call fb.bas:fbIncludeFile() to parse it immediately, recursively starting a parser-toplevel.bas:cProgram() for that #include file. The parser has to be able to handle the recursion that might happen during every lexSkipToken() at EOL, but luckily that is not a big deal. The parser needs a stack to keep track of compound statements anyways.

Note that PP directives are not handled during token look ahead (lex.bas:lexGetLookAhead()). If the parser were to look ahead across EOL, it could very well see a PP directive. Luckily though looking ahead across lines is never necessary.

Macro expansion in PP directives
The beginning of directives, the keyword following the '#', is parsed without macro expansion. This means redefining PP keywords (intentionally) has no effect on the PP directives. For example:

#define define foo
#define bar baz
will not intermediately be seen as:

#foo bar baz
Directives like #if & co. make use of the PP expression parser, which does expand macros. Afterall that's the point of PP expressions. For example:
#define foo 1
#if foo = 1
#endif
The #define and #macro directives don't do macro expansion at all. A macro's body is recorded as-is.

#define/#macro parsing
pp.bas:ppDefine() first parses the macro's identifier. If there is a '(' following, without space in between, then the parameter list is parsed too.

Then the macro body is parsed. For each token, its text representation is retrieved via lexGetText(), and it is appended to the macro body text. Space is preserved (but trimmed); comments are left out; in multi-line #macros empty lines are removed.

If the macro has parameters, the macro tokens will be created (as discussed in Macro Expansion). To do that, the macro parameters are added to a temporary hash table, which associates the parameter names to their indices. Then, identifiers in the macro body are looked up, and when a parameter is recognized, a parameter(index) macro token is created, instead of appending the token to the previous text() macro token (or creating a new text() for it). After that parameter(index), if there is other text again, a new text() macro token is created.

Using # on a parameter results in the creation of a stringify_parameter(index) macro token. The PP merge operator ## is simply ommitted from the macro body, so a##b becomes ab in a text() macro token. All normal text before/after/between parameters goes into text() macro tokens.

For example:

#define add(x, y) foo bar x + y

And the actions of the #define/#macro parser will be:

'add'    - The macro's name
'(' following the name, without space in between: Parse the parameter list.
    'x'    - Parameter 0.
    ','    - Next parameter.
    'y'    - Parameter 1.
    ')'    - End of parameter list.
Create the macro body in form of macro tokens.
' '    - Create new text(" ").
'foo'  - Append "foo".
' '    - Append " ".
'bar'  - Append "bar".
' '    - Append " ".
'x'    - Is parameter 0, create new param(0).
' '    - Create new text(" ").
'+'    - Append "+".
' '    - Append " ".
'y'    - Is parameter 1, create new param(1).
EOL    - End of macro body.

Resulting in this macro body:

text(" foo bar "), param(0), text(" + "), param(1)
The #define parser allows macros to be redefined, if the body is the same. For example:

#define a 1
#define a 1
does not result in a duplicated definition. However this would:

#define a 1
#define a 2
Since those are pure text #defines, the comparison is the bodies is a simple string comparison. This feature is not implemented for macros with parameters currently.

PP expressions
The preprocessor has its own (but fairly small and simple) expression parser (pp-cond.bas:ppExpression()). It works much like parser-expression.bas:cExpression(), except instead of creating AST nodes, ppExpression() immediately evaluates the expressions.

PP skipping
The preprocessor uses a simple stack to manage #if/#endif blocks. Those can be nested, and there may be #includes in them, but they cannot go across files. False blocks (#if 0, or the #else of an #if 1) are immediately skipped when parsing the #if 0 or the #else (pp-cond.bas:ppSkip()), before returning to lexSkipToken().

For example:

#if 1           (push to stack: is_true = TRUE, #else not visited yet, return to lexSkipToken())

...     (will be parsed)

#else           1) Set the #else visited flag for the current stack node,
           so further #else's are not allowed.
        2) Since the current stack node has is_true = TRUE,
           that means the #else block must be skipped, -> call ppSkip().

...     (skipped in ppSkip())

#endif          (parsed from ppSkip(), skipping ends, ppSkip() returns to #else parser,
         which returns to lexSkipToken())
Note that there are a few tricky bits about PP skipping. Since macros are allowed to contain PP directives, macro expansion must be done even during PP skipping, because an #else or #endif could be inside a multi-line macro. Also, multi-line #macro declarations are not handled during PP skipping. That means, something like this:

#if 0
#macro test()
#endif
#endmacro
will be seen as:

#if 0
 #macro test()
#endif
#endmacro
Resulting in an error (#endmacro without #macro).

So, this:

#if 0
#macro test()
    #endif
#endmacro
#endif
will not work as suggested by the indentation.


File contexts

Because #includes can occur in the middle of input files, the lexer needs to push file contexts to a stack. File input buffer, macro expansion buffer and the token queue form a so-called "context". It is file specific and thus it must be pushed onto a stack, so that the lexer can return to the parent (after parsing an #include), without losing any tokens or macro text. Note that macros can contain #includes too.

fb.bas:fbIncludeFile() basically just consists of:
lexPush()
cProgram()
lexPop()


Macros

Some terms used in the source code (Note the double meanings):
  • macro: The #defined/#macroed object that will be expanded to its replacement text
  • macro: a function-like macro, e.g. #define m(a, b)
  • define: an object-like macro, e.g. #define simple
  • argless define (should be called parameter-less): a function-like macro without parameters, e.g. #define f()

How macros are stored
Macros are basically stored as raw text, not as token runs (as in GCC's libcpp for example). The body of simple #defines without parameters is stored as one string. Macros with parameters are stored as sequence of "macro tokens". There are three types of macro tokens:
    • text("<text>")
Raw text, but spaces and empty lines trimmed (like in a #define without parameters)
    • textw("<wstring text>")
Same as above, just for Unicode input.
    • parameter(index)
A macro parameter was used here in the declaration. The index specifies which one. During expansion, the text of argument(index) is inserted where the parameter was in the declaration.
    • stringify_parameter(index)
Same as above, except the argument will be stringified during expansion.
Note: macro tokens are actually symb.bi:FB_DEFTOK structures, and they contain an id field holding on of the FB_DEFTOK_TYPE_* values to tell what they contain.

For example:

    #define add(x, y) x + y

becomes:

    parameter(0), text(" + "), parameter(1)

And the expansion text will be:

    argument(0) + " + " + argument(1)
Storing macros as text is a fairly easy implementation, but it requires to re-parse the macro body over and over again. For example, since GCC works with preprocessing tokens and tokenruns, macros are stored as tokens, making expansion very fast, because there is no need to tokenize the macro body again and again. fbc's implementation is not as flexible and maybe not as efficient, but is less complex (regarding code and memory management) and has an upside too: Implementation of ## (PP token merge) is trivial. ## simply is omitted while recording the macro's body, where as in token runs the tokens need to be merged explicitly.

When are macros expanded?
Because of token look ahead, macros must be expanded during tokenization, otherwise the wrong tokens might be loaded into the token queue. Afterall the parser should only get to see the final tokens, even during look ahead.

In lexNextToken(), each alphanumeric identifier is looked up in the symb module to check whether it is a keyword or a macro. Macros and keywords are kept in the same hash table. Note that macros cannot have the name of keywords; "#define integer" causes an error. If a macro is detected, it is immediately expanded, a process also called "loading" the macro (pp-define.bas:ppDefineLoad()).

Macro call parsing
If the macro takes arguments, the macro "call" must be parsed, much like a function call, syntax-wise. Since macro expansion already happens in lexNextToken(), the source of tokens, the parsing here is a little tricky. Forward movement is only possible by replacing (and losing) the current token. The token queue and token look ahead cannot be relied upon. Instead it can only replace the current token to move forward while parsing the macro's arguments.

Since lexNextToken() is used to parse the arguments, macros in the arguments themselves are recursively macro-expanded while the arguments are being parsed and recorded in text form. The argument texts are stored for use during the expansion.

So, a macro's arguments are expanded before that macro itself is expanded, which could be seen as both good and bad feature:

#define stringify(s) #s
stringify(__LINE__)
results in 2 in FB, but __LINE__ in C, because in C, macro parameters are not expanded when used with # or ##. In C, two macros have to be used to get the 2:

#define stringize(s) #s
#define stringify(s) stringize(s)
stringify(__LINE__)

Putting together the macro expansion text
The expansion text is a string build up from the macro's body tokens. For macro parameters, the argument text is retrieved from the argument array created by the macro call parser, using the indices stored in the parameter tokens. Parameter stringification is done here.

There is a specialty for the builtin defines (__LINE__, __FUNCTION__, __FB_DEBUG__, etc.):
A callback is used to retrieve their "value". For example: __LINE__'s callback simply returns a string containing the lexer's current line number.

Expansion
The macro expansion text (deftext) is stored by the lexer, and now it will read characters from there for a while, instead of reading from the file input buffer. Skipping chars in the macro text is like skipping chars in the file input: Once skipped it's lost, there is no going back. So, there never is "old" (parsed) macro text, only the current char and to-be-parsed text. New macro text is prepended to the front of existing macro text. That way macros inside macros are expanded.

This implementation does not (easily) allow to detect macro recursion. It would be hard to keep track of which characters in the macro text buffer belong to which macro, but that would be needed to be able to push and pop macros properly. It could be done more easily with a token run implementation as seen in GCC's libcpp. However C doesn't allow recursive macros in the first place: In C, a macro's identifier is undefined (does not trigger expansion) inside that macro's body. That is not the case in fbc, because (again) a way to detect when a macro body ends is not implemented.

Currently fbc only keeps track of the first (toplevel) macro expanded, because it's easy to detect when that specific macro's end is reached: as soon as there is no more macro text.

That's why the recursion is detected here:

#define a a
a
and here too:

#define a b
#define b a
a
but not here: (Note that fbc will run an infinite loop)

#define a a
#define m a
m


Tokens

Interface
The basic public interface of the lexer is from lex.bas:
    • lexGetToken(): Retrieve current token's id, an FB_TK_* value.
    • lexGetLookAhead(N): Look ahead N tokens
    • lexSkipToken(): Go to next token
    • lexGetText(): Returns a zstring ptr to the text of the current token, e.g. string/number literals (their values are retrieved like this), or the text representation of other tokens (e.g. operators).
    • some more lexGet*() accessors to data of the current token
    • lexPeekLine(): Used by error reporting to retrieve the current line of code.

Current token + look ahead tokens
Tokens are a pretty short-living thing. There only is the current token and a few look ahead tokens in the token queue. That's all the parser needs to decipher FB code. The usual pattern is to check the current token, decide what to do next based on what it is, then skip it and move on. Backward movement is not possible. The file name, line number and token position shown during error reporting also comes from the current lexer state.

The token queue is a static array of tokens, containing space for the current token plus the few look ahead tokens. The token structures contain fairly huge (static) buffers for token text. Each token has a pointer to the next one, so they form a circular list. This is a cheap way to move forward and skip tokens, without having to take care of an array index. Copying around the tokens themselves is out of question, because of the huge text buffers. The "head" points to the current token; the next "k" tokens are look ahead tokens; the rest is unused. When skipping we simply do "head = head->next". Unless the new head already contains a token (from some look ahead done before), we load a new token into the new current token struct (via lexNextToken()). Look ahead works by loading the following tokens in the queue (but without skipping the current one).

Tokenization
lex.bas:lexNextToken()

The lexer breaks down the file input into tokens. A token conceptually is an identifier, a keyword, a string literal, a number literal, an operator, EOL or EOF, or other characters like parentheses and commas. Each token as an unique value assigned to it that the parser will use to identify it, instead of doing string comparisons (which would be too slow).

lexNextToken() uses the current char, and if needed also the look ahead char, to parse the input. Number and string literals are handled here too. Alphanumeric identifiers are looked up in the symb hash table, which will tell whether it's a keyword, a macro, or another FB symbol (type, procedure, variable, ...).

Identifiers containing dots (QB compatibility) and identifier type suffixes (as in stringvar$) are handled here too (but not namespace/structure member access). Tokens can have a data type associated with them. That is also used with number literals, which can have type suffixes (as in &hFFFFFFFFFFFFFFFFull).

Side note on single-line comments
Quite unusual, single-line comments are handled by the parser instead of being skipped in the lexer. This is done so that usage of REM can easily be restricted as in QB, afterall REM is more like a statement than a comment. Besides that, comments can contain QB meta statements, so comments cannot just be ignored. Note that the parser will still skip the rest of a comment (without tokenizing it), if it does not find a QB meta statement.

(Multi-line comments are completely handled during tokenization though.)

File input
lex.bas:hReadChar()

The input file is opened in fb.bas:fbCompile(); the file number is stored in the global env context (similar for #includes in fb.bas:fbIncludeFile()). The lexer uses the file number from the env context to read input from. It has a static zstring buffer that is used to stream the file contents (instead of reading character per character), and for Unicode input, the lexer uses a wstring buffer and decodes UTF32 or UTF8 to UTF16. The lexer advances through the chars in the buffer and then reads in the next chunk from the file. EOF is represented by returning a NULL character.


Memory management
fbc tries to avoid memory allocations as much as possible, since they are pretty slow generally. The linked list implemented in list.bas comes with a builtin memory pool, so pretty much every list is pooled. The memory pool pre-allocates large chunks and can then quickly hand out many small nodes. Those lists are used for simple things like the list of libraries to link into an executable, but also for heavier things like AST nodes. The memory pool is supposed to speed things up (no idea if this was ever verified though).

In many places the compiler simply uses global/static variables, for example fixed-length strings, in order to avoid memory allocations. Tokens are a nice example: lex.bas parses input characters into tokens, and stores the token text in static buffers. Token text, that could be: variable names, string literals, and so on. All tokens are stored here though, so the preprocessor can correctly record macros. Now take into account the huge number of tokens the parser has to deal with: For example, FB's current Windows headers result in ~100k tokens. Dynamically allocating a buffer for every token would quickly become inefficient.

Of course the token length is limited by using a static buffer, but fbc's default of 1024 bytes should be enough for everyone. Similar length limitations apply to many things in the compiler because of the use of fixed-length buffers. In most situations, the buffers in the compiler are not used to their full potential, i.e. they are bigger than they need to be.

All that does not mean the compiler does not use dynamic memory allocations at all. It does, in situations when allocating is easier than using a list/pool and speed is not critical. FB's builtin string type is used in many places too. As long as the string's are kept allocated, they are very efficient. Expansion of macro parameter stringifying in the pre-processor uses a strReplace() based on string's, and it is fast (enough). Besides that, dynamic strings, which are basically the same as string's, are used everywhere in the pre-processor, from macro recording to macro expansion.

Out-of-memory situations/allocation failures are not seriously handled. There are NULL checks in some places where allocate() is called, but these checks are pointless, since the rest of fbc does not check for NULL. NULL is sometimes used to indicate an error, for example by some astNew*() functions. Also, the compiler does not deallocate() everything, but lets the OS do the cleanup.


Quick overview of compiler modules
This is a quick map of the main compiler modules. It is not a complete list of every source file, but it should point new contributors at the right subsystem before they start changing code.

fbc
Compiler driver and command-line frontend. It parses driver options, manages the compile/assemble/link pipeline, invokes external tools, adds libraries, and performs final driver-side actions such as linking or post-link commands.

Generic driver flow belongs here. Target-specific OS behavior should usually live in fbc-platform or one of the fbc-*-platform files instead of being added directly to fbc.bas.

fbc-platform
Compiler-driver platform hook dispatcher. This module defines the hook table used by fbc.bas and includes one platform module for each target in the FB_COMPTARGET table.

The current platform hooks cover:
  • choosing the linker tool
  • adding target default library search paths
  • adding graphics library dependencies
  • adding sound library dependencies
  • adding ordinary target default libraries
  • adding linker framework or target flag fragments
  • mapping library names when one target needs a different library spelling
The platform hook layer is for compiler-driver behavior tied to the selected OS target. It is not the place for parser rules, AST behavior, ABI layout, or backend code generation policy.

fbc-*-platform
Per-target driver modules. One compile target maps to one file named fbc-[target-id]-platform.bi, where [target-id] matches the target id from fb.bas:targetinfo().

These files keep target-specific linking and packaging rules out of fbc.bas. Examples include Win32 default system libraries, Darwin linker tool selection, JavaScript and Wii linker-driver selection, devkitPro library paths, X11 library search paths, OpenBSD library spelling differences, and other target toolchain quirks.

Platform modules currently exist for Win32, Cygwin, Linux, Android, Haiku, DOS, Xbox, FreeBSD, DragonFly, Solaris, illumos, OpenBSD, Darwin, NetBSD, JavaScript/Emscripten, and Wii.

objinfo
Object/library information section reader/writer, used by fbc. Includes small ELF/COFF object file readers.

fb
Compiler-wide state and parser interface. It owns the target information table, initializes target state, and starts the parser for each input/include file.

parser
Recursive parser. It asks lex for tokens and builds up the ast.

lex, pp
Lexer/tokenizer and preprocessor directive parsing.

error
Error reporting functions used by many parts of fbc, especially the parser.

rtl
Helper functions that build ast nodes for rtlib/gfxlib/sfxlib function calls. Declarations here must match the actual functions in the runtime, gfxlib2, and sfxlib source code.

symb
Symbol lookup and storage: variables, functions, scopes, namespaces, and name mangling. Used by the parser, ast, and emitters.

ast
Abstract syntax tree for per-function code flow and expressions.
astNew*() creates and connects nodes for the parser.
astLoad*() is the first step in emitting and calls ir after each function is parsed.

ir, ir-hlc, ir-llvm, ir-tac, ir-gas64
Intermediate representation layer used to emit the ast.
ir-hlc emits high-level C.
ir-llvm emits LLVM IR.
ir-tac is the three-address-code path used by the asm backend and calls emit.
ir-gas64 contains GAS-oriented 64-bit asm support.

reg
Register allocator for ir-tac.

emit, emit_SSE, emit_x86
Assembler emitter abstraction plus the x86/SSE-specific emitters.

edbg_stab
Stabs debug format emitting for emit_x86.

dstr
Dynamic z/wstrings, used mostly by lex.

hash
Generic hash table, used by symb and fbc.

hlp, hlp-str
Helper functions used throughout the compiler, plus another dynamic z/wstring implementation.

list
Generic linked list with built-in memory pool, used heavily throughout the compiler. It is often used as a pooled allocator, for example for AST nodes or symbols.

flist
list-based container for append-only lists.

pool
list-based allocator using multiple lists with node sizes from small to large. Used to store symbol identifiers with less wasted memory.

stack
Generic list-based stack.


Purpose
fb.bas: Main module for the compiler, parent module for parser/lexer/AST/IR/emitters, toplevel file & include file handling
parser*.bas: Parsing/compilation functions: lexer tokens -> AST nodes.
symb*.bas: Symbol tables and lookup, namespace/scope handling.
rtl*.bas: Helpers to build AST calls to rtlib/gfxlib functions.
The structure of the parser has a very close relation to the FreeBASIC grammar. Basically there is a parsing function for every element of the grammar.

The parser retrieves tokens from the lexer and validates the input source code. Most error messages (besides command line and file access errors) come from here. Additionally the parser functions build up the corresponding AST. This is the heart of the compilation process.

Many of the parser's (or rather compiler's) functions (prefixed with a 'c') parse and skip the grammar element they represent, or show an error if they don't find it. The parser is fairly recursive, mostly because of the expression parser and the #include parsing.

From parsing to emitting

When parsing code a corresponding AST is built up to represent the program. The AST is used to represent executable code, but also to hold temporary expressions, for example the values of constants or the initializers found while parsing type or procedure declarations. The AST does not contain nodes for code flow constructs like IF, DO/LOOP, GOTO, RETURN, EXIT DO, etc., but it contains labels and branches. Likewise, several operations (like IIF(), ANDALSO, ORELSE, field dereference, member access) are replaced by the corresponding set of lower-level operations in the AST.

After parsing a function, the AST for this function is optimized, and then emitted recursively via astLoad*() calls on each node, from the top down. Note that each AST node has its own implementation of astLoad().


Symbols

In order to be able to make the transition from tokens to AST, the parser needs to be able to recognize functions, variables, types, etc. The symb module keeps track of all these symbols and their namespaces and scopes. The parser can do lookups in the current scope, or in just specific namespaces. Many AST nodes have a corresponding symbol (e.g. variables and functions).


Top level parsing process

fb.bas:fbCompile() is called from the fbc frontend for every input file. Parsing (and compiling) of the file begins here.

fb.bas:fbCompile()
  • Open the input .bas
  • Start the emitter (ir) (Open the output .asm)
  • fbMainBegin() (Build the AST for the implicit main() or static constructor for module-level code)
  • fbPreIncludes()
    • fbIncludeFile() for every preinclude (found on the fbc command line)
  • cProgram()
  • fbMainEnd() (Close the implicit main())
  • Finish emitting (ir) (Finish generating the .asm and close it)
  • Close the input .bas

fb.bas:fbIncludeFile()
  • Include file search
  • lexPush() (Push a new lexer context to parse this #include file without disturbing the lexer's state in the parent file)
  • Open the include file
  • cProgram()
  • Close the include file
  • lexPop() (Restore the lexer state to the parent file)

parser-toplevel.bas:cProgram() is the root of the FB grammar, and parses a file. Here's a short & quick run down of what is done:
  • cLine() repeatedly until EOF
    • cLabel()
    • cStatement()
      • Declarations
        • UDT declarations, typedefs
        • Variables (DIM, VAR, ...)
        • Procedure declarations (DECLARE)
        • Procedure bodies (SUB, FUNCTION, ...)
(Procs temporarily replace the implicit module level procedure, so any AST nodes go into them instead of the implicit main())
      • Compounds statements (IF/ELSE, DO/LOOP, EXIT/CONTINUE DO, ...)
      • Procedure calls
      • Function result assignments
      • Quirk statements (special QB rtlib/gfxlib statements)
      • ASM blocks
      • Assignments
      • Procedure pointer calls

and most of them use cExpression() at some point.


The FreeBASIC test suite

The test suite is used to catch compiler, runtime, library, and target regressions. It is also used by the packaging scripts to prove that an installed compiler can compile and run basic programs from a clean layout.

Common make targets

make sanity
make quick-test
make full-test
make compiler-smoke
make bootstrap-emit-test

Use the smaller targets while developing a focused change and the broader suite before release or after changes to parser, code generation, runtime startup, string handling, graphics, sound, or cross-target support.

Package and platform smoke tests

The build_scripts/ directory contains installer and platform smoke tests for supported package families and target ports. Those scripts are the preferred way to test an installed package because they catch missing files, wrong paths, wrong compiler names, and packaging-only problems that an in-tree build can hide.

Graphics and sound work should be tested explicitly on the affected platform. SCREEN modes, SCREENSET behavior, input scaling, audio playback, and backend startup/shutdown have enough platform-specific behavior that compiler-only tests are not enough.


Representation of data types

Almost all parts of the compiler deal with data types in one way or another. Symbols worry about data types the most, this is what most of the compile-time type checks are based on. AST nodes also have data types, that takes care of expressions (including casting/conversions).

A data type is represented as a combination of:

    • dtype integer
      • 5 bits: raw type:
        • void (unknown type, e.g.: any ptr, type t as t)
        • byte, ubyte
        • char (zstring pointers and their deref expressions)
        • short, ushort
        • wchar (wstring pointers and their deref expressions)
        • integer, uinteger
        • enum (integer)
        • long, ulong
        • longint, ulongint
        • single, double
        • string (variable length)
        • fixstr (fixed length strings, string * N, N is the type's length)
        • struct (UDT, -> subtype is used)
        • namespace (used during name mangling?)
        • function (used for function pointers, -> subtype contains full function declaration)
        • forward reference (will be changed to actual raw type when known, -> subtype is used)
        • pointer (this value is only used temporarily as a result of the typeGet() macro)
        • xmmword (used by SSE emitter)
      • 4 bits: PTR count
How many PTR's there are on the type, maximum 8. If > 0, then the data type is a pointer.
      • 9 bits: CONST mask (8 PTR's + 1 "base")
    Example                     CONST mask

const integer                   000000001           (first CONST bit set)
integer const ptr               000000001           (ditto)
const integer ptr               000000010           (pointer to const)
const integer ptr const ptr     000000101           (const pointer to pointer to const)
    • subtype, which for some types points to symbol:
      • For UDTs types (structs/classes, enums) this points to the corresponding UDT symbol
      • For forward-referencing typedefs this points to a special forward reference symbol which will eventually be replaced by the actual subtype symbol, when it's known.
      • For procedure pointers, this points to an anonymous symbol further defining the calling convention etc. and most importantly the types of result and parameters.
    • length integer
This is used in places that have to calculate sizes (e.g. structure size calculations, pointer arithmetic, stack offsets).


Toolchain choice notes

FreeBASIC can use different external C/C++ and linker toolchains depending on the target. The current build system tries to keep that selection centralized instead of scattering compiler names through individual rules.

Where selection happens

Toolchain selection is mainly handled by the platform, CPU, compiler-config, toolchain-flags, host-tools, and OS flag modules under mk/. If a target is choosing the wrong compiler or linker, fix the target identity or toolchain module before changing unrelated compile rules.

General guidance

Use the distro or MSYS2 package scripts when building release artifacts. They install or locate the expected compiler family for that host. Use direct make variables for focused development or for bringing up a new target, then fold the working choices back into the proper mk/ module or build script.


Getting the source code

The FreeBASIC source tree is maintained in Git. A normal developer checkout is enough for compiler, runtime, documentation, and packaging work.

git clone https://github.com/freebasic/fbc.git
cd fbc

Some developers use a fork instead of the main repository. In that case, clone the fork and keep the upstream repository configured as a second remote:

git remote add upstream https://github.com/freebasic/fbc.git
git fetch upstream
Updating a checkout

git fetch --all --prune
git pull --ff-only

If the compiler was already built before the update, see Getting source code updates and rebuilding FreeBASIC. The build system can rebuild only what changed in many cases, but bootstrap and platform changes sometimes require a cleaner rebuild.

Windows notes

On Windows, Git for Windows and MSYS2 Git both work. Be consistent about which shell owns the checkout when running long build scripts. Mixing Windows paths, MSYS2 paths, and Cygwin paths in one command line is a common source of avoidable build failures.


Glossary - common terms used in fbc development

arg, argument
An expression passed to a parameter in a procedure call.

cast
A type cast changes the compile-time data type of an expression and either causes a conversion (e.g. float <-> int) or a reinterpretation of the expression value's bit representation (e.g. integer <-> uinteger).

comp, compound
  • Compound blocks in the language: Any code block that allows nested code such as IF blocks, SCOPE blocks, NAMESPACE blocks, etc. is called a compound.
  • Compound symbols: UDTs, sometimes also namespaces, because both may contain nested (namespaced) symbols and they share some common code.

conv, conversion
A conversion is an operation that translates between two different representations of the same value (e.g. float <-> int, or 32bit <-> 64bit).

cast and conv are often used interchangeably in the compiler sources. For example, the AST's CONV nodes represent type casts, no matter whether they perform conversions or not.

Some (but not all) casts require run-time conversions, for example:
short <-> integer
single <-> integer
single <-> double

Simple casts between types of equal class and size do not require a run-time conversion, because the bit representation wouldn't change anyways. For example:
short <-> ushort
integer <-> uinteger
These are also called noconv casts.

ctor, constructor
  • UDT constructor
  • module constructor

ctx, context
UDTs/"classes" in the fbc sources for holding global information shared amongst multiple procedures or modules.

desc, descriptor
  • Dynamic string descriptor
  • Dynamic array descriptor

dtor, destructor
  • UDT destructor
  • module destructor

fbc
  • The FreeBASIC compiler project as a whole, the Git repository, the project registered on Sourceforge
  • The compiler program binary/executable (fbc or fbc.exe), as built from the compiler sources
  • The compiler's main module/frontend/driver

fbctinf
FB compile-time information, also see objinfo.

fbgfx
FB graphics, usually referring to the use of FB's built-in graphics keywords, implemented in gfxlib2

frontend stage 1
Compilation of the .bas input files into the next intermediate format: .asm (-gen gas), .c (-gen gcc) or .ll (-gen llvm)

frontend stage 2
Compilation of the .c (-gen gcc) or .ll (-gen llvm) intermediate files into .asm files. (doesn't apply to -gen gas because there the FB compiler generates .asm itself directly)

function
A procedure with result value; sometimes also used in place of procedure, as in C.

gfxlib2
The FB graphics runtime library implementation from the fbc project.

hashtb
A hash table, often used together with a symbol table to allow fast lookup of the symbols in that symbol table.

libfb, libfbmt, libfbgfx, libfbgfxmt
Names of the libraries built from the rtlib/gfxlib2 sources. Libraries named lib*mt are the thread-safe versions of their lib* counterparts. They are built with the ENABLE_MT #define.

local
  • Sometimes: A variable allocated on stack
  • Any symbol in a nested scope, not the global/toplevel namespace. Scoped static variables also have the FB_SYMBATTRIB_LOCAL attribute, even though they are not allocated on stack.

method
  • A member-procedure with THIS parameter. Static member-procedures (those without the THIS parameter) do not have FB_SYMBATTRIB_METHOD.
  • Sometimes: Any member-procedure, with or without THIS parameter

noconv cast
A cast that does not require a conversion.

normal build
Described here: Normal vs. Standalone

objinfo
See DevObjinfo

param, parameter
Procedure parameters as declared in procedure DECLARE statements or bodies.

paramvar
For each parameter, the compiler will create a corresponding local variable in the procedure's scope, allowing the parameters to be accessed by user code.

proc, procedure
Any sub or function, including constructors/destructors, operator overloads, property setters/getters.

standalone build
Described here: Normal vs. Standalone

static
  • static variable allocation: on the heap instead of the stack, but still scoped -- also see local.
  • static member variables: are actually externs.
  • static member procedures: member-procedures without a THIS parameter, also see method.
  • "static array" is often used in place of "fixed-size array" (QB language)

struct, structure
TYPE or UNION, also known as struct/union in C.

sub
A procedure without result (with VOID result).

symtb
A symbol table: owns a linked list of FBSYMBOL in a specific scope. This is where FBSYMBOLs live.

rtlib
The FB runtime library implementation from the fbc project

UDT, user-defined type
TYPEs/UNIONs/ENUMs, sometimes just TYPEs/UNIONs.

vreg
Virtual registers are used when emitting the AST. The AST creates a vreg for the operands and results of all operations that make up the input program. Each backend emits them differently:
    • The ASM backend actually maps the vregs to real registers and also re-uses them as they become free again. The vregs then also let the x86 code emitter know which exact registers are used.
    • The C backend sometimes emits vregs as temporary variables, sometimes simply inserts the expression whose result is represented by a vreg in place of that vreg's first use.
    • The LLVM backend simply emits each vreg as a numbered intermediate value.
Since the C/LLVM backends don't re-use vregs, the vregs are almost in static-single-assignment form; although not quite because there still are self-operations etc. produced by the AST which don't take SSA form into account.


Header style guide

FreeBASIC headers should be accurate first and pleasant to read second. They are part of the public interface, so small ABI mistakes can become real program bugs.

Style rules

  • Use normal FreeBASIC declarations and keep formatting close to nearby headers.
  • Prefer explicit types from the translated C ABI over convenient guesses.
  • Keep platform conditionals obvious and local to the declarations they affect.
  • Document non-obvious ABI choices, packing rules, callback ownership, and string encoding expectations.
  • Avoid clever macro translations when a clear FreeBASIC declaration works.

Testing

A translated header should be tested by compiling small programs that include it, call representative functions, and inspect important structure sizes when binary layout matters.


Making releases

Release work is now driven by the modular make system, the package scripts in build_scripts/, and the repository publishing scripts. Older release notes that describe a single contrib/release/build.sh flow are no longer the complete process.

Release checklist

  1. Update version metadata in mk/version.mk and any package-specific changelog files.
  2. Update the manual, changelog, installer notes, and public package text.
  3. Build the native compiler packages and the cross-target packages.
  4. Run package smoke tests from installed layouts, not only from the source tree.
  5. Rebuild the HTML and CHM manuals.
  6. Publish the archives, installers, packages, scripts, and docs to the repository host.

What to verify

A release should prove the basics on every advertised platform: compiler startup, normal compile/link, runtime startup/shutdown, graphics when supported, sound when supported, and package manager installation or removal when packages are shipped.

Repository publishing

The repository host is fed from the files produced by the build scripts. Keep old packages archived when a target depends on a single obvious current file, and keep target directories clean enough that users can understand what to download without reading the source tree.


Normal vs. Standalone FreeBASIC

When built from source, FreeBASIC can be configured for and installed in one of these two different setups:

Normal build (default)
Standalone build
Normal directory layout:
  • bin/
    • fbc.exe
    • [<target>-]ld.exe
    • other tools for native/cross compilation...
  • include/
    • freebasic/
      • fbgfx.bi
      • other headers...
  • lib/
    • freebasic/
      • <target>/
        • libfb.a
        • other libraries...
Standalone directory layout:
  • bin/
    • <target>/
      • ld.exe
      • other tools...
  • inc/
    • fbgfx.bi
    • other headers...
  • lib/
    • <target>/
      • libfb.a
      • other libraries...
  • fbc.exe
Differences to the standalone build:

  • fbc is located in bin/, like other programs
  • looks for includes in include/freebasic/, instead of inc/, to cleanly separate FB headers from system headers
  • looks for its own libraries in lib/freebasic/ instead of lib/, to cleanly separate FB libraries from system libraries
  • looks for binutils/gcc 1) in bin/ and 2) by relying on PATH
  • looks for crt/gcc libraries 1) in lib/freebasic/ and 2) by running "gcc -print-file-name=..."
  • -target option accepts system triplets such as "i686-pc-linux-gnu" or "x86_64-w64-mingw32"
  • the target name given to the -target option is prepended to the gcc/binutils program names when cross-compiling
  • compatible with the standard /usr or /usr/local directories
  • typically used for the FB-linux release
  • uses windres from binutils to compile win32 resource scripts

This makes the normal FB build integrate with GNU/Linux distributions and other Unix-like systems pretty well, allows fbc to be installed into MinGW or DJGPP trees next to gcc, and allows fbc to work with binutils/gcc cross-compiling toolchains.
Differences to the normal build:

  • the fbc binary is located at the toplevel, not inside bin/
  • looks for tools inside bin/<target>/, i.e. it uses bin/<target>/ld.exe instead of bin/[<target>-]ld.exe
  • looks for FB includes in inc/, not in include/freebasic/
  • looks for libraries in lib/, not in lib/freebasic/
  • does not try to rely on PATH and use system tools
  • does not try to query gcc to find files
  • -target only accepts simple FB target names, no system triplets
  • typically used for the FB-dos and FB-win32 releases
  • uses GoRC to compile win32 resource scripts

The standalone build is intended to be used for self-contained installations such as the traditional FB-win32 and FB-dos releases. It also allows adding fbc to the PATH, without having to add the whole bin/ directory.


objinfo

fbc stores extra information into the object files (.o) it generates, in order to read it out again at link-time. The information that is stored currently consists of the -lang/-mt settings and all libraries/search paths (-l, #inclib, -p, #libpath) that were specified when compiling that object file. This way fbc can show a warning when mixing object files that were compiled with different options, because they may be incompatible, and fbc can automatically link in libraries that were specified via #inclib, even if the user compiles and links in separate steps.

This is accomplished by emitting an extra section called "fbctinf" (FreeBASIC compile time information?) when compiling, and reading it back in at link-time. Furthermore, when building a static library, fbc creates an extra object file (called __fb_ct.inf) containing just that extra information and adds it to the library. At link-time fbc looks at each library to figure out whether it has such an __fb_ct.inf file or not.

In order to do this fbc has a custom COFF, ELF32 and also archive file format readers that can extract the .fbctinf section content. Previously, fbc used libbfd from binutils to do this, however depending on libbfd is problematic especially because of its highly unstable ABI.


Getting source code updates and rebuilding FreeBASIC

This page describes the normal rebuild path after updating a source checkout.

To download updates made available in the fbc Git repository, use your preferred Git tool, or run:

git pull --ff-only

To inspect incoming changes before applying them:

# Update remote branches
git fetch

# Review the remote history with your preferred Git viewer.
gitk --all

# Merge the remote branch when you are ready.
git merge origin/master

Which rebuild path should I use?

There are now two common rebuild paths:
  • Use make directly for normal developer builds from an already prepared checkout.
  • Use a script from build_scripts/ for platform packaging, release builds, cross-builds, VM builds, emulator-based checks, or installer tests.
The direct make path is still valid, but it is no longer the whole build story. The top-level GNUmakefile is now a small entrypoint that loads the modular build system under mk/. The scripts in build_scripts/ sit above that make system and drive the platform-specific workflows.

Normal native rebuild

For a native development build on a system that already has a usable compiler or bootstrap state:

make print-config
make prereqs-fbc
make -j$(nproc)

Some systems use gmake instead of make. The package and VM scripts choose the right command for the platform where practical.

make print-config is the first thing to check when a rebuild does something surprising. It shows the resolved target identity, compiler tools, feature policy, and layout decisions. make prereqs-fbc checks whether the build has a usable compiler or bootstrap path before the real build starts.

If you use build options on the command line, pass them again on later rebuilds unless they are stored in config.mk:

make -j$(nproc) ENABLE_STANDALONE=1

When to clean first

A simple rebuild is usually enough after ordinary source edits. Clean first when switching targets, changing toolchains, changing major feature policy, updating generated bootstrap sources, or after source files have been renamed or removed:

make clean
make -j$(nproc)

The old DOS/DJGPP warning still matters, but it is no longer the only reason to clean. DOS is especially sensitive to stale objects because command-line length limits force parts of that build to rely on object wildcards. A clean rebuild is the safe choice after source graph changes.

Build scripts

Use build_scripts/ when the job is more than a simple local rebuild. The scripts are the supported entrypoints for the release and platform workflows.

Examples:

./build_scripts/debianubuntu-build-freebasic.sh --help
./build_scripts/msys2-build-freebasic.sh --help
./build_scripts/macos-build-freebasic.sh --help
./build_scripts/freebsd-vm-build-freebasic.sh --help

The folder also contains matrix builders, cross-build helpers, VM builders for the BSD and Solaris-like targets, Windows/MSYS2 installer builders, Android/JavaScript/Xbox/Wii builders, package-test runners, and smoke-test wrappers. Prefer those scripts for producing redistributable packages because they know the staging layout, bootstrap archive names, dependency setup, and per-target test flow.

Modular make system

The make system is intentionally split by responsibility. In broad terms:
  • mk/platform.mk resolves the target OS, CPU, runtime key, and package naming.
  • mk/platform-features.mk and mk/feature-policy.mk decide which features are enabled.
  • mk/compiler-config.mk, mk/toolchain-flags.mk, mk/host-tools.mk, and mk/os-flags.mk choose tools and flags.
  • mk/layout.mk and mk/build-layout.mk define install-tree and in-tree paths.
  • mk/source-graph.mk and mk/archives.mk decide what gets built.
  • mk/build/*.mk contains the actual compile, archive, dependency, target, and clean rules.
  • mk/bootstrap/*.mk owns compiler recovery and bootstrap source emission.
  • mk/tests*.mk and mk/tests/**/*.mk own build-system verification targets.
  • mk/inst_uninst.mk owns install, uninstall, and simple package payload layout.
For a more detailed map, read mk/README.md and mk/File-Map.md in the source tree.

Bootstrap notes

A rebuild needs either a runnable fbc or suitable bootstrap sources. The build now checks that state early instead of failing later with a malformed compiler command.

Useful bootstrap commands:

make prereqs-fbc
make bootstrap-minimal
make bootstrap-seed-peer

Use bootstrap-minimal when current-target bootstrap sources already exist. bootstrap-seed-peer is a manual recovery tool for bring-up work when an appropriate peer bootstrap is available; it is not part of the normal default build.

Most platform build scripts handle the bootstrap setup for their package workflow. If you are building a release package, start with the appropriate script before falling back to manual bootstrap commands.

Useful test targets

After a rebuild, useful high-level checks include:

make sanity
make quick-test
make compiler-smoke
make bootstrap-emit-test

Platform scripts may run stronger or more specific checks than these make targets, especially for installers, cross-build packages, VM targets, browsers, emulators, graphics, and sound.


Keyboard Input

Basics

Using FB's built-in functionality, there are four ways of getting keyboard input:

    • Inkey() returns a string containing an ASCII char corresponding to the key pressed by the user, or a 2-byte FB extended keycode for some special keys, such as the Arrow keys or Page Up/Down. It works pretty much like it did in QB.

    • Getkey() returns the same information as inkey(), but in form of an integer instead of a string. inkey() and getkey() belong together: They use the same code and they are located in the same modules.

    • Multikey() takes an FB scancode (SC_*) and checks whether that key is pressed at this moment.

    • Screenevent() returns key presses in form of EVENT_KEY_PRESS events (and others for key release or repeat). It returns the FB scancode in the Event.Scancode Field, and the ASCII char value or 0 in the EVENT.ascii field. EVENT.ascii does not use FB extended keycodes; the EVENT.scancode field can be checked instead in order to handle extended keys.

"scancode" refers to the SC_* #defines which are more or less matching the DOS keyboard scancodes. The values are not made up, they themselves correspond to certain ASCII chars, for example: SC_HOME = asc( "G" ) = &h47. They're also the same values that you get under DOS/DJGPP or from the Linux kernel as part of extended key code sequences. Besides their use in multikey() or screenevent(), scancodes are used in various places internally, for example when translating between different kinds of key codes, as an easy-to-use and portable representation of keycodes.

"key" refers to an ASCII char, or a 2-byte extended keycode string for other keys as returned by inkey(). The rtlib has several KEY_* #defines for the available 2-byte extended keycodes, in form of integers. These are used internally and also match the values returned by getkey().

FB's 2-byte extended keycodes consist of a &hFF byte followed by a byte containing the SC_* scancode value corresponding to the keypress. Checking for SC_HOME returned by inkey() could look like:
if( inkey( ) = chr( 255 ) + "G" ) then ...
Checking for SC_HOME returned by getkey():
if( getkey() = &h47FF ) then ...
if( getkey() = ((SC_HOME shl 8) or &hFF) ) then ...

inkey(), getkey() and multikey() use wrapper functions that call ...
    • the console-mode versions fb_ConsoleInkey(), fb_ConsoleGetkey(), fb_ConsoleMultikey() by default,
    • or the gfxlib versions fb_GfxInkey(), fb_GfxGetkey(), fb_GfxMultikey() if a graphics SCREEN is active,
by using function pointer hooks.

rtlib

The rtlib has separate console-mode implementations of the above functions, for each platform:

  • DOS
fb_ConsoleInkey() and fb_ConsoleGetkey() use DJGPP's getch() function to retrieve input characters anytime they're called. getch() returns ASCII chars, but also 2-byte sequences for special keys, which are easy to handle because they match the SC_* scancodes.

fb_ConsoleMultikey() installs an interrupt handler that uses port I/O to read keyboard information and updates a key state table which is checked by multikey().

  • Win32
fb_ConsoleInkey() and fb_ConsoleGetkey() (indirectly) use the Win32 API functions PeekConsoleInput() and ReadConsoleInput() to get queued key press/release events whenever needed. All currently pending events are handled during a call, and after very complex internal translation involving MapVirtualKey(), the keys are put into a buffer, from where fb_ConsoleInkey() and fb_ConsoleGetkey() read the keys they return.

SetConsoleCtrlHandler() is used to listen for console close/system shutdown events to provide SC_CLOSE events for console-mode (the win32 port of the rtlib might be the only one going this far).

fb_ConsoleMultikey() uses a FindWindow()/GetForegroundWindow() hack on older versions of windows (before Windows 7) to determine whether the console window is focused, and if yes, simply uses GetAsyncKeyState(). Newer versions of windows should limit function of GetAsyncKeyState() to the currently focused window. In newer versions of windows when using windows terminal, the FindWindow() hack causes issues and must be disabled.

  • Linux, *BSD
The Unix port of the rtlib runs a console keyboard handler (and a console mouse handler) in a background thread, in order to provide input for multikey() (and getmouse()).

fb_ConsoleInkey() and fb_ConsoleGetkey() read input bytes through the __fb_con.keyboard_getch() hook. By default, __fb_con.keyboard_getch() points to a simple function that just uses fgetc() on /dev/tty (indirectly; the Unix rtlib initialization code opens the handle, and changes I/O settings etc., not only for the purpose of keyboard input, but mostly).

The terminal returns ASCII chars for simple key presses, and special escape sequences for extended keys. On the first call, various termcap lookups (via tgetstr()) are done to determine these terminal-specific escape sequences for certain key press events, and they are put into a lookup tree to allow easy & fast translation to the corresponding FB extended keycodes. By doing the termcap query the Unix rtlib can support all the different terminals (e.g. xterm vs. linux) quite well, although there still are some keys not working here and there.

Only one "event" (ASCII char or escape sequence) is read at a time, the resulting key is added to a key buffer, from where fb_ConsoleInkey() and fb_ConsoleGetkey() can read it.

fb_ConsoleMultikey() is currently implemented for the Linux port only, not under *BSD though. In console-input mode (used under 'console'/'linux' terminals), it dup()licates the rtlib's /dev/tty handle, and switches it over into medium raw mode. Then it overrides the background thread's __fb_con.keyboard_handler() hook to a function that read()s kernel key codes from the duplicated /dev/tty handle.

Called from the background thread, it reads a fixed amount of input at once, whenever it arrives. After somewhat complex translation, a key state table is updated to reflect the state of pressed/released keys, to be checked by fb_ConsoleMultikey() at any time, and the keys are added to a key buffer from where an overridden __fb_con.keyboard_getch() reads them, whenever called by fb_ConsoleInkey() or fb_ConsoleGetkey() [why is this done?]. Furthermore, the keys are sent to the Linux fbdev gfxlib2 driver, if it's active.

In X11 mode (used under 'xterm' terminal), fb_ConsoleMultikey() sets the background thread's __fb_con.keyboard_handler() to a function that checks whether the xterm has input focus (XGetInputFocus()) and if yes, simply uses XQueryKeymap() to update the key state table for fb_ConsoleMultikey().

gfxlib2

In the gfxlib, fb_GfxInkey() and fb_GfxGetkey() use one key buffer (same code on all platforms), to which the different/platform-specific gfx drivers post keys to. Similar to that, there is a single key state table for fb_GfxMultikey(), and it is also updated by the gfx drivers. Whether or not the gfx drivers actually do post keys or update key states is up to them though.

  • DOS
The DOS gfxlib2 port (for all DOS gfx drivers) sets a hook/callback that's called by the same keyboard interrupt handler used by the DOS fb_ConsoleMultikey().

  • Win32 driver
The gfx window thread listens to WM_KEYDOWN, WM_CHAR and WM_CLOSE, translates the keys, and then updates the key state table, posts them to the fb_GfxInkey()/fb_GfxGetkey() buffer, and fills in & posts the corresponding EVENT for screenevent().

  • X11 driver
The gfx window thread listens to KeyPress and other XEvent's, translates the keys, then posts them etc., just like the Win32 driver.

  • Linux fbdev driver
As mentioned above, the fbdev driver gets its input from the same keyboard handler code that's used by the Linux fb_ConsoleMultikey().


Select Case

Basic implementation
dim i as integer		dim i as integer

                scope
select case i + 123			dim temp as integer = any
                    temp = i + 123

case 1					if( temp <> 1 ) then goto cmplabel1
                    scope
    print "1"				print "1"
                    end scope
                    goto endlabel

case 2					cmplabel1:
                    if( temp <> 2 ) then goto cmplabel2
                    scope
    print "2"				print "2"
                    end scope
                    goto endlabel

case else				cmplabel2:
                    scope
    print "else"				print "else"
                    end scope

end select				cmplabel3:    '' unused only because in this example the last CASE is not conditional
                    endlabel:
                end scope

  • SELECT CASE
    • opens the implicit outer scope
    • declares the temp var
      • when inside a procedure with STATIC, the temp var will be made STATIC
      • the FB_SYMBATTRIB_TEMP is removed from the temp var, because it lives longer than just one statement
    • emits the assignment
    • declares the end label
  • each CASE
    • if there was a previous CASE
      • closes the previous CASE's scope
      • emits a jump to the end label
    • emits the label for this CASE
    • emits a conditional branch that jumps to the next CASE if the CASE condition is not met
    • opens the CASE's scope
    • CASE ELSE does not emit a conditional branch
    • once CASE ELSE was used, no further CASE blocks are allowed
  • END SELECT
    • closes the previous CASE's scope
    • emits an extra CASE label at the end (There is no CASE coming anymore, but this allows the last CASE to jump to the end, if it is a conditional CASE. The last CASE could jump to the SELECT's end label instead, but that would require some special case handling code.)
    • emits the end label
  • any EXIT SELECTs jump immediately to the end label

SELECT CASE on strings/zstrings/fixstrs
dim s as string			dim s as string

                scope
select case s + "1"			dim temp as string

                    fb_StrAssign( temp, s )
                    fb_StrConcatAssign( temp, "1" )

case "1"				if( fb_StrCompare( temp, "1" ) <> 0 ) then goto cmplabel1
                    scope
    print "1"				print "1"
                    end scope
                    goto endlabel

                    cmplabel1:
end select				endlabel:
                    fb_StrDelete( temp )    '' destroying the temp var at scope end
                end scope

                fb_StrDelete( s )

  • SELECT CASE on string/zstring/fixstr expressions uses a string temp var
    • probably because that's easiest
    • knowing the string length will potentially speed up the following comparisons
    • the dynamic memory allocation can be a slow down too
  • the string temp var is destroyed at scope end or scope breaks (e.g. reaching END SELECT, or EXIT FUNCTION from within a CASE block)

SELECT CASE on wstrings
dim w as wstring * 10			dim w as wstring * 10

                    scope
select case w + wstr( "1" )			dim temp as wstring ptr

                        dim tempexpr as wstring ptr = w + wstr( "1" )
                        temp = fb_WstrAlloc( fb_WstrLen( tempexpr ) )
                        fb_WstrAssign( temp, tempexpr )

case wstr( "1" )				if( fb_WstrCompare( temp, wstr( "1" ) ) <> 0 ) then goto cmplabel1
                        scope
    print "1"					print "1"
                        end scope
                        goto endlabel

                        cmplabel1:
end select					endlabel:
                        fb_WstrDelete( temp )    '' destroying the temp var at scope end
                    end scope

  • similar to SELECT CASE on zstrings, for wstring expressions a wstring is dynamically allocated
  • the temp wstring is treated much like a dynamic wstring object would be
    • it is a VAR symbol with type WCHAR PTR
    • marked with FB_SYMBSTATS_WSTRING
    • this allows ctor/dtor checks to recognize it and give it the needed treatment
  • this way, the temp wstring is destroyed at scope end or scope breaks

SELECT CASE without temp var
When the expression given to the select statement is just a simple variable access, then no temporary variable needs to be created. In this case, the given variable itself will be used in the comparisons at each case statement. For example:

dim i as integer		dim i as integer

                scope
select case i

case 1					if( i <> 1 ) then goto cmplabel1
                    scope
    print "1"				print "1"
                    end scope
                    goto endlabel

end select				cmplabel1:
                    endlabel:
                end scope


Structure layout

Structure layout must match the target ABI when FreeBASIC code talks to C libraries, system calls, graphics backends, sound backends, or binary file formats. Do not assume that a structure layout proven on one CPU is correct on another CPU.

What affects layout

  • Field type and size.
  • Field alignment.
  • Structure packing.
  • Pointer size.
  • Calling convention and ABI family.
  • Compiler-specific C headers for the target platform.

Modern targets

Layout-sensitive code should be checked on every advertised CPU family. That includes 32-bit and 64-bit x86, ARM, AArch64, PowerPC-family targets, MIPS-family targets, s390x, WebAssembly/JavaScript where applicable, and the BSD, Haiku, Solaris-like, DOS, Windows, and mobile targets supported by the tree.

Practical rule

When a structure is shared with C, prefer a tiny C probe or a FreeBASIC test that checks sizeof and important field offsets on the real target. Guessing from a similar platform is not enough for release work.


Translating C headers

A FreeBASIC header should expose the C library interface accurately while still looking like normal FreeBASIC source. Start from the C declarations, translate types and calling conventions carefully, then keep platform-specific differences visible instead of hiding them behind guesses.

Basic process

  1. Identify the library version and platform ABI being translated.
  2. Translate constants, enums, structs, unions, callbacks, and functions.
  3. Use the C type comparison table for integer, pointer, string, and size types.
  4. Keep conditional declarations close to the platform condition that requires them.
  5. Compile small examples against the header on the target platform.

Useful pages

  • C type comparison table
  • Header style guide
  • Creating bindings


DrV
Daniel Verkamp (DrV)
i_am_drv [at] yahoo [dot] com
i_am_drv [at] users [dot] sf [dot] net
http://drv.nu/
Part of the FreeBASIC Development team; DOS port maintainer


BLOAD/BSAVE text mode work-around
These functions allow you to use BSAVE and BLOAD in a text mode.

Sub _bsave( file As String, p As Any Ptr, sz As Integer )

  Dim As Long ff
  ff = FreeFile

  Open file For Binary As ff
    fb_fileput( ff, 0, ByVal p, sz )

  Close

End Sub

Sub _bload( file As String, p As Any Ptr )

  Dim As Long ff
  ff = FreeFile

  Open file For Binary As ff
    fb_fileget( ff, 0, ByVal p, LOF( ff ) )

  Close

End Sub


GET/PUT image header example
Example showing the two different headers used for image buffers.
Note: ImageInfo is provided as a simpler alternative to reading the image structures directly.

'' fbgfx.bi contains the necessary structures and constants for working
'' directly with image headers
#include "fbgfx.bi"

'' in lang fb, structures and constants are contained in the FB namespace
#if __FB_LANG__ = "fb"
Using FB
#endif

'' function to show info on an image
Sub show_image_info( ByVal img As Any Ptr )
    Dim As PUT_HEADER Ptr header ' IMAGE can also be use instead of PUT_HEADER
    Dim As Integer w, h, bpp, pitch

    header = img
    If( header->Type = PUT_HEADER_NEW ) Then

        Print "New style header"

        w = header->Width
        h = header->height
        bpp = header->bpp
        pitch = header->pitch

    Else

        Print "Old style header"

        w = header->old.width
        h = header->old.height
        bpp = header->old.bpp
        pitch = w * bpp

    End If

    Print "Image dimensions are " & w & "*" & h
    Print "Image uses " & bpp & " bytes for each pixel"
    Print "A row of image pixels takes " & pitch & " bytes"

End Sub

Dim As Any Ptr picture

ScreenRes 320, 200, 32

picture = ImageCreate( 10, 10, RGB(128, 192, 255) )

Put( 40, 40 ), picture, PSet

show_image_info( picture )

ImageDestroy picture

Sleep

NOTE: To use this code with an array, pass your array to the function, like this:

show_image_info( VarPtr( myarray( L ) ) )

where L is the lower bound of myarray().


FMod
FMod is a 3d sound and music library. Though its license is strange, it works acceptably for playing sounds, and it nicely encapsulates 3D sound.

Related:
-Using libraries in FreeBASIC by SJ Zero


FreeBASIC
FreeBASIC is an open source (under GPL) compiler. It is designed to be syntax compatible with QuickBASIC, while expanding on the language and capabilities. It compiles for DOS, Microsoft Windows and Linux, and is being ported to other platforms.

See also

  • FreeBASIC FAQ
  • About
  • Main Features
  • Credits
  • Requirements
  • Installing
  • Running
  • FreeBASIC Dialects
  • FreeBASIC and QBasic
  • Differences from QB


Licenses

Compiler
The FreeBASIC compiler (fbc) is released under the GPL license.

Libraries
With the exception of LibFFI (which is used for ThreadCall), the runtime library (libfb) is released under the LGPL license, for both the single-threaded and multi-threaded versions, with this extension to allow linking to it statically:

"As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version."

The Gfx library (libfbgfx) is released under the following license, found at https://github.com/freebasic/fbc/blob/master/src/gfxlib2/license.txt:
libgfx2 - FreeBASIC's alternative gfx library
Copyright (C) 2005 Angelo Mottola (a.mottola@libero.it)

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

As a special exception, the copyright holders of this library give
you permission to link this library with independent modules to
produce an executable, regardless of the license terms of these
independent modules, and to copy and distribute the resulting
executable under terms of your choice, provided that you also meet,
for each linked independent module, the terms and conditions of the
license of that module. An independent module is a module which is
not derived from or based on this library. If you modify this library,
you may extend this exception to your version of the library, but
you are not obligated to do so. If you do not wish to do so, delete
this exception statement from your version.

LibFFI is released under the following license, found at http://github.com/atgreen/libffi/blob/master/LICENSE:
libffi - Copyright (c) 1996-2011 Anthony Green, Red Hat, Inc and others.
See source files for details.

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
``Software''), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Documentation
The documentation is released under the GFDL license.


SJ Zero
Jason K. Firth

Who I Am
I am a hobbyist software developer whose tool of choice is FreeBASIC.

What I Do
I'm an instrumentation engineering technologist, not a programmer.

Contact Me
Don't.


__FB_DRAGONFLY__

Syntax

__FB_DRAGONFLY__

Description

Defined without a value at compile time when the DragonFly BSD target is being used. It can be used to compile parts of a program only for DragonFly BSD.

Example

#IFDEF __FB_DRAGONFLY__
    print "DragonFly BSD target"
#ENDIF

Predefined Defines

See also
Compiler Option: -target

Compiler Meta-Commands


__FB_HAIKU__

Syntax

__FB_HAIKU__

Description

Defined without a value at compile time when the Haiku target is being used. It can be used to compile parts of a program only for Haiku.

Example

#IFDEF __FB_HAIKU__
    print "Haiku target"
#ENDIF

Predefined Defines

See also
Compiler Option: -target

Compiler Meta-Commands


__FB_ILLUMOS__

Syntax

__FB_ILLUMOS__

Description

Defined without a value at compile time when the illumos target is being used. The sunos target spelling also selects the illumos target.

When compiling for illumos, the compiler also defines __FB_SOLARIS__ for Solaris-family compatibility.

Example

#IFDEF __FB_ILLUMOS__
    print "illumos target"
#ENDIF

Predefined Defines

See also
__FB_SOLARIS__
Compiler Option: -target

Compiler Meta-Commands


__FB_SOLARIS__

Syntax

__FB_SOLARIS__

Description

Defined without a value at compile time when the Solaris target is being used.

The compiler also defines __FB_SOLARIS__ for the illumos target as a Solaris-family compatibility symbol. Use __FB_ILLUMOS__ when code must detect illumos specifically.

Example

#IFDEF __FB_SOLARIS__
    print "Solaris-family target"
#ENDIF

Predefined Defines

See also
__FB_ILLUMOS__
Compiler Option: -target

Compiler Meta-Commands


GETTOUCH
Gets position and identifier information for an active touch contact.

Syntax

Declare Function GetTouch ( ByVal index As Integer, ByRef x As Integer, ByRef y As Integer, ByRef id As Integer = 0 ) As Integer

Usage

result = GetTouch( index, x, y [, id] )

Parameters

index
The zero-based touch contact index to query.
x
Receives the touch x coordinate.
y
Receives the touch y coordinate.
id
Receives the backend touch identifier, if available.

Return Value

0 on success, or a non-zero runtime error value if the requested contact is not available.

Description

GetTouch retrieves one active touch contact from the current graphics screen. index is zero-based and should be less than the value returned by GetTouchCount.

x and y are returned in logical graphics coordinates relative to the upper-left corner of the graphics screen. They are not affected by View or Window.

id is intended to identify the same physical contact while it remains active. Some backends provide a native contact id. The mouse fallback uses id 0.

If the requested contact is not available, x, y and id are set to -1 when those arguments are provided.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __GetTouch.
Differences from QB

  • New to FreeBASIC 1.20.1.
See also

  • GetTouchCount
  • GetTouchHit
  • GetMouse
  • User Input Functions


GETTOUCHCOUNT
Gets the number of active touch contacts.

Syntax

Declare Function GetTouchCount ( ) As Integer

Usage

count = GetTouchCount( )

Return Value

The number of currently active touch contacts.

Description

GetTouchCount returns the number of active touch contacts reported by the current graphics driver.

On platforms with native touch support, this reports the active contacts known to the backend. On platforms without native touch support, GfxLib exposes a simple compatibility fallback: if the left mouse button is held down over the graphics screen, the mouse position is reported as one touch contact.

The returned coordinates for touch contacts are in the same logical graphics coordinate space used by GetMouse.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __GetTouchCount.
Differences from QB

  • New to FreeBASIC 1.20.1.
See also

  • GetTouch
  • GetTouchHit
  • GetMouse
  • User Input Functions


GETTOUCHHIT
Tests whether any active touch contact is inside a rectangle or circle.

Syntax

Declare Function GetTouchHit ( ByVal x1 As Integer, ByVal y1 As Integer, ByVal x2 As Integer, ByVal y2 As Integer ) As Integer
Declare Function GetTouchHit ( ByVal x As Integer, ByVal y As Integer, ByVal radius As Integer ) As Integer

Usage

hit = GetTouchHit( x1, y1, x2, y2 )
hit = GetTouchHit( x, y, radius )

Parameters

x1, y1, x2, y2
Opposite corners of the rectangle to test.
x, y
The center of the circle to test.
radius
The radius of the circle to test.

Return Value

-1 if one or more active touch contacts are inside the requested area, otherwise 0.

Description

GetTouchHit is a small helper for touch-oriented user interfaces. It checks all currently active touch contacts and reports whether any contact is inside the requested rectangle or circle.

The rectangle overload accepts the corners in either order. The circle overload treats a negative radius as its absolute value.

On platforms without native touch support, the normal mouse fallback applies: a held left mouse button can count as one touch contact.

Dialect Differences

  • Not available in the -lang qb dialect unless referenced with the alias __GetTouchHit.
Differences from QB

  • New to FreeBASIC 1.20.1.
See also

  • GetTouchCount
  • GetTouch
  • GetMouse
  • User Input Functions


sfxlib backends
Output and capture backends used by FreeBASIC's built-in sound library.

Description

sfxlib has a shared command layer and mixer, but it still needs a platform backend to talk to the actual operating system or hardware. The backend is the part that sends mixed audio to speakers, receives captured input, or talks to a platform MIDI system.

Most programs do not need to select a backend manually. sfxlib tries the compiled backend list for the target and uses a working output path. The DEVICE command family exists for diagnostics and programs that want to show or choose the available audio paths.

The null driver is special. It accepts sound output but does not play it through speakers. This is useful for automated tests, package smoke tests, servers, and systems where audio hardware is not available.

Common backends

  • Windows: WASAPI, WinMM, and the null driver.
  • Linux: PulseAudio, ALSA, and the null driver.
  • macOS: CoreAudio and the null driver.
  • Android: AAudio, OpenSL ES, and the null driver.
  • JavaScript: WebAudio and the null driver.
  • Haiku: native Haiku audio and the null driver.
  • DOS: Sound Blaster, PC speaker, and target-specific MIDI support where available.
  • BSD targets: OSS-style backends; OpenBSD uses sndio.
  • Solaris-like targets: the platform audio device backend.
  • Wii: ASND.
  • Xbox: XAudio.

Testing

Set SFXLIB_DRIVER=null before program startup to request the null driver when it is available. That lets a program exercise parsing, linking, mixing, and shutdown without requiring a real audio device.

See also

  • DEVICE
  • CAPTURE
  • Sound Library Reference


Laanan Fisher
(a.k.a. stylin)

FreeBASIC
I regularly use and recommend FreeBASIC to anyone needing a language that provides ease-of-use, low development times, portability and support for a variety of programming paradigms. I log on occasionally at the official FreeBASIC site [www.freebasic.net] and read about what's new with FreeBASIC and its great community at the forums there [www.freebasic.net/forum].

contact
Reach me via email at gmail.com with a username of "laananfisher".


GNU Free Documentation License

Version 1.2, November 2002

Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

0. PREAMBLE

The purpose of this License is to make a manual, textbook, or other functional and useful document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

1. APPLICABILITY AND DEFINITIONS

This License applies to any manual or other work, in any medium, that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. Such a notice grants a world-wide, royalty-free license, unlimited in duration, to use that work under the conditions stated herein. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you". You accept the license if you copy, modify or distribute the work in a way requiring permission under copyright law.

A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (Thus, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License. If a section does not fit the above definition of Secondary then it is not allowed to be designated as Invariant. The Document may contain zero Invariant Sections. If the Document does not identify any Invariant Sections then there are none.

The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License. A Front-Cover Text may be at most 5 words, and a Back-Cover Text may be at most 25 words.

A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, that is suitable for revising the document straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup, or absence of markup, has been arranged to thwart or discourage subsequent modification by readers is not Transparent. An image format is not Transparent if used for any substantial amount of text. A copy that is not "Transparent" is called "Opaque".

Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML, PostScript or PDF designed for human modification. Examples of transparent image formats include PNG, XCF and JPG. Opaque formats include proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML, PostScript or PDF produced by some word processors for output purposes only.

The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.

A section "Entitled XYZ" means a named subunit of the Document whose title either is precisely XYZ or contains XYZ in parentheses following text that translates XYZ in another language. (Here XYZ stands for a specific section name mentioned below, such as "Acknowledgements", "Dedications", "Endorsements", or "History".) To "Preserve the Title" of such a section when you modify the Document means that it remains a section "Entitled XYZ" according to this definition.

The Document may include Warranty Disclaimers next to the notice which states that this License applies to the Document. These Warranty Disclaimers are considered to be included by reference in this License, but only as regards disclaiming warranties: any other implication that these Warranty Disclaimers may have is void and has no effect on the meaning of this License.

2. VERBATIM COPYING

You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

You may also lend copies, under the same conditions stated above, and you may publicly display copies.

3. COPYING IN QUANTITY

If you publish printed copies (or copies in media that commonly have printed covers) of the Document, numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a computer-network location from which the general network-using public has access to download using public-standard network protocols a complete Transparent copy of the Document, free of added material. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.

4. MODIFICATIONS

You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

* A. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
* B. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has fewer than five), unless they release you from this requirement.
* C. State on the Title page the name of the publisher of the Modified Version, as the publisher.
* D. Preserve all the copyright notices of the Document.
* E. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
* F. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
* G. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
* H. Include an unaltered copy of this License.
* I. Preserve the section Entitled "History", Preserve its Title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section Entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
* J. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
* K. For any section Entitled "Acknowledgements" or "Dedications", Preserve the Title of the section, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
* L. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
* M. Delete any section Entitled "Endorsements". Such a section may not be included in the Modified Version.
* N. Do not retitle any existing section to be Entitled "Endorsements" or to conflict in title with any Invariant Section.
* O. Preserve any Warranty Disclaimers.

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.

You may add a section Entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.

5. COMBINING DOCUMENTS

You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice, and that you preserve all their Warranty Disclaimers.

The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections Entitled "History" in the various original documents, forming one section Entitled "History"; likewise combine any sections Entitled "Acknowledgements", and any sections Entitled "Dedications". You must delete all sections Entitled "Endorsements."

6. COLLECTIONS OF DOCUMENTS

You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.

7. AGGREGATION WITH INDEPENDENT WORKS

A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, is called an "aggregate" if the copyright resulting from the compilation is not used to limit the legal rights of the compilation's users beyond what the individual works permit. When the Document is included in an aggregate, this License does not apply to the other works in the aggregate which are not themselves derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one half of the entire aggregate, the Document's Cover Texts may be placed on covers that bracket the Document within the aggregate, or the electronic equivalent of covers if the Document is in electronic form. Otherwise they must appear on printed covers that bracket the whole aggregate.

8. TRANSLATION

Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License, and all the license notices in the Document, and any Warranty Disclaimers, provided that you also include the original English version of this License and the original versions of those notices and disclaimers. In case of a disagreement between the translation and the original version of this License or a notice or disclaimer, the original version will prevail.

If a section in the Document is Entitled "Acknowledgements", "Dedications", or "History", the requirement (section 4) to Preserve its Title (section 1) will typically require changing the actual title.

9. TERMINATION

You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

10. FUTURE REVISIONS OF THIS LICENSE

The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.

Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.


GNU GENERAL PUBLIC LICENSE

Version 2, June 1991

Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA

Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.

Preamble

The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.

To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.

For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.

We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.

Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.

Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.

The precise terms and conditions for copying, distribution and modification follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.

1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.

You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.

2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:

  • You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.

  • You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.

  • If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.

In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:

  • Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,

  • Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,

  • Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.

4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.

6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.

7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.

9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.

10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

NO WARRANTY

11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.


GNU LESSER GENERAL PUBLIC LICENSE

Version 2.1, February 1999

Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.]

Preamble

The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.

This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below.

When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things.

To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it.

For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights.

We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library.

To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others.

Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license.

Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs.

When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library.

We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances.

For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License.

In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system.

Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library.

The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run.

GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you".

A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.

The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)

"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.

Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.

1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.

You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.

2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:

a ) The modified work must itself be a software library.

b ) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.

c ) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.

d ) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.

(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)

These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.

In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.

3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.

Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.

This option is useful when you wish to copy part of the code of the Library into a program that is not a library.

4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.

If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.

5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.

However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.

When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.

If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)

Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.

6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.

You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:

a ) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)

b ) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with.

c ) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.

d ) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.

e ) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.

For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.

7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:

a ) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.

b ) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.

8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.

10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License.

11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.

This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.

12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.

13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.

14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.

NO WARRANTY

15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

END OF TERMS AND CONDITIONS

How to Apply These Terms to Your New Libraries

If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).

To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.

<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names:

Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker.

<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice

That's all there is to it!


OpenGL
OpenGL is one of the most well-recognised standard 3D graphics APIs in the world. FreeBASIC supports the use of OpenGL in conjunction with its own graphics library or SDL.

See also

  • OpenGL bindings


Shared Libraries - DOS
A shared library is compiled code that can be loaded and used later when running an executable.

Description

DOS supports shared libraries (dynamic link libraries) with some limitations compared to Linux and Windows. The DOS target uses the dxe3gen utility from DJ's GNU Programming Platform (DJGPP) for creating a DXE file (shared library).

Supported

  • shared library file names have .DXE file name extension
  • DyLibLoad, DyLibSymbol, and DyLibFree for dynamic loading
  • import libraries can be used (but see below in Limitations)
  • #inclib and Declare for declaring subs and functions when using import libraries.

Limitations

  • no global variables shared between DXE's (extern / common shared, etc)
  • no gfx functions allowed in DXE's (dylibload() will fail)
  • no static libraries allowed in DXE's (dylibload() will fail)
  • full file name with .DXE must be used with DyLibLoad
  • DXE's must only use procedures available in libfb or the library source code itself, otherwise dylibload() will fail (even though compilation of the DXE will have succeeded)
  • no complex Type's or types with Extends. Types having member procedures can not be exported
  • Export is allowed but also ignored; all public procedures are exported
  • Exported symbols on DOS have the underscore ( '_' ) and is required with DyLibSymbol.
  • Mutli-threaded build of the fb runtime not exported and is likely not to work (more testing needed at the time of this writing).

DXE3GEN

dxe3gen is a utility which allows you to create files which contain dynamically loadable code (DXE). DXE is used as a synonym for `dynamically loadable executable module'. See dxe3gen.

dxe3gen expects that DXE_LD_LIBRARY_PATH to be set. If the environment variable is not already set, fbc will set the variable to fbc's library path before invoking dxe3gen.

In fbc standalone set-up, custom linker script dxe.ld is expected to be present in fbc's library path since dxe3gen will invoke 'ld' linker to generate and executable, which in turn is converted to a .DXE file.

Shared Library Using Run-time Dynamic Loading

In this first example, we use d1.bas source for the DXE and m1.bas for the test. This example will also work on Linux and Windows with no changes, however, differences are noted in the example sources.

$ fbc d1.bas, on DOS, will produce d1.dxe dynamic link library and libd1_il.a import library.
$ fbc m1.bas, on DOS, will produce m1.exe executable.

Library loading and function pointer loading is handled manually by the user at run-time.

'' d1.bas - dynamic link library

'' tell fbc to build a dynamic link library
#cmdline "-dll"

'' on DOS:
''   - creates d1.dxe (dynamic link library)
''   - creates libd1_il.a (import library)
'' on Windows:
''   - creates d1.dll (dynamic link library)
''   - creates libd1.dll.a (import library)
''   - either d1.dll or libd1.dll.a must be found to link an
''     executable using d1.dll
''   - d1.dll must be found to load and run an executable
''     using d1.dll
'' on Linux:
''   - creates libd1.so (dynamic link library)
''   - libd1.so must be found to load and run an executable
''     using libd1.do
''   - $ LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./executable

Sub proc_d1( ByRef fromproc As Const String ) Export
    Print __FILE__ & " : " & __FUNCTION__ & " @ " & _
        Hex(ProcPtr(proc_d1),SizeOf(Any Ptr)*2) & _
        " called from: " & fromproc
End Sub

'' m1.bas - dynamically load "D1" library and symbol "PROC_D1" at run time
''
'' library loading and symbol pointers are managed
'' manually by the user.

#define NULL 0

'' variable to hold a handle to the loaded dynamic link libray
Dim library As Any Ptr

'' prototype the function pointer
Type PROC_D1_PTR As Sub( ByRef from As Const String )

'' variable to hold a pointer to the sub/function in the dll
Dim proc_d1 As PROC_D1_PTR

'' some differences in naming between DOS and other targets,
'' so assign the names to some constants so the build process
'' can work on DOS and other targets:
#ifdef __FB_DOS__
    Const D1_DLL_NAME = "d1.dxe"
    Const PROC_D1_NAME = "_PROC_D1"
#else
    Const D1_DLL_NAME = "d1"
    Const PROC_D1_NAME = "PROC_D1"
#endif

'' try to load in the libray
library = DyLibLoad( D1_DLL_NAME )

If( library = NULL ) Then
    Print "unable to load library " + D1_DLL_NAME
    End 1
End If

'' get a function pointer to the procedure defined in the DXE
proc_d1 = DyLibSymbol( library, PROC_D1_NAME )
If( proc_d1 = NULL ) Then
    Print "unable to load symbol " + PROC_D1_NAME
    End 1
End If

'' call the loaded procedure
proc_d1( __FILE__ & " : " & __FUNCTION__ )

'' release the library
DyLibFree( library )

Shared Library Using Import Library

In this example, we use d1.bas source (from above) for the DXE and m2.bas below for the test. This example will also work on Linux and Windows with no changes, however, differences are noted in the example sources.

$ fbc d1.bas (from above), on DOS, will produce d1.dxe dynamic link library and libd1_il.a import library.
$ fbc m2.bas, on DOS, will produce m2.exe executable.

Library loading and function exports are handled automatically by the executable loader and run time start up code. The include file d1.bi is not strictly needed, however is good practice to have a single place where functions are declared so there are no mismatches between usage across multiple modules.

'' d1.bi - include file for d1.bas declarations
#pragma Once
Declare Sub proc_d1( ByRef from As Const String )

'' m2.bas - link to import library

'' use an import library for d1.dxe/d1.dll/d1.so
''
'' library loading and symbol pointers are managed
'' automatically by the operating system and/or
'' runtime start-up code.

'' import libraries are named differently on
'' DOS compared to other targets.  Or in case of
'' at least win/linux not actually needed.  On DOS
'' #inclib will cause linker to look for libd1_il.a
'' when linking this executable.  The import library includes
'' some start-up code to export functions from the dynamic
'' link library when it is loaded at runtime.
#ifdef __FB_DOS__
    #inclib "d1_il"
#else
    #inclib "d1"
#endif

'' include declarations for the library from a header
#include Once "d1.bi"

'' call the function in the DLL
proc_d1( __FILE__ & " : " & __FUNCTION__ )

#ifdef __FB_DOS__

'' add a module constructor to initialize the exports from
'' rtlib at run time.  Should be the first call made.
'' Careful: module constructor order is not guaranteed so
'' this is likely the only constructor that can be present
'' in the entire application, aside from the implicit
'' constructors in the DXE file that initialize it's exports.
''
Private Sub __fb_init_libfb_dxe Constructor
    DyLibLoad( "" )
End Sub

#endif

Version

  • some support since fbc 1.06.0 with improved support in fbc 1.10.0

See also

  • Shared Libraries
  • #inclib
  • #include
  • Compiler Option: -dll
  • Compiler Option: -dylib


Microsoft QuickBASIC
A BASIC compiler, interpreter and IDE

QuickBASIC is a twenty year old interpreter/compiler upon which FreeBASIC is modeled. It runs in 16-bit MS-DOS.

More information from Wikipedia:

Microsoft QuickBASIC (often shortened, correctly, to QB, or incorrectly, to "QBasic", which is a different system) is a descendant of the BASIC programming language that was developed by the Microsoft Corporation for use with the MS-DOS Operating System. It was loosely based on GW-BASIC but in addition provided user-defined types, improved programming structures, better graphics and disk support and a compiler in addition to the interpreter. Microsoft sold QuickBASIC as a commercial development suite.

Microsoft released the first version of QuickBASIC on August 18, 1985 stored on a single 5.25" floppy disk. QuickBASIC came with a markedly different Integrated Design Environment (IDE) from the one supplied with previous versions of BASIC. Line numbers were no longer needed since users could insert and remove lines directly via an onscreen text editor.

Microsoft's "PC BASIC Compiler" was included which could be used to compile programs into DOS executables. The editor also had an interpreter built in which would run the program without leaving the editor at all, and could be used to debug the program before creating an executable file. Unfortunately there were some small, subtle differences between the interpreter and the compiler, so that sometimes programs running perfectly well in the interpreter would fail after compilation, or even not compile at all.

The last version of QuickBASIC was 4.5 (1988) although there was continued development of the Microsoft Basic Professional Development System (PDS), the last release of which was version 7.1 (June 1990). The PDS version of the IDE was called QuickBASIC Extended (QBX). The successor to QuickBASIC and PDS was Visual Basic for MSDOS 1.0 provided in Standard and Professional versions. Later versions of Visual Basic did not include DOS versions as Microsoft wanted developers to concentrate on Windows applications.

A replacement for GW-BASIC, based on QuickBASIC 4.5 was included with MS-DOS 5 and later versions. This is called QBASIC. Compared to QuickBASIC, it is limited as it lacks a few functions, can only handle programs of a limited size, lacks support for separate modules, and is an interpreter only. It cannot be used to produce executable files directly although programs developed using it can still be compiled by a QuickBASIC 4.5, PDS 7.1 or VBDOS 1.0 compiler, if one is available.

To learn more about the language, history, and community of QuickBASIC and its free interpreter-only counterpart, you should see also en.wikipedia.org/wiki/QBasic. There are more links, and more information, including a barebones tutorial for Quick/QBasic programming.

External links
Pete's QB Site. One of the oldest remaining QB sites (since Oct 1998).
QQN/QBN: QBasic/QuickBasic News.
QQN's Newbies Section which includes a link for downloading QBasic.


GFX_NULL
    Using the GFX_NULL driver in Windows

    The client area of the window is updated using GfxLib. Menus, toolbars or dialogs can be added to the window using normal Win API calls.

    '' Example of use of the GFX_NULL driver in windows
    '' The GfxLib is set up in the ON_Create sub
    '' The GFXLib buffer is drawn  to screen in th On_Paint Sub
    '' The GfXLib is updated in the event loop

    #include "fbgfx.bi"
    #include Once "windows.bi"

    Using fb

    Dim Shared bmi As bitmapv4header
    Dim Shared mywin As rect

    ''
    ''--------------------------------------------------------------------------
    Function on_paint(ByVal hwnd As HWND,ByVal wparam As WPARAM,ByVal lparam As LPARAM) As Integer

        Dim rct As RECT
        Dim pnt As PAINTSTRUCT
        Dim hDC As HDC

        'draw the gfx buffer to screen
        hDC = BeginPaint(hWnd, @pnt)
        GetClientRect( hWnd, @rct )
        With rct
            StretchDIBits hDC, 0, 0,.Right-.Left+1,.bottom-.top+1, 0, 0, .Right-.Left+1,_
                .bottom-.top+1,ScreenPtr,CPtr(bitmapinfo Ptr, @bmi), DIB_RGB_COLORS, SRCCOPY
        End With

        EndPaint hWnd, @pnt

        Function = 0

    End Function

    ''
    ''---------------------------------------------------------------------------
    Function on_Create(ByVal hwnd As HWND,ByVal wparam As WPARAM,ByVal lparam As LPARAM) As Integer
        Dim rct As RECT
        'set a gfxscreen of the size of the client area
        GetClientRect( hWnd, @mywin)
        ScreenRes mywin.right+1,mywin.bottom+1, 32, 1, GFX_NULL
        'and create a bmp header,required to paint it yo screen
        With bmi
          .bV4Size = Len(BITMAPV4HEADER)
          .bv4width=mywin.right+1
          .bv4height=-(mywin.bottom+1)   'negative value=>top to bottom bmp
          '(standard BMP's are bottom to top)
          .bv4planes=  1
          .bv4bitcount=32
          .bv4v4compression=0
          .bv4sizeimage=mywin.right+1*mywin.bottom+1*4
          .bV4RedMask = &h0F00
          .bV4GreenMask = &h00F0
          .bV4BlueMask = &h000F
          .bV4AlphaMask = &hF000
        End With

        Function = 0

    End Function

    ''
    ''---------------------------------------------------------------------------
    Function on_Destroy(ByVal hwnd As HWND,ByVal wparam As WPARAM,ByVal lparam As LPARAM) As Integer
        'clear arrays....
        PostQuitMessage( 0 )

        Function = 0

    End Function

    ''
    ''----------------------------------------------------------------------------
    Function WndProc ( ByVal hWnd As HWND,ByVal message As UINT, _
                       ByVal wParam As WPARAM,ByVal lParam As LPARAM ) As LRESULT

        Function = 0

        Select Case As Const  message
        Case WM_CREATE
            Function = On_create(hwnd,wparam,lparam)
        Case WM_PAINT
            Function = On_paint(hwnd,wparam,lparam)
        Case WM_DESTROY
            Function = On_destroy(hwnd,wparam,lparam)
        Case Else
            Function = DefWindowProc( hWnd, message, wParam, lParam )
        End Select

    End Function

    ''
    ''------------------------------------------------------------------------------
    ''main program create window + event loop

        Dim wMsg As MSG
        Dim wcls As WNDCLASS
        Dim szAppName As ZString * 30 => "Random Rectangles"
        Dim hWnd As HWND
        Dim i As Integer

        With wcls
            .style         = CS_HREDRAW Or CS_VREDRAW
            .lpfnWndProc   = @WndProc
            .cbClsExtra    = 0
            .cbWndExtra    = 0
            .hInstance     = GetModuleHandle( null )
            .hIcon         = LoadIcon( NULL, IDI_APPLICATION )
            .hCursor       = LoadCursor( NULL, IDC_ARROW )
            .hbrBackground = GetStockObject(WHITE_BRUSH )
            .lpszMenuName  = NULL
            .lpszClassName = @szAppName
        End With

        If( RegisterClass( @wcls ) = False ) Then
            End
        End If

        'make a non-resizable screen
        hWnd = CreateWindowEx( 0,szAppName,"Example of GFX_NULL",_
            WS_OVERLAPPEDWINDOW And Not (WS_sizebox Or ws_maximizebox),_
            CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT, _
            NULL,NULL, wcls.hinstance,NULL )

        ShowWindow( hWnd, SW_NORMAL )
        UpdateWindow( hWnd )

        While 1
            If PeekMessage( @wMsg, NULL, 0,0, PM_Remove) Then
                If wmsg.message=WM_QUIT Then
                    Exit While
                End If
                TranslateMessage( @wMsg )
                DispatchMessage( @wMsg )
            Else
                'update the gfx buffer
                Line (Rnd*mywin.right,Rnd*mywin.bottom)-(Rnd*mywin.right,Rnd*mywin.bottom),_
                RGB(Rnd*255,Rnd*255,Rnd*255),bf
                redrawwindow (hwnd,0,0,rdw_invalidate)
            End If
        Wend

        End wMsg.wparam


TekRat
TekRat

Contact me at: tekrat@2d.com


Basic Input
Get Information into your Program.

Input is the life of any program. If you can't get something into your program, how are you going to get anything out of it? What you will find here is the basics of how to get information into your FreeBASIC program.

Here's a very basic program that will ask for your name:

'Create a place to put the user's name
Dim As String strMyName

' Ask for the user's name and store it in the string 'strMyName'
Input "What is your name? ", strMyName

' Wait half a second
Sleep 500

' Show them their name
Print
Print "I now know your name is "; strMyName
Print

' Wait until someone presses a button before you exit
Print "Press any button to exit"
Sleep

INPUT is the easiest way to get information from someone. They just type in some text and press Enter when they are done.

What if you you only want one keystroke? The easiest way is to use the GetKey method. GetKey gives you the ASCII value of a key that was pressed.

' Ask the user for input
Print "Press your favorite key:"

' Set a place to keep the ASCII value of the key
Dim As Integer strKeyPress

' Keep going until a key is pressed
Do
    strKeyPress = GetKey
Loop Until strKeyPress <> 0

' Show the key the user pressed
Print
Print "Your favorite key is: "; Chr(strKeyPress)

' Wait until someone presses a button before you exit
Print
Print "Press any button to exit"
Sleep

For more information check out the User Input Section.

Last Reviewed by Sancho3 on February 06, 2018


Beginners Guide to Types as Objects (Part 1)

Introduction

This tutorial is aimed at people who want to know more about the new features added to Type, commonly being referred to as 'types as objects', and 'that OOP stuff'. It aims to walk you through these new features, so is aimed at people who don't really understand it yet, but want to learn. A Type in FreeBASIC is an aggregate data type, like a struct in C, or a record in Pascal. Here's just a short sample of typical Type usage.

Type person_info
  first_name As String
  last_name As String
  house_number As Integer
  street_name As String
  town As String
End Type

In this usage it's used as a kind of container for related data; in this example it could be as an entry in an address book. With the new features, however, it can be used more like the class in C++, in that it can do much more than contain just simple fields of data. It becomes a way to express an idea of an object, and this makes object oriented programming much simpler. We will now look at these new features.

Property

We'll start by looking at property. When you add a property to a Type, you access it as if it were an ordinary member, but what happens, is instead of just getting or setting a variable as normal, it calls a function instead. Take a look at this example:

Type bar
  Declare Property x() As Integer
  Declare Property x(ByVal n As Integer)
  p_x As Integer
End Type

Property bar.x() As Integer
  Print "bar.x()"
  Property = p_x
End Property

Property bar.x(ByVal n As Integer)
  Print "bar.x(ByVal n As Integer)"
  p_x = n
End Property

'---

Dim foo As bar

foo.x = 5
Print foo.x

We include in our Type some declarations for a Property; they are very similar to ordinary function declarations. The first one declares a getter, the second a setter. The p_x member is just an ordinary Integer member.

Next we write the code for the properties; again, the syntax is very similar to that of normal functions. Note the way we return a value: instead of Function = value, we do Property = value. You can do Return value as well. Also note that you can refer to the member directly as p_x; you can also use the keyword this, for example this.p_x = n; using this isn't usually needed, but it can help in some ambiguous circumstances.

Then follows some testing code; this shows how we can use the property as if it were any ordinary member. When you run the program it will also print to screen to show that the property get/set code has been called.

Now this code is fairly trivial, but as you get used to the idea you'll see it can be put to some good uses. Imagine as an example you are writing a GUI, and the TYPE represents a button on the screen, you could do button.text = "Hello World!", and make the property code update the screen to show the changes. Or maybe you are using the Type to maintain some kind of list; you could do list.size += 10 and then put some code in your property to make the list larger.

Constructor/Destructor

Constructors are functions that are called when the Type gets created - when you use Dim, for example. A Destructor is a function that gets called when the Type goes out of scope; this could be when the program ends, for a Type in the main code, or when a function ends, for a local Type. Look at the following example, expanded from the last.

Type bar
  Declare Constructor()
  Declare Destructor()
  Declare Property x() As Integer
  Declare Property x(ByVal n As Integer)
  p_x As Integer Ptr
End Type

Constructor bar()
  Print "Constructor bar()"
  p_x = Allocate(SizeOf(Integer))
  *p_x = 10
End Constructor

Destructor bar()
  Print "Destructor bar()"
  Deallocate(p_x)
End Destructor

Property bar.x() As Integer
  Print "bar.x()"
  Property = *p_x
End Property

Property bar.x(ByVal n As Integer)
  Print "bar.x(ByVal n As Integer)"
  *p_x = n
End Property

'---

Dim foo As bar

Print foo.x
foo.x = 5
Print foo.x

Again the syntax is somewhat similar to normal functions. Note that this time I changed p_x to be an Integer ptr. The constructor then Allocates the memory for this when foo is created, and gives it a default value; then it De-Allocates this memory once it is destroyed. So you can use Constructors and Destructors to set things up for you, then clean up once its finished with. Again a trivial example, but bring back the example of some kind of list, and having it set the list up for you, and clean it up when it's finished with can be quite handy.

Methods

You can also have regular Subs and Functions inside your Type; in some terminology, these are referred to as methods. We'll carry on our example:

Type bar
  Declare Constructor()
  Declare Destructor()
  Declare Property x() As Integer
  Declare Property x(ByVal n As Integer)
  Declare Sub Mul5()
  Declare Function Addr() As Integer Ptr
  p_x As Integer Ptr
End Type

Constructor bar()
  Print "Constructor bar()"
  p_x = Allocate(SizeOf(Integer))
  *p_x = 10
End Constructor

Destructor bar()
  Print "Destructor bar()"
  Deallocate(p_x)
End Destructor

Property bar.x() As Integer
  Print "bar.x()"
  Property = *p_x
End Property

Property bar.x(ByVal n As Integer)
  Print "bar.x(ByVal n As Integer)"
  *p_x = n
End Property

Sub bar.mul5()
  *p_x *= 5
End Sub

Function bar.Addr() As Integer Ptr
  Function = p_x
End Function

'---

Dim foo As bar

Print foo.x
foo.x = 5
Print foo.x
foo.mul5()
Print foo.x
Print "address p_x points to", foo.Addr()

So this time we added a Sub, that multiplies the integer pointed to by p_x by five, and a function that gets the memory address that the pointer holds.

Private/Public

By default all of the members of the bar type are public; that means that we can read/write or call them. However, sometimes you might want to make them private. Take for example our member p_x; we can currently do Print *foo.p_x, and it will allow us to print the value it points to. We might want to make it private, so that only the members of the bar type (the constructor, destructor, property, and methods) can access it. That way we can make sure we only deal with p_x by the ways we choose. If for example we did 'DeAllocate(foo.p_x)' in our main code, then when the destructor runs, it would try to free it again, known as a 'double free'. Change the Type declaration as follows:

Type bar
  Declare Constructor()
  Declare Destructor()
  Declare Property x() As Integer
  Declare Property x(ByVal n As Integer)
  Declare Sub Mul5()
  Declare Function Addr() As Integer Ptr
Private:
  p_x As Integer Ptr
End Type

Now try adding Print *foo.p_x to the main code and compile it. You'll get a message from fbc "error 173: Illegal member access, found 'p_x' in 'Print *foo.p_x'", showing that indeed the compiler is now enforcing the fact we made p_x private. When you use private: or public:, any members following that statement follow the rule. Here's a rather pointless example just to show the syntax:

Type bar
Private:
  a As Integer
  b As Integer
Public:
  c As Integer
  d As Integer
Private:
  e As Integer
End Type

In the above type, the members a, b, and e are private; c and d are public.

Operator overloading

Operator overloading is a way of telling the compiler what to do in the case where we want to perform some kind of operation involving our Type. Take this example:

Type bar
  n As Integer
End Type

Dim As bar x, y, z

z = x + y

Now normally the compiler will throw an error when it sees this, as it has no idea how to add together two Types, but we can define what we want to happen. Here's how:

Type bar
  n As Integer
End Type

Operator +(ByRef lhs As bar, ByRef rhs As bar) As bar
  Operator = Type(lhs.n + rhs.n)
End Operator

Dim As bar x, y, z

x.n = 5
y.n = 10
z = x + y
Print z.n

In this code, I use lhs and rhs to refer to the left and right hand side operands of the operator. Note also the expression type(lhs.n + rhs.n); this builds the Type that will be returned. If you had a type like:

Type bar
  x As Integer
  y As Integer
  z As Integer
End Type

Then you would build it like type(xpart, ypart, zpart).

Most or all operators can be overloaded, and most of them are binary ops, meaning they have two operands like the + example above. Some are unary ops having only a right hand side, like Not and unary minus. They would be done like 'Operator Not(ByRef rhs As bar) As bar'.

There are some special cases where they have to be declared inside the Type; these are the assignment operators and casts.

Assignment operators are things like += -= mod= etc, and also Let. Let is used when you do an assignment like:

Dim As bar foo
Dim As Integer x
foo = x

And casts are kind of the reverse; they are used when you cast to another datatype like:

Dim As bar foo
Dim As Integer x
x = foo

Heres a short example using Let and Cast:

Type bar
  n As Integer
  Declare Operator Let(ByRef rhs As Integer)
  Declare Operator Let(ByRef rhs As String)
  Declare Operator Cast() As String
End Type

Operator bar.Let(ByRef rhs As Integer)
  n = rhs
End Operator

Operator bar.Let(ByRef rhs As String)
  n = Val(rhs)
End Operator

Operator bar.Cast() As String
  Operator = Str(n)
End Operator

Operator +(ByRef lhs As bar, ByRef rhs As bar) As bar
  Operator = Type(lhs.n + rhs.n)
End Operator

Dim As bar x, y, z

x = 5
y = "10"
z = x + y
Print z

You need to have separate lets and casts for each data type you want to support. The operators that need declaring within the type are known as non-static, and the ones that don't are known as global. There is a technical reason for this; the non-static ones need to know which instance (in the technical jargon, in our example above, we would say that x is an instance of bar) of the Type they are referring to, and this is accomplished by a hidden 'this' reference. This hidden 'this' reference is how the other members like operators and methods know which instance of the Type the call refers to. Most operators can be overloaded; here's a list of the ones that currently can be:

Specific ops:
cast, @, [], new, new[], delete, delete[], for, step, next
Assignment ops:
let, +=, -=, *=, &=, /=, \=, mod=, shl=, shr=, and=, or=, xor=, imp=, eqv=, ^=
Unary ops:
-, not, *, ->, abs, sgn, fix, frac, int, exp, log, sin, asin, cos, acos, tan, atn, len
Binary ops:
+, -, *, &, /, \, mod, shl, shr, and, or, xor, imp, eqv, ^, =, <>, <, >, <=, >=

Overloaded Constructors/Methods

As with normal functions, our Type's constructor and methods can be overloaded. For constructors, this provides a way to specify details on how the instance should be constructed. Here's a short example:

Type bar
  Declare Constructor()
  Declare Constructor(ByVal initial_val As Integer)
  x As Integer
End Type

Constructor bar()
  x = 10
End Constructor

Constructor bar(ByVal initial_val As Integer)
  x = initial_val
End Constructor

Dim foo As bar
Print foo.x

Dim baz As bar = bar(25)
Print baz.x

The first Constructor, that had no arguments, is known as the default constructor. This sets up foo.x to an initial value of 10. However, we have also specified another constructor that will accept an initial value. Note the way we ask for this to be called Dim baz As bar = bar(25). You can also leave out the default constructor, and then you will always have to specify the initial value using the constructor that takes an argument. You can't have an overloaded destructor, because there's no way to manually choose which one would be called.

Overloaded methods are very similar:

Type bar
  Declare Sub foo()
  Declare Sub foo(ByVal some_value As Integer)
  Declare Sub foo(ByRef some_value As String, ByVal some_other As Integer)
  x As Integer
End Type

They work just they same as normal overloaded functions.

Closing

I hope this tutorial has been useful for you, although there are still a few things left to learn; if you've got this far, it shouldn't be too hard for you to pick them up. There is some more information available in the wiki and on the forums, and also in part 2 of this tutorial, available here - Beginners Guide to Types as Objects (Part 2)

More reading

  • Property
  • Constructor
  • Destructor
  • Operator
  • This
  • Type
  • Types as Objects
  • Public:
  • Private:
  • Protected:

Last Reviewed by Sancho3 on February 06, 2018


Beginners Guide to Types as Objects (Part 2)

Introduction.

Welcome to the second part of the tutorial, In this part I assume that you have read through Part 1, tried the examples, and experimented with some tests of your own. I'll now cover some topics that I didn't include in Part 1.

Indexed property.

An indexed property is a property that behaves like an array, except that like in the case of a regular property, a function gets called when you access it. I'll start with a very short example just to show the syntax.

Type foo
  Declare Property bar(ByVal index As Integer, ByVal value As Integer)
  Declare Property bar(ByVal index As Integer) As Integer
  dummy As Integer
End Type

Property foo.bar(ByVal index As Integer, ByVal value As Integer)
  Print "Property set, index=" & index & ", value=" & value
End Property

Property foo.bar(ByVal index As Integer) As Integer
  Print "Property get, index=" & index
  Property = 0
End Property

Dim baz As foo

baz.bar(0) = 42
Print baz.bar(0)

As you can see, the declaration for our indexed property is very similar to a regular one, except this time we add an argument for the index. I include a dummy integer member, because a type must have at least one data member. As you can see, the property is then used with (0), to denote we want to get/set the zeroth index, just the same as we would for an ordinary array. Now I'll show you a slightly more useful example, and I will describe it:

Type foo
  Declare Constructor(ByVal num_elements As Integer)
  Declare Destructor()
  Declare Property bar(ByVal index As Integer, ByVal value As Integer)
  Declare Property bar(ByVal index As Integer) As Integer
Private:
  x As Integer Ptr
  size As Integer
End Type

Constructor foo(ByVal num_elements As Integer)
  x = CAllocate(num_elements * SizeOf(Integer))
  size = num_elements
End Constructor

Destructor foo()
  Deallocate(x)
End Destructor

Property foo.bar(ByVal index As Integer, ByVal value As Integer)
  If (index >= 0) And (index < size) Then
    x[index] = value
  Else
    Error 6
  End If
End Property

Property foo.bar(ByVal index As Integer) As Integer
  If (index >= 0) And (index < size) Then
    Property = x[index]
  Else
    Error 6
  End If
End Property

Dim baz As foo = foo(10)

baz.bar(1) = 42
Print baz.bar(1)

This time, I've added a constructor and destructor, which will allocate and deallocate a dynamic memory array, x, with the number of elements specified in the constructor. Then when the property functions are invoked, I check if the index is within the bounds of the array, if it is then I perform the requested get or set. If the index specified is out of bounds, then 'Error 6' occurs, which is a way to abort the program with FB's 'out of bounds error', you could replace this with your own error handling routines. Try this out, by changing the code 'baz.bar(1) = 42' to 'baz.bar(10) = 42', and you'll see it in action, as we specified only 10 elements (index 0-9)

Copy constructor.

A copy constructor is a special type of constructor, that is used to make a copy from an existing object. When you write code like this:

Type foo
...
End Type

Dim As foo a
Dim As foo b = a

What happens is FreeBASIC automatically generates hidden code to construct b, by making a copy of a, this is the default copy constructor, and simply copies the data fields (members) across. We can define our own copy constructor, here's just a brief snippet to show how we declare it.

Type foo
  Declare Constructor(ByRef obj As foo)
  ...
End Type

This will come in very useful for a reason I will now explain.

Deep/Shallow copy.

In that previous example, where we did the code 'Dim As foo b = a', that was what is known a shallow copy, it just simply copied the data fields across, however sometimes this is not desirable, imagine that one of the members is a pointer, what will happen is that the address that pointer points to will be copied across, so both objects will point to the same memory. An example of this follows:

Type foo
  x As Integer Ptr
End Type

Dim As foo a

a.x = Allocate(SizeOf(Integer))
*a.x = 42

Dim As foo b = a

Print *a.x, *b.x

*a.x = 420

Print *a.x, *b.x

Deallocate(a.x)

As you see, because they both point to the same memory, changing one affects the other. As explained in the previous section on the copy constructor, FreeBASIC creates the code to do shallow copies by default. This is also true if we do an assignment like:

Dim As foo a, b

b = a

In this case also, FreeBASIC creates a default assignment operator (Let) to perform a shallow copy. In order to do deep copies, we need to define a copy constructor, and an assignment operator, that is overloaded to accept our type. Here's an example using them.

Type foo
  Declare Constructor()
  Declare Constructor(ByRef obj As foo)
  Declare Destructor()
  Declare Operator Let(ByRef obj As foo)
  x As Integer Ptr
End Type

Constructor foo()
  Print "Default ctor"
  x = CAllocate(SizeOf(Integer))
End Constructor

Constructor foo(ByRef obj As foo)
  Print "Copy ctor"
  x = CAllocate(SizeOf(Integer))
  *x = *obj.x
End Constructor

Destructor foo()
  Print "dtor"
  Deallocate(x)
End Destructor

Operator foo.Let(ByRef obj As foo)
  Print "Let"
  *x = *obj.x
End Operator

Dim As foo a

*a.x = 42

Dim As foo b = a 'Uses the copy constructor

Print *a.x, *b.x

*a.x = 420

Print *a.x, *b.x

As you can see, the copy constructor gets called on the line 'Dim As foo b = a' and this time, we allocate some memory, and copy the data in the new copy constructor, so that we can adjust x in one object without it affecting the other. If we change the main code as follows:

Dim As foo a, b

*a.x = 42
b = a    'The assignment operator (Let) gets used this time.

Print *a.x, *b.x

*a.x = 420

Print *a.x, *b.x

Then this time the assignment operator is used. Note that in the assignment operator code, we don't need to allocate any memory because it has already been allocated in the default constructor, we just need to copy the data across. The line '*x = *obj.x' performs this copy. If we had something more advanced, like a dynamic memory array, then we would need to reallocate the memory to be the correct size to fit the data being copied. Here's a more advanced version just to show that.

Type foo
  Declare Constructor(ByVal num_elements As Integer)
  Declare Constructor(ByRef obj As foo)
  Declare Destructor()
  Declare Operator Let(ByRef obj As foo)
  x As Integer Ptr
  size As Integer
End Type

Constructor foo(ByVal num_elements As Integer)
  Print "Default ctor"
  x = CAllocate(SizeOf(Integer) * num_elements)
  size = num_elements
End Constructor

Constructor foo(ByRef obj As foo)
  Print "Copy ctor"
  x = CAllocate(SizeOf(Integer) * obj.size)
  size = obj.size
  For i As Integer = 0 To size - 1
    x[i] = obj.x[i]
  Next i
End Constructor

Destructor foo()
  Print "dtor"
  Deallocate(x)
End Destructor

Operator foo.Let(ByRef obj As foo)
  Print "Let"
  x = Reallocate(x, SizeOf(Integer) * obj.size)
  size = obj.size
  For i As Integer = 0 To size - 1
    x[i] = obj.x[i]
  Next i
End Operator

Dim As foo a = foo(5)

a.x[0] = 42
a.x[1] = 420

Dim As foo b = a 'Uses the copy constructor

Print a.x[0], a.x[1], b.x[0], b.x[1]

b.x[0] = 10
b.x[1] = 20

Print a.x[0], a.x[1], b.x[0], b.x[1]

b = a ' Now using the assignment operator

Print a.x[0], a.x[1], b.x[0], b.x[1]

This may seem quite complex at first, it's worth just reading through it again, and experimenting with the examples, it's not too tricky once you're used to it.

Passing objects to functions ByVal

The idea of deep and shallow copies also applies to passing an object to a function by value. When you pass a reference to an object (ByRef), you can modify the object, and these modifications will persist, however you can also pass by value, which will mean you can modify it without the changes persisting outside of the function. When an object is passed by value to a function, a new copy is created, and if that object has a copy constructor, then this is invoked, if it doesn't, then the hidden shallow copy is performed. Once the function ends, the objects destructor is called.

New/Delete

New and delete are special operators for dynamically allocating memory, then destroying it. Because it is used with dynamic memory, it is used with pointers. In all the examples up until now, we just used Dim to create our objects, this will create them on the stack, but by using new we can create them dynamically, which can allow more flexibility, just like using Allocate/DeAllocate with normal memory. Another important thing about new, is that you don't need to check if the pointer is NULL after new, like you would if you did allocate. If new fails, it causes an exception, which will end the program. In later versions of FreeBASIC, it is likely that some kind of try..catch mechanism will be created to allow better exception handling, but as of the time of writing, this is not yet implemented.

There are two different varieties of the new/delete. The first type, creates just a single element or object, for example:

Dim As Integer Ptr foo = New Integer

*foo = 1
Print *foo

Delete foo

This will create a new Integer, then destroy it when we call delete. Remember I used ptr, because it is dynamic memory. For simple data types you can also specify a default value, by placing it in parenthesis after the data type, ie:

Dim As Integer Ptr foo = New Integer(42)

Print *foo

Delete foo

This also works for UDT's with just simple data fields:

Type foo
  x As Integer
  y As Integer
End Type

Dim As foo Ptr bar = New foo(1, 2)

Print bar->x, bar->y

Delete bar

This initialization won't work for more complex types involving constructors/destructors etc., however a useful feature is that when using new/delete with objects, it also calls the constructor and destructor, try the following example:

Type foo
  Declare Constructor()
  Declare Destructor()
  x As Integer
  y As Integer
End Type

Constructor foo()
  Print "ctor"
End Constructor

Destructor foo()
  Print "dtor"
End Destructor

Dim As foo Ptr bar = New foo

Delete bar

You will see that the constructor and destructor for the object are called.

The second type of new/delete is for creating arrays, this time the number of elements is placed after the dataype in square brackets '[]'. When using the array version, you must also use 'delete[]' instead of 'delete', so that FreeBASIC knows you are deleting an array, here is a simple example using the Integer type:

Dim As Integer Ptr foo = New Integer[20]

foo[1] = 1
Print foo[1]

Delete[] foo

This will create a dynamic array, with 20 Integer elements. It should be noted this is different from Allocate, which takes the number of bytes as its argument; using new, you should specify the number of elements. The array method works just the same for objects:

Type foo
  Declare Constructor()
  Declare Destructor()
  x As Integer
  y As Integer
End Type

Constructor foo()
  Print "ctor"
End Constructor

Destructor foo()
  Print "dtor"
End Destructor

Dim As foo Ptr bar = New foo[3]

Delete[] bar

When you run this code, you will see that three constructor/destructor pairs are called, because we created an array of three instances of foo.

You must remember to call Delete, or Delete[] for any memory allocated with New, or you will cause a memory leak, just like the way you must rememeber to call DeAllocate for any memory you allocate with the Allocate function.

Name Mangling

Name mangling, also known as name decoration, is something that happens behind the scenes, at a lower level, and as such is not essential to know about. The reason for name mangling is to resolve problems that are involved with more than one function sharing the same name, which happens when functions are overloaded, or are part of a type. Take for example the overloaded subs shown below:

Sub foo Overload ()

End Sub

Sub foo(ByVal i As Integer)

End Sub

If we didn't have name mangling, then both might be known at a lower level as FOO, which would cause a name clash, so they have to be decorated in order to know which one should be called when they are used. For the first sub, the compiler actually creates a sub called _Z3FOOv, and for the second it creates a sub called _Z3FOOi. The compiler then remembers these, and chooses the appropriate sub to call, depending on how you call it, for example 'foo()' will actually call _Z3FOOv, and 'foo(1)' will actually call _Z3FOOi. We can spot something from this, that the 'v' stands for void (no argument), and 'i' stands for integer. The full details of name mangling are quite complex, and vary between compilers, the Microsoft compilers use a different name mangling scheme to GNU compilers, and other compilers may use different schemes as well. The main thing we need to know, is that FreeBASIC follows the GCC 3.x ABI (Application binary interface), meaning that any overloaded functions, or complex types will only be compatible with other compilers using the same scheme. This is an unfortunate limitation, but it is not really a FreeBASIC problem, it is common of all the compilers that use advanced features, and even if all the compiler authors agreed on a common name mangling scheme, there are still other issues that would cause incompatability.

Implicit this

This again is not necessary to know about mostly, its something that happens behind the scenes at a lower level. When you call a member function of an object, what actually happens is a hidden first parameter is passed, so that the function knows which instance of the object is being refered to. This is also true for the property/constructor/destructor/operator members. If we look at a very simple example:

Type foo
  Declare Sub bar(ByVal n As Integer)
  x As Integer
End Type

Sub foo.bar(ByVal n As Integer)
  x = n
End Sub

Dim baz As foo
baz.bar(5)

What actually happens behind the scenes is something essentially equivalent to this:

Type foo
  x As Integer
End Type

Sub foo_bar(ByRef _this As foo, ByVal n As Integer)
  _this.x = n
End Sub

Dim baz As foo
foo_bar(baz, 5)

This method using an explicit 'this' is often used in languages that do not have facilities to make it easier. OOP is really just a set of concepts, that can be mostly coded in almost any language, some things are more difficult to implement, such as constructors, you would have to explicitly call a 'create', or 'init' function. For some things such as private/public distinction, it is even more difficult or impossible because the compiler does not know to enforce them. The reason for adding OOP features to a language is to hide a lot of this, and add syntactic sugar to make it simpler to do, or more transparent in use, such as the way we can use properties as if they were ordinary data members, rather than functions, which is what they really are.

Hints for debugging/profiling

When using GDB or other debuggers, and the gprof profiling tool, the information shown is in the C++ syntax, and all your variable names and other symbols are shown in upper case, here is just a very short overview to help you understand how these are shown:

Here's an example type:

Type bar
  Declare Constructor()
  Declare Constructor(ByRef obj As bar)
  Declare Constructor(ByVal n As Integer)
  Declare Destructor()
  Declare Operator Cast() As Any Ptr
  Declare Operator Let(ByVal n As Integer)
  Declare Property foo(ByVal n As Integer)
  Declare Property foo() As Integer
  member As Any Ptr
End Type

When using GDB, these will be shown as follows (note in C++ they use :: where we would use . (dot), '::' is known as the scope resolution operator):

BAR::BAR() - The default constructor
BAR::BAR(BAR&) - The copy constructor (& in C++ means a reference, like byref)
BAR::BAR(int) - The constructor taking an integer argument (note there is no special symbol to denote ByVal, as this is the default passing method in C/C++)
BAR::~BAR() - The destructor
BAR::operator void*() - A cast to Any ptr (void is similar to Any, * means pointer)
BAR::operator=(int) - The assignment operator (Let), dentoted by '=', in C/C++ '=' is assignment, '==' is equality testing.
BAR::FOO(int) - Property foo setter, taking an integer argument
BAR::FOO() - Property foo getter

Member sub/functions are shown in the same way as properties, indexed properties are shown the same also, just with the extra argument for the index.

Here is how the FB data types will be shown:

Any ptr - void *
ZString ptr - char *
String - FBSTRING
byte - signed char
ubyte - bool
short - short
ushort - unsigned short
integer - int
uinteger - unsigned int
longint - long long
ulongint - unsigned long long

I hope that helps you get started with understanding how things are displayed in GDB/gprof, a little experimentation will always help.

More reading

http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgOpNew
http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgOpDelete
http://en.wikipedia.org/wiki/Copy_constructor
http://en.wikipedia.org/wiki/Object_copy
http://en.wikipedia.org/wiki/Name_mangling

Last Reviewed by Sancho3 on February 06, 2018


Conditional Compilation And You

Written by aetherFox for QB Express Issue #9

Conditional Compilation is one of those parts of programming that sit in the dusty corners of the knowledge banks of programmers world-over, yet is one of the most ingenious additions to any language. Usually something that was reserved for C programmers, with the power of freeBASIC's new preprocessor, you can now use conditional compilation to help your program.

The preprocessor allows you flexibility in changing the way code is generated through the use of conditional compilation. Take this scenario: you are debugging the code in your program, and you want to add some extra code to output a few variables, but remove them in the final version. The code would be something like this:

    #define DEBUG

    #ifdef DEBUG
        Print "Debug Value"
    #endif 'DEBUG

Note you do not need the comment after the #endif, but is it good practice.

Basically, the above code checks to see whether DEBUG has been defined, and if it has, then the code between the #ifdef...#endif will be executed. While this may seem silly, the uses this has are amazing. If you simply remove one line at the top of your program (#define DEBUG), then all the 'debug code' that you've added won't be sent to the compiler -- the preprocessor removes it, reducing the bloat of the final executable.

    'Turn on debugging
    #define DEBUG

    'Turn off debugging
    #undef DEBUG

The #undef directive is a way of 'undefining' something, in this case DEBUG. While it is strictly not needed (just commenting out the line '#define DEBUG' is enough), it makes the code much clearer, and has other uses:

    #ifndef DEBUG
        Print "Production Version"
    #endif 'DEBUG

While not the most useful example, this demonstrates the use of another directive: #ifndef. This directive will cause the code to be compiled if the symbol is not defined.

Much like a normal programming language, the sense of the conditional can be reversed using a variant of else, #else:

    #ifdef DEBUG
        Print "Test Version"
    #else
        Print "Production Version"
    #endif 'DEBUG

Of course, there are many applications to this. Who says you need to do this on debug code only? You could actually check the effect of a new piece of code, or some test routines by simply defining a name like TESTCODE and using the preprocessor directives to encompass your code for conditional compilation:

    #define TESTCODE

    #ifdef TESTCODE
        BulletRoutine()
        TestFireRoutine()
    #endif

The scope of this tutorial is a limited one, but this method is used by professionals. It makes life easy when programming.

Avinash 'aetherFox' Vora
avinashvora [at] gmail [dot] com.

Last reviewed by ""sancho3" on February 07, 2018 Notes: Removed dead links


Const Qualifiers and You
Note: As with all things regarding scope, Const qualifiers may be a bit difficult to understand. You should have a thorough understanding of variable scope before attempting to understand Const qualfiers.

Also note my cliche title, which I chose because of it's clicheness.

What the heck are Const qualifiers? they're a standard part of C++ and they exist in FreeBASIC too. Const qualifiers are yet another form of protection - they allow some "variables" to act like constants to certain parts of your program, in other words some parts of the program are allowed to access (read) them but not modify them. Just another kind of type safety, really, but an extremely useful one. In particular, they are very useful in OO situations, but you can probably benefit from them to some degree even if you aren't interested in OOP.

The Const qualifier in FreeBASIC is essentially an extension to data type declarations, and they may be used with Dim, UDT members, and procedure parameters. Generally you put it right after the "As" part of the variables's data type declaration:

Dim As Const Integer my_const_int = 5

(By the way, throughout this tutorial I use only Integers and Integer Ptrs as examples - however, Const qualifiers should work the same way with all other variable types, including Types, Enums, and anything else that declares something. If for any reason it doesn't, it's probably a bug and you should report it.)

Note in this case we are allowed to change it once - when we create it. But after that, you may not change it any more. In fact, you must initialize it - the compiler will give an error if you don't (interestingly, you are allowed to set it equal to "Any", in which the contents are not guaranteed and could be anything). But you may not do anything that modifies it after that. It will actually give you an error if, for example, you try to do something like this:

my_const_int = 3

Yet, since this doesn't change the variable any, you can do

Print my_const_int

Now this is all very good, but it doesn't seem much different from the normal usage of Const. That is, the following two lines so far seem to mean, for all practical purposes, the same thing:

Dim As Const Integer my_const_int = 5
Const my_int As Integer = 5

Do they? Not quite. You see, the Const qualifier allows you to create consts that act as variables except that they can't be modified. That means you can put them inside Types and other places. What's more, you can put them inside Sub/Function declarations - and this is a very key reason for their existence:

Sub my_sub (some_num As Integer)
End Sub

Normally functions are allowed to modify the variables you send to them. Of course, whether they modify the original variable or just a local copy of the variable depends on whether you use ByVal or ByRef (and of course pointers is a whole different things altogether), but they normally are allowed to modify a variable. This may be undesirable, for whatever reason, and the Const qualifier exists to prevent that. In the function given above, some_num can be modified by the function. Normally it would only be a local copy that is modified, which is fine, since it won't affect the original Const Integer, but what if we declare the function like this?

Sub my_sub (ByRef some_num As Integer)
End Sub

Now my_sub has direct access to whatever variable you pass to it, and for that reason you are not allowed to do this sort of thing

my_sub(my_const_int)

Why? Simply because the function may modify the variable. We don't know for sure that it will, of course, but it might, so we can't do that. In fact, the error you'll get if you try to compile that is "Invalid assignment/conversion." It's almost as if the Const Integer is a different variable type, but only when it's ByRef. In that case, it would act like trying to pass a string to an integer argument (or vice-versa). Yet if it's not passed ByRef, we don't have a problem, since there's no way the function can possibly modify the variable!

And of course, if we did something like this:

Sub my_sub (ByRef some_num As Const Integer)
End Sub

Then it compiles just fine, but if you try to do the following within the function, you get an error:

some_num = 3

Why? Once again, the original variable has been passed ByRef to the sub. It's now in local scope, but because it's ByRef, any modifications to the variable would modify the original, which cannot be done. Once again, it's entirely possible to create a copy of the variable and modify it all you want:

Dim As Integer copy_of_some_num = some_num
copy_of_some_num = 3

But you can't modify some_num itself!

Now we come to pointers. What about them? For pointers it's a bit more complicated; it's possible to declare the pointer itself as Const, OR what the pointer points to - or even BOTH! So all of the following are valid:

Declare Sub my_sub_a (ByRef ptr_A As Const Byte Ptr)

Declare Sub my_sub_b (ByRef ptr_B As Byte Const Ptr)

Declare Sub my_sub_c (ByRef ptr_C As Const Byte Const Ptr)

The first one makes it so you can change the pointer itself all you want, but not the data that the pointer points to (even if you change *what* the pointer points to). The second allows you to change what the pointer points to, but you can't make it point to anything else. The third won't let you change what the pointer points to OR the pointer itself! In all cases you can make a copy of the pointer - but it must be a Const Integer Ptr or a Const Integer Const Ptr since otherwise you would be able to change the contents of whatever the original pointer points to! This is great protection against anything being modified!

In case the behaviour of the Const qualifier seems a bit strange to you, I'll explain exactly how it decides what's safe to allow and what isn't. It can actually be summed up pretty quickly: The Const qualifier aims to protect the original data. It doesn't care if you make a copy of the data, or change that copy, it just doesn't want you to be able to change the original data. Remembering this will help you a great deal. Of course, it needs to know what the original data is, which is why when there's pointers involved there are so many different places to put the Const qualifier (and you can even put it in twice - or more, depending on how many pointers there are!) So long as you remember what the Const qualifier is for, you'll never have any difficulty figuring out where to put it - or even if you need it at all (or if you need to not use it).

You can also use the Const qualifier in UDTs. In fact, it's actually a very important thing to OOP (in a similar fashion to Namespaces, which while not a direct part of OOP nevertheless are very much related) - but even if you don't use OOP you can still use Const qualifiers in your Types. I don't even really need to show you an example, as it's pretty obvious by now how it works, but here's an example for you:

Type my_type
  As Const Integer t_int= 5
End Type

Dim As my_type t

t.t_int = 3

And obviously this won't compile, since the member t_int is Const. Furthermore, you can also declare the variable of that type (in this case, t) with the Const qualifier. The following will not compile either, since ALL members of t are Const:

Type my_type
  As Integer t_int= 5
End Type

Dim As Const my_type t

t.t_int = 3

As for the OOP side of things (and if you aren't interested in OOP you can skip this part) - you may be wondering about methods. Methods implicitly pass the object ByRef as this when called. Is there a way to create constant objects? Of course! We've already seen that. But some object methods will modify the object, and some won't. Is there a distinction? The answer is yes. As of November 23, 2007, we now have Const procs. That means you can do this:

Type my_object
  Public:
    Declare Sub modifier_sub ()

    'Subs that do not modify the object are declared Const...
    Declare Const Sub non_modifier_sub ()
  Private:
    some_num As Integer = 3
End Type

Sub my_object.modifier_sub ()
  this.some_num = 3
End Sub

Sub my_object.non_modifier_sub()
  Print this.some_num
End Sub

'Note that only Const objects must be initialized (though in this case the non-Const object will also be),
'just like variables.  Thus, you must either have a Constructor for the object, or else you must give all variables
'default initial values (as I did here), in which case the compiler makes a default constructor for you.
Dim As Const my_object t = my_object
Dim As my_object u

'Both of these will compile:
t.non_modifier_sub()
u.non_modifier_sub()

'...but the first of these will not compile, since non-Const methods of Const objects may not be called!
t.modifier_sub()
u.modifier_sub()

'Sleep so we can see the results
Sleep

Once again, the way this works is based on the simple rule. Since the implicitly passed copy of this is passed ByRef, any method is normally able to modify the contents of the object - and if the object is declared As Const, that's not supposed to happen! Thus, there are essentially two kinds of method. The two kinds are given names in the C++ documentation page (listed below in the references): there are mutators and inspectors. Mutators may modify objects, but inspectors do not. Thus, for objects declared As Const, only the inspector methods for those objects may be used - while all methods may be called for non-Const objects. The inspector methods are, of course, the ones declared as Const methods. Thus, for Const objects only their Const methods may be used.

This is all very good, but some of you may be asking - Why do I need this? Well, a direct answer would be out of the *scope* (heh) of this tutorial, so I'll counter - why do we need scope at all? The reason for Const qualifiers (and the future Const methods) is the same as the reason for scope within procedures and modules, and the same reason for hiding of variables in objects: because we want to be certain that something won't unexpectedly change in the middle of the program, when we least expect it. Sometimes we want things to change, and that's when we don't use the Const qualifier. But when you want something to stay what it is, you use the Const qualifier, and you can be certain it will not change (and the compiler won't compile the code if there is danger of it happening!) This is the definition of Const, how it works, and it's the reason you use it! And in general, it's the reason you use any scoping control or data hiding.

Some final notes
If you use Const qualifiers, remember that it is a relatively new feature. There is very little documentation to tell us what is "wrong" or "right", so generally it will take some experimenting. If you feel that it does something it shouldn't do (or doesn't do something it should), by all means report it on the forum! If it is seen as a problem by anyone else, submit a bug report. In general, however, it should work exactly as I've said and all the examples given should do as I say they will (compile if I say they will, not compile if I say they won't). One very important thing to remember, of course, is that they aren't in the latest official release - you must have the latest SVN release for them to work (if the compiler gives an error about one of the examples given here that I told you will compile, then you'll know you need a newer version).

If you have any other difficulties with Const qualifiers, remember that even though there's no documentation for them there are plenty of people on the forum who know about and understand them, and can help you with any questions you may have.

If you still don't understand Const qualifiers, you probably are a newbie who doesn't know much about scope yet anyways - and that's fine, you'll learn as you go. Eventually some decent documentation for this feature will be created, but until then this is all you have. Bear in mind: if you don't understand how they work, you probably won't need them. I for one have written fine programs long before they were around, and I'll probably continue to do so without using them anywhere they aren't needed. There are specific instances when they're useful, and if you understand those instances then you may as well use them when those instances arise. But if you don't understand, that's fine!

Finally, here are some links that should be helpful. The first is a C++ documentation page about Const qualifiers in C++ - of course, it only makes sense if you understand C++, and they also talk about things we don't have yet (i.e., Const methods). Nevertheless it's a fine place to start if you know any C++, so check it out if you like. There is also a link to a forum topic in which I asked about FreeBASIC development (and learned about Const qualifiers), and a link to the original SourceForge Feature Request page in which Const qualifiers were originally requested as a feature:

https://isocpp.org/wiki/faq/const-correctness
http://www.freebasic.net/forum/viewtopic.php?t=9975&postdays=0&postorder=asc&start=0
http://sourceforge.net/tracker/index.php?func=detail&aid=1480621&group_id=122342&atid=693199


Dynamic Arrays in Types
Written by rdc

Introduction

A dynamic array in a type definition is a very useful feature, but FreeBASIC did not support it before. Or rather, it did not support it directly before. However, you could create dynamic arrays by using pointers and the associated memory functions.

An array is simply a contiguous block of memory that holds a certain data type. Arrays in FreeBASIC use an array descriptor to describe the data contained within the array, and you can use this same technique to build a dynamic array within a type. The two elements you need within your type-def are a pointer to a particular data type, and a size indicator.

You can then use the ptr field to allocate a block of memory to the needed size, and save that size in the size indicator field. The size field is used to tell you how many elements are currently in the array. Once the array has been initialized, you can then use pointer indexing to access each element in the array.

Getting the Point(er) in Code

The following program illustrates the steps in creating, initializing and resizing a dynamic type-def array.
'Define type:
'size is current size of array
'darray will contain array data
Type DType
    size As Integer
    darray As Integer Ptr
End Type

'Create an instance of type
Dim myType As DType
Dim As Integer i, tmp

'Create enough space for elements
myType.darray = CAllocate(5, SizeOf(Integer))
'Set the length of the array
'in the array size indicator
myType.size = 5

'Load data into array
For i = 0 To myType.Size - 1
    myType.darray[i] = i
Next

'Print data
For i = 0 To myType.Size - 1
    Print "darray[";i;" ]:";myType.darray[i]
Next
Print "Press any key..."
Sleep
Print

'Save the current array size
tmp = myType.size
'Now resize the array
'myType.darray = Reallocate(myType.darray, 10)
myType.darray = Reallocate(myType.darray, 10 * SizeOf(Integer)) ' Editors Note: above code line changed to this
'Set the length indicator
myType.size = 10

'Load in data into new allocation
For i = tmp To myType.Size - 1
    myType.darray[i] = i
Next

'Print out contents
For i = 0 To myType.Size - 1
    Print "darray[";i;" ]:";myType.darray[i]
Next
Print "Press any key..."
Sleep

'Free allocated space
Deallocate myType.darray

End

How it Works

The first step is, of course, to define the type-def:
Type DType
    size As Integer
    darray As Integer Ptr
End Type

Since this is just an example there are only two elements within the type, a size indicator and the array pointer. Notice that the array pointer is defined as an Integer ptr. When you define a pointer to a particular type, you are creating a "typed" pointer. The compiler can use this type information to check to make sure the values being placed into the array are valid, and will also use this information for pointer arithmetic.

The next step is to define the working variables.
Dim myType As DType
Dim As Integer i, tmp

Here an instance of the type is created, as well as some working variables that are used in the following code. WARNING: You must initialize the array pointer before you can use it; using an uninitialized ptr can cause program crashes, system lockups and all sorts of bad things.
myType.darray = CAllocate(5, SizeOf(Integer))
myType.size = 5

These two lines of code initialize the array pointer to hold 5 integers. Callocate is used to allocate the memory segment, since Callocate will initialize the segment to zeros.

The size field stores the current length of the array. Now, of course, you could calculate the size of the array by simply dividing the number of bytes in the allocation by the size of an integer, but using a size indicator within the type is much cleaner and saves you a calculation in your program.

For i = 0 To myType.Size - 1
    myType.darray[i] = i
Next

This section of code loads the array with some values. You can see why saving the size of the array simplifies the coding process. Since the array is a typed pointer, you can access the array using the pointer indexing method, which is almost like accessing a predefined array.

For i = 0 To myType.Size - 1
    Print "darray[";i;" ]:";myType.darray[i]
Next

This section simply prints out the values using the same method that was used to load the array.

Of course, this should be a dynamic array, so you should be able to resize the array, and this is exactly what the next section of code will do.
tmp = myType.size
'myType.darray = Reallocate(myType.darray, 10)
myType.darray = Reallocate(myType.darray, 10 * SizeOf(Integer)) ' Editors Note: above code line changed to this

myType.size = 10

The first line of code saves the current size of the array so that the new memory segment can be initialized while not overwriting any existing data. You will see this in a moment.

The second line uses the Reallocate function to resize the memory segment, that is, resize the array. In this case, the array is being made larger; you could of course make the array smaller. If you were to make the array smaller, any data not in the new segment would be lost, as you would expect.

The last line of code above saves the new array size in the size indicator.

For i = tmp To myType.Size - 1
    myType.darray[i] = i
Next

Here, you can see why the old array size was saved. In the For statement, the initialization procedure iterates through the newly added indexes, storing data within the memory segment. This is like using the Redim Preserve statement on a normal array.

For i = 0 To myType.Size - 1
    Print "darray[";i;" ]:";myType.darray[i]
Next

This code section simply prints out the new values.

Deallocate myType.darray

This is vitally important. You should always deallocate any allocated memory that you have created in your program to prevent memory leaks.

When you run the program you should see the following output:
darray[ 0 ]: 0
darray[ 1 ]: 1
darray[ 2 ]: 2
darray[ 3 ]: 3
darray[ 4 ]: 4
Press Any key...

darray[ 0 ]: 0
darray[ 1 ]: 1
darray[ 2 ]: 2
darray[ 3 ]: 3
darray[ 4 ]: 4
darray[ 5 ]: 5
darray[ 6 ]: 6
darray[ 7 ]: 7
darray[ 8 ]: 8
darray[ 9 ]: 9
Press Any key...

The first print out shows the original array. The second print out shows the newly resized array.

Dynamic arrays fields as non-static members are now supported inside UDT

Previous example but transposed, by using a dynamic array field as non-static member inside the UDT (feature now supported):
'Define type:
'darray will contain array data
Type DType
    darray(Any) As Integer
End Type

'Create an instance of type
Dim myType As DType
Dim As Integer i, tmp

'Create enough space for elements
ReDim myType.darray(4)

'Load data into array
For i = 0 To UBound(myType.darray)
    myType.darray(i) = i
Next

'Print data
For i = 0 To UBound(myType.darray)
    Print "darray(";i;" ):"; myType.darray(i)
Next
Print "Press any key..."
Sleep
Print

'Save the current array upper bound
tmp = UBound(myType.darray)
'Now resize the array
ReDim Preserve myType.darray(10)

'Load in data into new allocation
For i = tmp + 1 To UBound(myType.darray)
    myType.darray(i) = i
Next

'Print out contents
For i = 0 To UBound(myType.darray)
    Print "darray(";i;" ):";myType.darray(i)
Next
Print "Press any key..."

Sleep

Last reviewed by sancho3 on February 08, 2018 Notes: Error in code fixed and noted


Dynamic Arrays

Hello, this page explains the proper use of dynamic arrays in FreeBASIC. While it isn't a very long tutorial, many people have had some troubles with this and I felt it was worth putting into the "Getting Started" tutorial page.

Arrays are neat; they can be used and resized throughout a program, with little difficulties. Firstly, we should discuss both ways a dynamic array can be created. In the same code, I will explain how to redimension a Dynamic Shared Array within a sub or function. Read the comments within the code to understand it better.

Declare Sub mySub ()

' as of 0.17, OPTION DYNAMIC and '$DYNAMIC are unecessary. you must define an array to be dynamic each time
' as you can see, both following ways are successful at creating a dynamic array
Dim Shared myArray1() As UByte
ReDim Shared myArray2(0) As UByte

mySub

' because we shared the arrays, they are accessable from anywhere within the module
Print myArray1(5) ' will print 2
Print myArray2(6) ' will print 3

Sub mySub ()
    ' do NOT use "redim shared" within a sub or function! even if it is shared, you must omit the word "shared" for it to work
    ReDim myArray1(0 To 9) As UByte
    ReDim myArray2(0 To 9) As UByte
    myArray1(5) = 2
    myArray2(6) = 3
End Sub

Now, you may be wondering how you can redimension an array while using the PRESERVE keyword. Normally, you simply add PRESERVE as the syntax for REDIM will state. Yet in fact, this only works if the first array dimension is the only one changing! For example, the following program would not work properly:

' declare the dynamic array the cleaner way
ReDim Shared myArray(0 To 9, 0 To 9) As UByte
Dim As UByte x, y, i

' fill the array with values
For y = 0 To 9
    For x = 0 To 9
        i += 1
        myArray(x, y) = i
    Next x
Next y

' proves the values are good originally:
For y = 0 To 9
    For x = 0 To 9
        Print Using "##,"; myArray(x, y);
    Next x
    Print
Next y
Print
Print "Press a key..."
Sleep
Cls

' redimension the arrays
ReDim Preserve myArray(0 To 18, 0 To 12) As UByte

' the values have not been preserved properly!
For y = 0 To 9
    For x = 0 To 9
        Print Using "##,"; myArray(x, y);
    Next x
    Print
Next y

Sleep
End

Try it out! You can see that it does not work properly. This is because only the first dimension in an array may change sizes, while the rest remain the same size, in order for PRESERVE to work properly.

There is a workaround, which I will post later, after I edit it in order to make sense to any program, not just mine, and make some revisions so it does not go out of bounds. For the moment, get creative ;)

Last Reviewed by Sancho3 on February 06, 2018


FBgfx Image and Font Buffers
Creating and understanding your FBgfx image and font buffers

The FBgfx Image Buffer
Creating Buffers
Buffer Format
Getting Pixels
The FBgfx Font Header
Header Details
Creating a Font Buffer
Assigning Font Characters
Tips & Tricks
Coloring your Custom Fonts
ScrPtr vs ImgBuf

The FBgfx Image Buffer

FBgfx has a new data type in .17 and above. This type is called IMAGE. You can use it by including the FBgfx Header in your program (#include "fbgfx.bi") and then accessing the namespace for FBgfx, via FB.IMAGE. When we create buffers in this tutorial, we're going to be using the fb.Image Ptr type. A pointer, because it's dynamic memory which we can resize.

To use an image in the FBgfx Library, you have to create it via image buffer. Your buffer is an area of memory allocated (created, made available) for your image. You have to deallocate (free, make available to other programs) the buffer when you are done using it at the end of your program. FBgfx has its own internal pixel format, as well as an image header at the beginning of every buffer created. The image header contains information about your image. Things like its width, height, bit depth, etc., while the pixel Buffer contains the actual colors for each individual pixel in RGB (red, blue, green) format.

Creating Buffers

The size of the buffer you create will vary depending on screen depth. Your bytes-per-pixel are the number of bytes needed to store individual pixels. Thus, a 32-bit pixel depth screen will need 4 bytes per pixel (8 bits in a byte). You don't need to worry about this, however, as using the fb.Image Ptr setup to create your buffer makes it very easy to get the information we need from our buffers. You only need to know this information to understand how much size a buffer may take up total, for memory usage information.

Actually creating the buffer is very simple. It's just a simple creation of an fb.Image Ptr, and a call to ImageCreate (Example1.bas):

#include "fbgfx.bi"

  '' Our image width/height
Const ImgW = 64
Const ImgH = 64

  '' Screens have to be created before a call to imagecreate
ScreenRes 640, 480, 32

  '' Create our buffer
Dim As FB.Image Ptr myBuf = ImageCreate(ImgW, ImgH)

  '' Print the address of our buffer.
Print "Buffer created at: " & myBuf
Sleep

  '' Destroy our buffer.  Always DESTROY buffers you CREATE
ImageDestroy( myBuf )
Print "Our buffer was destroyed."
Sleep

Code Dissection

#include "fbgfx.bi"

This includes the header file which contains the definition for the fb.Image type.

  '' Our image width/height
Const ImgW = 64
Const ImgH = 64

This creates constants which will be used to decide the size of our image. FBgfx doesn't know about these. We'll have to pass them to ImageCreate when we use it.

  '' Screens have to be created before a call to imagecreate
ScreenRes 640, 480, 32

This creates our FBgfx screen. ImageCreate needs to know our bit depth beforehand. However, FBgfx's ImageCreate now has an extra parameter allowing you to set the depth yourself.

  '' Create our buffer
Dim As FB.Image Ptr myBuf = ImageCreate(ImgW, ImgH)

This first of all creates a pointer that is of the fb.Image type. It's just a location of memory. We haven't filled it with anything yet. In fact, right now it equals zero, and could not be used. That's considered to be null.

The ImageCreate call returns the address of an area in memory of a newly created fb.Image which we initialize our pointer with. The size of this buffer depends on the bit depth, but the width/height of the image contained in the buffer is going to be the ones we set earlier. ImageCreate can also take a fill color and depth as the third and fourth arguments, respectively; if not specified, the image will be created filled with the transparent color and match the current screen color depth.

We now have allocated a space in memory. It's enough space to hold an ImgWxImgH image, along with the data FBgfx holds within its fb.Image type. We'll need to destroy it later for proper memory management.

  '' Print the address of our buffer.
Print "Buffer created at: " & myBuf
Sleep

This is just there to let you know what we've done. We print the address of myBuf. If it's not 0, we can assume that ImageCreate had worked.

  '' Destroy our buffer.  Always DESTROY buffers you CREATE
ImageDestroy( myBuf )
Print "Our buffer was destroyed."
Sleep

Here we destroy our buffer with a call to ImageDestroy. We don't have to use ImageDestroy to deallocate our buffer, but it's best to use it for consistency and clarity.

Buffer Format

Now that we know how to create buffers, we might want to know more information about what's being held inside of them. You can open up the fbgfx.bi header file and find the fb.Image type, and you can see all of this cool stuff inside of it.

We actually don't need to know much about the format itself. The reason for this is, we used an fb.Image Ptr. Everything after the Buf + SizeOf(fb.Image) in memory belongs to pixels. Everything before that is the header. The header can be accessed very easily because we used the fb.Image Ptr. All you have to know is what you want to look for.

FB.IMAGE Data Type

    '' Image buffer header, new style (incorporates old header)
    ''
    Type IMAGE Field = 1
        Union
            old As _OLD_HEADER
            Type As ULong
        End Union
        bpp As Long
        Width As ULong
        height As ULong
        pitch As ULong
        _reserved(1 To 12) As UByte

'  '' properties
'  declare property pixels() as ubyte ptr
    End Type

    '' This is a trick to obtain a pointer to the pixels data area
    ''
' property IMAGE.pixels() as ubyte ptr
'  return cast(ubyte ptr, @this) + sizeof(IMAGE)
' end property

This same information can be found in fbgfx.bi. As you can see, this data type saves a *lot* of neat information about your buffer. The Width, Height, Pitch (bytes per row), and Bit Depth (bytes per pixel) are all contained. In the union is included the type of header, and the old header itself within the same space. The new header format is indicated by a type value of 7. The old header format is not used in the default dialect in the newer versions of FB, so we're not going to cover it here.

How do we access that information within the header? If you're familiar with pointers (which you should be, we used a pointer for our buffer in the first example), then all you have to do is access your buffer like a pointer, and directly access the data within. This may leave you to believe that all that's contained in your buffer is the fb.Image type itself, but that's just not true. Using a fb.Image Ptr allows the compiler to think that's what's contained in the buffer, even though only the first part does so.

Getting Pixels

The first section of our buffer which FreeBASIC helps us out with contains the header information. Add the size of the fb.Image to our address, and the rest of our buffer contains pixels (Example2.bas).

  '' We have to include this to use our FB.IMAGE datatype, remember.
#include "fbgfx.bi"

Remember to include our fb.Image data type!

  '' This one is very important.
  '' We cast to a ubyte ptr first off, to get the exact byte our pixels begin.
  '' We then cast to a uLong ptr, simply to avoid "suspicious assignment"
  '' warnings.
Dim As ULong Ptr myPix = Cast( ULong Ptr, ( Cast( UByte Ptr, myBuf ) + SizeOf(FB.Image) ) )

Phew. Alright. We have to make sure we get the exact address of our pixels. A ulong contains 4 bytes. 3 of these are used for our RGB, and the extra is generally used for alpha when you need it (some people are very resourceful and will use the alpha byte - or channel - to store all kinds of data). If we're even ONE BYTE off, your Red can become your Green, and your Blue into your Red! So we have to cast to a UByte Ptr first.

You probably also noticed that we simply added sizeof(fb.Image) to our address. That's another perk of using fb.Image! If you add its size to the start of the buffer, we have just skipped all the memory addresses relating to the header and are now at our pixels.

Finally, we cast it all to a ULong Ptr, mainly for safety. We're in 32 bit depth mode, so we need 4 bytes per pixels. A ULong has that.

Here's a small line if you still don't understand how this works. Here is our buffer: |FB.IMAGE Header|Pixels|

If what's contained in the first section of our buffer is the fb.Image Header, it's obviously going to be that big in size. So, we can get our address for the pixels, simply by adding the size of the fb.Image datatype onto our original address.

One problem though! If we add that size to our buffer address, to try and get a new one, we end up with strange results. This is because our datatype isn't one byte long. We have to cast to a UByte Ptr first, then add the address. A UByte is one byte long, so we'll get the exact byte we need in memory to work with.

Finally, we're in 32-bits. We just cast to a UByte Ptr. Although we *can* just assign the uLong ptr the address of the UByte, it's best practice to cast it to a ULong Ptr first. We finally have the address of our pixels, in the right datatype (one per pixel!). We could manipulate those pixels directly now, if we'd like.

  '' Print information stored in our buffer.
Print "Image Width: " & myBuf->Width
Print "Image Height: " & myBuf->Height
Print "Image Byte Depth: " & myBuf->BPP
Print "Image Pitch: " & myBuf->Pitch
Print ""

This is what I was talking about earlier. FB will treat your pointer as if it's an fb.Image Ptr, so you can access the data in the header directly. Since we have the size of the image as well as its pixels address now, we could edit and manipulate them as if they were a pointer to our screen buffer! See ScrPtr vs ImgBuf.bas for an example on this.

FBGfx Font Header

Header Details

The first row of an image buffer that will be used as a font contains the header information for your font, on a byte by byte basis (remember that the first row of pixels are going to be the first byes since it's stored in row->column).

The very first byte tells us what version of the header we're using. Currently, only 0 is supported, as only one header version has been released. The second byte tells us the first character supported in our font, and the third byte tells us the last.

0; Byte; Header Version
1; Byte; First Character Supported
2; Byte; Last Character Supported
3 to (3 + LastChar - FirstChar); Byte; Width of each Character in our font.

Creating a Font Buffer

If you had a font that supported character 37 as the first, and character 200 as the last, your bytes would contain:

0 for the header version. It's the current only version supported.
37 for the first character supported.
200 for the last character supported.
94 bytes containing the widths of each character.

Since the first row is taken up for header data, the font buffer will be an image buffer whose height is the font height plus one. That is, if you have a font height of 8, you need a buffer height of 9. You'll be putting the font in the second row of your buffer, rather than the first as you usually would.

Here's an example (Example3.bas), which creates a font buffer. It only creates it and assigns header data right now, not the actual font:

  '' The first supported character
Const FirstChar = 32
  '' Last supported character
Const LastChar = 190
  '' Number of characters total.
Const NumChar = (LastChar - FirstChar) + 1

These constants help us. It makes the code cleaner and faster.

  '' Create a font buffer large enough to hold 96 characters, with widths of 8.
  '' Remember to make our buffer one height larger than the font itself.
Dim As FB.Image Ptr myFont = ImageCreate( ( NumChar * 8 ), 8 + 1 )

Create our font buffer. Remember, we need to add horizontal space for each character in the font (8 pixels wide). We also need to add an extra row for our font header information.

  '' Our font header information.
  '' Cast to uByte ptr for safety and consistency, remember.
Dim As UByte Ptr myHeader = Cast(UByte Ptr, myFont )

Get the exact, cast, and having no warnings address of our font buffer. The header goes on a byte by byte basis, so we can't work on this with an fb.Image type.

  '' Assign font buffer header.
  '' Header version
myHeader[0] = 0
  '' First supported character
myHeader[1] = FirstChar
  '' Last supported character
myHeader[2] = LastChar

Assign the header information described above, into the first three bytes. The header version, the first supported character, and the last supported character.

  '' Assign the widths of each character in the font.
For DoVar As Integer = 0 To NumChar - 1
    '' Skip the header, if you recall
  myHeader[3 + DoVar] = 8
Next

Each character in our font can have its own width, so we have to assign these. The 3 + skips the header information. ##DoVar## starts at 0, so the first time it runs through that code, we'll be at index 3. Give all supported characters a width of 8.

  '' Remember to destroy our image buffer.
ImageDestroy( myFont )

Just reminding you :D

Assigning Font Characters

This is fairly simple. We'll use FreeBASIC's default font to draw onto our buffer. Remember to draw starting at column 1, rather than column 0, as the very first column is reserved for header data. Start the character you're drawing at your first supported character, and give it the color you want. Be warned, you can't have custom colors when drawing your font. When you add a character to our buffer, it's stuck the color you draw it as! See the tips & tricks section on how to get around this, however.

Here's the modified code (Example4.bas), where we'll add the font drawing via FreeBASIC's default font onto our buffer.

  '' NEW!!!
  '' Our current font character.
Dim As UByte CurChar

Just to have a quick index of the current ASCII character we're drawing onto our font.

Draw String myFont, ( DoVar * 8, 1 ), Chr(CurChar), RGB(Rnd * 255, Rnd * 255, Rnd * 255)

Skip the first row of our image buffer, as that contains font buffer information. Draw our font using FBgfx's font as our custom font. Fill it with a random color. You should note that we're drawing right into our buffer, with "Draw String myFont...".

Print Chr(CurChar);

Just for clarity, so you can see the characters we're drawing into the buffer.

  '' Use our font buffer to draw some text!
Draw String (0, 80), "Hello!", , myFont
Draw String (0, 88), "HOW ARE ya DOIN Today?!  YA DOIN FINE?!", , myFont
Sleep

Test out our new font. Of course, it's the same one we're used to. You could have created your own from your own custom font buffer somewhere.

Tips & Tricks

Coloring Your Custom Fonts

Alright, so by now you have realized that once you color a custom font, you can't use Draw String to change that color. Well, no fear, we can get around that (CustFontCol.bas). It might be a bit slow, however.

We can create a font object, which has a function to return a font buffer. What the code does is it redraws the font buffer every time we change color, and returns the font buffer stored in the object. This *could* in theory be sped up if we knew the range of characters to redraw, so we could only redraw from the lowest to the highest. Figuring out that range in itself, could also be slow.

#include "fbgfx.bi"

Type Font
    '' Our font buffer.
  Buf     As FB.Image Ptr
    '' Font header.
  Hdr     As UByte Ptr

    '' Current font color.
  Col     As UInteger

    '' Make our font buffer.
  Declare Sub Make( ByVal _Col_ As UInteger = RGB(255, 255, 255) )
    '' Change the font color and edit the font buffer.
    '' Return the new font.
  Declare Function myFont( ByVal _Col_ As UInteger = RGB(255, 255, 255) ) As FB.Image Ptr

    '' Create/Destroy our font.
      '' Set a default color to it if you like.
  Declare Constructor( ByVal _Col_ As UInteger = RGB(255, 255, 255) )
  Declare Destructor()
End Type

  '' Create our font's buffer.
Constructor Font( ByVal _Col_ As UInteger = RGB(255, 255, 255) )
  This.Make( _Col_ )
End Constructor

  '' Destroy font buffer.
Destructor Font()
  ImageDestroy( Buf )
End Destructor

  '' Assign the FBgfx font into our font buffer.
Sub Font.Make( ByVal _Col_ As UInteger = RGB(255, 255, 255) )
    '' No image buffer data.  Create it.
  If This.Buf = 0 Then

      '' No screen created yet.
    If ScreenPtr = 0 Then Exit Sub

      '' Support 256 characters, 8 in width.
      '' Add the extra row for the font header.
    This.Buf = ImageCreate( 256 * 8, 9 )

      '' Get the address of the font header,
      '' which is the same as getting our pixel address
      '' Except that we always will use a ubyte.
    This.Hdr = Cast(UByte Ptr, This.Buf) + SizeOf(FB.Image)

      '' Assign header information.
    This.Hdr[0] = 0
      '' First supported character
    This.Hdr[1] = 0
      '' Last supported character
    This.Hdr[2] = 255
  Else
    If This.Col = _Col_ Then Exit Sub

  End If

    '' Draw our font.
  For DoVar As Integer = 0 To 255
      '' Set font width information.
    This.Hdr[3 + DoVar] = 8

    Draw String This.Buf, (DoVar * 8, 1), Chr(DoVar), _Col_
  Next

    '' Remember our font color.
  This.Col = _Col_
End Sub

  '' Get the buffer for our font.
  '' Remake the font if the color's different.
Function Font.myFont( ByVal _Col_ As UInteger = RGB(255, 255, 255) ) As FB.Image Ptr
    '' If our colors match, just return the current buffer.
  If _Col_ = Col Then
    Return Buf
  End If

    '' Make the font with a new color.
  This.Make( _Col_ )
    '' Return out buffer.
  Return This.Buf
End Function

  '' MAIN CODE HERE!
ScreenRes 640, 480, 32

  '' Create our font.
Dim As Font myFont = RGB(255, 255, 255)

  '' Draw a string using our custom font.
Draw String (0,0), "Hello.  I am the custom font.",, myFont.myFont()
  '' Gasp.  A new color!
Draw String (0,8), "Hello.  I am the custom font.",, myFont.myFont(RGB(255, 0, 0))
Sleep

  '' Speed test.  Turns out it's quite slow.
Scope
  Randomize Timer
    '' Our timer.
  Dim As Double T = Timer

    '' Time how long it takes to make a new font this way.
  For DoVar As Integer = 0 To 499
    myFont.Make( RGB(Rnd * 255, Rnd * 255, Rnd * 255) )
  Next

    '' And we're all done.  Print important data.
  Locate 3, 1
  Print "Time to Re-Draw font 499 times: " & ( Timer - T )
  Print "Time per Re-Draw: " & ( Timer - T ) / 500
  Sleep
End Scope

ScrPtr vs ImgBuf

Comparison of how to draw onto image buffer pixels, versus how to draw on the screen's buffer (ScrPtr vs ImgBuf.bas):

#include "fbgfx.bi"

ScreenRes 640, 480, 32

  '' Create a buffer the size of our screen.
Dim As FB.IMAGE Ptr myBuf = ImageCreate( 640, 480 )

  '' Get the address of our screen's buffer.
Dim As ULong Ptr myScrPix = ScreenPtr
  '' Get the address of our pixel's buffer.
Dim As ULong Ptr myBufPix = Cast( ULong Ptr, Cast( UByte Ptr, myBuf ) + SizeOf(FB.IMAGE) )

  '' Lock our page.  Fill the entire page with white.
ScreenLock

  '' Alternatively, if the screen resolution's unknown, use ScreenInfo to
  '' make this more secure

  '' Note: this code assumes no padding between rows.  To prevent this,
  '' you need to use ScreenInfo to get the screen's pitch, and calculate
  '' row offsets using that instead.
  For xVar As Integer = 0 To 639
    For yVar As Integer = 0 To 479
      myScrPix[ ( yVar * 640 ) + xVar ] = RGB(255, 255, 255)
    Next
  Next

ScreenUnlock
Sleep

  '' Draw onto our image buffer all red.
For xVar As Integer = 0 To myBuf->Width - 1
  For yVar As Integer = 0 To myBuf->Height - 1
    myBufPix[ ( yVar * (myBuf->Pitch \ SizeOf(*myBufPix)) ) + xVar ] = RGB(255, 0, 0)
  Next
Next

  '' Put the red buffer on the screen.
Put (0,0), myBuf, PSet
Sleep

ImageDestroy( myBuf )

/'
  ScreenPtr:
 1) Get address of screen buffer
    (remember that FBgfx uses a dummy buffer that it flips automatically)
 2) Lock page
 3) Draw onto screen address
 4) Unlock page to show buffer

  Image Buffer:
 1) Create an image buffer
 2) Get the address of image pixels
 3) Draw onto image pixels
    (you can use neat stuff like the buffer information to help you here)
 4) Put down Image where you please
    (another big plus!)

  About Drawing:
 cast(ubyte ptr, mybuff) + Y * Pitch + X * Bpp

 Every Y contains PITCH number of bytes.  In order to reach your new Y, you
 have to skip an entire row.

 It should be safe to do the pointer arithmetic in cases where the pointer's data
 type is not one byte long, so you may find it easier to use a pointer type to
 match your bit depth.
 In these cases you should divide the Pitch and BPP by the size of the pointer type.
 Conveniently, in this case the Pitch should always be divisible by the pixel's type
 size. And, obviously, so will the BPP, which will just cancel to 1 :D

'/


Function Overloading
written by :stylin:

What is It?

Function overloading is as close as you can come to generic programming without having templates. In functional (or modular) programming, the emphasis is on value, while in generic programming, the emphasis is on type. Similar functions are called based on the type of the argument passed. Function overloading is a side-step into generic programming, allowing a function identifier to be associated with a variety of functions that work with a variety of different types - and making it all transparent to the client (you).

Simply put, function overloading involves defining functions that have the same name, but different signatures. A function's signature is a combination of all the information needed to correctly reference the function, and includes the function's parameter list and return type. These are what we redefine, or overload. Let's start off with a small example. Say we need functions that output the string respresentation of a number. We simply write:

#lang "fblite"
Option Explicit      '' force explicit declaration of variables
Option ByVal         '' default passing convention as by value

'' to declare functions with similar functionality but that accept different argument types,
'' we 'simply' create new function names :(
Declare Function print_byte( As Byte )      '' outputs a stringified byte
Declare Function print_short( As Short )    '' outputs a stringified short

Dim As Byte b = 102
Dim As Short s = 10240

print_byte( b )
print_short( s )

Sleep : End 0

'' function definitions squished for brevity - don't do this outside a space-constrained  tutorial ;}
Function print_byte( n As Byte ) : Print Str( n ) : Return 0 : End Function
Function print_short( n As Short ) : Print Str( n ) : Return 0 : End Function

What Does It Do For Me?

The problem here is that not only do we have two different function signatures, but we have two different function identifiers as well; we - not the compiler - have to remember both in order to call the right function. As you may be able to imagine, this can be pretty confusing if you decide you want to support INTEGERs, SINGLEs and DOUBLEs as well. Plus, for completeness, you may want to have functions that accept both the signed and unsigned versions of each of these. Clearly, you're going to have some kind of naming-scheme setup to make this easier on yourself. And, of course you'll want to support your own TYPEs as well, and - oh wait, we forgot about pointers. OK, now you'll need to double the list of function names you not only need to come up with, but also try and remember when you're actually writing code that uses these functions. Since, after all, you do have implicit conversions available to/forced upon you, and the compiler will happily let you slip a DOUBLE in to your print_integer function - woops! Bug-city, here we come! Surely there must be a better way?

There is, and don't call me Shirley. I mentioned before that the compiler uses two primary components to establish a function signature: the parameter list and the return type. I also mentioned that through overloading, we can define multiple functions with different signatures, and still keep the same function name for all of them. You may be thinking this is our way out of our dilema, convoluted name space and all. Well, you're right - check this out:

#lang "fblite"
Option Explicit      '' force explicit declaration of variables
Option ByVal         '' default passing convention as by value

'' to overload function print_numeric that we can redefine to accept different argument
'' types while keeping the name intact, we use the OVERLOAD keyword on our intial function:
Declare Function print_numeric Overload( As Byte )      '' outputs a stringified byte
Declare Function print_numeric( As Short )              '' outputs a stringified short
Declare Function print_numeric( As Integer )            '' outputs a stringified integer
Declare Function print_numeric( As LongInt )            '' outputs a stringified longint

'' define some variables
Dim As Byte b = 102
Dim As Short s = 10240
Dim As Integer i = 1024000000
Dim As LongInt li = 1024000000000000000

'' enter the wonderful world of function overloading :)
print_numeric( b )
print_numeric( s )
print_numeric( i )
print_numeric( li )

Sleep : End 0

'' define our function overloads
Function print_numeric( n As Byte ) : Print Str( n ) : Return 0 : End Function
Function print_numeric( n As Short ) : Print Str( n ) : Return 0 : End Function
Function print_numeric( n As Integer ) : Print Str( n ) : Return 0 : End Function
Function print_numeric( n As LongInt ) : Print Str( n ) : Return 0 : End Function

What does It Mean?

One thing that should stand out right away is how incredibly easy it is to do this. That might seem strange considering the freedom, flexibility and type-safety if offers you, but then again most higher-level constructs are like that. In a nutshell, using methods like this will not only make your life a whole lot easier, but you'll be spending less time debugging, and that's a good thing no matter what kind of code you write.

It means flexibility. Function overloading offers the ability to add more features ( print_numeric( f as fraction) ) while still keeping your current code intact. Your code doesn't break because you want to support printing the numeric representation of a handkerchief, or armor, or whatever else. You may now be thinking that the above code is not so trivial anymore, and that what seems really simple - because it is - is really the foundation of writing better code. You'd be right.

It means maintainability: So you've got your 80 functions of print_some_long_name_you_need_to_look_up_everytime_you_want_to_use_it written and debugged. Everything's great in your little torturous, self-loathing world. What happens when something needs to change? Even if only 1 of those functions needs to change, BAM! A maintenence nightmare. You're going to have to search the entire code-base to be completely sure you haven't missed a function here or there; sad way to spend a Saturday night, my friend.

It means safety: You may notice that I utilize two OPTIONs in these examples: Option Explicit and Option ByVal. I'm big on safety, and I'm even bigger on having the compiler watch my back for me. I use these because it is safer to, and I'll take all the safety I can get. Function overloading also affords you safety - safety against evil (read: accidental) implicit conversions. Consider if we were actually returning a value from these functions that was dependent on the argument we passed to it. As above, if a double were allowed to get truncated without our knowledge, that spells many pills of excedrin trying to make that debugging headache go away. It's all about the type-safety, something which cause many to scoff at Cpp.

Wrapping Up

I hope you have learned at least the basics of function overloading (since that's all I covered). And I hope you start thinking about the themes I've brought up, if you haven't before. Next time I'll discuss overloading functions with different numbers of parameters, different return types, as well as the joys and pitfalls of both. Stay tuned.

Last reviewed by sancho3 on February 08, 2018


Getting Started
This is a good introduction to FB for QBasic programmers, based on SJ Zero's tutorial.

Getting started with the software

You can download FreeBASIC here: http://www.freebasic.net
And FBIDE here: http://fbide.sourceforge.net/

When installing FBIDE, select "FBIDE only," to not install the old version of FB included in that package.
When running FBIDE the first time, you will have to browse to find the FB compiler on your computer.

Hello World!

Open up FBIDE and type the following:

PRINT "Hello World!"
SLEEP

Now press F5. Congratulations, you've just seen how much like QB FreeBASIC really is. Now you can use most console commands for QB just like you remember. For example:

Last Reviewed by Sancho3 on February 06, 2018

LOCATE 10,10
PRINT "I'm the center of the universe!"
SLEEP

The Amazing Screen 13

Now, put "SCREEN 13" before your code, to see how easy it is to use graphics modes:
SCREEN 13
PRINT "Hello World!"
SLEEP

From there, all of the standard QB graphics commands work as you remember, as you can see in this example:

SCREEN 13
LINE (1,1)-(100,100),1,bf
PRINT "Hello World!"
CIRCLE (10,10),10,11
PSET (30,15),15

SLEEP

FreeBASIC also has new graphics features. For example, QB has never had a screen 14 or greater. Try running this program:
SCREEN 15
LINE (1,1)-(100,100),1,bf
PRINT "Hello World!"
CIRCLE (10,10),10,11
PSET (30,15),15

SLEEP

After opening a graphics window via the SCREEN command, you can also hit ALT-ENTER to change between windowed and fullscreen modes.

Another nice feature of the graphics library in FreeBASIC is that you can do page flipping in any video mode. The following code demonstrates this.

DIM as integer page
DIM as integer notpage
DIM as integer a, b

screen 12, , 2 'This sets the screen for 2 pages
notpage = 1    'This sets the backpage

DO
   IF page = 0 THEN page = 1 ELSE page = 0          'These two lines flip the page and the
   IF notpage = 1 THEN notpage = 0 ELSE notpage = 1 'backpage

   SCREENSET page, notpage 'This flips the page

   CLS  'First we clear the screen
   b = b + 1
   IF b > 100 THEN b = 0
   FOR a = 1 TO 128
      PSET (b,a),a 'Then we draw a line. It moves without flickering.
   NEXT a
   SLEEP 10

LOOP UNTIL INKEY = CHR(27) 'Press Escape key to quit the program.

This works for any mode, so you can use the high resolution modes for your programs with page flipping, using standard QB graphics commands!

Why ASM is No Longer Required

I wouldn't be saying this if it wasn't true. Using ASM in BASIC to increase the functionality of your program is no longer necessary. Ignoring SDL, Allegro, DirectX, OpenGL, et. al. for a minute, you've got the above page flipping and advanced graphics modes at your disposal, as well as Inkey, which we've all grown to love or hate, but there are also two new input commands which do things QBers have had to resort to assembly code to do since the dawn of time:

DIM as integer x, y, buttons
CONST as integer escapeKey = 1
SCREEN 12

WHILE NOT MULTIKEY(escapeKey) 'this checks the escape key every frame
   GETMOUSE x, y, , buttons    'This gets the mouse state
   PRINT x,y,buttons
WEND

With this knowledge, you should be able to begin programming in FreeBASIC, with all the perks that it entails; Speed, power, and portability!


How to Program a Game: Lesson 1

Introduction by Lachie Dazdarian
The objective of this series of lessons is to help newbies who know very little of BASIC to learn the basics of programming in FreeBASIC necessary to create any computer game. Some elementary BASIC knowledge would help a lot, though I believe that people who don't know BASIC at all should comprehend these lessons too. I'm using here the word (well, it's an acronym) "BASIC" and not FreeBASIC, because if you know the basics of QuickBASIC, Visual BASIC or any other variant of BASIC, these lessons should be easy to comprehend.

I'm starting this series because I feel that tutorials of this kind were always something what our community was lacking, even before FreeBASIC. I've corresponded during my programming lifetime with quite few programming newbies, and they all had almost identical problems when trying to program a game. So I think I'm able to detect what beginners need quite well and on what way the stuff needs to be explained to them. I also remember my beginnings and the problems I had with using separated routines that were never meant to be combined and used to create a game. The breaking point for me was the moment when I discovered RelLib (a QuickBASIC graphics library by R.E.Lope) and the scrolling engine that was created with it. That scrolling engine motivated me to explore its mechanics and expand on it (with some help from R.E.Lope). In one single moment I acquired the ability to program most of the stuff (necessary to complete a game) by myself. It's like driving a bike. The moment when you acquire the actual skill lasts for one second.

So that's my goal with this series. To teach you enough so you would be self-sufficient in 90% of cases. And the best way to learn new things is to see them applied. Many tutorials fail in this by being too generic. You will always need help from more expert programmers, but the point is that you don't need it on every step. Have in mind that this depends on the type of game you are developing and the graphics library / tools you are using.

The example programs and mini-games we'll create will be coded in GFXlib (the FreeBASIC's built-in graphics library). Lynn's Legacy, ArKade, Mighty Line and Poxie were coded in it (among many others), and I think those games are good references. But don't worry. Switching from one graphics library to another is relatively easy when you know how to code in at least one.

This tutorial will not deal with raycasting engines (3D programming) or something "advance" like that. If you want that but are a beginner, you NEED the following lessons FIRST.

Since we are going to code in FreeBASIC you need to get FreeBASIC first (if you don't have it yet) from http://www.freebasic.net (the examples were compiled with version 0.18b), and one of the FreeBASIC IDEs available. I recommend FBIDE or FBEdit.

Example #1: A simple program - The circle moves!

We'll start with some elementary stuff. The first program we'll code will not feature external graphics, because loading graphics from external files (usually BMP images) is always a dirty business and will confuse you at this point. Trust me on this. Be patient.

The program we'll create will allow you to move a circle around the screen. A very simple program, but through making it we'll learn important facts and a lot of elementary statements and methods necessary to create any game with GFXlib.

As we are using GFXlib you need to be aware of the gfxlib.txt file(GFXlib's documentation) placed in the /FreeBASIC/docs directory. That's our Bible and very useful with these lessons since I will not explain every parameter of every statement used in the example programs (most likely). This document is somewhat outdated as FreeBASIC moved on with new versions, so be sure to refer to this online FreeBASIC manual too (part of the FreeBASIC Wiki).

Open a new program in FBIDE. First thing we'll do is set the graphic mode. What's setting a graphic mode? Choosing the program's graphic resolution and color depth in bits (8-bit, 16-bit, ...). For example, 8-bit color depth is the standard 256 colors mode (8 bits per pixel). The graphic mode is set with the SCREEN statement like this:

    Screen 13,8,2,0

13 means 320*200 graphic resolution, 8 means 8-bit graphics, 2 means two work pages, and 0 means window mode (input 1 for full screen mode). Minimum of 2 work pages is recommended for any graphics dependant program. These things will become clearer a little bit later. For more details about the SCREEN statement refer to GFXlib's documentation or FreeBASIC Wiki (a more "advanced" version of the SCREEN statement is SCREENRES).

The next thing we'll do is set a loop that plays until the user pushes the letter Q on the keyboard. Loops are foundation of any program, not just a computer game. Coding a program on a way it would stop/halt every now and then and wait for the user to type something in is a BAD and WRONG way to program anything you want for other people to play. We'll use loops as places where the program waits for the user to do something (clicks with mouse or pushes a key) and where the program executes some routine according to user's action. It will also be used as a place where objects not controlled by the player (enemies) are managed/moved. Loops are a must have.

If you are aware of all these things, you can skip to the end of this section and download the completed example (with comments). If there is something in it you don't understand, then get back here.

We can set a loop on more ways (with WHILE:WEND statements, using the GOTO statement - Noooo!), but the best way is to use DO...LOOP. This type of loop simply repeats a block of statements in it until the condition is met. You set the condition(s) after LOOP with UNTIL. Check the following code:

    Screen 13,8,2,0 ' Sets the graphic mode
    Do
    ' We'll put our statements here later
    Loop Until Inkey$ = "Q" Or Inkey$ = "q"

(Editors Note: The suffixes on commands such as the $ on INKEY$ is:
obligatory in the -lang qb dialect.
optional in the -lang fblite dialect.
forbidden in the -lang fb dialec
Check the documentation for the specific keyword)

If you compile this code and run it, you'll get a small black empty 320*200 window which you can turn off by pushing the letter Q (you might need to hold it). The program simply loops until you press "Q or "q". I used both upper and lower case "Q" symbol in case Caps Lock is turned on on your keyboard. INKEY$ is a statement that returns the last key pushed on the keyboard. I will explain later why it shouldn't be used with games and what's a better substitute.

To draw a circle we'll use the CIRCLE statement (refer to GFXlib's documentation). Check the following code:

    Screen 13,8,2,0 ' Sets the graphic mode

    Do

    Circle (150, 90), 10, 15

    Loop Until Inkey$ = "Q" Or Inkey$ = "q"

The last code draws a small circle on coordinates 150, 90 with a radius of 10 and color 15 (plain white) in a loop, which you can check if you compile the code. So how to move that circle? We need to connect its coordinates with VARIABLES. For this we'll use two variables named circlex and circley. Check the following code:

    Dim Shared As Single circlex, circley

    Screen 13,8,2,0 ' Sets the graphic mode

    circlex = 150 ' Initial circle position
    circley = 90

    Do

    Circle (circlex, circlex), 10, 15

    Loop Until Inkey$ = "Q" Or Inkey$ = "q"

This makes no change in the result of our program, but it's a step to what we want to accomplish. You can change the amounts to which circlex and circley equal to change the circle's initial position, but that's not what we really want. In order to move the circle we need to connect circlex and circley variables with keyboard statements.

We declared first two variables in our program. All variables in FreeBASIC programs MUST be declared, although if you use -lang qb command line during compiling you can compile using old QBasic compatibility dialect (I don't recommend it as it will keep you deprived of possible advances and extensions which default FB compatibility already provides and will provide). For more info on this check the appropriate page of the FreeBASIC wiki - Using the command line. Variables are declared (dimensioned) on this way:

    Dim variable_name [As type_of_variable]

Or...

    Dim [As type_of_variable] variable1, variable2, variable3, ...

The data inside [ ] is optional and the brackets are not used. Types of variables available in FreeBASIC are BYTE, SHORT, INTEGER, STRING, SINGLE, DOUBLE and few others, but I don't find details about them important on this level. What you need to know now is that you should declare variables or arrays AS INTEGER when they hold graphics data (memory buffers holding graphics) or when they represent data which doesn't need decimal precision (number of lives, points, etc.). Variables that need decimal precision are declared AS SINGLE or DOUBLE. Those are usually variables used in games which rely on physics formulae like arcade car driving games or jump 'n run games (gravity effect). Simply, the difference between the speed of two pixels per cycle and the speed of one pixel per cycle is most often too large, and in those limits you can't emulate effects like fluid movement on the most satisfactory way. Also, behind DIM you should put SHARED which makes that the specific variable readable in the entire program (all subroutines). Don't use SHARED only with variables declared inside subroutines (I do it very rarely). If you are going to declare ARRAYS inside a subroutine, I advise you to replace DIM with REDIM. Strings are used to hold text data. Like YourName = "Dodo", but you need to declare YourName AS STRING first.

Now I will introduce a new statement instead of INKEY$ which can detect multiple keypresses and is much more responsive (perfect response) than INKEY$. The flaw of INKEY$, as well as being very non-responsive (which you probably were able to detect when trying to shut down the previously compiled examples), is that it can detect only one keypress at any given moment which renders it completely unusable in games.

The substitute we'll use is MULTIKEY (a GFXlib statement) which features only one parameter, and that's the DOS scancode of the key you want to query. You might be lost now. DOS scancode is nothing but a code referred by the computer to a certain keyboard key. If you check Appendix A of the GFXlib's documentation, you will see what each code stands for. For example, MULTIKEY(&h1C) queries if you pushed ENTER. GFXlib allows you to replace these scancodes with "easy to read" constants like it's explained in Appendix A. To use GFXlib you need to include its .bi file (fbgfx.bi) into your source. What's a .bi file? Well, it can be any kind of module you would attach to your source code and which can feature various subroutines (if you don't know what a subroutine is, we'll get on that later) and declarations used in your main module. The code you need to add are these two lines as it follows:

    #include "fbgfx.bi"
    Using FB

It's best to put these two lines somewhere on the beginning of your program (before or after the sub declarations). You don't need to set a path to fbgfx.bi since it's placed in the /FreeBASIC/inc directory. You only need to set a path to a .bi file if it's not in that directory or not in the directory where the source code is. Using FB tells the program that we will be accessing GFXlib symbols without namespace, meaning, without having to put 'FB.' in front of every GFXlib symbol. Refer to FreeBASIC Wiki on USING.

Now the fun starts.

We will add a new variable named circlespeed which flags (sets) how many pixels the circle will move in one cycle (loop). The movement will be done with the arrows key. Every time the user pushes a certain arrow key we will tell the program to change either circlex or circley (depends on the pushed key) by the amount of circlespeed. Check the following code:

    #include "fbgfx.bi"
    Using FB

    Dim Shared As Single circlex, circley, circlespeed

    Screen 13,8,2,0 ' Sets the graphic mode

    circlex = 150   ' Initial circle position
    circley = 90
    circlespeed = 1 ' Circle's speed => 1 pixel per loop

    Do

    Circle (circlex, circley), 10, 15

    ' According to pushed key we change the circle's coordinates.
    If MultiKey(SC_RIGHT) Then circlex = circlex + circlespeed
    If MultiKey(SC_LEFT) Then circlex = circlex - circlespeed
    If MultiKey(SC_DOWN) Then circley = circley + circlespeed
    If MultiKey(SC_UP) Then circley = circley - circlespeed

    Loop Until MultiKey(SC_Q) Or MultiKey(SC_ESCAPE)

As you see we also changed the condition after UNTIL since we are using MULTIKEY now. Now you can exit the program by pressing ESCAPE too (I added one more condition).

If you compile the last version of the code, two things we don't want to happen will happen. The program will run so fast you won't even notice the movement of the circle, and the circle will "smear" the screen (the circles drawn on different coordinates in previous cycles will remain on the screen). To avoid smearing you need to have the CLS statement (clears the screen) in the loop so that in every new cycle the old circle from the previous cycle is erased before the new is drawn.

To reduce the speed of the program the quickest fix is the SLEEP command. What it does? It waits until the specified amount of time has elapsed (in milliseconds) or a key is pressed. To escape the key press option use SLEEP milliseconds, 1. This statement is also an efficient solution for the 100 % CPU usage problem. You see, if you don't use that statement any kind of FreeBASIC program with a loop (even the simplest one) will hold up all the computer cycles and make all the other Windows tasks you might be running to crawl. It also makes difficult for you to operate with other tasks while that kind of FreeBASIC program is running. Err...this is not a huge problem and a fair amount of programmers that have released FreeBASIC games so far did not bother to fix it.

Copy and paste the following code and compile it:

    #include "fbgfx.bi"
    Using FB

    Dim Shared As Single circlex, circley, circlespeed

    Screen 13,8,2,0 ' Sets the graphic mode

    circlex = 150   ' Initial circle position
    circley = 90
    circlespeed = 1 ' Circle's speed => 1 pixel per loop

    Do

    Cls
    Circle (circlex, circley), 10, 15

    ' According to pushed key we change the circle's coordinates.
    If MultiKey(SC_RIGHT) Then circlex = circlex + circlespeed
    If MultiKey(SC_LEFT) Then circlex = circlex - circlespeed
    If MultiKey(SC_DOWN) Then circley = circley + circlespeed
    If MultiKey(SC_UP) Then circley = circley - circlespeed

    Sleep 10, 1

    Loop Until MultiKey(SC_Q) Or MultiKey(SC_ESCAPE)

Viola! Our circle is moving and "slow enough".

The last version of the code does not represent the desirable way of coding, but I had to simplify the code in order to make this lesson easy to understand. What we need to do next is declare our variables on the way they should be declared in any "serious" program, and show why we are having two work pages and what we can do with them.

The way variables are declared in the above code is not the most convenient in larger projects where we have huge amount of variables usually associated to several objects (an object can be the player, enemy or anything that is defined with MORE THAN ONE variable).

So first we'll define a user defined data type with the statement TYPE that can contain more variables/arrays (stay with me). We'll name this user data type ObjectType. The code:

    Type ObjectType
         x As Single
         y As Single
         speed As Single
    End Type

After this we declare our circle as an object:

    Dim Shared CircleM As ObjectType
    ' We can't declare this variable with "Circle"
    ' since then FB can't differ it from
    ' the statement CIRCLE, thus "CircleM".

How is this method beneficial? It allows us to manage the program variables on a more efficient and cleaner way. Instead of (in this example) having to declare each circle's characteristic (it's position, speed, etc.) separately, we'll simply use a type:def that includes all these variables and associate a variable or an array to it (in this case that's CircleM). So now the circle's x position is flagged with CircleM.X, circle's y position with CircleM.Y and circle's speed with CircleM.speed. I hope you see now why this is better. One user defined type can be connected with more variables or arrays. In this example you can add another object with something like DIM SHARED EnemyCircle(8) AS ObjectType which would allow us to manage 8 "evil" circles with a specific set of routines (an AI of some sort) using the variables from the ObjectType type:def (x, y, speed), and these circles could "attack" the user's circle on some way. In the next lesson all this will become more clear. Have in mind that not ALL variables need to be declared using a type:def. This is only for "objects" in your game that are defined (characterized) with more variables (like a hero determined by health, money, score, strength, etc.).

After the change the final version of the code looks like this:

    #include "fbgfx.bi"
    Using FB

    ' Our user defined type.
    Type ObjectType
         x As Single
         y As Single
         speed As Single
    End Type

    Dim Shared CircleM As ObjectType
    ' We can't declare this variable with "Circle"
    ' since then FB can't differ it from
    ' the statement CIRCLE, thus "CircleM".

    Screen 13,8,2,0 ' Sets the graphic mode
    SetMouse 0,0,0 ' Hides the mouse cursor

    CircleM.x = 150   ' Initial circle's position
    CircleM.y = 90
    CircleM.speed = 1 ' Circle's speed => 1 pixel per loop

    Do

    Cls
    Circle (CircleM.x, CircleM.y), 10, 15

    ' According to pushed key we change the circle's coordinates.
    If MultiKey(SC_RIGHT) Then CircleM.x = CircleM.x + CircleM.speed
    If MultiKey(SC_LEFT) Then CircleM.x = CircleM.x - CircleM.speed
    If MultiKey(SC_DOWN) Then CircleM.y = CircleM.y + CircleM.speed
    If MultiKey(SC_UP) Then CircleM.y = CircleM.y - CircleM.speed

    Sleep 10, 1 ' Wait for 10 milliseconds.

    Loop Until MultiKey(SC_Q) Or MultiKey(SC_ESCAPE)

You will notice I added one more statement in the code. The SETMOUSE statement positions the system mouse cursor (first two parameters) and shows or hides it (third parameter; 0 hides it). You should input this statement with these parameters in every program AFTER the SCREEN statement (IMPORTANT!) by default, because even if your program is going to feature a mouse controllable interface, you will most likely draw your own cursor. Trust me on this. Uh, I using that line way too often.

Download the completed example with extra comments inside the source: move_circle.zip

Phew, we are done with the first example. Some of you might think I went into too many details, but I feel all this dance was needed to make the next examples and lessons a more enjoyable adventure.

Nevertheless, this example is far from what we want, right? So the next chapter will learn you how to load graphics from external files among other things.

Example 2: A warrior running around a green field

In the next example we will be applying all the knowledge from the first example, so don't expect for this example to go into every statement again. I will explain every new statement and just brush off the old ones.

In this section we'll start to code our mini-game which won't be completed in this lesson. In this lesson we'll just create a program where a warrior runs around a green field (single screen).

First I'll show you what graphics we'll be using. We are going to work in 8-bit color depth mode, so the images that we are going to use need to be saved in that mode (256 colors mode). For warrior sprites I'll use the sprites of the main character from my first game Dark Quest.

http://hmcsoft.org/fb/htpagl1-sprites.png
(Editors Note: The link above is not active anymore. The game Dark Quest is still available for download at http://lachie.phatcode.net/downloads.php)

As you see this image features 12 sprites of our warrior, each 20*20 pixels large. Two for each direction (walk animation) and one sprite for each direction when the warrior is swinging with his sword. Sword swinging won't be implemented in the first lesson but will become necessary later.

Second image is the background image which you can check/download if you click here (320*200 pixels large, 8-bit BMP image).

Download both images and place them where you will place the source, or just download the completed example at the end of this section.

On the beginning of our program we should include fbgfx.bi, same as in the first example, and then set the same graphic mode. The code:

    #include "fbgfx.bi"
    Using FB

    Screen 13,8,2,0 ' Sets the graphic mode
    SetMouse 0,0,0  ' Hides the mouse cursor

Now we will declare two memory pointers that will point to memory buffers where our graphics will be stored (one for the sprites and one for the background).

The first pointer we'll name background1 and declare it with the following line:

    Dim Shared background1 As Any Ptr

ANY PTR tells us that background1 will actually be a memory pointer. A pointer defined as an ANY PTR disables the compiler checking for the type of data it points to. It is useful as it can point to different types of data. We'll use pointers because we will allocate memory for our graphics using the IMAGECREATE statement. IMAGECREATE allocates the right amount of memory for a piece of graphics (sprite/image) if we input its height and width. Otherwise we would have to do it manually, meaning, calculate the needed amount of memory as the result of the sprite size, bit-depth and variable size. IMAGECREATE does this for use. As IMAGECREATE results with a pointer, we need to refer a pointer to it and not a variable. Don't worry if you don't know anything about pointers. You don't need to (to comprehend this tutorial).

The next pointer we'll declare will point to the memory buffer that holds the 12 warrior sprites. We will dimension this pointer as a single dimension array, each element in the array representing one sprite.

    Dim Shared WarriorSprite(12) As Any Ptr

Both these lines should be put in the code before the SCREEN statement. That's the way you'll write every program. Subroutine declarations, then variable declarations, then extra subroutine declarations if needed, and then the real code. The beginning of our program should now look like this:

    #include "fbgfx.bi"
    Using FB

    Dim Shared background1 As Any Ptr ' A pointer that points to a memory
                                      ' buffer holding the background graphics
    Dim Shared WarriorSprite(12) As Any Ptr ' A pointer that points to a memory
                                            ' buffer holding the warrior sprites

    Screen 13,8,2,0 ' Sets the graphic mode
    SetMouse 0,0,0  ' Hides the mouse cursor

After the screen resolution, color depth and number of work pages are set, we will hide our work page before loading graphics onto it since we don't want for the user to see all of the program's graphics every time he or she starts our program. To accomplish that we'll use the SCREENSET statement. What it does? It sets the work page (first parameter) and the visible page (second parameter). In our case we will set page 1 as the work page and page 0 as the visible page. After using 'SCREENSET 1, 0' every time we draw or load something on the screen it will be loaded/drawn on the work page and won't be visible to the user until we use the statement SCREENCOPY or SCREENSET with different parameters (SCREENSET 1, 1). This allows us to load graphics onto the screen we don't want for the user to see and delete it before coping the content on the work page to the visible page. This page flipping is also useful in loops with "graphics demanding" programs to avoid flicker or some other unwanted occurrence.


The IF Statement
Written by rdc

You can think of the If statement block as a question that requires a True or False answer. The answer determines which section of code your program will execute. Since computers only work with numbers, you frame the question as a conditional equation that will result in either 0 for False or non-zero for True.

The If statement has the following formats.

If <expression> Then Do something[:Do something]

The <expression> is the question that requires a True or False answer. If the answer is True, i.e. not False, then the code following the Then is executed. If the answer is False then the next line of code is executed.

You can execute more than one statement after the Then if you separate the statements with a colon. All of the statements must be on the same line. An easier format is to use the IF code block, as shown below.

If <expression> Then
    Do something 1
    Do something 2
    ...
End If

In this format if the answer is True then the code block following the Then is executed, starting with statement 1 and executing all statements until the End If is reached. The program will then start executing code after the End If. If the answer is False, the code in the code block is skipped and the code following the End If is executed.

If <expression> Then
    Do something
    ...
Else
    Do something Else
    ...
End If

In this format if <expression> is True then the code following the Then is executed. If the answer is False then the code following the Else is executed. In this format you can address both the True and False answers of the <expression>.

If <expression> Then
    Do something
ElseIf <expression> Then
    Do something
End If

In this format if <expression> is True then the code following the Then is executed. If the answer is False then the ElseIf is executed. If the ElseIf is True, the code following the Then (of the ElseIf) is executed, otherwise the code following the End If is executed. You can have as many ElseIf statements as you need to fully cover the range of questions you need to ask.

If <expression> Then
    Do something
ElseIf <expression> Then
    Do something
Else
    Do something Else
End If

This format is a combination of all the other formats. If <expression> is True then the code following the Then is executed. If the answer is False then the ElseIf is executed. If the ElseIf is True, the code following the Then (of the ElseIf) is executed, otherwise the code following the Else is executed.

This format enables you to ask a series of questions and if the answer is False to all the questions, you can take a default course of action based on the Else block.

As you can see you can frame the question in a number of ways and then execute the code based on a number of answer combinations. This gives you a lot of flexibility in how to both frame a question and what to do based on the answers.

The <expression> is the question that needs an answer and you frame the question using logical operators.

You can mix arithmetic and logical operators, as well as parenthesis, within an If statement. The compiler evaluates the conditional statements from left to right, taking into account the precedence of the operators. For example, all of the following code snippets are legal If statement constructs.

If var1 = 5 Then
If (var1 = 5) And (var2 < 3) Then
If (var1 + 6) > 10 Then

You will notice that parenthesis are used to group the different parts of the expressions. You should use parenthesis to make sure that you are executing logical portions of the expressions. The expression must ultimately resolve to either True or False, even if you are using arithmetic operators within the expression.

Using Bitwise Operators in an If Statement

Remember that the operators And, Or and Not are bitwise operators. That is, they return a value based on the bitwise operation that they perform. You should take care when using bitwise operators within an If statement to make sure that the result will evaluate correctly.

Take the second code snippet listed above.

If (var1 = 5) And (var2 < 3) Then

If var1 equals 5, the compiler will return True, or -1 for the expression. If var2 is less than 3 then the compiler will return True or -1 for this expression. The compiler will then evaluate the And operator with -1 And -1 returning -1. Since -1 is non-zero, or True, the code following the Then will be executed.

If either of the statements within the parenthesis evaluate to 0, then And will return 0, which is False, and the code following the Then clause will be skipped. When using bitwise operators you should frame the conditional expressions on either side of the bitwise operator so that they return either True or False. This will give you consistent results in your evaluations.

The Not Problem

The Not bitwise operator can be a problem in an If statement. You may be used to writing If Not var Then, with Not performing a logical, rather than a bitwise operation. In FreeBASIC Not performs a bitwise operation, not a logical operation.

If var were to contain the value of 3, then Not 3 is -4, which will be regarded as a True result and the code following the Then will be executed, which is probably not what you wanted. Instead of writing If Not var Then, you should write If var <> 0 Then.

Overlapping Conditions

When using compound conditions care must be taken to ensure that the conditions do not overlap. In most cases overlapping conditions will produce unpredictable results. Each condition must produce a unique result, and the combination of the individual results, must itself express a unique result. This is very important in If-ElseIf constructs; an overlapping condition within an If-ElseIf block may execute the wrong code at the wrong time.

Nested If Statements

At times it may become necessary to nest If statements in order to better describe the decision making process of the evaluation. While the If statement can handle multiple arguments within an expression, there are times when you may want to incrementally check for certain ranges of values which you can do using a nested If block.

If <expression> Then
    <statement>
    ...
    If <expression> Then
        <statement>
        <statement>
        ...
    End If
End If

It is important to close each block properly with an End If when opened by an If to avoid compiler or logical errors. Compiler errors are fairly easy to fix, while logical errors can be tricky to track down and correct. The best way to make sure you are closing the blocks properly is to indent the nested If statements and then indent the matching End If statements at the same level as the If. In the example above, the indentation tells you at a glance which End If goes with which If.

The IIF Function

The Iif, or "immediate If" function returns one of two numeric values based on an expression. You can think if the Iif function as an in-line If statement that acts as a function call.

Value = IIf(<expression>, numeric_value_if_true, numeric_value_if_false)

Iif can be used as a standalone function or inside other expressions where you do not want to split the expression to insert an If statement. The numeric values can be literal values, variables or numeric function calls. The limitation of the function is that it will only return a numeric value, not a string value, however you can work around this limitation by using pointers.

The Iif statement will evaluate both the True and False conditions so you need to take care that you do not perform an illegal operation such as divide by zero in a condition, even if that condition is not returned from the function.

Framing the Question

The If statement is a powerful tool, but you need to make sure that you are framing the question, that is the expression, correctly. Each expression must resolve to True or False, with True always executing the code following the Then.

When writing an If statement you must ask yourself, does this expression resolve to True or False? This is especially true for compound expressions that have a number of terms within the expression. Each term must resolve to True or False and the sum of the terms must resolve to True or False. If there is any doubt that a compound expression is evaluating correctly, break it into nested If statements.

Checking For Range Values

Often times you will need to check for a range of values within an If statement, and in order for the compiler to evaluate the range condition correctly, you must frame the expressions correctly. There are basically two type of range expressions: exclusive and inclusive ranges. Exclusive range expressions exclude a range of values. Inclusive range expressions include a range of values. Each has a particular format that must be followed for proper evaluation.

Excluding a Range of Values

Suppose that you have a range of values and you want to do something special if the value is less than or equal to 1 or the value is greater than or equal to 10. To put this another way, you want to exclude the numbers 2 through 9 from the special action.

You can frame this as a question that can then be translated into code.

Is the value a number less than Or equal To 1 Or a number greater than Or equal To 10?
If Yes, Then Do special action.
If No, Then Do standard action.

The key here is the OR. If the lower bound of the value is equal to or less than 1 OR the upper bound of the value is equal to or greater than 10 then do the special action.

If (value <= 1) Or (value >= 10) Then
    do_special
Else
    do_standard
End If

Remember that OR will return True if either condition is True. If the value is 1 or less, or the value is 10 or more, then the expression will return True and the special action will be performed.

Including a Range of Values

Inclusion is the opposite of exclusion. As you might guess, the format is similar you but you use the And operator which is the opposite of the OR operator.

Suppose you want to do something special if the value is a 5, 6 or 7. That is you want to include these numbers within your range expression. Again, you can start by asking a question.

Is the value a number between 5 And 7 (inclusive)?
If Yes, Then Do special action
If No, Then Do standard action

Here you want to include the numbers 5, 6, 7 for consideration. That is if the value is 5 or greater AND the value is 7 or less then do something special. This translates to the following code snippet.

If (value >= 5) And (value <= 7) Then
    do_special
Else
    do_standard
End If

Remember that the And operator will only return True if both operands return True. If value is 6, 6 is greater than 5 and 6 is also less than 7, so both statements are True and the expression evaluates to True.

Editors Note: See also AndAlso and OrElse

Last Reviewed By Sancho3, February 06, 2018


SDL_Net: Getting Started

A compleate Step by step guide of getting your program from hello world to hello world over a TCP/IP connection, using the SDL_Net SDL library. This tutorial will list all componets required and where to download them at the time of writing followed by how to get each componet in the proper place to perform the proper functions and finally how to write the actuall code. I will assume you have zero previous knowledge and because of that some readers may want to skip the first few bits of the tutorial.
Written by GregF (Paragon)

Step 1: What you need.

Ok, lets pretend that you just sat down and installed the compiler and an IDE. This list takes it from there.
    • SDL_Net.bi - Installed with the compiller.
    • SDL_Net.dll Binary -http://www.libsdl.org/projects/SDL_net/
    • SDL.dll runtime library - http://www.libsdl.org/download-1.2.php

Step 2: Where you put it.

The .bi file can be put pretty much where ever you want to put it, you will tell the compiler where to find it in the '$Include command. The .dll however need to be placed in specific places. The easiest way to make sure that the program will be able to use these files is to have the .dll in the same folder as the compiled executable. You can also put them in any folder that is listed in your Enviroment variable, but I don't recommend that because it will be easier to find and remeber that you need the .dlls if you just put them in the same folder as the executable, which will proably be the same folder as your .bas file for the main program.

Tutorial in progress...


Interfacing with C
NOTE! Have to do some spell checking, verify text, code and filenames.

Foreword

This is a tiny basic tutorial on how to write a simple library in C and then use it in FreeBASIC. The tutorial should be possible to follow without to much knowledge of C or FreeBASIC. After doing this tutorial you should be able to compile a static and a dynamically linked C library. Translate the necessary header files to FreeBASIC header files and understand how to use the libraries in a FreeBASIC project.

What is a library

Prerequisite

This tutorial was written and tested with FreeBASIC 0.16b and the latest Current release of MinGW32 at the time. As a note Dev-cpp uses MinGW32 as it's compiler tool chain. You also get code::blocks with a mingw32 bundle.

Formal description of the task at hand

To demonstrate usage of a C library in FreeBASIC we need to create the simplest possible library with a few functions. A test file in C to demonstrate that our library works as intended. Then we have to translate the library header file to a FreeBASIC header file (*.bi) and finally create a test project in FreeBASIC using the library.

Creating the files

So our file list will look like this:
myClib.c: C file implementing our library.
myClib.h: C header file describing the libraries interface.
myClibCTest.c: C file implementing our test program in C.
myClib.bi: FreeBASIC header file. A translation of myClib.h.
myClibFBTest.bas: FreeBASIC
make.cmd: A sample shell script compiling the library and test files.

The C file to become a static library. myClib.c
(C)
/* A function adding two integers and returning the result */
#include "myClib.h"
int SampleAddInt(int i1, int i2)
{
    return i1 + i2;
}

/* A function doing nothing ;) */
void SampleFunction1()
{
    /* insert code here */

}

/* A function always returning zero */
int SampleFunction2()
{
    /* insert code here */

    return 10;
}
The header file myClib.h
(C)
int  SampleAddInt(int i1, int i2);
void SampleFunction1();
int  SampleFunction2();

A C test project to verify that the static lib is C compatible. myClibCTest.c:
(C)
#include
#include
#include "myClib.h"
int main(int argc, char *argv[])
{
  printf("SampleAddInt(5, 5):=%d\n", SampleAddInt(5, 5));
  system("PAUSE");
  return 0;
}

Translating the C header file to a FreeBASIC header file
myClib.bi: To interface the static library and automatically include it (#inclib "myClib" ) i have this file.
''include file for libmyClib.a
#ifndef __myClib_bi__
#define __myClib_bi__
#inclib "myClib"

Declare Function SampleAddInt cdecl Alias "SampleAddInt" (ByVal i1 As Integer, ByVal i2 As Integer) As Integer
Declare Sub SampleFunction1 cdecl Alias "SampleFunction1" ()
Declare Function SampleFunction2 cdecl Alias "SampleFunction2" () As Integer
#endif

And finally the FreeBASIC file using the library
myClibFBTest.bas:
''Testing functions in myClib.bi
#include "myClib.bi"
''
Print "SampleAddInt(10, 10):=", SampleAddInt(10, 10)
'' Just a dumy call
SampleFunction1()
''
Print "SampleFunction2():=", SampleFunction2()

The make file: make.cmd
I have created a batch file to compile all the files. Including a sample in C using the static library. Note the config lines at the top which has to be modified to suite your setup.
(cmd)
@REM TODO: Set PATH's for this session.
SET PATH=C:\mingw32\bin;c:\mingw32\mingw32\bin
SET MINGW_INCLUDE="C:/MinGW32/include"
SET MINGW_LIB="C:/MinGW32/lib"

@REM
@REM fbc testing SET fbc="C:\portableapps\FreeBASIC\fbc.exe"
SET fbc="C:\FreeBasic16b\fbc.exe"
@echo *** Verify pat's to compilers
@pause
@echo off

@REM
@REM Remove old files
DEL /F *.o  *.a myClibFBTest.exe

@REM
@REM Create static lib from c source
gcc.exe -c myClib.c -o myClib.o -I%MINGW_INCLUDE%

@REM
@REM ar: creating libstatictest.a
ar r libmyClib.a myClib.o

@REM
@REM No nead for ranlib anymore? ar is supposed to take care of it
ranlib libmyClib.a

@REM
@REM Create a test with a C file

gcc.exe -c myClibCTest.c -o myClibCTest.o -I%MINGW_INCLUDE%
gcc.exe myClibCTest.o -o "myClibCTest.exe" -L%MINGW_LIB% libmyClib.a

echo =====================================
echo RUnning C sample
echo =====================================
myClibCTest.exe

echo =====================================
echo Creating FreeBASIC sample
echo =====================================
REM I thought this explicit reference is unnecessary as I use #inclib
SET fbcop= -I myClib
SET fbcfl="myClibFBTest.bas"
%fbc% %fbcop% %fbcfl%
echo =====================================
echo RUnning FreeBASIC sample
echo =====================================
myClibFBTest.exe
@pause

Encountered error messages and their solutions

undefined reference to
Trying to link against the static C library without using the cdecl alias "functionname" in the FreeBASIC header file results in errors like this.
(cmd)
C:\code>"C:\FreeBasic16b\fbc.exe"     "myClibFBTest.bas"
myClibFBTest.o:fake:(.text+0x3d): undefined reference to `SAMPLEADDINT@8'
myClibFBTest.o:fake:(.text+0x4a): undefined reference to `SAMPLEFUNCTION1@0'
myClibFBTest.o:fake:(.text+0x67): undefined reference to `SAMPLEFUNCTION2@0'
Press any key to continue . . .

To resolve this you will have to locate function declarations in a *.bi file that look like this:
Declare Function SampleAddInt(ByVal i1 As Integer, ByVal i2 As Integer) As Integer

And change it to something like this:
Declare Function SampleAddInt cdecl Alias "SampleAddInt" (ByVal i1 As Integer, ByVal i2 As Integer) As Integer

Appendix A: links

The basis for this tutorial is several threads in the forum.
When it evolves and can stand alone the links to the threads might be removed.
Some interesting links containing information on interfacing libraries created in FreeBASIC and used by other languages or visa versa.

How do I compile a C project as a static lib for inclusion..


Introduction To Arrays
Written by rdc

Arrays are probably the single most useful programming construct that is available to you in FreeBASIC. Many problems that you will try to solve with a programming solution involve data arranged in tabular format, and arrays are perfect for managing this type of data. Understanding arrays is a crucial skill in becoming a competent programmer.

Arrays are contiguous memory segments of a single or composite data type. You can think of an array as a table, with rows and columns of data. An array can have one or more rows, and each row can have one or more columns. The number of rows and columns define the dimensions of the array. FreeBASIC uses the row-major scheme for arrays, which means that the first dimension references the row in an array that has more than one dimension. FreeBASIC supports up to eight dimensions in an array.

One-Dimensional Arrays

An array with a single row is called a one-dimensional array. If an array is a single-dimensional array, then the row is not defined in the declaration, only the number of columns in the row. Since an array requires a minimum of one row, the row is understood to exist in this case. The following code snippets create a single-dimension integer array using the different array definition schemes available in FreeBASIC.

Dim myArray(10) As Integer

Dim myArray(1 To 10) As Integer

The first method will define an array with a single row and 11 columns, with column indexes (numbers) ranging from 0 to 10. The second method defines the lower and upper bounds using the To keyword. Here the indexes will range from 1 to 10.

One-Dimensional Array Indexes

You access each element of an array using an index value. In the case of a single-dimension array, the index would refer to a column number in the default row. The format is to use the array variable, with the index surrounded by parenthesis.

myArray(5) = 7

This would set column 5 in the array to 7.

myInt = myArray(5)

This will set the value of myInt to the current value of column 5 in myArray.

Two-Dimensional Arrays

A two-dimensional array is an array that has more than one row, along with the defined columns. A two-dimensional array is like a table, with a defined number of rows, where each row has a defined number of columns. The following code snippet defines an array using the default method.

Dim myArray(2, 10) As Integer

The first dimension defines the number of rows in the array, while the second dimension defines the number of columns in each row. In this example, the array has 3 rows, numbered 0 to 2, and each row has 11 columns, numbered 0 to 10.

You can also define the lower and upper bounds of the array.

Dim myArray(1 To 2, 1 To 10) As Integer

This definition would set the number of rows to 2, numbered 1 to 2 and the number of columns to 10, numbered 1 to 10.

Two-Dimensional Array Indexes

To access the array elements of a two-dimensional array, you would use two indexes. The first index selects the row, and the second index selects a column within that row.

myArray(1, 5) = 7

This code would set column 5 in row 1 to 7.

myInt = myArray(1, 5)

This code would set myInt to the current value contained within column 5 of row 1 of the array.

Multi-Dimensional Arrays

For arrays of three or more dimensions, you would use the same format as listed above, taking into account the progression of the array dimensions. For a three-dimensional array, the first dimension would be the row, the second the column, the third would be the z-order, or depth, of each column.

For example, to define a cube in space, you would use the y,x,z format, where y defines the vertical axis, x defines the horizontal axis and z defines the depth axis. To create an array in this format you could define the array as:

Dim myCube(y, x, z) As Integer.

MyCube(10, 10, 10) would create a cube with 11 vertical units, 0 to 10, 11 horizontal units, 0 to 10 and 10 depth units, 0 to 10. To access the center of the cube, you would use iCenter = myCube(5, 5, 5).

You will probably never need to use arrays of more than three dimensions, unless you are doing some advanced mathematical calculations. However, if you need to use higher-dimensional arrays, the same principles apply.

Dynamic Arrays

The arrays described above are static arrays; the array size cannot change during program execution. You can also create dynamic arrays that can change size during execution. Dynamic arrays are useful for creating data structures such as stacks or queues.

Data for dynamic arrays are allocated from the computer's pool of memory called heap. The compiler dynamically allocates memory for array data based on the requested dimensions of the array.

You specify a dynamic array by using the ReDim keyword.

ReDim myArray(1 To 5, 1 To 5) As Integer

If you don't know the needed array bounds at the start of the program execution, you can define an array with empty indexes.

Dim myArray() As Integer

In this case the compiler sets a default value of 0 for the array size. You can then use the ReDim at some point in the program to set the array bounds.

ReDim and ReDim Preserve

Dynamic arrays can change sizes during execution. ReDim will clear the contents of the array to the default data type values, while ReDim Preserve will keep intact the existing contents, unless the array size is smaller than the previous size.

Array Functions

There are a number of functions that you can use with arrays.

Arrays of Composite Types

Type definitions allow you to group related data into a single entity, and often you will need more than one instance of a type to fully express the data. Arrays of types allow you create multiple instances of a type definition that can be easily managed using the arrays functions. An example of this usage may be an inventory system for your RPG, a series of document descriptions within an editor, and a set of employee records from a random access database.

You create arrays of types just as you would with any of the intrinsic data types. The following code snippet illustrates the syntax.

Type myPoint
    row As Integer
    col As Integer
End Type

Type myLine
    p1 As myPoint
    p2 As myPoint
    char As String * 1
End Type

Dim myLineSet (1 To 3) As myLine

The code defines a set of 3 lines, with endpoints p1 and p2, where each endpoint is located at row and col. You access the array elements by using a combination of array index and dot operator.

myLineSet(1).p1.row = 1
myLineSet(1).p1.col = 1
myLineSet(1).p2.row = 10
myLineSet(1).p2.col = 10
myLineSet(1).char = Chr(219)

Arrays in Types

Not only can you create an array of a composite type, you can have an array as a field in a composite type. The above example can be written more efficiently by replacing p1 and p2 with an array.

Type myPoint
    row As Integer
    col As Integer
End Type

Type myLine
    pts(1 To 2) As myPoint
    char As String * 1
End Type

Dim myLineSet (1 To 3) As myLine

Here pts is an array of myPoint. To access this structure you would use a combination of indexes and dot operators.

myLineSet(1).pts(1).row = 1
myLineSet(1).pts(1).col = 1
myLineSet(1).pts(2).row = 10
myLineSet(1).pts(2).col = 10
myLineSet(1).char = Chr(219)

myLineSet is an array, so you use an index value. pts is an element of the type, so you need to qualify it with the dot operator. However, pts is also an array, so you use an index to select each pts array element. Row and col are elements of the myPoint type and are accessed with the dot operator.

Using an array for the endpoints enables you to easily extend the line definition to support not only lines, but triangles and squares. The following code snippet shows one possible definition.

Type myObj
    objid As Integer
    Union
            myLine(1 To 2) As myPoint
        myTriangle(1 To 3) As myPoint
        mySquare(1 To 4) As myPoint
    End Union
    char As String * 1
End Type

The objid field would indicate which type of object is contained within the union definition. That is, a 1 may indicate a line, a 2 may indicate a triangle and a 3 may indicate a square. Since the definition defines a single object, a Union is used to enclose the endpoint arrays to maximize memory usage.

To print the object to the screen, you would examine the objid and then use the Lbound and Ubound on the appropriate endpoint array definition, printing the number of lines that correspond to the type of object.

One further enhancement you can make to this program is to add a function pointer to the type definition, and then write print routines that correspond to the type of object being printed. Using this technique will enable you to further extend the usefulness of the code by simplifying the process of adding new objects to the type definition.

For example, if you needed to be able to describe a cube, you would simply add an new array to the union, add a cube print function, and the type definition would be able to print a cube by simply adding a few lines of code, while keeping the original functionality intact.

Array Initialization

You can initialize an array with values when using the Dim statement in a manner similar to initializing any of the other intrinsic data types, and type definitions. The following code snippet illustrates the syntax using a one dimensional array.

Dim aArray(1 To 5) As Integer => {1, 2, 3, 4, 5}

This code snippet dimensions an integer array with 5 elements, then sets the elements to the list contained within the curly brackets. The arrow operator, => tells the compiler that the list following the Dim statement should be used to initialize the array.

You can also dimension multidimensional arrays in the same manner, by specifying blocks of data enclosed within curly braces as the following code snippet illustrates.

Dim bArray(1 To 2, 1 To 5) As Integer => {{1, 2, 3, 4, 5},{6, 7, 8, 9, 10}}

In this example, the first block, {1, 2, 3, 4, 5}, corresponds to row 1, and the second block, {6, 7, 8, 9, 10}, corresponds to row 2. Remember that FreeBASIC arrays are row-major, so the row is specified before the column. When you initialize an array in this manner, you must be sure that the number of elements defined will fit into the array.

Type Array Initialization

Not only can you initialize an array of simple data types, you can also initialize an array with composite types. The following code snippet illustrates a type array that contains an array as an element of the type.

Type aType
    a As Integer
    b As Byte
    c(1 To 2) As String * 10
End Type

Dim As aType myType(1 To 2) => { (1234, 12, {"Hello", "World"}), (5678, 24, {"From", "Freebasic"})}

The curly brackets signify that this is an array initialization, while the parenthesis indicate the type initialization. Since the type has an embedded array, you use the curly brackets to load the data into the embedded array, just as you would a stand-alone array. If the embedded array was a multidimensional array, then you would need to wrap each row in { and } just as you would a stand-alone array.

Using the -exx Compiler Switch

The -exx compiler switch will enable error and bounds checking within your program. If you go outside the bounds of an array within your program, the compiler will generate an "out of bounds" error while the program is running.

This is a great help in debugging your program, and finding problems associated with arrays. -exx will also inform of you of Null pointer assignments, so it is quite useful when working with pointers as well.

Using -exx does add quite of bit of additional code to your program, so once your program is functioning correctly, you will want to compile the program without the -exx switch.

Last Reviewed by Sancho3 on February 06, 2018


Introduction to the Extended Type
Written by rdc

Introduction

FreeBASIC is moving towards implementing Object Oriented programming. While classes have not yet been added to the language, the Type definition has been extended to include some Object Oriented constructs as a first step towards full class support. This article introduces some of the concepts of Object Oriented design and explains some of the extended type constructs.

Object Oriented Programming

Object Oriented Programming, usually shortened to OOP, is a methodology that enables the programmer to build code units called objects. An object is a thing; it is a unit of code that represents something that needs to be manipulated in a program. You can think of an object as a noun: a person, place or thing. An object could be a sprite, a drawing primitive or something more elaborate like a tank in a game. Any concrete entity that has a set of characteristics and actions can be represented as an object.

An object contains both the data needed by the object, and the methods (subroutines and functions) that act on the data. This grouping of data and methods into a single entity is called encapsulation. Encapsulation allows you to create modular units that can be reused in multiple programs. This idea of code reuse was the main motivation in the creation of the OOP paradigm.

Another beneficial consequence of encapsulation is information hiding. The data inside the object is shielded from the outside world so that unwanted changes to the data cannot occur. Instead of accessing a variable directly, the object has a public interface that the external program should use to access and change data members. By using an interface, you can control how the object behaves and ensure that its operation is consistent across many programs.

The interface also allows you to make internal changes to the code, without changing the way the object is accessed. As long as you don't change the published interface, that is change any existing public methods, you can improve the object without breaking any existing code that relies on the object. In the cases where a program my need an improved method, you can leave the old method in place to maintain compatibility, and just add a new method with the improved functionality. New programs can use the new method, while old programs can still use the old method.

Another advantage of using a public interface is so that other programmers can use your object without worrying about the internal details of the object. As long as the published interface is stable and well documented, anyone should be able to use your object, even beginners.

The Published Contract

As already stated, OOP was designed to enable code reuse among programmers. In order for code reuse to be helpful, the published interface must remain stable. That is, once an object has been released and is being used in programs, the published interface should not change so that programs that use the object continue to work correctly. There is an implicit contract between you as the author of the object and the end-user of your object that you will maintain the published interface across changes that may need to be made to the object. This implicit contract between author and user is the main strength of the OOP paradigm, and is the main reason that OOP has become such a powerful programming methodology.

The Characteristics of an Object

As already mentioned, an object contains both data and methods. The data describes the properties of an object, while the methods describe what the object can do. A simple, and not-really-useful example will illustrate this concept.

Suppose you want to create an object that draws a rectangle on the screen. A rectangle can have several properties that would be contained within the data members of the object. A rectangle has an origin on the screen, normally the top left corner, which can be represented by x and y data members. A rectangle has a width and a height, so the object would have width and height data members. A rectangle can either be outlined or filled, so a filled flag data member can be added to the object. Of course, if you are going to draw a rectangle, you will want to draw it in a particular color, so the object will need to have a color data member, and to have the object be a bit more flexible, you can add a color member for the outline and a different color member for the fill. Of course you will need a method to actually draw the rectangle on the screen, so you can add a draw routine to the object definition.

So our rectangle object has the following preliminary properties and methods:

Property: x and y origin
Property: width
Property: height
Property: filled
Property: outline color
Property: fill color
Method: DrawRect

This list is called the object definition. In FreeBASIC you define an object using the extended Type definition. The extended Type is similar to the standard Type, with some added language constructs that implements a subset of OOP features.

A Rectangle Type Definition

The following code snippet is a partial rectangle definition:
Type myRect
  Private:
    X_ As Integer
    Y_ As Integer
    Width_ As Integer
    Height_ As Integer
    Filled_ As Integer
    Otlncolor_ As Integer
    Fillcolor_ As Integer
    Public:
    Declare Sub DrawRect()
End Type

As you can see, the extended Type looks much like a standard Type except for the Private: and Public: keywords and the sub declaration. The Private: keyword tells the compiler that the data members that follow are private to the type, that is cannot be accessed outside of the type. The private state extends to all object members until a new qualifier is encountered, which in this case is the Public: qualifier just above the Sub declaration. All of the data members are hidden from the outside world and cannot be changed from outside the scope of the Type, a process called information hiding. The underscore appended to the private variables is the common way to define private variables.

Information hiding is a way to maintain the integrity of the object. You should never allow an outside process to directly access a data member. All data access should be through the use of Property members so that you can control what is being passed to your object. Strict control over your object's data will help prevent many errors that may occur when a programmer uses your object.
Type myRect
  Private:
    X_ As Integer
    Y_ As Integer
    Width_ As Integer
    Height_ As Integer
    Filled_ As Integer
    Otlncolor_ As Integer
    Fillcolor_ As Integer
    Public:
    Declare Sub DrawRect()
    Declare Property X(ByVal xx_ As Integer)
    Declare Property X() As Integer
    Declare Property Y(ByVal yy_ As Integer)
    Declare Property Y() As Integer
    Declare Property Width(ByVal w_ As Integer)
    Declare Property Width() As Integer
    Declare Property Height(ByVal h_ As Integer)
    Declare Property Height() As Integer
    Declare Property Filled(ByVal f_ As Integer)
    Declare Property Filled() As Integer
    Declare Property Otlncolor(ByVal oc_ As Integer)
    Declare Property Otlncolor() As Integer
    Declare Property FillColor(ByVal fc_ As Integer)
    Declare Property FillColor() As Integer
End Type

The Declare statements following the Public: qualifier comprises the public interface to your object. Since the variables of the type are defined with the Private: keyword, the only way to access the variables is through the Property members maintaining the integrity of the object. Since you define the code in each Property member, you have full control over what is being put into your object. A common example of this is to put range checking code in your property members so that the object does not contain invalid data.

In this example, the variables can be both written and read. The compiler distinguishes between a read Property and a write Property by the type of the method. A subroutine-formatted Property is a write property since you are passing a value that will be saved in a private variable. A function-formatted Property is a read property since a private variable will be returned to the caller. You can create read-only Properties by adding just a function-formatted Property or write-only Properties by just adding a subroutine-formatted Property.

Creating Well-Behaved Objects

The definition looks complete at this point, but there is a problem. What would happen if some or all of the variables were not initialized? The object would not perform correctly and potentially generate a runtime error. It would be better to have a set of default values for the object variables just in case one or more variables did not get initialized. You can initialize the object at the moment of creation by using a Constructor.

A Constructor is a subroutine that is called when the object is created using the Dim (or New) statement. Constructors are useful for initializing an object, either with default values, or values you pass to the Constructor. The updated type definition now looks like the following:
Type myRect
  Private:
    X_ As Integer
    Y_ As Integer
    Width_ As Integer
    Height_ As Integer
    Filled_ As Integer
    Otlncolor_ As Integer
    Fillcolor_ As Integer
    Public:
    Declare Sub DrawRect()
    Declare Property X(ByVal xx_ As Integer)
    Declare Property X() As Integer
    Declare Property Y(ByVal yy_ As Integer)
    Declare Property Y() As Integer
    Declare Property Width(ByVal w_ As Integer)
    Declare Property Width() As Integer
    Declare Property Height(ByVal h_ As Integer)
    Declare Property Height() As Integer
    Declare Property Filled(ByVal f_ As Integer)
    Declare Property Filled() As Integer
    Declare Property Otlncolor(ByVal oc_ As Integer)
    Declare Property Otlncolor() As Integer
    Declare Property FillColor(ByVal fc_ As Integer)
    Declare Property FillColor() As Integer
    Declare Constructor()
    Declare Constructor(xx_ As Integer, yy_ As Integer, w_ As Integer, _
                        h_ As Integer, f_ As Integer, oc_ As Integer, _
                        fc_ As Integer)

End Type

You will notice in the definition that we have two Constructors, one that takes a set of parameters and one that doesn't. This is called overloading and can be used not only with Constructors but also with other subroutines and functions. Overloading is useful for situations where you need to handle different parameter types with a single method call. The compiler will determine which method to call based on the parameters passed to the method. You can overload as many methods as you want, as long as the number and type of parameters for each method is unique.

In this instance, if the Constructor is not passed any parameter values, it will initialize the variables to a set of default values. If the Constructor is called with parameters, then it will use the passed values to initialize the object's variables.

There is also a Destructor method that is called when the object is destroyed. You can use the Destructor to perform any cleanup tasks that must be carried out before the object is released from memory. If the object created any pointer references, or opened any files, then you would clean up those references in the Destructor. Since the Rectangle object doesn't create any outside references, a Destructor is not needed.

Filling in the Object Methods

The type definition is a template for the object type and tells the compiler how to set up the object in memory. However, in order to actually use the object, you need to create the actual method calls, which is shown in the next listing.
Type myRect
  Private:
    X_ As Integer
    Y_ As Integer
    Width_ As Integer
    Height_ As Integer
    Filled_ As Integer
    Otlncolor_ As Integer
    Fillcolor_ As Integer
    Public:
    Declare Sub DrawRect()
    Declare Property X(ByVal xx_ As Integer)
    Declare Property X() As Integer
    Declare Property Y(ByVal yy_ As Integer)
    Declare Property Y() As Integer
    Declare Property Width(ByVal w_ As Integer)
    Declare Property Width() As Integer
    Declare Property Height(ByVal h_ As Integer)
    Declare Property Height() As Integer
    Declare Property Filled(ByVal f_ As Integer)
    Declare Property Filled() As Integer
    Declare Property Otlncolor(ByVal oc_ As Integer)
    Declare Property Otlncolor() As Integer
    Declare Property FillColor(ByVal fc_ As Integer)
    Declare Property FillColor() As Integer
    Declare Constructor()
    Declare Constructor(xx_ As Integer, yy_ As Integer, w_ As Integer, _
                        h_ As Integer, f_ As Integer, oc_ As Integer, _
                        fc_ As Integer)
End Type

Sub myRect.DrawRect()
    Line (this.x_, this.y_)-(this.x_ + Width - 1, this.y_ + this.height_ - 1), this.Otlncolor_, B
    If this.Filled_ <> 0 Then
        Paint (this.x_ + 1, this.y_ + 1), this.Fillcolor_, this.Otlncolor_
    End If
End Sub

Property myRect.x(ByVal xx_ As Integer)
    this.X_ = xx_
End Property

Property myRect.x() As Integer
    Return this.X_
End Property

Property myRect.y(ByVal yy_ As Integer)
    this.Y_ = yy_
End Property

Property myRect.y() As Integer
     Return this.y_
End Property

Property myRect.Width(ByVal w_ As Integer)
    this.Width_ = w_
End Property

Property myRect.Width() As Integer
    Return this.Width_
End Property

Property myRect.Height(ByVal h_ As Integer)
    this.Height_ = h_
End Property

Property myRect.Height() As Integer
    Return this.Height_
End Property

Property myRect.Filled(ByVal f_ As Integer)
    this.Filled_ = f_
End Property

Property myRect.Filled() As Integer
    Return this.Filled_
End Property

Property myRect.Otlncolor(ByVal oc_ As Integer)
    this.Otlncolor_ = oc_
End Property

Property myRect.Otlncolor() As Integer
    Return this.Otlncolor_
End Property

Property myRect.FillColor(ByVal fc_ As Integer)
    this.Fillcolor_ = fc_
End Property

Property myRect.FillColor() As Integer
    Return this.Fillcolor_
End Property

Constructor myRect
    this.X_ = 0
    this.Y_ = 0
    this.Width_ = 10
    this.Height_ = 10
    this.Filled_ = 0
    this.Otlncolor_ = 15
    this.Fillcolor_ = 7
End Constructor

Constructor MyRect (xx_ As Integer, yy_ As Integer, w_ As Integer, _
                        h_ As Integer, f_ As Integer, oc_ As Integer, _
                        fc_ As Integer)

    this.X_ = xx_
    this.Y_ = yy_
    this.Width_ = w_
    this.Height_ = h_
    this.Filled_ = f_
    this.Otlncolor_ = oc_
    this.Fillcolor_ = fc_
End Constructor

The Methods and Properties are defined using the Sub/Function/Property TypeName.methodname syntax. This tells the compiler how to match up methods with the proper type definition. The Constructors are defined with the type name for the same reason. The this identifier is a hidden parameter that is passed to the methods that refers to the defined type. You use the this identifier to specify that you want to access the type constructs.

Using Your Object

The object is now complete can be used in a program which is listed below.
Type myRect
  Private:
    X_ As Integer
    Y_ As Integer
    Width_ As Integer
    Height_ As Integer
    Filled_ As Integer
    Otlncolor_ As Integer
    Fillcolor_ As Integer
    Public:
    Declare Sub DrawRect()
    Declare Property X(ByVal xx_ As Integer)
    Declare Property X() As Integer
    Declare Property Y(ByVal yy_ As Integer)
    Declare Property Y() As Integer
    Declare Property Width(ByVal w_ As Integer)
    Declare Property Width() As Integer
    Declare Property Height(ByVal h_ As Integer)
    Declare Property Height() As Integer
    Declare Property Filled(ByVal f_ As Integer)
    Declare Property Filled() As Integer
    Declare Property Otlncolor(ByVal oc_ As Integer)
    Declare Property Otlncolor() As Integer
    Declare Property FillColor(ByVal fc_ As Integer)
    Declare Property FillColor() As Integer
    Declare Constructor()
    Declare Constructor(xx_ As Integer, yy_ As Integer, w_ As Integer, _
                        h_ As Integer, f_ As Integer, oc_ As Integer, _
                        fc_ As Integer)
End Type

Sub myRect.DrawRect()
    Line (this.x_, this.y_)-(this.x_ + this.Width_ - 1, this.y_ + this.height_ - 1), this.Otlncolor_, B
    If this.Filled_ <> 0 Then
        Paint (this.x_ + 1, this.y_ + 1), this.Fillcolor_, this.Otlncolor_
    End If
End Sub

Property myRect.x(ByVal xx_ As Integer)
    this.X_ = xx_
End Property

Property myRect.x() As Integer
    Return this.X_
End Property

Property myRect.y(ByVal yy_ As Integer)
    this.Y_ = yy_
End Property

Property myRect.y() As Integer
     Return this.y_
End Property

Property myRect.Width(ByVal w_ As Integer)
    this.Width_ = w_
End Property

Property myRect.Width() As Integer
    Return this.Width_
End Property

Property myRect.Height(ByVal h_ As Integer)
    this.Height_ = h_
End Property

Property myRect.Height() As Integer
    Return this.Height_
End Property

Property myRect.Filled(ByVal f_ As Integer)
    this.Filled_ = f_
End Property

Property myRect.Filled() As Integer
    Return this.Filled_
End Property

Property myRect.Otlncolor(ByVal oc_ As Integer)
    this.Otlncolor_ = oc_
End Property

Property myRect.Otlncolor() As Integer
    Return this.Otlncolor_
End Property

Property myRect.FillColor(ByVal fc_ As Integer)
    this.Fillcolor_ = fc_
End Property

Property myRect.FillColor() As Integer
    Return this.Fillcolor_
End Property

Constructor myRect
    this.X_ = 0
    this.Y_ = 0
    this.Width_ = 10
    this.Height_ = 10
    this.Filled_ = 0
    this.Otlncolor_ = 15
    this.Fillcolor_ = 7
End Constructor

Constructor MyRect (xx_ As Integer, yy_ As Integer, w_ As Integer, _
                        h_ As Integer, f_ As Integer, oc_ As Integer, _
                        fc_ As Integer)

    this.X_ = xx_
    this.Y_ = yy_
    this.Width_ = w_
    this.Height_ = h_
    this.Filled_ = f_
    this.Otlncolor_ = oc_
    this.Fillcolor_ = fc_
End Constructor

'Create a graphic screen
Screen 18

'Create an object using the default constrcutor
Dim aRect As myRect
'Create an object by explicitly setting the constructor values
Dim bRect As myRect = myRect(200, 200, 200, 100, 1, 15, 9)

'Draw the rectangles on the screen
aRect.DrawRect
bRect.DrawRect

'Update aRect properties
aRect.X = 90
aRect.Y = 20
aRect.Filled = 1
aRect.FillColor = 15

'Draw new rect
aRect.DrawRect
Sleep
End

To initialize the object using the default Constructor, you simply Dim the extended Type just as you would the standard type. If the Constructor only takes a single value then you can use the Dim var as Typename = value syntax. To initialize the object with a set of values, you Dim the type and then use the = typename(par1m parm1...) syntax. You can see that accessing the members of the object is just like accessing the member of a standard type.

Thanks to cha0s at the FreeBASIC forums for the information regarding Properties.


Introduction to Variable Scope
Written by rdc

Variable Scope

Scope refers to the visibility of a variable, where you can access a variable within a program. Before you can understand the different levels of scope, you need to understand the structure of a program in FreeBASIC.

Program Structure

A complete program is composed of one or more .bas files, called modules. Each module can contain both module level code, and code contained within subroutines and functions. Module level code is code that is not contained within a subroutine or function. The following snippet illustrates the various parts of a module.

Dim aInt As Integer 'Variable declared at module level

Sub DoSomething
    Dim aInt As Integer 'Variable declared at sub level

    ... 'This code is local to sub
End Sub

Function DoSomethingElse() As Integer
    Dim aInt As Integer 'Variable declared at func level

    ... 'This code is local to func
End Function

'Module level code
aInt = 5
DoSomething
aInt = DoSomethingElse()

Local Variables

If you define a variable at the module level (and not using Shared), the variable is said to have local module level scope. It is visible to the module level code, but not to any subroutine or function within the module. In the example above the module variable aInt is only visible to the module level code.

Variables defined within a subroutine or function are local to the subroutine or function and are not visible to module level code or any other subroutine or function.

Variables Defined Within Control Structures

Variables that are defined within If, For-Next, While-Wend and Do-Loop constructs are local to the control structure block code. That is, they are not visible outside the bounds of the begin and end of the control block, just like a variable declared within a subroutine or function.

Shared Variables

In the example, if you wanted aInt to be visible within the subroutine or function, you would need to declare the variable as Shared and then not declare a variable with the same name within any subroutine, function or control block. Shared variables are visible to module level code, subroutine or function level code and within control structure blocks.

Scope Conflicts

In the code snippet above, if aInt were declared as Shared, and each subroutine and function declared aInt, there would be a scope conflict, since there is one variable name used for different levels of scope.

The compiler resolves this by taking the current scope into account and mapping the variable within that scope. Since subroutines and functions have a lower level of scope than the module, aInt would refer to the variable declared within the sub or func, and not the one declared at the module level, even though it is declared as a shared variable.

Multiple Modules

Scope is limited to a single module, that is a single .bas file. However, it is sometimes necessary to extend the scope from one module to another. You would use the Common statement when you declare a variable that needs to be shared among modules.

Each module must have the same Common declaration in order for the compiler to match up the common variables. If you declare a variable in module1 as Common aInt as Integer then module2 must also have Common aInt as Integer. Without the common declaration aInt would not be visible within module2.

You can add the Shared attribute to Common, that is Common Shared to not only extend scope to multiple modules, but to extend scope within a module. Common Shared operates the same as Shared within a single module. As with Common, you need to have matching declarations in each module that needs access to the variable.

Scope...End Scope

You can create a temporary scope block by using the Scope, End Scope keywords. The scope block is very useful when creating multi-line macros where you may need to create some temporary working variables but do not want to introduce name conflicts in the program. The following snippet illustrates how to create a scope block.

Scope
    Dim tmp As Integer
    ... 'Some code
End Scope

The scope of any variable created within a scope block is limited to the block itself. However, the scope block inherits the visibility of the surrounding scope so that variables created at the same scope as the scope block are visible within the block.

For example, if you have aInt which is at module level scope, and the scope block is at module level scope, then aInt would be visible inside the scope block. Unless of course there is a scope conflict, in which case the variable inside the scope block would override the variable with the same name outside the scope block.

Variable Lifetime

Not only does scope set the visibility of a variable, it also determines the lifetime of a variable. A variable goes through several stages in its lifetime; creation, initialization, access and destruction. When this occurs depends on the scope of a variable, that is, where the variable has been defined within the program.

Module Level Variables

Module level variables exist for the life of a program, since they are declared within the main body of the program. Module level code is the main executing code of the program, and terminates when the program ends.

Subroutine and Function Level Variables

Variables declared within a subroutine and function exist as long as the program is within the body of the subroutine and function. On entering the sub/func, the variable is created, initialized and can be accessed within the sub/func. Once the subroutine or function exits, the variable is destroyed.

Static Variables

One exception to the declared sub/func variable is the Static variable. Static variables maintain their value between calls to the subroutine or function and so have a module level lifespan.

Control Block Variables

Variables declared within a control block, such as a For-Next, exist as long as the control block is executing. Upon leaving the control block, the variables are destroyed.

Scope...End Scope Variables

Variables declared within a scope block exist as long as the the scope block exists. Once the program leaves the scope block, any variables created within the scope block are destroyed.

Last Reviewed by Sancho3 on February 06, 2018


Introduction to the Type Def
Written by rdc

There are times when creating a program that you may want to define an aggregate structure such as a personnel record, or an enemy in a game. While you can do this using individual data types, it is hard to manage within a program. Composite data types allow you to group together related data items into a single structure that can be manipulated as a single entity. FreeBASIC offers two composite data types, the Type and Union.

Types

FreeBASIC allows you to group several data types into a unified structure called a Type definition which you can use to describe these aggregate data structures.

The basic structure of a type definition is:

Type typename
    Var definition
    Var definition
    ...
End Type

The Type-End Type block defines the scope of the definition. You define the elements of the type structure in the same manner as using the Dim keyword, without using Dim. The following code snippet shows how to build an employee type.

Type EmployeeType
    fname As String * 10
    lname As String * 10
    empid As Integer
    dept As Integer
End Type

You can use any of the supported data types as data elements, including pointers and other type definitions. When you create the type definition, such as in the example above, you are just creating a template for the compiler. In order to use the type definition, you need to create a variable of the type, as the following code snippet illustrates.

Dim Employee As EmployeeType

Once you have created a variable of the type, you can access each element within the type using the dot notation var_name.field_name.

Using the above example, to access the fname field you would use:

Employee.fname = "Susan"

Using With

To access multiple fields at a time, you can use the With-End With block. The following code snippet shows how to use the With block with the above example.

With Employee
    .fname = "Susan"
    .lname = "Jones"
    .empid = 1001
        .dept = 24
End With

The compiler will automatically bind the variable Employee to the individual data elements within the With block. Not only does mean that you don't have as much typing, but the structure is optimized and is a bit faster than using the full dot notation.

Passing Types to Subroutines and Functions

One advantage to using types in your program is that you can pass the structure to a subroutine or function and operate on the structure as a whole. The following code fragment shows a partial subroutine definition.

Sub UpdateEmployeeDept(ByRef Emp As EmployeeType)
    .
    .
    .
End Sub

Notice that the parameter is qualified with Byref. This is important since you want to update the type within the subroutine. There are two parameter passing modes in FreeBASIC: Byref and Byval.

ByRef and ByVal: A Quick Introduction

Byref and Byval tell the compiler how to pass a reference to the subroutine or function. When you use Byref, or By Reference, you are passing a pointer reference to the parameter, and any changes you make to the parameter inside the sub or func will be reflected in the actual variable that was passed. In other words, the Byref parameter points to the actual variable in memory.

Byval, or By Value, on the other hand makes a copy of the parameter, and any changes you make inside the sub or func are local and will not be reflected in the actual variable that was passed. The Byval parameter points to a copy of the variable not the actual variable itself.

The default for FreeBASIC .17 is to pass parameters using Byval. In order to change a passed parameter, you need to specify the Byref qualifier. In this example, the subroutine updates the the department id of the employee type, so the parameter is qualified as Byref so that the subroutine can update the dept field of the type variable.
(Editors Note: Byval remains the default in the current version of FreeBASIC)

On the other hand you may not need to update the type as in the following code fragment.

Sub PrintEmployeeRecord(Emp As EmployeeType)
    .
    .
    .
End Sub

In this sub you are just printing the employee record to the screen or a printer and do not need to change anything in the type variable. Here the default Byval is used which passes a copy of the employee record to the sub rather than a reference to the variable. By using Byval in this case, you won't accidentally change something in the type variable that you didn't intend to change.

You should only use Byref if you intend to change the parameter data. It is much safer to use Byval in cases where you need to have the parameter data, but want to prevent accidental changes to the data. These accidental changes generate hard-to-find bugs in your program.

Types Within Types

In addition to the intrinsic data types, type fields can also be based on a type definition. Why would you want to do this? One reason is data abstraction. The more general your data structures, the more you can reuse the code in other parts of your program. The less code you have to write, the less chance of errors finding their way into your program.

Using the Employee example, suppose for a moment that you needed to track more dept information than just the department id. You might need to keep track of the department manager, the location of the department, such as the floor or the building, or the main telephone number of the department. By putting this information into a separate type definition, you could use this information by itself, or as part of another type definition such as the Employee type. By generalizing your data structures, your program will be smaller, and much more robust.

Using a type within a type is the same as using one of the intrinsic data types. The following code snippets illustrates an expanded department type and an updated employee type.

Type DepartmentType
    id As Integer
    managerid As Integer
    floor As Integer
End Type

Type EmployeeType
    fname As String * 10
       lname As String * 10
        empid As Integer
        dept As DepartmentType
End Type

Dim Employee As EmployeeType

Notice that in the Employee definition the dept field is defined as DepartmentType rather than as one of the intrinsic data types. To access the department information within the Employee type, you use the compound dot notation to access the dept fields.

Employee.dept.id = 24
Employee.dept.managerid = 1012
Employee.dept.floor = 13

The top level of the type definition is Employee, so that reference comes first. Since dept is now a type definition as well, you need to use the dept identifier to access the individual fields within the DepartmentType. Employee refers to the employee type, dept refers to the department type and id, managerid and floor are fields within the department type.

You can even carry this further, by including a type within a type within a type. You would simply use the dot notation of the additional type level as needed. While there is no limit on the levels of nested type definitions, it gets to be a bit unwieldy when used with several levels.

With and Nested Types

You can also use the With-End With block with nested types, by nesting the With block, as illustrated in the following code snippet.

With Employee
       .fname = "Susan"
        .lname = "Jones"
        .empid = 1001
        With .dept
            .id = 24
            .managerid = 1012
            .floor = 13
        End With
End With

Notice that the second With uses the dot notation, .dept, to specify the next level of type definitions. When using nested With blocks, be sure that you match all the End With statements with their correct With statements to avoid a compile error.

Type Assignments

Extending the idea of data abstraction further, it would be nice to be able to separate the initialization of the department type from the initialization of the employee type. By separating the two functions, you can easily add additional department information as needed. This is where you can use type assignments.

Just as you can assign one intrinsic data type to another, you can assign one type variable to another type variable, providing they share the same type definition.

The following code snippet abstracts the department initialization function and assigns the result to the department type within the Employee type.

'This function will init the dept type and return it to caller
Function InitDept(deptid As Integer) As DepartmentType
    Dim tmpDpt As DepartmentType

    Select Case deptid
        Case 24 'dept 24
        With tmpDpt
                    .id = deptid
                    .managerid = 1012
                    .floor = 13
            End With
        Case 48 'dept 48
             With tmpDpt
                    .id = deptid
                    .managerid = 1024
                    .floor  = 12
                End With
        Case Else 'In case a bad department id was passed
                With tmpDpt
                    .id = 0
                    .managerid  = 0
                    .floor  = 0
                End With
    End Select

    'Return the dept info
    Return tmpDpt
End Function

'Create an instance of the type
Dim Employee As EmployeeType

'Initialize the Employee type
With Employee
    .fname = "Susan"
    .lname = "Jones"
    .empid = 1001
    .dept = InitDept(24) 'get dept info
End With

As you can see in the snippet, the dept field of the employee type is initialized with a function call. The InitDept function returns a DepartmentType and the compiler will assign that type to the dept field of the Employee record.

By just adding a simple function to the program, you have made the program easier to maintain. If a new department is created, you can simply update the InitDept function with the new department information, recompile and the program is ready to go.

Bit Fields

There is yet another data type that can be used in type definitions, the bit field. Bit fields are defined as variable_name: bits As DataType. The variable name must be followed with a colon, the number of bits, followed by the data type. Only integer types (all numeric types excluding the two floating-point types 'single' and 'double', and excluding also the 64-bit types for 32-bit development) are allowed within a bit field. Bit fields are useful when you need to keep track of boolean type information. A bit can be either 0 or 1, which may represent Yes or No, On or Off or even Black and White.

The following code snippet illustrates a bit field definition.

Type BitType
    b1: 1 As Integer
    b2: 4 As Integer
End Type

b1 is defined as a single bit, and b2 is defined as four bits. You initialize the bitfields by passing the individual bits to the type fields.

myBitType.b1 = 1
myBitType.b2 = 1101

The data type of the bit field determines how many bits you can declare in a bit field. Since an integer is 32/64 bits long (on 32/64bit systems), you could declare up to 32/64 bits in the field. However, in most cases you would declare a single bit for each field, and use a number of fields to define the bit masking that you wish to use. Using a single bit simplifies the coding you need to do to determine if a bit is set or cleared and allows you to easily identify what a bit means within the type definition.

The Field Property

When you create a variable of a type definition, the type is padded in memory. The padding allows for faster access of the type members since the type fields are aligned on a 4 byte or Word boundary. However, this can cause problems when trying to read a type record from a file that is not padded. You can use the use field property to change the padding of a type definition.

The field keyword is used right after the type name and can have the values 1, for 1 byte alignment (no padding), 2 for 2 byte alignment and 4 for 4 byte alignment. To define a type with no padding you would use the following syntax.

Type myType Field = 1
     v1 As Integer
    v2 As Byte
End Type

For 2 byte alignment you would use field = 2. If no field = property is assigned, then the padding will be 4 bytes. If you are reading a type definition created by FreeBASIC using the default alignment, then you do not need to use the field property.

If you are reading a "Quick Basic" type record, then you will need to use field = 1, as QB used byte alignment by default.

Type Initialization

You can initialize a type definition when you dimension the type just as you can any of the intrinsic variables. The following code snippet illustrates the syntax.

Type aType
        a As Integer
        b As Byte
        c As String * 10
End Type

Dim myType As aType => (12345, 12, "Hello")

In the Dim statement, the arrow operator => is used to tell the compiler that you are initializing the type variable. The type element values must be enclosed in parenthesis, and separated by commas. The order of the value list corresponds to the order of the type elements, where a will be set to 12345, b to 12 and c to "Hello".

You cannot initialize a dynamic string within a type definition using this method. The string must be fixed length.

Initializing a type definition in a Dim statement is useful when you need to have a set of initial values for a type, or values that will not change during program execution. Since the values are known at compile time, the compiler can doesn't have to spend cycles loading the values during runtime.

Unions

Unions look similar to Types in their definition.

Union aUnion
    b As Byte
    s As Short
    i As Integer
End Union

If this were a Type, you could access each field within the definition. For a Union however, you can only access one field at any given time; all the fields within a Union occupy the same memory segment, and the size of the Union is the size of the largest member.

In this case, the Union would occupy four/eight bytes (on 32/64bit systems), the size of an Integer, with the b field occupying 1 byte, the s field occupying 2 bytes, and the i occupying the full 4/8 bytes (on 32/64bit systems). Each field starts at the first byte, so the s field would include the b field, and the i field would include both the b and s fields.

Types in Unions

A good example of using a type definition in a union is the Large_Integer definition found in winnt.bi. The Large_Integer data type is used in a number of Windows functions within the C Runtime Library. The following code snippet shows the Large_Integer definition.

Union LARGE_INTEGER
    Type
        LowPart As DWORD
        HighPart As Long
    End Type
    QuadPart As LONGLONG
End Union

The Dword data type is defined in windef.bi as a FreeBASIC Ulong, and the Longlong type is defined as a Longint. A Long is a fixed 32-bit integer type. Remember that a type occupies contiguous memory locations, so the HighPart field follows the LowPart part field in memory. Since this is a union, the type occupies the same memory segment as the QuadPart field.

When you set QuardPart to a large integer value, you are also setting the values of the type fields, which you can then extract as the LowPartand HighPart. You can also do the reverse; that is by setting the LowPart and HighPart of the type, you are setting the value of the QuadPart field.

As you can see, using a type within a union is an easy way to set or retrieve individual values of a component data type without resorting to a lot of conversion code. The layout of the memory segments does the conversion for you, providing that the memory segments make sense within the context of the component type.

In the Large_Integer case, the LowPart and HighPart have been defined to return the appropriate component values. Using values other than Dword and Long would not return correct values for LowPart and HighPart. You need to make sure when defining a type within a union, you are segmenting the union memory segment correctly within the type definition.

Unions in Types

A union within a type definition is an efficient way to manage data when one field within a type can only one of several values. The most common example of this is the Variant data type found in other programing languages.

FreeBASIC does not have a native Variant data type at this time. However, by using the extended Type syntax, you could create a Variant data type for use in your program.

When using a Union within a type it is common practice to create an id field within the type that indicates what the union contains at any given moment. The following code snippet illustrates this concept.

'Union field ids
#define vInteger 0
#define vDouble 1

'Define type def with variable data fields
Type vType
    vt_id As Integer
    Union
        d As Double
        i As Integer
    End Union
End Type

The union definition here is called an anonymous union since it isn't defined with a name. The vt_id field of the type definition indicates the value of the union. To initialize the type you would use code like the following.

Dim myVarianti As vType
Dim myVariantd As vType

myVarianti.vt_id = vInteger
myVarianti.i = 300

myVariantd.vt_id = vDouble
myVariantd.d = 356.56

myVarianti contains an integer value so the id is set to vInteger. myVariantd contains a double so the id is set to vDouble. If you were to create a subroutine that had a vType parameter, you could examine the vt_type field to determine whether an integer or double had been passed to the subroutine.

You cannot use dynamic strings within a union.

Using a combination of unions and types within a program allows you to design custom data types that have a lot of flexibility, but care must be taken to ensure that you are using the data constructs correctly. Improper use of these data types can lead to hard-to-find bugs. The benefits however, out-weigh the risks and once mastered, are a powerful programming tool.

Last Reviewed by Sancho3 on February 06, 2018


Using Linked Lists

A linked list is a structure that is easily expandable by using a single function, and it comes in very useful when you need an array of something but you have no idea how many. The concept behind a linked list is that each node structure has a pointer to the next and previous node structure. This is called a double linked list, as it links to two different nodes. By using a pointer to a structure, you can specify a null pointer if there is no next or previous node, and since the pointer stores a memory address, the amount of nodes you can store is limited only by memory.

The only downside to using a linked list is that in order to store say an integer, you have to allocate space not only for that integer, but also a structure that contains a pointer to the integer and a pointer to the surrounding nodes. This doesn't make much of a difference on today's computers however, unless you are storing millions of nodes.

The basic structure of the linked list is the node. The declaration is this:
Type listnode
    As Any Ptr pData
    As listnode Ptr pNext
    As listnode Ptr pPrev
End Type

As a side note, if whoever has access to these scripts would like to update it so it contains the keywords new to FreeBASIC (such as ptr), feel free to :) Also, LIST doesn't appear to be an FB keyword (correct me if I'm wrong).

This structure contains three pointers. The first is a pointer to anything (Any Ptr), that means that you can store strings, integers, characters, even user defined types and unions. But it also means that you must pass a pointer. You can obtain a pointer by using the Allocate (or CAllocate) function.
The next two pointers are pointers to listnodes, that is, you are technically allowed to do this:
Print node->pNext->pNext->pNext->pNext->pNext...
since each node contains a pointer to another node. The problem with the above syntax is that you are limited to how many nodes you can access and the code gets hard to understand. You can use the ListGetNext function for this purpose, and loop with a While loop.

Before we go any further, let's see all the declarations for using linked lists. Note that every function has a prefix of "List".

Declare Function ListCreate() As listnode Ptr
Declare Function ListAdd(list As listnode Ptr, item As Any Ptr) As Any Ptr
Declare Function ListAddHead(list As listnode Ptr, item As Any Ptr) As Any Ptr
Declare Function ListGetFirst(list As listnode Ptr) As listnode Ptr
Declare Function ListGetLast(list As listnode Ptr) As listnode Ptr
Declare Function ListGetNext(list As listnode Ptr) As listnode Ptr
Declare Function ListGetPrev(list As listnode Ptr) As listnode Ptr
Declare Function ListGetData(list As listnode Ptr) As Any Ptr
Declare Function ListRemove(list As listnode Ptr, bDelete As Integer = 0) As listnode Ptr
Declare Sub ListRemoveAll(list As listnode Ptr, bDelete As Integer = 0)

Edit: Hmm, it doesn't seem to like my use of "Rem" in a function. It compiles fine though.

You can see that there is a function to create a linked list, to add an item, to get various nodes, get data, and to remove nodes. Currently we'll focus on the ListCreate function. It takes no parameters and returns a listnode pointer. The structure that it creates has no data filled out. The whole structure is null, but it is still a structure. If you add a node, the pNext member will change and point to the new item, so it won't stay as a null node, since there would be no purpose of that. However, the value returned by ListCreate won't have any data stored in it and it won't have a previous node.

The function ListCreate looks like this:
' CREATE
Function ListCreate() As listnode Ptr
    Dim As listnode Ptr pTemp
    pTemp = CAllocate(Len(listnode))
    ' CAllocate automatically zeroes memory.

    Return pTemp
End Function

I prefer to use the Return instruction to return a value from a function, but FUNCTION = pTemp and ListCreate = pTemp are also allowed, although they don't immediately exit the function.

The point of this function is easy to see, a node is allocated and returned. The comment says that the CAllocate function automatically zeroes memory. If you used the Allocate function, the memory would not be zeroed automatically and you would have to do that on your own.

The next functions, ListAdd and ListAddHead, add a node to the list. ListAdd appends a node to the end of the list (the tail), while ListAddHead puts a node at the very top (the head).

' ADD, ADDHEAD

Function ListAdd(list As listnode Ptr, item As Any Ptr) As Any Ptr
    Dim As listnode Ptr pTemp

    If (list = 0) Then Return item

    pTemp = ListGetLast(list)

    pTemp->pNext = CAllocate(Len(listnode))
    pTemp->pNext->pPrev = pTemp
    pTemp->pNext->pData = item

    Return item
End Function

Function ListAddHead(list As listnode Ptr, item As Any Ptr) As Any Ptr
    Dim As listnode Ptr pTemp

    If (list = 0) Then Return item

    pTemp = list->pNext
    list->pNext = CAllocate(Len(listnode))

    list->pNext->pPrev = list
    list->pNext->pData = item
    list->pNext->pNext = pTemp

    If (pTemp <> 0) Then
        pTemp->pPrev = list->pNext
    End If

    Return item
End Function

You can see that ListAdd makes a reference to a function not shown yet, ListGetLast. For now all you have to know is that it returns a pointer to the last node in the list. It will be covered later.

ListAdd retrieves the last node and sets its pNext pointer to a new listnode structure. This won't cause memory loss since the last node has a null pNext value because nothing comes after it. Once our node is added, we can access it using the -> operator. The line
pTemp->pNext->pPrev = pTemp
is the whole basis of linked lists, the linking part. What this says is that we have a reference to a node. That node knows where the next node is, and now we're telling the node after that next one where the previous one is. It may look a little redundant at first, but the compiler doesn't know where the nodes are until you set them. Once you've done this, you can step through the linked list.

The ListAddHead function is a little more complicated, since we're actually inserting a node between the current first node and the null node from ListCreate. What it does basically is allocates space to hold the current first node, creates a new node there, and links them all together. If you study it a little, it should seem a lot clearer. The If statement at the end just makes sure that we're not trying to access memory that doesn't exist (NULL->pPrev). If pTemp does not in fact equal zero, then its pPrev member will be assigned. Otherwise, there is no reason to worry about it.

The next functions are ListGetFirst and ListGetLast. I implemented them next because ListGetLast was referenced in an above function.

' GETFIRST, GETLAST

Function ListGetFirst(list As listnode Ptr) As listnode Ptr
    If (list = 0) Then Return 0

    Return list->pNext
End Function

Function ListGetLast(list As listnode Ptr) As listnode Ptr
    Dim As listnode Ptr pTemp

    If (list = 0) Then Return 0

    pTemp = list
    While (pTemp->pNext <> 0)
        pTemp = pTemp->pNext
    Wend

    Return pTemp
End Function

The first function is probably the shortest and easiest function to understand, although it relies on the fact that you are holding a pointer to the node returned by ListCreate. If you don't do this, it could return any random node. All it does is return a pointer to the first node, or the node that comes right after the null node.

The second function, ListGetLast, loops through the list until it finds a null node. The reason I check if pTemp->pNext = 0 instead of pTemp = 0 is that I don't want to return zero. I want to return the last node, which is the node that has its pNext value set to zero. Once that node is found, ListGetLast returns it.

The next 3 functions are just helper functions, and could be easily accomplished with one line of code. They really exist because the original implementation not written by me had a ListGetNext function.

' GETNEXT, GETPREV

Function ListGetNext(list As listnode Ptr) As listnode Ptr
    If (list = 0) Then Return 0

    Return list->pNext
End Function

Function ListGetPrev(list As listnode Ptr) As listnode Ptr
    ' can't do anything to a null list
    If (list = 0) Then Return 0
    ' this is needed for below
    If (list->pPrev = 0) Then Return 0
    ' since the list starts with a null node (pPrev and pData = 0),
    ' the first should be the one right after the real first.
    If (list->pPrev->pPrev = 0) Then Return 0

    Return list->pPrev
End Function

' GETDATA

Function ListGetData(list As listnode Ptr) As Any Ptr
    If (list = 0) Then Return 0

    Return list->pData
End Function

The first function, ListGetNext, is the exact same as ListGetFirst, but the difference is in your point of view. Although you could use ListGetFirst on a node value in this implementation, it isn't a smart idea because some other implementations may loop to the beginning of the list in order to find the first node, in which case you'd be stuck in an infinite loop.

The ListGetPrev function is a little more complicated, since I don't want to return the null node. The first and third line of code (not comments) are the ones that are actually needed, but the second one ensures that we're not accessing null memory. The third line says that if two nodes up is null, we should return zero. That means that if you are at the top node (not the null node), there is no previous node that you can do anything with, although there does exist a previous node, and it should return zero. The last line handles the default case, where there is in fact a previous node, and it should be returned.

The ListGetData function is as easy and brief as the ListGetFirst and ListGetNext functions. It just returns a pointer to the node's data.

The final two functions remove nodes from the list.
' REMOVE, REMOVEALL

Function ListRemove(list As listnode Ptr, bDelete As Integer = 0) As listnode Ptr
    Dim As listnode Ptr pPrev
    Dim As listnode Ptr pNext

    If (list = 0) Then Return 0

    pPrev = list->pPrev
    pNext = list->pNext

    If ((list->pData <> 0) And (bDelete <> 0)) Then Deallocate list->pData

    Deallocate list

    If (pPrev <> 0) Then
        pPrev->pNext = pNext
    End If
    If (pNext <> 0) Then
        pNext->pPrev = pPrev
    End If

    Return pNext
End Function

Sub ListRemoveAll(list As listnode Ptr, bDelete As Integer = 0)
    Dim As listnode Ptr node

    node = list
    If (list = 0) Then Return

    While (node <> 0)
        If ((node->pData <> 0) And (bDelete <> 0)) Then Deallocate node->pData
        node = ListRemove(node)
    Wend
End Sub

The ListRemove function has two jobs: To remove the node you specified, and to link the two surrounding nodes together. You can see that it stores a previous and next pointer to do this. The optional parameter, bDelete, specifies if the data item should be deleted. If you are just storing integers, or even structures with no pointers in them, you can pass 1 for this parameter and the item will be deleted for you. But if you have a structure with pointers in it, the best idea is to delete all the data yourself and have ListRemove only handle the list part to ensure that there is no memory loss. The listnode pointer is deallocated regardless of whether or not you told it to delete the data.

ListRemoveAll relies on the ListRemove function to delete the nodes. It simply loops through the list using a While loop and deletes every node. The original code used a For loop, but FB doesn't seem to like my doing
For node = list To 0 Step ListRemove(node)
so it has been changed.

That's it, here's the whole file that includes a sample at the top of how to use them. This is my first time writing a tutorial, so feel free to leave comments on ways I could improve. Also, if you catch a bug in my code (I found a couple while writing this), please let me know. Feel free to edit the bug out also, but I'd like to know about it too.

Type listnode
    As Any Ptr pData
    As listnode Ptr pNext
    As listnode Ptr pPrev
End Type

Declare Function ListCreate() As listnode Ptr
Declare Function ListAdd(list As listnode Ptr, item As Any Ptr) As Any Ptr
Declare Function ListAddHead(list As listnode Ptr, item As Any Ptr) As Any Ptr
Declare Function ListGetFirst(list As listnode Ptr) As listnode Ptr
Declare Function ListGetLast(list As listnode Ptr) As listnode Ptr
Declare Function ListGetNext(list As listnode Ptr) As listnode Ptr
Declare Function ListGetPrev(list As listnode Ptr) As listnode Ptr
Declare Function ListGetData(list As listnode Ptr) As Any Ptr
Declare Function ListRemove(list As listnode Ptr, bDelete As Integer = 0) As listnode Ptr
Declare Sub ListRemoveAll(list As listnode Ptr, bDelete As Integer = 0)

Dim As listnode Ptr list, node
Dim As Integer Ptr item
list = ListCreate()
item = ListAdd(list, CAllocate(Len(Integer)))
*item = 4
item = ListAdd(list, CAllocate(Len(Integer)))
*item = 44
item = 0 ' just to show it works
node = ListGetFirst(list)

While node <> 0
    Print "found item"
    item = ListGetData(node)
    Print *item
    node = ListRemove(node,1)
Wend

While Inkey$ = "" : Wend

' CREATE
Function ListCreate() As listnode Ptr
    Dim As listnode Ptr pTemp
    pTemp = CAllocate(Len(listnode))
    ' CAllocate automatically zeroes memory.

    Return pTemp
End Function

' ADD, ADDHEAD

Function ListAdd(list As listnode Ptr, item As Any Ptr) As Any Ptr
    Dim As listnode Ptr pTemp

    If (list = 0) Then Return item

    pTemp = ListGetLast(list)

    pTemp->pNext = CAllocate(Len(listnode))
    pTemp->pNext->pPrev = pTemp
    pTemp->pNext->pData = item

    Return item
End Function

Function ListAddHead(list As listnode Ptr, item As Any Ptr) As Any Ptr
    Dim As listnode Ptr pTemp

    If (list = 0) Then Return item

    pTemp = list->pNext
    list->pNext = CAllocate(Len(listnode))

    list->pNext->pPrev = list
    list->pNext->pData = item
    list->pNext->pNext = pTemp

    If (pTemp <> 0) Then
        pTemp->pPrev = list->pNext
    End If

    Return item
End Function

' GETFIRST, GETLAST

Function ListGetFirst(list As listnode Ptr) As listnode Ptr
    If (list = 0) Then Return 0

    Return list->pNext
End Function

Function ListGetLast(list As listnode Ptr) As listnode Ptr
    Dim As listnode Ptr pTemp

    If (list = 0) Then Return 0

    pTemp = list
    While (pTemp->pNext <> 0)
        pTemp = pTemp->pNext
    Wend

    Return pTemp
End Function

' GETNEXT, GETPREV

Function ListGetNext(list As listnode Ptr) As listnode Ptr
    If (list = 0) Then Return 0

    Return list->pNext
End Function

Function ListGetPrev(list As listnode Ptr) As listnode Ptr
    ' can't do anything to a null list
    If (list = 0) Then Return 0
    ' this is needed for below
    If (list->pPrev = 0) Then Return 0
    ' since the list starts with a null node (pPrev and pData = 0),
    ' the first should be the one right after the real first.
    If (list->pPrev->pPrev = 0) Then Return 0

    Return list->pPrev
End Function

' GETDATA

Function ListGetData(list As listnode Ptr) As Any Ptr
    If (list = 0) Then Return 0

    Return list->pData
End Function

' REMOVE, REMOVEALL

Function ListRemove(list As listnode Ptr, bDelete As Integer = 0) As listnode Ptr
    Dim As listnode Ptr pPrev
    Dim As listnode Ptr pNext

    If (list = 0) Then Return 0

    pPrev = list->pPrev
    pNext = list->pNext

    If ((list->pData <> 0) And (bDelete <> 0)) Then Deallocate list->pData

    Deallocate list

    If (pPrev <> 0) Then
        pPrev->pNext = pNext
    End If
    If (pNext <> 0) Then
        pNext->pPrev = pPrev
    End If

    Return pNext
End Function

Sub ListRemoveAll(list As listnode Ptr, bDelete As Integer = 0)
    Dim As listnode Ptr node

    node = list
    If (list = 0) Then Return

    While (node <> 0)
        If ((node->pData <> 0) And (bDelete <> 0)) Then Deallocate node->pData
        node = ListRemove(node)
    Wend
End Sub

If you haven't noticed already, ListAdd and ListAddHead return a pointer to the data you inputted. The sample code (see above) shows how to use this functionality. ListRemove returns a pointer to next node. That's how ListRemoveAll removes the nodes. ListRemoveAll is the only function that doesn't return anything. There is no need, since the whole list will be empty after you have called it.

Last edited by sancho3 on February 8, 2018


Different ways angles are measured
Written by RandyKeeling

This very simple tutorial assumes that you know what an angle is.

There are three commonly used ways to measure the size of an angle:

  • Degrees (deg)
  • Radians (rad)
  • Gradients (grad)

Degrees

Most people are familiar with angles measured in degrees. A full circle measures 360?. Parts of a degree are often measured two different ways, degrees decimal and DMS (degree, minute, second).

We can always show a degree as we would any decimal number by showing its whole units followed by its fractional portion. For example, 75.23? means that we have 75 degrees and twenty-three hundredths of a degree.

In the DMS system, each degree is made up of 60 minutes (or arcminutes) and is marked with a ` . Each minute is made up of 60 seconds (or arcseconds) and is marked with a `` . So a degree measure might look like this 36? 14` 52``. This is read as 36 degrees, 14 minutes, 52 seconds.

To convert DMS to decimal degrees you can use the following code.

Dim D As Integer
Dim M As Integer
Dim S As Integer
Dim DD As Single

'' Convert to degree decimal
DD = D + M / 60 + S / 3600    '' 3600 comes from 1/60 * 1/60

Radians

Radians are more common in computer programming and mathematics. To understand radians, you must understand the constant Pi (often given the symbol of the lowercase Greek letter pi). Pi is an irrational and transcendental number (its decimal notation never ends) and is the circumference of any circle divided by that circle's diameter. An approximate value (to 20 decimal places) is Pi = 3.1415926535897932385. The value of Pi can also be found using this code.

Pi = 4 * Atn ( 1 )

With the radian system, a full circle has 2*Pi (6.2831853071795864770) radians. Unlike degrees, radians are not marked with any form of a symbol. FreeBASIC, like most programming languages, accepts angle measurements in radians and not degrees.

To convert between radians and degrees (decimal) you can use the following code.

Const PI As Double = 3.1415926535897932

Dim D As Double
Dim R As Double

R = D * PI / 180    '' A full circle has 360 degrees, and a full circle has 2*PI Radians
D = R * 180 / PI

The value of PI is used so often, it is not uncommon to find it defined in libraries and commonly used routines. The following are useful constants.

Const PI As Double = 3.1415926535897932
Const TWO_PI As Double = 6.283185307179586
Const HALF_PI As Double = 1.570796326794896
Const DegToRAD As Double = 0.01745329251994330    '' PI/180
Const RADToDeg As Double = 57.29577951308233      '' 180/PI

Gradients

Gradients are used mainly in some forms of engineering. Within the gradient system a circle has 400 grads.

Last reviewed by sancho3 on February 08, 2018


A Brief Introduction To Trigonometry
Written by RandyKeeling

This tutorial includes:

  • Right Triangles
  • Pythagoras' Theorem
  • Trigonometric Functions
  • Applying Trigonometric functions
  • Inverse Trigonometric functions
  • Other Trigonometric functions
  • Law of Sines, Law of Cosines, and other relationships

Trigonometry can be thought of as the study of triangles. There is more to it than that, but this will suffice for this tutorial. While this may seem to be of limited use, many problems in both the real and virtual worlds can be solved by creative application of triangles.

A triangle has three sides and in 'normal' (i.e. Euclidean) space has three angles whose measurements add to be exactly 180 degrees (or Pi radians). For this tutorial we will deal only with 'normal' triangles (for those interested in other spaces, search for non-Euclidean triangles or non-Euclidean geometry).

Right Triangles
To begin with, we will deal with a special class of triangles known as right triangles. A right triangle has one angle that measures 90 degrees. Because the angles of a triangles must be exactly 180 degrees, there can be only one 90 degree angle in a triangle (and it is the largest angle in a right triangle). Below is FreeBASIC code to draw an image of a right triangle. (This image will be referred to throughout the tutorial.) In this image, uppercase letters denote sides, and their corresponding lowercase letters denote the angle opposite of the side. For example, angle y is the angle opposite side Y.

ScreenRes 640,480,8

'Triangle
Color 7
Line (220,140) - (220,340)
Line (220,140) - (420,340)
Line (220,340) - (420,340)

'right angle
Color 12
Line (220,320) - (240,320)
Line (240,320) - (240,340)

'angles
Color 13
Locate 20,29
Print "x"
Locate 42,50
Print "y"

'Sides
Color 14
Locate 31,43
Print "Z"
Locate 31, 26
Print "Y"
Locate 45, 40
Print "X"

Sleep

The box in the lower right hand corner means that it is a right angle (measures 90 degrees). The side opposite of that angle (side Z) is called the hypotenuse and is the longest side in a right triangle.

Pythagoras' Theorem

Perhaps the first bit of trigonometry that most people learn is the relationship commonly known as Pythagoras' Theorem. It simply states that the square of the hypotenuse of a right triangle is equal to the sum of the square of the other two sides. It is easier to understand in equation form.

Z^2 = X^2 + Y^2

A trivial example application of this law might be the following.

If player one is 100 meters due east of a marked location (the origin) and player two is 150 meters due north of the same location, how far apart are they?

D = SQR(100^2 + 150^2)

Trigonometric Functions

Long ago people discovered that regardless of the size of the triangle, certain ratios were always the same. For example, in the image of the triangle above, if the measure of angle y is 45 degrees, then regardless of the size of the triangle, the ratio Y/X will always be the same. Collections of these ratios are trigonometric functions.

The three primary functions are Sine (Sin), Cosine (Cos), and Tangent (TAN). There are many different ways to define these three functions. One way is with relationships between sides of a right triangle.

  • Sine (Sin) is the ratio of the side opposite the angle in question to the hypotenuse. In the above triangle, the sine of the angle y (written as SIN(y)) is the length of side Y divided by the length of side Z.
  • Cosine (Cos) is the ratio of the side adjacent to the angle in question to the hypotenuse. In the above triangle, the cosine of angle y (written COS(y)) is the length of Side X divided by the length of side Z.
  • Tangent (Tan) is the ratio of the side opposite to the angle in question to the side adjacent to the angle in question. In the above triangle, the tangent of angle y (written as Tan(y)) is the length of side Y divided by the length of side X.

Many people remember these relationships with the mnemonic device SOHCAHTOA (pronounced Sow Cah Toe-a) which is of course Sin = opposite/hypotenuse, Cos = adjacent/hypotenuse, and Tan = opposite/adjacent.

FreeBASIC has functions for these trigonometric functions and others.

Applying Trigonometric functions

Referring again to the triangle image above, let's say that player one is on the ground at the point near angle y and player two is at the point near angle x (off of the ground). If player one knows how far he or she is from the side Y (let's say 25.2 meters) and can measure the value of angle y (let's say 31.5 degrees) how far off the ground is player two? How far away is player one from player two?

To solve this we look at what pieces of information we know. We know the adjacent side to angle y (25.2 meters) and the measure of angle y (31.5 degrees). This is enough information to use the tangent function. Tan ( y ) = Opposite/adjacent, or TAN(31.5 degrees) = Opposite/25.2 meters. Using a little algebra to rearrange this we get opposite = Tan(31.5 degrees) * 25.2 meters. To find the distance between the players we could use Pythagoras's Theorem now that we know the two non-hypotenuse sides of the triangle or we could use the cosine. Using cosine would give Cos ( y ) = adjacent/hypotenuse. With some algebra we get, hypotenuse = 25.2/Cos(31.5 degrees).

Before we can write a program to solve this, we must remember that FreeBASIC, like most programming languages, works with radians, not degrees (see Angles ).

In FreeBASIC we could get the answer with this code.

Const PI As Double = 3.1415926535897932
Dim Opposite As Double
Dim Hypotenuse As Double
Dim Angle As Double

Angle = 31.5 * Pi / 180

Opposite = Tan ( Angle ) * 25.2
Hypotenuse = 25.2 / Cos ( Angle )

Print Opposite
Print Hypotenuse

Sleep

The above code tells us that player two is about 15.4 meters off the ground and around 29.5 meters away (along the hypotenuse).

Inverse Trigonometric functions

But what if you know the sides of a triangle and need to find the angle? You would then use the inverse trigonometric functions.

  • ArcSine (or Inverse Sine)
  • ArcCosine (or Inverse Cosine)
  • ArcTangent (or Inverse Tangent)

For example, using the above set-up, if player two was 30 meters off the ground and 50 meters away from player one (along the hypotenuse) what is the measure of angle y? Looking at our trigonometric functions it looks like we have need of the sine function (an opposite and a hypotenuse). Sin ( y ) = opposite/hypotenuse, ArcSine (opposite/hypotenuse) = y.

Print Asin (30/50)

This gives an angle of about 0.6435 radians, or around 36.9 degrees. The FreeBASIC command for each of these inverse functions are:

  • Asin (arcsine)
  • Acos (arccosine)
  • Atn (arctan, there is also Atan2 which takes the opposite and adjacent sides of the triangle, not their ratio)
Editors note:
ATN returns the arctangent of the argument number as a Double within the range of -Pi/2 to Pi/2.
ATAN2 returns the arctangent of the ratio y/x as a Double within the range of -Pi to Pi

Other Trigonometric functions

There are other trigonometric functions that are defined in terms of the above functions. Although none of the below are defined in FreeBASIC.

  • Secant (sec(y)) is 1/Cos(y)
  • Cosecant (csc(y)) is 1/Sin(y)
  • Cotangent (cot(y)) is 1/Tan(y)

Each of these has an inverse (or arc) functions as well.

Law of Sines, Law of Cosines, and other relationships
All of the above has assumed a right triangle, but this was an aid in explaining the basic trigonometric functions. The following does not rely on right triangles; these identities are valid for any triangle.

Law of Sines
Sin (y)/Y = Sin (x)/X = Sin (z)/Z

Law of Cosines
Z^2 = X^2 + Y^2 - 2*X*Y*Cos(z)

Other Identities

Sin^2(y) + Cos^2(y) = 1
This means the same as Sin(y)*Sin(y) + Cos(y)*Cos(y) = 1

Tan(y) = Sin((y)/Cos(y)

There are several more useful identities out there. Search for trigonometric identities or consult any higher mathematical reference.

Last reviewed by sancho3 on February 15, 2018 Note: Added clarification of ATN and ATAN2 funcitons as requested


Introduction to Message-Based Programming
Written by rdc

Historically, programming languages have been categorized as procedural and message-based. For example, QuickBasic could be categorized as a procedural language and Visual Basic could be categorized as a message-based (or event-driven) language. In a procedural language you generally start at the top, do some things and exit in a somewhat linear manner. In a message-based language, you generally initialize the system and then the program sits in an idle loop and waits for something to happen. When something happens, you handle it and then the program returns to the idle loop, eventually exiting the loop when the user closes the program.

In a procedural language you have full control over what the user sees and does. In a message-based system, you work in cooperation with the operating system and user, handling only those messages that you are interested in, and letting the operating system handle the rest. The real stumbling block for programmers that come to a message-based language from a procedural language is the concept of responding only to messages. However, we are really talking about shades of gray, rather than black and white. In most languages, including procedural, messages play an important role.

If you have ever used a language that supports subroutine and function calls, then you have used message-based programming. For example, say you have written a game in QuickBasic that sits in a loop waiting for one of the arrow keys to be pressed. If the up arrow key is pressed, you call a subroutine that updates the position of a sprite on the screen. If the A key is pressed, you ignore it, since you don't care about the A key. This is message-based programming. The message is the key press and the sprite update subroutine is the message handler.

Any structured programming language could be categorized as a message-based programming language. Message-based programming is a concept, a way to handle user input and react to that input. It is more methodology than it is a type of language. It became the dominant feature of some programming languages when operating systems evolved from the command line to graphical user interfaces (GUIs).

In a GUI based operating system, such as Windows, the OS manages all the graphical elements internally. Since the programmer isn't building a text edit field from scratch, he/she is just using the edit field built into the graphical shell, there had to be a way to notify the programmer that the user wants to update the edit field. The most natural method is to send a message to the program indicating that the edit field has been updated. Under Windows, this borrowing of GUI elements and receiving of messages has been formalized into what is called the Windows Software Development Kit, or more commonly, the Windows SDK.

The Windows SDK is a collection of application programming interfaces (APIs) in a set of dynamic link libraries (DLLs) that form the majority of the operating system. Any GUI based program running under Windows uses the SDK, even if it isn't readily apparent. In Rapid Application Development (RAD) languages such as Delphi, Visual Basic or Real Basic, the languages hide the details of the SDK by using properties and events, but under the hood they are using the SDK.

While RAD languages enable the programmer to quickly build GUI-based programs, it also means that the finer details of the SDK are not accessible. For example, it is quite difficult to subclass a control using VB, but is quite straightforward using the SDK. However, the SDK is huge, and the shear size of the API is enough to make many programmers give up on the idea of SDK programming. The common thought is that it is too complicated and hard to use, but the opposite is true. Because the operating system handles all the graphical elements for the program, the programmer can concentrate on the most important aspect of program design, user interaction. After all, a GUI program is all about user interaction.

FreeBASIC doesn't have a RAD system for Windows programming. To create a Windows program in FreeBASIC, you will have to use the SDK, as this is the only option. While the SDK is massive, and would probably take a lifetime to fully understand, for 99% of all Windows programs, only a small subset of the SDK needs to be used. The reality is that Windows SDK programming is no harder than any other type of programming, and for GUI-based programs, is actually easier than a language where you would have to create all the GUI elements yourself.

Putting aside all the gritty details of the Windows API for the moment, it is important to understand the mechanism of messages in an SDK program. This is best accomplished by looking at our old friend, the Hello World program. In the examples\Windows\gui folder of the FreeBASIC .15b distribution (which is required for the code in this article), there is a nice Hello World program that I am going to steal--I mean borrow, as the base for this example.

#include Once "windows.bi"

Declare Function        WinMain     ( ByVal hInstance As HINSTANCE, _
                                      ByVal hPrevInstance As HINSTANCE, _
                                      szCmdLine As String, _
                                      ByVal iCmdShow As Integer ) As Integer

    ''
    '' Entry point
    ''
    End WinMain( GetModuleHandle( null ), null, Command$, SW_NORMAL )

'' ::::::::
'' name: WndProc
'' desc: Processes windows messages
''
'' ::::::::
Function WndProc ( ByVal hWnd As HWND, _
                   ByVal message As UINT, _
                   ByVal wParam As WPARAM, _
                   ByVal lParam As LPARAM ) As LRESULT

    Function = 0

    ''
    '' Process messages
    ''
    Select Case( message )
        ''
        '' Window was created
        ''
        Case WM_CREATE
            Exit Function

        '' User clicked the form
        Case WM_LBUTTONUP
            MessageBox NULL, "Hello world from FreeBasic", "FB Win", MB_OK
        ''
        '' Windows is being repainted
        ''
        Case WM_PAINT
            Dim rct As RECT
            Dim pnt As PAINTSTRUCT
            Dim hDC As HDC

            hDC = BeginPaint( hWnd, @pnt )
            GetClientRect( hWnd, @rct )

            DrawText( hDC, _
                      "Hello Windows from FreeBasic!", _
                      -1, _
                      @rct, _
                      DT_SINGLELINE Or DT_CENTER Or DT_VCENTER )

            EndPaint( hWnd, @pnt )

            Exit Function

        ''
        '' Key pressed
        ''
        Case WM_KEYDOWN
            'Close if esc key pressed
            If( LoByte( wParam ) = 27 ) Then
                PostMessage( hWnd, WM_CLOSE, 0, 0 )
            End If

        ''
        '' Window was closed
        ''
        Case WM_DESTROY
            PostQuitMessage( 0 )
            Exit Function
    End Select

    ''
    '' Message doesn't concern us, send it to the default handler
    '' and get result
    ''
    Function = DefWindowProc( hWnd, message, wParam, lParam )

End Function

'' ::::::::
'' name: WinMain
'' desc: A win2 gui program entry point
''
'' ::::::::
Function WinMain ( ByVal hInstance As HINSTANCE, _
                   ByVal hPrevInstance As HINSTANCE, _
                   szCmdLine As String, _
                   ByVal iCmdShow As Integer ) As Integer

    Dim wMsg As MSG
    Dim wcls As WNDCLASS
    Dim szAppName As String
    Dim hWnd As HWND

    Function = 0

    ''
    '' Setup window class
    ''
    szAppName = "HelloWin"

    With wcls
        .style         = CS_HREDRAW Or CS_VREDRAW
        .lpfnWndProc   = @WndProc
        .cbClsExtra    = 0
        .cbWndExtra    = 0
        .hInstance     = hInstance
        .hIcon         = LoadIcon( NULL, IDI_APPLICATION )
        .hCursor       = LoadCursor( NULL, IDC_ARROW )
        .hbrBackground = GetStockObject( WHITE_BRUSH )
        .lpszMenuName  = NULL
        .lpszClassName = StrPtr( szAppName )
    End With

    ''
    '' Register the window class
    ''
    If( RegisterClass( @wcls ) = False ) Then
       MessageBox( null, "Failed to register wcls!", szAppName, MB_ICONERROR )
       Exit Function
    End If

    ''
    '' Create the window and show it
    ''
    hWnd = CreateWindowEx( 0, _
                            szAppName, _
                           "The Hello Program", _
                           WS_OVERLAPPEDWINDOW, _
                           CW_USEDEFAULT, _
                           CW_USEDEFAULT, _
                           CW_USEDEFAULT, _
                           CW_USEDEFAULT, _
                           NULL, _
                           NULL, _
                           hInstance, _
                           NULL )

    ShowWindow( hWnd, iCmdShow )
    UpdateWindow( hWnd )

    ''
    '' Process windows messages
    ''
    While( GetMessage( @wMsg, NULL, 0, 0 ) <> False )
        TranslateMessage( @wMsg )
        DispatchMessage( @wMsg )
    Wend

    ''
    '' Program has ended
    ''
    Function = wMsg.wParam

End Function

If you have successfully compiled and run the program, you will see a standard window with a message printed on the form. If you click the form, a message box will be displayed, and if you press the Escape key, the program will close.

Take a moment to examine the window. You will see that the form has the max, min and restore buttons, a system menu and can be resized. Now look at the code above. There isn't any code needed to handle the mentioned window properties, the OS handles all that for you. It also only takes a single line of code to display the messagebox, which in itself, is a rather complex object. The ratio of result to amount of code, is quite remarkable. If you were to try and recreate this simple program using FreeBASIC's standard graphical commands, the program would be a hundred times larger.

The first thing you should notice about the code listed above is the format. This is the basic format of any FreeBASIC Windows program. Every Windows program, no matter how simple or complex, will follow this same basic format. The two key ingredients of this program are the WinMain and WinProc procedures.

The WinMain procedure is the procedure Windows calls when a program is started. It is the entry point of a Windows program. In WinMain, you build and register the main program window and then enter into the message loop to process messages. Once the program enters the message loop, it will start processing messages with the WinProc procedure. Since this article is about the message model in a Windows program, we will only discuss the message loop in WinMain and the WinProc procedure.

When the Windows operating system is running, there are messages being generated all the time. When a Windows program is running, the OS will send messages to the program, when something happens that the OS thinks the program should know about. Some of these program specific messages are sent directly to the WinProc (or similar) procedure, and others, primarily user-generated messages, are placed into a message queue. Since most of a program is concerned with user interaction, it is important to understand the message queue.

A queue is a data structure where data in added to the "back" of the queue and removed from the "front". This is called a First-In-First-Out, or FIFO stack. If you have ever stood in line to buy movie tickets, you have experienced a queue.

For a program, the message queue will hold one or more messages, lined up like the folks in a movie ticket line. The idle loop of a Windows program sits and waits for messages to arrive and then translates and dispatches the messages to the program. This message loop is contained within the following code excerpt from WinMain.

    ''
    '' Process windows messages
    ''
    While( GetMessage( @wMsg, NULL, 0, 0 ) <> False )
        TranslateMessage( @wMsg )
        DispatchMessage( @wMsg )
    Wend

The GetMessage procedure retrieves a message from the queue via the wMsg parameter. The wMsg parameter is a MSG type-def that contains the necessary information related to a particular message. Here is the definition of the MSG type-def.

Type MSG
hwnd As HWND
    message As UINT
    wParam As WPARAM
    lParam As LPARAM
    Time As DWORD
    pt As Point
End Type

hwnd is the handle of the window that needs to process the message. This message will be processed by that window's WinProc procedure.

Message is the message identifier. This could be, for example, WM_CREATE, which signals that a window has been created, but not yet shown.

wParam and lParam both specify additional information based on the message type. For example, when a key is pressed, you can retrieve the key code by using the lobyte of wParam.

time specifies the time that the message was posted and pt is a structure that contains the position of the cursor when the message was posted.

TranslateMessage converts virtual key messages to character messages, and then puts them back into the queue so that the key can processed if desired. Any program that uses the keyboard will need this procedure. The DispatchMethod then sends a message to the windows WinProc (or similar) procedure associated with the window identified by the hWnd parameter.

To summarize the actions here, a user generated message will be placed into the "back" of the message queue. GetMessage retrieves the first waiting message, passes it to TranslateMessage which converts the message if necessary, and puts it back into the queue. The message is then passed onto DispatchMessage, which examines the message to see which window should get the message, and then passes the message onto the windows handler procedure, which in our example, is WinProc.

Before we discuss the WinProc procedure however, we need to ask a question: What happens if there is more than one window in a program? How does WinMain know what procedure to use? The answer is contained within the WNDCLASS structure. In our example, wcls is defined as WNDCLASS in the WinMain procedure.

    With wcls
        .style         = CS_HREDRAW Or CS_VREDRAW
        .lpfnWndProc   = @WndProc
        .cbClsExtra    = 0
        .cbWndExtra    = 0
        .hInstance     = hInstance
        .hIcon         = LoadIcon( NULL, IDI_APPLICATION )
        .hCursor       = LoadCursor( NULL, IDC_ARROW )
        .hbrBackground = GetStockObject( WHITE_BRUSH )
        .lpszMenuName  = NULL
        .lpszClassName = StrPtr( szAppName )
    End With

As you can see, the WNDCLASS structure holds all of the information pertaining to a particular window. In relation to messages, the important item is the .lpfnWndProc field. This field holds the address of our message handler for this window. The @ operator in FreeBASIC returns the address of an object, in this case the address of our WinProc procedure. Once this window is registered using the RegisterClass method, Windows will know what procedure to use to process messages.

As you can see, there is no special significance to the name WinProc. WinProc could be called MyWinProc, or WinHandler. The actual SDK name is WindowProc, which is just a placeholder for the user defined message handler name. The important piece of information is that whatever you call it, the message handler has to have the same parameters as we have defined in our WinProc, and the address of that procedure has to be stored in .lpfnWndProc.

All messages, whether user generated or system generated pass through the defined message handler, which in our example, is WinProc. Looking at the parameter list of WinProc we see that we have most of the components of the message structure retrieved by GetMessage. The hwnd is the windows handle, message is the message id and wParam and lParam hold additional message information. Once a message has been passed to WinProc, we then must decide if we are interested in the message.

This is usually done with a select case where we examine the message parameter to see if we want to handle the message. For example, if the message were WM_PAINT, then we would put our drawing code under the WM_PAINT message so that our window would be updated each time all or part of the window is redrawn. If we don't care about a message, then we simply pass the message on to the DefWindowProc message to be handled by the default message handler.

The action here is quite straightforward. WinMain or Windows sends us messages, and we respond to those messages of interest. As messages come into the message queue, they are processed and sent to WinProc, where they may be further processed, and then passed along to DefWindowProc to be processed by the operating system. This loop continues for the life of program, until the WM_QUIT message is received, at which point the window is destroyed and the program is terminated.

In our example, program we are only concerned with the three messages, WM_LBUTTONUP, WM_PAINT and WM_KEYDOWN. The WM_CREATE and WM_DESTROY are basically boilerplate that you would find any windows program. Since we are only interested in these three messages, we only need to write code for these three messages. The rest of the messages we might receive do not concern us, so we don't even bother looking for them.

In a message-based language, you are writing code to handle an event that has occurred. We know that an event has taken place because we received a message describing the event. If we are interested in that event, then we write code to respond to it. Instead of writing huge amounts of code to handle every aspect of the program like we must do in a procedural language, we only need to write code for certain events, and we let the operating system handle everything else.

Now of course, you have to write code to create a window and controls, but this is mostly boilerplate type of code. You simply follow the API and pass along the appropriate parameters to the CreateWindow function. Once you understand the boilerplate, it is simply a matter of plugging that code into your program when needed. The real action occurs in the WinProc function when you interact with the window or controls.

Message-based programming requires a different mind-set than procedural programming. In a procedural language, the user must respond to the program; the programmer is in charge. In a GUI program, the program must respond to the user and the user is in charge. To write effective GUI programs, the programmer has to relinquish control over the program, and work in cooperation with the operating system and the user.

When you design a GUI program, you have to ask yourself, "How do I want my program to respond to the user?" For example, when the application is minimized, should the program ignore the event, or should it do something like put itself in the system tray? This is the essence of message-based programming. Defining what events are important, and then writing individual routines that handle each event. A message-based program is simply a collection of specific routines written in response to specific messages.

Despite the reputation of the SDK, the basic concept of message-based programming is quite simple. You are writing a collection of routines to handle messages. This is the core task. All the other stuff like creating a window, or when to repaint the window is done by the operating system. It is the scope of the SDK that gets to most people. There is a lot in there. However, like the cliché says, the best way to eat an elephant is one bite and a time. The best way to master the SDK is to simply understand the concept of message-based programming and learn the boilerplate code. Once that is done, creating sophisticated Windows programs isn't all that hard.


So Biff wants to have a high score table in his game
Written by Lachie Dazdarian (September, 2007)

Introduction

On more than one occasion I was inquired by a programming newbie about a set of routines that load a high score table from an external file, input a new high score properly, and then save the modified high scores table.
Using the same set of routines for high scores since the days of Ball Blazing Fantasy, I decided to write a tutorial on them and implement some lacking flexibility (plus few fixes) there, something that was long needed to be done but wasn't due the fact the routines did their job perfectly.
The tutorial will also point you out to some useful (for high scores table managing) additional routines, like the name inputting and file encryption ones, not written by me.

Let's do it!

It's fairly obvious we'll need two separate subroutines, one for loading/reading our high score table, and one for writing/modifying it.
We'll start with loading/reading of a high score table, as that part is easier and a logical start.
The subroutine for reading a high score table should work relatively simple. It will open a file which contains name and score entries, storing them in appropriate variables and then printing them on the screen, this part being most dependent on the developer's wishes and needs (the method of printing, position of the high score table, its formatting, etc.).
First, we should create a text file containing our name and score entries. Create a file named 'high_scores.dat', open it with Notepad and input this:

FRED
10000
BILL
9000
SARAH
8000
BOB
7000
RED
6000
SUE
5000
DAVID
4000
GREG
3000
TIM
2000
GEORGE
1000

It contains 10 high score entries, formatted with name followed by the accompanying score. I find this formatting the most suitable for editing, although you can pick one where all the names all listed first, and then followed by all the scores. Still, no important benefits from any type of these two formattings, so we'll work with the one I stared with.

This file will be used with the following 'ReadHighScore' subroutine.

Let's start our main program with some needed initiation statements:

#include "fbgfx.bi"
Using FB

Const num_of_entries = 10

'num_of_entries' will flag the number of score entries (names or scores in the high score table), and should correspond with the number of entries in the 'high_score.dat' file (not lines, but high score ENTRIES!).

We should now declare our subroutine with:

Declare Sub ReadHighScore (highscore_file As String)

The 'highscore_file' variable will flag the file you want for the 'ReadHighScore' subroutine to open. Not necessary to declare the subroutine like this, but this adds some flexibility to it.

After this, we should declare the following variables:

Dim Shared workpage As Integer
Dim Shared hname(num_of_entries) As String
Dim Shared hscore(num_of_entries) As String

'workpage' variable is not related to this tutorial and will be used to swap screen work pages inside the loop where the high score table will be drawn. 'hname' array will hold the name entries, while 'hscore' array will hold the score entries from the high score table.

Finally, let's initialize our screen and work/visible pages with:

ScreenRes 640, 480, 32, 2, GFX_ALPHA_PRIMITIVES+GFX_WINDOWED
ScreenSet 1, 0

Following this code we should place this:

ReadHighScore "high_scores.dat"
End

Sub ReadHighScore (highscore_file As String)

End Sub

You can compile this code, but nothing will happen as the 'ReadHighScore' subroutine is empty. Let's fill it up!
We need to start it by opening the 'high_scores.dat' file and reading the needed data from it. Please refer to FreeBASIC's OPEN statement for info on file opening in FreeBASIC if not familiar with it.

As we want to open the file using a FREE file handle, we need to dimension a variable that will hold this information and pass it into it. Use this code:

Dim free_filehandle As Integer

free_filehandle = FreeFile

We should now open the high score file with:

Open highscore_file For Input As #free_filehandle

After the file is opened for reading (FOR INPUT), let's use a for loop to retrieve all the data from it and store it in our 'hname' and 'hscore' variables:

For count_entry As Integer = 1 To num_of_entries
Input #free_filehandle, hname(count_entry)
Input #free_filehandle, hscore(count_entry)
' If the end of file is reached, exit the FOR loop.
If EOF(free_filehandle) Then Exit For
Next count_entry

Note how the 'count_entry' variable is used and how for each entry the name is stored FOLLOWED by the accompanying score. 'hname(1)' will flag the name with the top score, while 'hscore(1)' the top score. 'hname(num_of_entries)' will flag the name with the lowest score, while 'hscore(num_of_entries)' the lowest score in the high score table.

Don't forget now to close the file with:

Close #free_filehandle

All we need now is a loop that will display all these names and scores, nicely arranged in a table.

Do

ScreenLock
ScreenSet workpage, workpage Xor 1

Line (0,0)-(639,479), RGBA(0, 0, 0, 255), BF

Draw String (285, 120), "TOP SCORES", RGBA(255,255, 255, 255)

For count_entry As Integer = 1 To num_of_entries
Draw String (270, 140 + count_entry * 12), hname(count_entry), RGBA(255,255, 255, 250-count_entry*10)
Draw String (340, 140 + (count_entry) * 12), hscore(count_entry), RGBA(255,255, 255, 250-count_entry*10)
Next count_entry

Draw String (245, 400), "Press ESCAPE to exit", RGBA(255,255, 255, 220)

workpage Xor = 1
ScreenUnlock

Sleep 10

Loop Until MultiKey(SC_ESCAPE)

A simple DO...LOOP that ends when the user pushes ESCAPE.
I used Draw String to print the names and the scores. Another FOR loop is used to loop through the name and score entries, and to display them lower score under the next higher one (note how the Y position of the text to display is connected with the 'count_entry' variable - increase 12 to get more space between scores vertically). I also used a small trick to display each next score with lower translucency (last parameter in the RGBA function).

After placing all this code in the 'ReadHighScore' subroutine, you can compile it and the desired result will appear on the screen.

Now when we are done with the easy part of the problem, let's move onto writing new entries into our high score table.

I constructed the 'WriteHighScore' subroutine like this:

Sub WriteHighScore (highscore_file As String, users_score As Integer)

Which means it will be called with a high scores table file and a score we want to input. If this score evaluates to be lower that the lowest in the high score table, no code will be executed.

This subroutine should start with the following code:

Dim free_filehandle As Integer

Dim startwrite As Integer

free_filehandle = FreeFile

Open highscore_file For Input As #free_filehandle

For count_entry As Integer = 1 To num_of_entries
Input #free_filehandle, hname(count_entry)
Input #free_filehandle, hscore(count_entry)
' If the end of file is reached, exit the FOR loop.
If EOF(free_filehandle) Then Exit For
Next count_entry

Close #free_filehandle

As you see it starts as the 'ReadHighScore' subroutine. In order to evaluate the user's score and alter the very high score table we need to open the file containing our high score entries and store them in appropriated variables. 'startwrite' variable will flag where the new entry is to be placed inside the high score table (on which position).
The code that follows should be opened with an IF clause that will execute the code inside it only if the user's score is higher than the lowest score in the high score table (naturally):

If users_score > hscore(num_of_entries) Then

For check_score As Integer = 1 To num_of_entries

If users_score > hscore(check_score) Then
InputName
' Record the position where the new score is
' to placed and exit FOR loop.
startwrite = check_score
Exit For
End If

Next check_score

The FOR loop 'goes' through the high score entries from the highest to the lowest, and when an entry with a lower score is found this is the place (flagged with 'startwrite' and 'check_score') where our new entry will be recorded. For example, in the first loop the program checks for 'hscore(1)' - the top score in the high score table. If the user's score ends up being higher than it, it's obvious the user's score is the new top score and 'startwrite' needs to be 1. 'InputName' is a subroutine we'll create later, and inside it the user will be...inputting his name. :P

What follows is the 'nexus' of our routine, the code that places the new high score entry on the proper position, and bumps all the lower ones one position down.

Check the following code:

If startwrite = num_of_entries Then
hscore(startwrite) = users_score
hname(startwrite) = playername
Else

For write_pos As Integer = (num_of_entries - 1) To startwrite Step -1
hscore(write_pos + 1) = hscore(write_pos)
hname(write_pos + 1) = hname(write_pos)
Next write_pos
hscore(startwrite) = users_score
hname(startwrite) = playername
End If

First condition checks if the new entry is the lowest (last) in the high score table. If this is the case, we don't need to bump down any entries with a lower score as there are none, but only replace the lowest score entry with the new one.
If this is NOT the case, a FOR loop is executed which loops from the lowest high score entry to the new high score entry (flagged with 'startwrite'), meaning, from bottom to top.

For example, if our high score table has 10 entries and the new entry needs to be placed on position 5, the loop goes from 9 to 5. When "write_pos" is 9, values from 'hscore(9)' and 'hname(9)' are passed to 'hscore(9+1)' and 'hname(9+1)'. When 'write_pos' is 8, values from 'hscore(8)' and 'hname(8)' are passed to 'hscore(8+1)' and 'hname(8+1)'. And so on.

After the FOR loop we need to input the new entry on its appropriate position (flagged with 'startwrite'), new entry being set with 'users_score' and 'playername', where 'playername' will be inputted inside the 'InputName' sub.

The last thing in the 'WriteHighScore' sub we need to do is to store the new high score entries back to file:

free_filehandle = FreeFile

Open highscore_file For Output As free_filehandle
For count_entry As Integer = 1 To num_of_entries
Print #free_filehandle, hname(count_entry)
Print #free_filehandle, hscore(count_entry)
Next count_entry
Close free_filehandle

Note how FOR OUTPUT is used and PRINT for writing data into external files.
After this I placed a 'ReadHighScore' call and closed with END IF as I find it good that a new high score table should display after a new entry has been inputted in it.
All we need now is to create the 'InputName' sub like this:

Sub InputName

ScreenSet workpage, workpage Xor 1
ScreenSet 0,0
Line (0,0)-(639,479), RGBA(0, 0, 0, 255), BF
Locate 12, 17
Input ; "Please input your name: ", playername

End Sub

Of course, this will look totally different in your game. Perhaps you'll ask the player to input his/her name on a different place in the game (like when he/she starts a new game). Just have in mind you need one.

To test the routines just place...

ReadHighScore "high_scores.dat"
WriteHighScore "high_scores.dat", 4500
End

...after first SCREENSET (outside subroutines). Change the second parameter with 'WriteHighScore' call to input different scores on different locations in the high score table. I'm sure you are aware that when calling 'WriteHighScore' the second parameter mustn't be hard-coded with a static number, but with a variable in which you'll store player's score, whatever that may be in your case (ie. 'Player.Score').

What's next?

The only other things I wish to share regarding this issue is related to high score encryption and better name inputting routine. As both routines I'm using are not by me, I will only brush off them and provide them in an example program you can easily use for your own needs.
Encryption is done using two functions, 'neoENCpass' and 'neodeENCpass'. One for encryption and one for decryption. They are called with a string (high score entry string in our case) and password, password being any string you choose and the same must be used for encrypting and decrypting (of course).
Just after you retrieve an string entry from a file you decrypt it like this:

Input #free_filehandle, hname(count_entry)
neoENCdepass SAdd(hname(count_entry)), Len(hname(count_entry)), "yourpass"

With 'hscore' variables, being INTEGER, we need to use a temporary STRING variable which has to be decrypted and then pass its value to 'hscore'.
The only annoying feature of this method is the fact you need a separate source code to encrypt/decrypt your high score files, as the routines inside a project will work only if the high score file is previously encrypted. I provided a small program which does this encrypting for you. It is recommended you keep a backup of your high score file in a separate folder (I also provided this in the zip downloads), even if not encrypting it.
Instead of encryption you can use BINARY files, which I don't know how to use at this moment (don't have time to learn; I'm submitting the tutorial in the nick of time), and which also AREN'T the same as ENCRYPTION. Encrypted files using these routines people can only decrypt if they know the password (well, most people), while BINARIES can be read by anyone having your source. Ah yes, when providing your source code to public be sure to change the encryption passwords inside it.
Anyway, you might not need or prefer encryption at all. But I personally like having my high score/script files encrypted so than not every Dick and Tom can change/read them with Notepad. Unencrypted high scores might kill the challenge to beat them with some players.
Name inputting routine I won't go describing as that's irrelevant. You have to code, read it. It's much better than plain INPUT (you can use it with custom font printing libraries) and allows you to limit the number of characters in the name. The routine was done by 'Ryan Szrama', and all thanks go to him.
Download the extended example (with encryption and better name inputting): http://lachie.phatcode.net/Downloads/Managing_A_High_Score_Table.zip

And that's it for this tutorial.
Until next time, have fun!

A tutorial written by Lachie D. (mailto CHR$(58) lachie13 CHR$(64) yahoo CHR$(46) com ; http://lachie.phatcode.net - The Maker Of Stuff)


New to Programming?
If you're new to programming in general, you should probably learn what some basic concepts are:

How Your Program Is Run
  • What a Compiler Is
  • Syntax
  • Program Flow
Variables
-Basic DataTypes
Input/Output (IO)

The above being the most important programming concepts for an absolute beginner to programming to learn. I will go over how these concepts work in FreeBASIC. It is also important to learn how to use the manual, located at www.freebasic.net/wiki Most programming communities and language will have manuals with both descriptions and demonstrations. ALWAYS refer to the manual before looking elsewhere. Chances are the information you want is in the manual, and if it's not, it can be added.

This tutorial's on Version 1.0. Don't care for the revision number ^^;;

How Your Program is Run

What a Compiler Is

FreeBASIC is a compiled programming language, rather than interpreted. What this means, is that FreeBASIC takes the code that you type in, such as "PRINT" or "SLEEP" and translates that directly into Assembly or Machine Code. Assembler and Machine Code are what the computer understands. In general, you will never code in Machine Code, no matter how "low level" (how close you are to programming machine code) you go.

FreeBASIC is a High Level programming language. FreeBASIC makes it so the programmer has to do less work to get more done. With higher level programming, you don't have to worry about the more complex areas of programming. In programming languages such as C and ASM, which are lower level, the programmer has the advantage of manipulating the computer on a more precise and less human level, with the disadvantage of having to know more about internals.

Your compiler of choice will depend on your situation. If you want complete control over every action being taken by your computer, you may wish to code in ASM or C. However, as computers and compilers have progressed, you no longer have to worry as much about the speed and lower level details of your code. In many ways, the entire purpose of higher level programming is to make sure you don't have to worry about those things. FreeBASIC handles many optimizations and improvements that you would normally have to do by hand for you, while still allowing you to access lower level areas of control if you wish. One problem with this, however, which is a common problem in most higher level forms of programming, is the high levels of implicit actions being taken by the compiler. If you want to work with lower level code in a higher level language, you need to know how to explicitly control certain aspects of your code.

Syntax

Syntax is how words and commands are grouped together in programming. The order that they are in, what commands are allowed where, and if consistent, will lay down rules as to how you will structure your program.

For example, in programming, you will come across the task of calling commands, and giving those commands something to do. Your syntax rules will tell you how you can call this command, and what is or isn't allowed. They will help you call the command intelligently, and help prevent possible errors that could occur in a more "syntax free" (what is essentially impossible in most forms of programming) environment.

The syntax for FreeBASIC generally goes as follows: CommandName [Argument,] [Another Argument]

While the above may look confusing at first, it's actually very simple. All that says is that you give the compiler a command, and then give your arguments after the command. The comma is what separates the arguments from one another. An example command could be: Draw Circle, 10. We can assume that Draw will draw something, Circle will be the shape that it draws, and 10 will be the radius of the circle being drawn. In that case, the syntax rules for that command may look something like this: Draw [Shape,] [Size]

FreeBASIC is *not* case sensitive. Calling a command 'DRaW' is the same as calling the command 'draw'.

Program Flow

FreeBASIC's code is read from the TOP of the code, to the BOTTOM, one line at a time. When the line of code is read by the compiler, or the compiled code for that line is read by the computer, the command that's on the line will be executed (it will be ran, it will happen, your computer will do what the code tells it to). Example code can be:

Print "HI"
Sleep

Since the code PRINT is on the line above SLEEP, PRINT will be run first. SLEEP is the next line and will be executed after PRINT is finished executing.

Comments can be made in FreeBASIC, which are ignored, and will not become a part of your program. Comments can take up a full line if you begin them with ', or can be multiple line comments if you begin them with /' and end them with '/. Here's an example of using comments. Notice how none of the code or characters within the comments are even noticed by the compiler.

'  ABLASHD
' PRINT "HI!" ' This line of code will never even be printed, because it's commented.
Print "This is not a comment.  This event will occur."  ' Print that it's not a comment onto the screen.
Sleep ' pause the program until the user hits a key.

Variables

What are variables? They're the most important part of programming, that's what. Any time you do anything useful in programming, you're going to store information in variables.

Do you recall doing algebra or using letters in math, in school? An example might be something like: x = 4, 1 + x = 5. When programming, variables are exactly that. They are words or letters that hold values in programming. These values may store important data such as a name, how much health your main character in your video game has, or even something as simple as the color of one pixel. When you make a variable, you actually are storing the data it holds small piece of the computer's memory. In FreeBASIC, and most programming languages, you will work with variables a lot.

To create variables in FreeBASIC, we use the DIM command. What does DIM stand for? I'm not sure, but it could stand for DIMENSION, where programmers would define the "size" of their variable. DIM is used to tell FreeBASIC that we're creating a new variable in our program. The command is described in high detail on This Page, but we're going to explain it in less detail here.

The most simple syntax for DIM is: DIM [VariableName] AS [DataType] [="[Value] ]

What this does, is it tells FreeBASIC:
- We're making a variable, because we typed in the command DIM
- We're naming the variable [VariableName] (Where VariableName is the name of the variable you want to make. A variable can be named X, Y, AlexPritchard, BLahblh, Foo, etc.)
- We're specifying the type of variable, because we typed in AS after VariableName
- We're making the variable of the type [DataType] (Datatype can be something that holds numbers, letters, or a whole bunch of stuff!)
- We can also assign the value of the variable by putting EQUALS (=) after our variable creation.

Example:
Dim foo As Integer = 5
Print foo
Sleep

In our program, we created foo. FOO was created as an INTEGER (A datatype which holds numbers) We gave foo the value of 5. We then call the command PRINT, which PRINTS information on our screen. We PRINT foo, so the number 5 should be PRINTed on the screen. We then SLEEP, which pauses our program until we hit a key.

Basic DataTypes

Variables are a tough subject, I think, to begin with in programming. There's a lot of different types of variables! The type of variable is the data type. 'The kind of data that's held in this type of variable', and you wondered why they shortened it to datatype? ^^;; For starters, we'll get you familiar with these types of variables:

Integer - Hold numbers WITHOUT DECIMAL PLACES. Will generally be the size of your computer's registry, which is not a topic I will be going over.
Double - Holds numbers WITH DECIMAL PLACES. Holds very large and small numbers, with high levels of precision (how close to any value you give the variable it's actual value will be)
String - A nice feature in FreeBASIC. STRING is a datatype which holds letters and numbers for you. Built for storing information such as your name, and cool information to put on the screen, such as cooking directions.

Remember, follow the proper DIM syntax. DIM variablename as INTEGER will make an INTEGER called variablename, which you can use in your program. You can replace INTEGER with DOUBLE, or STRING. BE CAREFUL! Data Types are *NOT* always compatible with each other! You can not give a STRING the value of 5! You can however, give it the value of "5" (quotes specify string characters in FB). You can not have an integer or double equal "5", as "5" is a string, and not a number.

Here is a really cool example, which demonstrates how you can use variables to store your name.

'' Create the variable MyName.  Assign it's value to be 'Alex'
Dim As String MyName = "Alex"

'' Print The MyName variable
Print MyName

'' pause the program until the user hits a key.
Sleep

Input/Output

Input is the receiving of information. When your get input on something, someone or something else is giving it to you. You are getting their ouput. (GETTING SOMETHING, Retrieving Something)

Output is the sending of information. When you ouput to something, you're the one giving input to them. (GIVING SOMETHING, Sending Something)

Input and Output are often put together, and are shortened as I/O, or IO.

FreeBASIC has MANY methods of input and output. For a beginner, most of these could end up confusing you, because they generally require better knowledge of variables and more complex forms of programming. We're going to study the very basics of I/O.

You remember the command PRINT in the above examples? That's OUTPUT. PRINT OUTPUTS to the computer screen. PRINT is a very basic form of output, and it's easy to learn, too! You just call the command PRINT, then tell it what you want it to print. If you want to Print words, you put those enclosed in Double-Quotes. If you want to print variables, you just give PRINT the name of the variable you want to print.

Print [WhatToPrint]
Example:

'' Print the words, HI! to the screen
Print "HI!"

'' create a new integer and name it foo.  Give it the value of 10.
Dim As Integer foo = 10

'' Print the value of foo.
Print foo
Sleep

INPUT isn't much harder, either. However, whenever you input, you have to get that input and put it into something. Just how like in order to give output, we have to give the PRINT command something to output. We already know variables, right? We will use our variables to store information that the user inputs.

1) We need a variable to store that information in.
2) We need to call a command to get input.
3) We need to print the input to make sure we stored the information correctly.

I know how to do 1 and 3, but what about 2? We're going to learn a new command for this. Can you guess it's name? INPUT! Yup ^^;; To get INPUT, we will use the command, INPUT.

Input's syntax is as follows: INPUT [VariableToInputTo]

You can also use input like so: INPUT [Output String To Tell User What to Input,] [VariableToInputTo]

The first version of INPUT will let you get input, and put it right into the variable. The second version OUTPUTS your message to the screen before asking for input. This way, the user will know what to input! Alternatively, you can just use the PRINT command before INPUT to send the user a message, but sometimes being able to put related code on one line is a convenience.

Example:

'' Create a string.  We will hold the user's name in the string!
Dim As String MyName

'' Get the user's name!
'' The message Please Enter Your Name is posted on the screen,
'' and then the user has a chance to enter in their name!
Input "Please enter your name!", MyName

'' Print the user's name that we just got.
'' Just like input, we can print several messages or
'' execute different types of commands by separating them by commas.
Print "Your Name Is: ", MyName

'' pause the program until the user hits a key
Sleep

That demonstrates both INPUT and OUTPUT! Both are essential in programming, or at least graphical programming. OUTPUT can be many different things, as well as INPUT. You might be getting input from a robotic arm's sensors rather than from the user's keyboard. You might be outputting to a power drill rather than a monitor. It really depends on the hardware and the purpose of your program.

At the time, and in most cases, you don't have to worry so much about where the input comes from, or where it's going to when dealing with your standard I/O functions. More advanced methods of I/O let you decide where it's coming from (which input to get) and where it's going to (where to send output to).

Programming Definitions

Argument: See Parameter

ASM: The lowest level code that a human will want to read. This can be compiled directly into machine code.

Compiling: The process of turning text in one language to another. Ex: BASIC in FreeBASIC compiles into ASM. That ASM compiles into machine code.

Machine Code: 0's and 1's. This is *the* code that your computer will understand.

Parameter: Data that you pass to a command you call in programming. Parameters being passed allow commands to be directed as to HOW they will do something, or what they will do. Passing a parameter 'Rectangle' to a command 'Draw', it would make sense if that drew a rectangle onto your screen.

Pixel: One 'dot' on your monitor. Monitors are made up of thousands of tiny dots which are lit up of different colors. The color depends on the value of the pixel variable that the monitor receives. Believe it or not, even your hardware will use variables, in many ways.

Syntax: How words are grouped together. Your syntax in programming are sets of rules that tell you what code can be placed where. It makes sure that only logical code is allowed. Ex: Print "Hi". PRINT is the COMMAND, "Hi" is what the command will PRINT. Make sense?

Variable: A word that holds data in programming. You assign these words values, and with those values, you can save information on your program.

Last Reviewed by Sancho3 on February 06, 2018


OOP In Non-OOP Languages

Contrary to popular belief object oriented programming does not require an OO language.

What you get with an OO language is a set of built in constructs that assist you in writing OO programs but in many cases they are unnecessary and sometimes they are counterproductive.

Anyway, this isn't a rant against OO languages but rather a rant against the unquestioning acceptance of the idea that a specifically OO language is necessary to write object oriented programs.

In order to demonstrate that it is not necessary to have an OO language this example presents a technique that is usually presented as an example of class based programming; and so it is but you won't find the word class in this example.

If you have to concatenate a lot of strings in most Basics you usually find that it is a time consuming process. Actually FreeBASIC string operations are remarkably quick but you can still do better using a string builder.

A string builder is simply a class that maintains a string buffer in such a way as to avoid repeated calls to the memory allocation function because this is a relatively expensive operation. The methods of the class provide ways of manipulating the buffer and converting between it and the native string type.

The trick that makes it faster than the built type for large strings and large numbers of appends is that the string is held in a heap allocated buffer that is always larger than the actual length of the string. This means that appending to the end of the string usually simply means copying the contents of the new string to the memory location following the last character of the current string. In this implementation the buffer is a ZString so it is easy to convert it to an ordinary dynamic string.

The FreeBASIC module encapsulates a type definition for a struct. Instances of this struct hold the attributes of the object. The methods are simply normal FreeBASIC public functions and subs defined in the same module. When you want to call a method you use the normal FreeBASIC syntax:
 s = StringB_ToString(AStringBInstance)

By convention all methods names begin with the name of the class and an underscore and the first argument is always the instance of the type. This argument should always be passed by reference to ensure that changes to the state are permanent and also to avoid unnecessary, time-consuming, copying.

To add a new method you simply add a new function or sub following those rules.

You can easily implement composition of objects but inheritance in the usually expected ways can't be done. You can extend classes simply by defining new functions elsewhere that take arguments of the class type. If the original class defines all of its methods as overloaded you can even create new methods of the same name so long as they have different signatures.

Here is the example code:
'-----------------------------------------------------------------------------
' Classes without built in oop.

' Define a struct for the properties and a sub or function for each
' method.  Pass the struct as the first argument in all calls.

' By convention the argument will be Me as in VB Classic

' Strings in FB are so fast that a string builder class is
' not needed most of the time but if you are concatenating
' thousands of strings to build web pages for instance this might be useful.

' And please don't start complaining about the lack of inheritance; that
' is not a requirement for the use of objects.  There is no legal definition of
' Object Oriented Programming but the most important part of any definition
' is the close association between the data and the code that manipulates it.

'You can easily extend this class to provide more methods.
'-----------------------------------------------------------------------------

Type StringB
  Len As Integer ' used length
  allocated As Integer
  s As ZString Ptr   ' buffer of at least len characters
End Type

'-----------------------------------------------------------------------------
' Create a new StringB by calling one of these constructors.
'-----------------------------------------------------------------------------
Public Function StringB_New Overload (ByVal InitialSize As Integer) As StringB
  Dim sb As StringB
  sb.allocated = InitialSize
  sb.s = Allocate(InitialSize)
  *sb.s = ""
  StringB_New = sb
End Function

Public Function StringB_New(ByRef InitialValue As String) As StringB
  Dim sb As StringB
  sb = StringB_New(Len(InitialValue))
  *sb.s = InitialValue
  sb.len = Len(InitialValue)
  StringB_New = sb
End Function

Public Sub StringB_Dispose(ByRef Me As StringB)
  Deallocate Me.s
End Sub

Public Function StringB_ToString(ByRef Me As StringB) As String
  StringB_ToString = *Me.s
End Function

Sub StringB_Append Overload(ByRef Me As StringB, ByRef s As String)

  Dim i As Integer = Me.len
  Me.len += Len(s)
  If Me.len >= Me.allocated Then
    Me.allocated = 2*Me.len
    Dim As ZString Ptr p = Reallocate(Me.s, Me.allocated )
    If p=0 Then
      ' failed to reallocate
      Print "StringB_Append failed to reallocate", Me.allocated
      Return
    End If
    Me.s = p
  End If
  *(Me.s + i) = s

End Sub

Sub StringB_Append(ByRef Me As StringB, ByRef other As StringB)
  StringB_Append Me, StringB_ToString(other)
End Sub


Introduction to Pointers
Written by rdc

What is a Pointer?

A pointer is a 4-byte data type (on 32bit systems) or 8-byte data type (on 64bit systems) that holds an address to a memory location. A pointer doesn't contain data, it points to data once it has been initialized. An uninitialized pointer points to nothing and is undefined.

To understand pointers, think of an egg carton that has numbers 1 through 12 printed on the bottom of each "hole" (where you put the eggs). These holes are like memory locations in a computer; each hole, or memory location, has an address, in this example, 1 through 12. If an egg represents a data item, then an egg in hole 1 has an address of 1.

Normally, you would access the data directly through the use of variables. When you DIMension a variable of a particular type, you are setting aside storage space for the data. You do not need to know, or care, where the data resides since you can access the data directly through the variable. This is like reaching out and picking up the egg in hole 1 (reading the data) or putting an egg in hole 1 (setting the data) without looking at the numbers written on the bottom of the hole.

Using pointers is a bit different. Imagine you have a little scrap of paper that will represent our pointer. Right now it is blank and doesn't point to anything. This undefined pointer can't be used until it is initialized. To initialize the pointer, write a 1 on it. Now our pointer is "pointing" to hole 1 in our egg carton. To put data (an egg) in hole 1, we look at our scrap of paper, match it to hole 1 and place the egg in the hole. To retrieve the egg we do just the opposite. We match our slip of paper to hole 1 and then grab the egg. All the putting and getting of the egg has to be done through the slip of paper and is called dereferencing the pointer. That is, we get to the data through the referring address contained in the pointer, the number 1. The pointer doesn't contain the data; it contains a referring address to the data.

In FreeBASIC we define a pointer using the Dim and Ptr statements:

Dim aptr As Integer Ptr

This statement corresponds to our blank piece of paper in the above example. The pointer doesn't point to anything and is undefined. If we tried to use the pointer right now, more than likely the program would crash.

In order for a pointer to be useful, it must be initialized:

Dim aptr As Integer Ptr

aptr = Allocate(SizeOf(Integer))

Here we are using Allocate to set aside enough space in memory for an Integer and loading the address of that space into aptr. The SizeOf macro returns the size in bytes of the passed data type. You could use len instead of SizeOf (since .13b) if you prefer.

Once we have initialized the pointer, we can now use it:

*aptr = 5
Print "aptr: "; *aptr

Notice the * prefix on aptr. The * is the dereference operator. This is like matching the number on the slip of paper to the number on the hole in the egg carton. By using the * operator, we are able to get at the data (egg) contained in the hole pointed at by aptr.

Here is a complete example program:

Dim aptr As Integer Ptr

aptr = Allocate(SizeOf(Integer))
*aptr = 5
Print "aptr: "; *aptr
Deallocate aptr
Sleep

The Deallocate function frees the memory pointed at by aptr, and makes aptr undefined once again. This is like erasing the number on our slip of paper. If we were to use aptr after deallocating it, the program would crash.

What Good are Pointers?

A major reason for adding pointers to FreeBASIC is that many external libraries require pointers to type structures and pointers to strings. For example, the Win32 API has many structures that must be filled out and then passed to a function through a pointer.

Another use of a pointer is in a Type definition. Type defs in FreeBASIC can only contain fixed length strings, but what if you don't know the length of a string until the program is running? A pointer can serve this purpose.

(It should be stated that the Type definitions can now support variable length strings.)

Type mytptr
    sptr As ZString Ptr
End Type
'This function will allocate space for the passed string
'and load it into a memory location, returning the
'pointer to the string.
Declare Function pSetString(ByVal s As String) As ZString Ptr

'type var
Dim mytype As mytptr

'Set a variable string into the type def
mytype.sptr = pSetString("Hello World From FreeBASIC!")
Print "aptr: "; *mytype.sptr
Deallocate(mytype.sptr)
Sleep
End

Function pSetString(ByVal s As String) As ZString Ptr
    Dim sz As ZString Ptr

    'allocate some space + 1 for the chr(0)
    sz = Allocate(Len(s) + 1)
    'load the string into the memory location
    *sz = s
    'return the pointer
    Return sz
End Function

Here we define our type with a field sptr as ZString Ptr. Zstrings are null terminated strings and are used by many external libraries and are designed for dynamic allocations. Once we define our type we create an instance of it with the Dim statement:

Dim mytype As mytptr

We then call our function pSetString to get the address of the variable length string we want in our Type def.

mytype.sptr = pSetString("Hello World From FreeBASIC!")

Remember sptr is defined as a pointer, not a string variable, so pSetString is returning a pointer (memory address) to the string not the string itself. In other words, if the string is in hole #1, pSetString returns 1.

The function pSetString uses a temporary ZString sz, to Allocate space for the passed string parameter s. Because a ZString is a null terminated string, we must add 1 to the length of s for the null terminator in the Allocate function.

'allocate some space + 1 for the chr(0)
sz = Allocate(Len(s) + 1)

Once we have allocated space for the string, we use the dereference operator * to load the data into the memory location.

'load the string into the memory location
*sz = s

We then return a pointer (the address of the string) back to our type, which is saved in mytype.sptr.

'return the pointer
Return sz

We can now dereference the string in our type using the dereference operator.

Print "aptr: "; *mytype.sptr

Pointers can be confusing for the uninitiated, however they need not be if it is kept in mind that the pointer doesn't contain data, it simply points to some data. The pointer is a memory address, and you manipulate that data through the dereference operator *. It really isn't much different than a normal variable.

Last reviewed by sancho3 on February 07, 2018


Pointers, Data Types and Memory
Written by rdc

If you read the article Introduction to Pointers you know that pointers contain memory location addresses. You can manipulate the data in these memory locations using the dereference operator *. Using pointers with single data item isn't a problem, but what if you need to store multiple data items together and manipulate them using a pointer? It can get a bit tricky unless you understand how data is stored in memory.

A single memory location in a computer is 1 byte long. Big enough to hold a single ANSI character (as opposed to Unicode characters, which are wide characters and are two bytes. We won't be discussing Unicode characters in this article.) However, all data types are not a single byte in width. Here is a simple program that displays the length in bytes of each data type.

Dim a As Byte
Dim b As Short
Dim c As Integer
Dim d As LongInt
Dim au As UByte
Dim bu As UShort
Dim cu As UInteger
Dim du As ULongInt
Dim e As Single
Dim f As Double
Dim g As Integer Ptr
Dim h As Byte Ptr
Dim s1 As String * 10 'fixed string
Dim s2 As String      'variable length string
Dim s3 As ZString Ptr 'zstring

s1 = "Hello World!"
s2 = "Hello World from FreeBasic!"
s3 = Allocate( Len( s2 ) + 1 )
*s3 = s2

Print "Byte: ";Len(a)
Print "Short: ";Len(b)
Print "Integer: ";Len(c)
Print "Longint: ";Len(d)
Print "UByte: ";Len(au)
Print "UShort: ";Len(bu)
Print "UInteger: ";Len(cu)
Print "ULongint: ";Len(du)
Print "Single: ";Len(e)
Print "Double: ";Len(f)
Print "Integer Pointer: ";Len(g)
Print "Byte Pointer: ";Len(h)
Print "Fixed String: ";Len(s1)
Print "Variable String: ";Len(s2)
Print "ZString: ";Len(*s3)

Deallocate s3

Sleep

The output is (on 32bit systems):

Byte:  1
Short:  2
Integer:  4
LongInt:  8
UByte:  1
UShort:  2
UInteger:  4
ULongInt:  8
Single:  4
Double:  8
Integer Pointer:  4
Byte Pointer:  4
Fixed String:  10
Variable String:  27
ZString:  27

Notice that the length of a pointer is always 4 bytes long on 32bit systems or 8 bytes long on 64bit systems, the same as an integer, regardless of the data being pointed to, since a pointer contains a memory address and not data.

Looking at the length of the different data types, you can see that if you were to Allocate enough space for 10 integers, it would take 40/80 bytes of memory (on 32/64bit systems). Each integer takes up 4/8 bytes (on 32/64bit systems). So the question is, how do you access each integer value from the memory buffer? The answer, pointer math. Take a look at the following program.

Dim aptr As Integer Ptr

'Allocate enough space for 2 integers
aptr = Allocate(Len(Integer) * 2)
'Load our first integer
*aptr = 1
Print "Int #1:", "address: "; aptr, "value: "; *aptr
'Move the pointer to the next integer position
'aptr + 1
'Load our second integer
*(aptr + 1) = 2
Print "Int #2:", "address: "; aptr + 1, "value: "; *(aptr + 1)

Deallocate aptr
Sleep

In this program we dimension two variables, an Integer and an Integer Pointer, aptr. Aptr will point to our memory buffer that will contain two integers. The Allocate function requires the size of the buffer we need, so we multiply the size of an Integer by 2 to reserve 8/16 bytes of memory (on 32/64bit systems, each integer will take 4/8 bytes of space (on 32/64bit systems).

After the allocation process, aptr contains the address of the first byte of our memory buffer. Storing the first integer is simply a matter of using the dereference operator and setting the value to 1. To print out the value, we again just use *aptr.

Now, let me ask you a question: How does the compiler know that the value 1 requires 4/8 bytes (on 32/64bit systems) and not 1 or 2 bytes? Because we dimensioned aptr as an integer ptr. The compiler knows that an integer takes 4/8 bytes (on 32/64bit systems) and so loads the data into four bytes of memory. This is why when we print out the value we get 1 and not some strange number.

To load the second value into our buffer, we use:

*(aptr + 1) = 2

This may look a little strange at first glance. Aptr points to the first byte in our memory buffer. An integer is 4/8 bytes long (on 32/64bit systems), so to get to the next integer byte position, we must add 4/8 to the address (value of aptr). But the compiler knows that the data being used with this pointer is of size Integer or 4/8 bytes (on 32/64bit systems), so to access the second element, you need to only add 1 to the pointer, which can be expressed as *(aptr + 1).
We need the parenthesis around the add operation because the dereference operator * has a higher precedence than +. The parenthesis ensure that we perform the add operation first, and then apply the indirection operator.

Notice that we didn't increment aptr directly. If we did, aptr would no longer point to the start of the memory buffer and the program would crash when we deallocated the buffer since it would Deallocate memory outside the memory buffer. If the need arises to directly increment a pointer, then create a temporary pointer variable and increment that, rather than the pointer used in the original allocation.

Memory buffers and pointers are a powerful way to store and manipulate data in memory. Care must be taken though to ensure that you are accessing the data correctly according to the type of data being stored in the buffer.

Last reviewed by sancho3 on February 07, 2018


Compiling a Big QB program in FB

Let's try to compile a big (4000+ lines) graphical QB program in FreeBASIC, to see how compatible FB is with QB.
As an example I will use Jark's TCRay a great raytracer with quadric, cubic and quadratic shapes, perlin noise programmed in 2004. You can get TCRay.zip from
http://www.petesqbsite.com/downloads/graphics.shtml

Notice TCRay is a QB4.5 interpreted program, Jark never had the patience to compile his work,
he just tested it interpreted and went on adding features.

The program is made of has 3 files:

TcRay21C.bas - The Main file.
TcLib17L.bas - The SVGA graphics library.
Tclib17.bi - The include file for the library.

Porting TCLib17.bas

In TCLib17.bas
It is a "pure QB" SVGA library. Most of its functions are obsoleted by FB as they are implemented as QB-style keywords. I had my share in developing that lib so you can trust me for this part ;)

Comment out the contents of the ClearScreen sub and add this
CLS

Comment out the contents of the Point24 sub and add this:
a& = Point(x%,y%)
red% = a& Shr 16
green% = (a& Shr 8) And 255
blue% = a& And 255

Comment out the contents of the Pset24 sub and add this:
PSet (x%,y%), red% Shl 16 Or green Shl 8 Or blue

Comment out the contents of the Screenshot sub and add this:
BSave Name$+".bmp"

Comment out the contents of the SelectVga sub, we will work with a fixed size most pc's will support. Comment out the contents of the SetText sub, we are able to output text in HiRes graphics so mode switching is not required.

Comment out the contents of the SetVGA Sub excluding the Powers of two calculation at the end and add these four lines:
Screen 20,32 '1024x768, 32 bits
scrheight=768
scrwidth=1024
Fullscreen

In TCRay17.bas
Add a SetSVGA as the first line in the Menu sub (we are not switching modes so mode must be set before outputting text),

Compiling
Ok, stop trusting me, now you can start trying to compile. You'll receive some errors.

Compile with: fbc -s gui -w 1 -lang qb TcRay21C.bas TcLib17L.bas

I warn you all changes required except two come from a couple of (wise) limitations in the FB syntax:
  • A variable name can't be a keyword plus a type suffix
  • A simple variable can't have the same name as an array

In TCLib17.bi
ERROR: Duplicated definition, found 'RGB' (RGB is a keyword in FB)
Add:
#undefine RGB
Before the line giving the error.

ERROR: Duplicated definition, found 'ScreenRes' (ScreenRes is a keyword in FB)
Add:
#undefine ScreenRes
Before the line giving the error.

ERROR: Duplicated definition, found 'Name' (Name is a keyword in QB)
Add
#undefine Name
Before the line giving the error.

In TCRay17.bas
ERROR: Duplicated definition, found 'Acos' (Acos is a keyword in QB)
Add
#undefine Acos
Before the line giving the error.

ERROR: Argument count mismatch Clear (Clear is not required in FB, the keyword has been reused (not a clever decision?))
'comment out CLEAR

ERROR: Illegal specification, at parameter 2 (Type) of Init.Cubic() (Type is a keyword in QB)
We can undefine it so search and replace type$ to _type$

ERROR: Expected 'END IF', found 'END' END FUNCTION
This is an error caused by a quirk introduced in FB. Single line If's having a colon after THEN require an ENDIF, it has to do with macros... What reason had Jark to put colons past his THEN's escapes me. QB does not require them at all and FB behaves as expected without them. Remove all colons after THEN keywords. Search and Replace THEN : to THEN

ERROR: Array access, index expected, before '=' xn = x * x - y * y + zx0
We have an array names xn, and a variable named xn. Substitute xn with _xn in the lines which error when you try to compile.

The same error with x0, we have an array called x0.
Substitute xo with _xo in the lines which error when you try to compile.

ERROR: Array access, index expected, before '*' dadY = Amplitude * dAdR * drdY. Same problem with Amplitude.
Substitute Amplitude with _Amplitude in the lines which error when you try to compile.

Ok. At this point all modules compile. We're now going to fix a few linker errors.

Linker Errors
After compiling the linker ties together all the modules with a runtime library, finds the final addresses of every sub/function and substitutes the labels in the calls with these addresses. If a sub/function is called in the code and its nowhere to be found, the linker complains and gives us the name of the offending function. It can't give us the line numbers (the linker doesn't work with the source) so we will have to do a text search to find where the problem occurs. Notice the linker gives us "mangled" function names (an ampersand and the size of the parameters passed is added to the end), just ignore the ampersand and what's after.

TcRay21C.o:fake:(.text+0x174d): undefined reference to `LINE24@20'
A call to an undefined Line24 sub is made in the program, you can find this call inside Draw.Axis, in TCRay21.bas, a sub that's it's never called (you can do a search to confirrm it)
Probably the QB4.5 compiler would complain too about this. (Remember: this program never compiled in QB4.5) Just comment out the contents of the sub Draw.Axis

TcRay21C.o:fake:(.text+0x181b3): undefined reference to `FFIX@0'
Ffix was that useful v1ctor's floating point patch for QB 4.5. It's not needed in FreeBASIC. Just comment out the line calling it just after the declarations in tcray21c.bas

And that's all, the program compiles and works. Not a lot of changes for 4000+ lines...
Enjoy!


The Pointer Data Type
Written by rdc

The pointer data type is unique among the FreeBASIC numeric data types. Instead of containing data, like the other numeric types, a pointer contains the memory address of data.

On a 32-bit system, the pointer data type is 4 bytes (8 bytes on a 64-bit system). FreeBASIC uses pointers for a number of functions such as ImageCreate, and pointers are used heavily in external libraries such as the Windows API. Pointers are also quite fast, since the compiler can directly access the memory location that a pointer points to. A proper understanding of pointers is essential to effective programming in FreeBASIC.

For many beginning programmers, pointers seem like a strange and mysterious beast. However, if you keep one rule in mind, you should not have any problems using pointers in your program. The rule is very simple: a pointer contains an address, not data. If you keep this simple rule in mind, you should have no problems using pointers.

Pointers and Memory

You can think of the memory in your computer as a set of post office boxes (P.O. Box) at your local post office. When you go in to rent a P.O. Box, the clerk will give you a number, such as 100. This is the address of your P.O. Box. You decide to write the number down an a slip of paper and put it in your wallet. The next day you go to the post office and pull out the slip of paper. You locate box 100 and look inside the box and find a nice stack of junk mail. Of course, you want to toss the junk mail, but there isn't a trash can handy, so you decide to just put the mail back in the box and toss it later. Working with pointers in FreeBASIC is very similar to using a P.O. Box.

When you declare a pointer, it isn't pointing to anything which is analogous to the blank slip of paper. In order to use a pointer, it must be initialized to a memory address, which is the same as writing down the number 100 on the slip of paper. Once you have the address, find the right P.O. Box, you can dereference the pointer, open the mail box, to add or retrieve data from the pointed-to memory location. As you can see there are three basic steps to using pointers.

Declare a pointer variable.
Initialize the pointer to a memory address.
Dereference the pointer to manipulate the data at the pointed-to memory location.

This isn't really any different than using a standard variable, and you use pointers in much the same way as standard variables. The only real difference between the two is that in a standard variable, you can access the data directly, and with a pointer you must dereference the pointer to interact with the data.

Typed and Untyped Pointers

FreeBASIC has two types of pointers, typed and untyped. A typed pointer is declared with an asscoaited data type.

Dim myPointer As Integer Ptr

This tells the compiler that this pointer will be used for integer data. Using typed pointers allows the compiler to do type checking to make sure that you are not using the wrong type of data with the pointer, and simplifies pointer arithmetic.

Untyped pointers are declared using the Any keyword.

Dim myPointer As Any Ptr

Untyped pointers have no type checking and default to size of byte. Untyped pointers are used in the C Runtime Library and many third party libraries, such as the Win32 API, to accommodate the void pointer type in C. Unless you specifically need an untyped pointer, you should use typed pointers so that the compiler can check the pointer assignments.

Pointer Operators

FreeBASIC has the following pointer operators.

You will notice that the addressof operator not only returns the memory address of a variable, but it can also return the address of a subroutine or function. You would use the address of a subroutine or function to create a callback function such as used in the CRT QSort function.

Memory Functions

FreeBASIC also has a number of memory functions that are used with pointers.

These functions are useful for creating a number of dynamic structures such as linked lists, ragged or dynamic arrays and buffers used with third party libraries.

When using the Allocate function you must specify the storage size based on the data type using the equation number_of_elements * Sizeof(datatype). To allocate space for 10 integers your code would look like this: myPointer = Allocate(10 * Sizeof(Integer)). An integer is 4/8 bytes (on 32/64 bit systems) so allocating 10 integers will set aside 40/80 bytes of memory (on 32/64bit systems). Allocate does not clear the memory segment, so any data in the segment will be random, meaningless data until it is initialized.

Callocate works in the same fashion, except that the calculation is done internally. To allocate the same 10 integers using Callocate your code would look like this: myPointer = Callocate(10, Sizeof(Integer)). Unlike Allocate, Callocate will clear the memory segment.

Reallocate will change the size of an existing memory segment, making it larger or smaller as needed. If the new segment is larger than the existing segment, then the data in the existing segment will be preserved. If the new segment is smaller than the existing segment, the data in the existing segment will be truncated. Reallocate does not clear the added memory or change any existing data.

All of these functions will return a memory address if successful. If the functions cannot allocate the memory segment, then a NULL pointer (0) is returned. You should check the return value each time you use these functions to be sure that the memory segment was successfully created. Trying to use a bad pointer will result in undesirable behavior or system crashes.

There is no intrinsic method for determining the size of an allocation. You must keep track of this information yourself.

Be careful not to use the same pointer variable to allocate two or more memory segments. Reusing a pointer without first deallocating the segment it points to will result in the memory segment being lost causing a memory leak.

Pointer Arithmetic and Pointer Indexing

When you create a memory segment using the allocation functions, you will need a way to access the data contained within the segment. In FreeBASIC there are two methods for accessing data in the segment; using the indirection operator with pointer arithmetic, and pointer indexing.

Pointer arithmetic, as the name suggests, adds and subtracts values to a pointer to access individual elements within a memory segment. When you create a typed pointer such as Dim myPointer as Integer ptr, the compiler knows that the data being used with this pointer is of size Integer or 4 bytes. The pointer, when initialized, points to the first element of the segment. You can express this as *(myPtr + 0). To access the second element, you need to add 1 to the pointer, which can be expressed as *(myPtr + 1).

Since the compiler knows that the pointer is an Integer pointer, adding 1 to the pointer reference will actually increment the address contained in myPtr by 4/8 (on 32/64bit systems), the size of an Integer. This is why using typed pointers is preferable over untyped pointers. The compiler does much of the work for you in accessing the data in the memory segment.

Notice that the construct is *(myPtr + 1) and not *myPtr + 1. The * operator has higher precedence than +, so *myPtr + 1 will actually increment the contents myPtr points to, and not the pointer address.

myPtr will be evaluated first, which returns the contents of the memory location and then +1 will be evaluated, adding 1 to the memory location. By wrapping myPtr + 1 within parenthesis, you force the compiler to evaluate myPtr + 1 first, which increments the pointer address, and then the * is applied to return the contents of the new address.

Pointer indexing works the same way as pointer arithmetic, but the details are handled by the compiler. *(myPtr + 1) is equivalent to myPtr[1]. Again, since the compiler knows that myPtr is an integer pointer, it can calculate the correct memory offsets to return the proper values using the index. Which format you use if up to you, but the index method resembles the standard array access method and visually is easier to understand than the indirection operator.

Pointer Functions

Freebasic has a set of pointer functions to complement the pointer operators.

CPtr Converts expression to a data_type pointer. Expression can be another pointer or an integer.
Peek Returns the contents of memory location pointed to by pointer. Data_type specifies the type of expected data.
Poke Puts the value of expression into the memory location pointed to by pointer. The data_type specifies the type of data being placed into the memory location.
SAdd Returns the location in memory where the string data in a dynamic string is located.
StrPtr The same as Sadd.
ProcPtr Returns the address of a function. This works the same way as the addressof operator @.
VarPtr This function works the same way as the addressof operator @.

The Sadd and Strptr functions work with the string data types to return the address of the string data. The Peek and Poke functions have been added for the purposes of supporting legacy code. Procptr and Varptr both work just like the address of operator @, but Proptr only works on subroutines and functions and Varptr only works on variables. Cptr is useful for casting an untyped pointer to a typed pointer, such as a return value from a third party library.

Subroutine and Function Pointers

Subroutines and functions, like variables, reside in memory and have an address associated with their entry point. You can use these addresses to create events in your programs, to create pseudo-objects and are used in callback functions. You create a sub or function pointer just like any other pointer except you declare your variable as a pointer to a subroutine or function rather than as a pointer to a data type.

Before using a function pointer, it must be initialized to the address of a subroutine or function using Procptr or @. Once initialized, you use the pointer in the same manner as calling the original subroutine or function.

You declare a function pointer using the anonymous declaration syntax.

Dim FuncPtr As Function(x As Integer, y As Integer) As Integer

You then need to asscoate this function pointer with an actual subroutine or function within your code.

Function Power(number As Integer, pwr As Integer) As Integer
    Return number^pwr
End Function

FuncPtr = @Power

You can then call the function pointer much like you would call the real function.

FuncPtr(2, 4)

While this may not be useful at first glance, you can use this technique to implement polymorphic functions where a single variable instance can point to one of several different subroutine or functions.

For example, suppose you have a dog and cat object. Both objects need a Speak method. By defining Speak as a function pointer and associate Speak with a Bark subroutine for a dog and a Meow subroutine for a cat, you can make Speak either issue a "Woof!" or "Meow!" depending on the object type.

Creating a Callback Function

One of the primary uses for function pointers is to create callback functions. A callback function is a function that you have created in your program that is called by another function or subroutine either in your own code space or in an external library. Windows uses callback functions to enumerate through Window objects like fonts, printers and forms.

The qsort, function contained within the C Runtime Library sorts the elements of an array using a callback function to determine the sort order. The prototype for the qsort function is contained in crt/stdlib.bi:

Declare Sub qsort (ByVal As Any Ptr, ByVal As size_t, ByVal As size_t, ByVal As Function(ByVal As Any Ptr, ByVal As Any Ptr) As Long)

The following lists the parameter information for the qsort subroutine.

The first parameter is the address to the first element of the array. The easiest way to pass this information to qsort is to append the address of operator to the first element index: @myArray(0).
The second parameter is the number of elements in the array, that is the array count.
The third parameter is the size of each element in bytes. For an array of integers, the element size would be 4 bytes.
The fourth parameter is a function pointer to the user created compare function. The function must be declared using the Cdecl passing model, as shown in this parameter.

Using this information, you can see how qsort works. By passing the address of the first element along with the count of elements, and the size of each element, qsort can iterate through the array using pointer arithmetic.

Qsort will take two array elements, pass them to your user defined compare function and use the compare function's return value to sort the array elements. It does this repeatedly until each array element is in sorted order.

You need to declare the function prototype as Cdecl which ensures that the parameters are passed in the correct order.

Declare Function QCompare cdecl (ByVal e1 As Any Ptr, ByVal e2 As Any Ptr) As Long

You would then define the function like the following.

'The qsort function expects three numbers
'from the compare function:
'-1: if e1 is less than e2
'0: if e1 is equal to e2
'1: if e1 is greater than e2
Function QCompare cdecl (ByVal e1 As Any Ptr, ByVal e2 As Any Ptr) As Long
    Dim As Integer el1, el2
    Static cnt As Integer

    'Get the call count and items passed
    cnt += 1
    'Get the values, must cast to integer ptr
    el1 = *(CPtr(Integer Ptr, e1))
    el2 = *(CPtr(Integer Ptr, e2))
    Print "Qsort called";cnt;" time(s) with";el1;" and";el2;"."
    'Compare the values
    If el1 < el2 Then
        Return -1
    ElseIf el1 > el2 Then
        Return 1
    Else
        Return 0
    End If
End Function

You would then call the QSort function passing the address of the callback function.

#include "crt/stdlib.bi"

qsort @myArray(0), 10, SizeOf(Integer), @QCompare

Pointer to Pointer

In FreeBASIC you can create a pointer to any of the supported data types, including the pointer data type. A pointer to a pointer is useful in situations where you need to return a pointer to a function or in creating specialized data structures such as linked-lists and ragged arrays. A pointer to a pointer is called multi-level indirection.

One application of a pointer to pointer is the creation of a memory segment that behaves just like an array. For example, suppose you want to create a memory segment to hold an unknown number of integers. You can create a dynamic memory segment that you can resize as needed during runtime to handle as many integers as you need. You would start by creating a pointer-to-pointer variable.

Dim myMemArray As Integer Ptr Ptr

You would then initialize the pointer reference by using Allocate or Callocate.

'Create 10 rows of integer pointers
myMemArray = CAllocate(10, SizeOf(Integer Ptr))

Notice that the variable is initialized to an Integer Ptr since this list is going to point to another list; this is the pointer that points to another pointer. You can then initialize the individual pointer references just created to point to the needed memory segments.

'Add 10 columns of integers to each row
For i As Integer = 0 To 9
    myMemArray[i] = CAllocate(10, SizeOf(Integer))
Next

In this code snippet, the individual pointers in the list are initialized to 10 memory segments that will contain the actual integer data.

'Add some data to the memory segment
For i As Integer = 0 To 9
    For j As Integer = 0 To 9
        myMemArray[i][j] = Int(Rnd * 10)
    Next
Next

This code snippet uses the index method to load the actual data into the memory segments. Notice that this looks and acts just like a two-dimensional array. While this may not seem useful within this context, you could use this code to create a dynamic array within a type definition. Since you cannot have a standard dynamic array within a type, this allows you get the same functionality.
Editors Note: Dynamic arrays are allowed in recent releases of FreeBASIC

One thing you need to be aware of is how to deallocate a structure such as this. The rule is to just do the reverse of the allocation operations. Since the last allocate operation initialized the data memory segments, you deallocate these memory segments first and then you can deallocate the base pointer.

'Free memory segment
For i As Integer = 0 To 9
    Deallocate myMemArray[i]
Next

'Free the pointer to pointer
Deallocate myMemArray

You need to be sure that you deallocate in the right order, otherwise you will end up with memory segments that are not freed, but inaccessible. These are memory leaks and can cause a whole host of problems in your program.

Last reviewed by sancho3 on February 07, 2018


The Select Case Statement
Written by rdc

The Select Case block can be viewed as an optimized If-ElseIf ladder, and works in much the same way. The standard Select Case can use any of the standard data types for <expression> and the specialized Select Case As Const format is optimized for integer values.

This code snippet shows the syntax of the standard select case. Expression is usually a variable which can be of any of the standard data types, or individual elements of a Type or array.

Select Case <expression>
    Case <list>
        <statement>
        <statement>
        ...
    Case Else
        <statement>
        <statement>
        ...
End Select

The <list> clause of the Case statement can be any of the following formats.

Case <value>: Value is one of the supported data types or an enumeration.
Case <value> To <value>: Specifies a range of values.
Case Is <operator> <value>: Operator is any of the logical operators.
Case <value>, <value>, ...: List of values separated with commas.
Case <variable>: A variable that contains a value.

The following snippet illustrates how these different formats may be used in a program.

Case 47

Case 47 To 59

Case Is > 60

Case 47, 48, 53

Case keycode

The Select Case As Const is a faster version of the Select statement designed to work with integer expressions in the range of 0 to 4097.

Select Case As Const <integer_expression>
    Case <list>
        <statement>
        <statement>
        ...
    Case Else
        <statement>
        <statement>
        ...
End Select

The <list> statement formats for the Select Case As Const are limited to values or enumerations of values. That is, the operator expressions are not allowed within a Case As Const.

When a Case block is executed, the statements following the Case keyword up to the next Case keyword (or End Select) will be executed. Only one block of statements within a Case will execute at any one time. If a Case Else is present, then the statements within the Else block will execute if no Case matches the <expression> portion of the Select statement. The following program illustrates using the Select Case statement block.

'Ascii code of key press
Dim As Integer keycode

'Loop until esc key is pressed
Do
    keycode = Asc(Inkey)
    Select Case As Const keycode
        Case 48 To 57
            Print "You pressed a number key."
        Case 65 To 90
            Print "You pressed an upper case letter key."
        Case 97 To 122
            Print "You pressed a lower case key."
        End Select
        Sleep 1
Loop Until keycode = 27 '27 is the ascii code for Escape

End

In the program, when you press a key, the value is translated to a number using the Asc function. Since this will always be an integer value that is less than 4097 (since ascii character codes range from 0 to 255), the Select Case as Const format is used.

The compiler will check the value of keycode against the Case ranges to see if the block should execute. If keycode falls within a particular range, the Print statement will execute, and then the flow of the program will continue with the next line following the End Select. If keycode doesn't match any Case range, then the program will continue with the next line following the End Select.

A Select Case can usually be translated from an If-Elseif ladder. To illustrate this, the previous program is shown below as an If-ElseIf ladder.

'Ascii code of key press
Dim As Integer keycode

'Loop until esc key is pressed
Do
    keycode = Asc(Inkey)
    If (keycode >= 48) And (keycode <= 57) Then
        Print "You pressed a number key."
    ElseIf (keycode >= 65) And (keycode <= 90) Then
        Print "You pressed an upper case letter key."
    ElseIf (keycode >= 97) And (keycode <= 122) Then
        Print "You pressed a lower case key."
    End If
    Sleep 1
Loop Until keycode = 27 '27 is the ascii code for Escape

End

If you compare the two programs, you can see that the logic is quite similar, however the Select Case is much more readable and understandable than the If-ElseIf ladder.

Last Reviewed by Sancho3 on February 06, 2018


Simulating Polymorphism
Written by rdc

Introduction

Polymorphism is a powerful tool in object-oriented program. A polymorphic method (Sub or Function) behaves differently depending on the definition of the object. For example, an animal object may have a speak method that will issue a bark for a dog and a meow for a cat. FreeBASIC did not support true polymorphism before. However, you could simulate polymorphic methods using method pointers.

Polymorphism

Polymorphic methods are subroutines or functions that have the same type and parameter list, but behave differently when bound to different objects. An animal object may have a Speak method that will issue a bark for a dog and a meow for a cat. Since FreeBASIC doesn't yet have classes, you cannot implement true polymorphic methods, but you can simulate the behavior by using method pointers.

The following listing shows a couple of defines and an extended type declaration:
#define isdog 1
#define iscat 2

Type animal
    Public:
    speak As Sub()
    Declare Constructor (anid As Integer)
End Type

The #defines are passed to the Constructor to signal what type of object is being created. The speak As Sub() definition defines the method pointer. As you will see, the address of two different subroutines will be passed to the speak method pointer. The following listing shows the different speak subroutines and the Constructor method:
'Speak method for dog object
Sub Bark()
    Print "Woof!"
End Sub

'Speak method for cat object
Sub Meow()
    Print "Meow!"
End Sub

'Set the proper method pointer based on animal id
Constructor animal(anid As Integer)
    If anid = isdog Then
        this.speak = @Bark
    ElseIf anid = iscat Then
        this.speak = @Meow
    End If
End Constructor

The Bark subroutine will be called if the object is a dog and the Meow subroutine will be called if the object is a cat. You may be wondering why you can't just overload the method? For overloaded methods, the type and parameter list must be unique, where in a polymorphic method, the type and parameter list must be the same. Since Bark and Meow have the same parameter list, that is no parameters, you cannot overload the method.

The Constructor code is where the program decides what method call to use. If anid is equal to isdog, then the Speak method pointer will be set to the address of the Bark subroutine. If anid is equal to iscat then Speak will be set to the address of the Meow subroutine. The addressof operator @ is used to pass the address of Bark and Meow to the Speak pointer.

The this object reference is a hidden parameter that is passed to the Constructor that references the type, which in this case is animal. You can use this to reference the internal variables within the type.

The only thing left to do is to create and initialize the object:
'Create a dog and cat object
Dim myDog As animal = isdog
Dim mycat As animal = iscat

Here myDog and myCat are created with the appropriate flags passed to the Constructor so that the proper references can be set up. Once the object are created you can call the speak method of each object.
'Have the animals speak
Print "My dog says ";
myDog.speak()
Print "My cat says ";
myCat.speak()

Notice that you are calling the same speak method, yet the output is different:
My dog says Woof!
My cat says Meow!

This is the essence of polymorphic methods.

Here is the complete program listing:
'Simulated Polymorphism Using Method Pointers
'Richard D. Clark
'Requires the CVS version of FreeBASIC
'**********************************************

#define isdog 1
#define iscat 2

Type animal
    Public:
    speak As Sub()
    Declare Constructor (anid As Integer)
End Type

'Speak method for dog object
Sub Bark()
    Print "Woof!"
End Sub

'Speak mehod for cat object
Sub Meow()
    Print "Meow!"
End Sub

'Set the proper method pointer based on animal id
Constructor animal(anid As Integer)
    If anid = isdog Then
        this.speak = @Bark
    ElseIf anid = iscat Then
        this.speak = @Meow
    End If
End Constructor

'Create a dog and cat object
Dim myDog As animal = isdog
Dim mycat As animal = iscat

'Have the animals speak
Print "My dog says ";
myDog.speak()
Print "My cat says ";
myCat.speak()

Sleep
End

Polymorphism through inheritance and virtuality is now supported

Previous example now transposed, by using polymorphism through inheritance with abstract/virtual methods (feature now supported):
'Base-type animal
Type animal Extends Object
    Declare Abstract Sub speak ()
End Type

'Derived-type dog
Type dog Extends animal
    Declare Virtual Sub speak () Override
End Type

'Speak method for dog object
Virtual Sub dog.speak ()
    Print "Woof!"
End Sub

'Derived-type cat
Type cat Extends animal
    Declare Virtual Sub speak () Override
End Type

'Speak mehod for cat object
Virtual Sub cat.speak ()
    Print "Meow!"
End Sub

'Create a dog and cat as dynamic object through animal pointer
Dim myDog As animal Ptr = New dog
Dim mycat As animal Ptr = New cat

'Have the animals speak
Print "My dog says ";
myDog->speak()
Print "My cat says ";
myCat->speak()

Sleep

'Delete the dynamic objects
Delete myDog
Delete myCat


Using FreeBASIC Built Libraries with GCC
by Jeff Marshall

Shows how to create a static library with FreeBASIC and then call it from a C program using GCC as the compiler.

This article shows Windows usage throughout, but application to FreeBASIC on other platforms is similar.

In this tutorial:
A Simple Test
FreeBASIC Library With Dependencies
Using FreeBASIC as a Smart Linker

A Simple Test

For this simple test we are going to create a FreeBASIC static library, one without any dependencies. This will make it easier the first time around, and will allow us to check that the basics are working:

First we need a library, and for for this it will be just a single trivial function that will add two integers together and return the result. Notice the use of cdecl and Alias in our procedure definition. By default, C uses the cdecl calling convention. Using Alias in the FreeBASIC declaration makes matching case sensitivity between FreeBASIC and C easier. C is case sensitive, whereas FreeBASIC normally is not.

'' mylib1.bas

Function Add2Numbers cdecl Alias "Add2Numbers" _
    ( _
        ByVal x As Integer, _
        ByVal y As Integer _
    ) As Integer

    Return x + y

End Function

Create a file called mylib1.bas as above and compile it with:

fbc -lib mylib1.bas.

This will create our static library libmylib1.a. Next we need a C program that is going to call the library we just made. We must add a prototype that exactly matches the function we have in the FreeBASIC library. The C listing below is our main entry point, will set up a couple of variables to call Add2Numbers(), and print the results.

/* test1.c */

#include <stdio.h>

/* Prototype from libmylib.a */
Int Add2Numbers( Int x, Int y );

Int main ()
{
    Int a = 5;
    Int b = 7;
    Int c = Add2Numbers( a, b );

    printf( "a = %d\n", a );
    printf( "c = %d\n", b );
    printf( "a + b = %d\n", c );

    Return 0;
}

To compile this C program using the FreeBASIC library we just made we need to compile test1.c as we normally would but also tell it which libraries are needed. In our case, it is libmylib1.a.

gcc test1.c -L . -l mylib1 -o test1.exe

The '-L .' option tells the linker to search in the current directory for libraries, and the '-l mylib1' indicates that we want to link with the library we just created. This is the simplest case becase the libmylib1.a library has no dependencies. If mylib1.bas needed other libraries, for example the FreeBASIC run-time library libfb.a, we would need to specify that as well to gcc.

FreeBASIC Library With Dependencies

Here we create a FreeBASIC library that uses some features from the FreeBASIC runtime and graphics library. In this case we will have to specify any additional needed libraries to GCC.

'' mylib2.bas

Sub TestGfx cdecl Alias "TestGfx" ()

    Screen 12

    Line (0,0)-(100,100),15

    Sleep

End Sub

Create a file called mylib2.bas with the listing above and compile it with:

fbc -lib mylib2.bas.

This will create our static library libmylib2.a. Next we need a C program that is going to call the library we just made. We must add a prototype that exactly matches the function we have in the FreeBASIC library. This C listing will provide our entry point and just call TestGfx() before terminating.

/* test2.c */

void TestGfx();

Int main()
{

    TestGfx();

    Return 0;

}

To compile and link test2.c directly with gcc, not only do we need to tell gcc that we want to link with libmylib2.a, but also every other library that libmylib2.a needs.

gcc test2.c -L. -lmylib2 -L"C:\FreeBASIC\lib\win32" "C:\FreeBASIC\lib\win32\fbrt0.o" -lfbgfx -lfb -lgdi32 -o test2.exe

Depending on what our FreeBASIC library uses, it we may use several additional libraries, we must specify all the names of the libraries on the gcc command line. In this example, FreeBASIC is located in "C:\FreeBASIC", but you should specify whatever directory you installed FreeBASIC to. "C:\"FreeBASIC\lib\win32\fbrt0.o" is a special startup file that will initialize the FreeBASIC runtime library. More specifically, it is initialized after the C runtime library, but before any of our program code is called. The additional -lfbgfx, -lfb, -lgdi32, are the additional libraries needed to complete linking. The actual libraries will vary depending on which FreeBASIC runtime functions are used, and which platform, for DOS or Linux, the program is being compiled for.

Using FreeBASIC as a Smart Linker

FreeBASIC has a neat built-in feature that stores a little bit of extra information in the library indicating what compile time options were used, and which dependent libraries are needed. This is a FreeBASIC only feature, so this kind of capability won't be found when using gcc as the main compiler and linker.

If we reuse the examples from the previous section, mylib2.bas and test2.c, but just go about compiling and linking them differently, we can save ourselves a bunch of typing. Plus we usually won't have to know or remember what our FreeBASIC built library's dependencies are. Compile mylib2.bas as before in to a static library.

fbc -lib mytest2.bas

Next we compile our C test program. Notice the '-c' option for the gcc command line. This indicates that we are just going to compile the C source, but not link it yet. test2.o will still have the entry point, but we are going to put it in an object file instead of trying to create an executable right away.

gcc -c test2.c -o test2.o

Lastly, we use fbc to perform the link step. We are not compiling any basic source files here, but we are going to use the smart linking capabilities of FreeBASIC such that the command line is fairly simple:

fbc test2.o -l mylib2

This will create an executable named test2.exe because test2.o was specified first on the command line. FreeBASIC will read the extra information stored in libmylib2.a and automatically know which additional libraries to link with. That's loads easier than using gcc directly, especially when many extra FreeBASIC built libraries are needed.

See also

  • Static Libraries


Using Libraries
This is an excerpt from an article published in QBXL Magazine, with permission by SJ Zero, the author.

FreeBASIC's greatest strength is it's ability to seamlessly integrate with a number of standard C libraries while maintaining the ease of use that is QB. Even before FB had a built-in graphics library, intrepid coders were using SDL to get graphics and sound routines working. Before the current version included a SDL_net and Winsock, a number of coders, myself included, fought with the headers to get network support into FreeBASIC. Today, I'm just going to cover how to get started with three advanced libraries: SDL, fmod, and tinyPTC. After understanding the fundamentals, you'll see that using C libraries is simple enough that with few exceptions, C libraries are no more difficult to use in FreeBASIC than QB libraries were to use.

What are these Libraries, Anyway?

These libraries are particularly useful because they tend to provide functions for games.

SDL is a library with graphics and input support built in, and a bunch of sub-libraries for network, TrueType font support, and audio. It can be used with OpenGL, but I won't be covering that today.

TinyPTC is primarily a graphics library, the simplest one available. It does little more than give you a pointer to the graphics region to draw to.

FMod is a 3d sound and music library. Though its license is strange, it works acceptably for playing sounds, and it nicely encapsulates 3D sound.

Including the Library

The first step in getting any of these libraries to work is including their header files in your project.
For SDL, it's simply
 #Include "SDL\SDL.bi"
For FMOD, it's
 #Include "fmod.bi"
and for tinyPTC, you'll want
 #Include "tinyptc.bi"
'2. Initilizing the library, loading a file'

Obviously, you can't just include the lib and fire away if it's got to do stuff first.
To initilize SDL and load a bitmap into memory, you must:
CONST SCR_WIDTH = 640
CONST SCR_HEIGHT = 480
DIM MenuScreen AS SDL_Surface ptr 'our bitmap
DIM Shared video AS SDL_Surface ptr 'our screen surface

SDL_Init ( SDL_INIT_VIDEO )
video = SDL_SetVideoMode( SCR_WIDTH, SCR_HEIGHT, 32, 0 ) 'sets the video mode for 640x480x32
MenuScreen = SDL_LoadBMP("bitmap.bmp")
To initilize FMOD and load a sound into memory, you must:
DIM sound AS INTEGER 'it's just a handle, so it's an int!

IF FSOUND_GetVersion <= FMOD_VERSION THEN
ErrorQuit "FMOD version " + STR$(FMOD_VERSION) + " or greater required"
End If

If FSOUND_Init(44100, 32, 0) = FALSE Then
ErrorQuit "Can't initialize FMOD"
End If

sound = FSOUND_Sample_Load(FSOUND_FREE,"sound.wav", FSOUND_HW3D, 0, 0)
Finally, there's no data formats to load with tinyPTC because it's so simple, but you initilize it by going:
const SCR_WIDTH = 320
const SCR_HEIGHT = 200
const SCR_SIZE = SCR_WIDTH*SCR_HEIGHT

if( ptc_open( "tinyPTC test", SCR_WIDTH, SCR_HEIGHT ) = 0 ) then
end -1
end if
Blitting, Playing, or Plotting

The most important step, obviously, is to get whatever you want to do to the screen or speakers. This part is relatively easy, and can be encapsulated further into a wrapper function. For SDL, sending an image to the screen means going:

SUB BlitImage(x as integer,y as integer,image as sdl_surface ptr, dest as sdl_surface ptr)
DIM Rectangle as SDL_Rect
DIM Rectangle2 as SDL_Rect

Rectangle.X = 0
Rectangle.Y = 0
rectangle.w = image->w
rectangle.h = image->h
Rectangle2.x = x
Rectangle2.y = y

SDL_BlitSurface image, @rectangle, dest, @rectangle2

END SUB

For FMOD, the steps to play a sound aren't that difficult either:
FUNCTION fModPlayWave( samp1 as integer ) AS INTEGER
'where samp1 is the number returned by FSOUND_SampleLoad

DIM position(0 to 2)' as FSound_Vector
DIM vel(0 to 2)' FSound_Vector

fModPlayWave = FSOUND_PlaySoundEx(FSOUND_FREE, samp1, NULL, TRUE)

END FUNCTION
And TinyPTC, which is again, not a high level library like the other two, can plot pixels using the following code:
SUB putd(BYREF buffer(), BYVAL x AS INTEGER, BYVAL y AS INTEGER, BYVAL colr as INTEGER)
        buffer((y * SCR_WIDTH) + x) = colr
        ptc_update @buffer(0) 'This is a pageFlip
END SUB

Shutting Down
So you don't have to manage memory and do all the boring mundane tasks, you must remember to shut down the library before your program exits. Luckily, all three programs allow this with one line. If you can't shut it down, the library no longer cares. It's beautiful.

SDL: SDL_Quit ()

fmod: FSOUND_Close ()

tinyPTC: PTC_Close ()
That's all there is to quitting!
As you can see, there is nothing inherently more difficult in using libraries in FreeBASIC compared to QuickBASIC. In fact, because coders don't need to jump through hoops to get to memory, it's actually much easier, even with the more modern OS and hardware.

Last Reviewed by Sancho3 on February 06, 2018


Using the Mouse in FreeBASIC

After doing some searches, I quickly noticed that there simply wasn't an official tutorial or technique for manipulating the mouse in a windows console application in FreeBASIC. Therefore, I decided to write this technique in order to give such an example to the FreeBASIC Community. As you know A Windows Console is already mouse aware by ways of the fact that it is a windows console, which means it's created with the use of the Windows API, which means that the mouse can be accessed from the Console Window. So There's no need to turn the mouse on or off in your code. All you need to do is Get or Set the X and Y coordinates and get the states of the mouse buttons. We will be covering the following subjects in this tutorial.

  • Getting Mouse Coordinates:
The mouse cursor, when the mouse is moved, continuously updates its position. You can get these values to determine where the pointer currently is on the screen.

  • Setting Mouse Coordinates:
For some reason there may be a need to position the mouse pointer at a different location than where the pointer currently is.

  • Getting The Mouse Button Statuses:
Quite simply, when the user presses a button on the mouse, it returns a value that says that a button is pressed, and which buttons are pressed, too. From these values you can decide what part of your code gets executed.

As with most tutorials, this one too can be better explained with the use of an example program. We will be creating a very simple program that acts upon the user's interaction with the mouse and certain areas of the screen. It should provide the bases of code needed to efficiently operate and control the mouse in your own programming projects.

Editors Note:
- The mouse functions will work with a graphics window as demonstrated in this tutorial.
- They will work with console window however in Windows, Quick Edit mode must be turned off (this conflict between Quick Edit and FreeBASIC mouse is fixed from fbc version 1.08).

THE SAMPLE PROGRAM DESCRIPTION

For the sake of a demonstration program, things will be quite simple and as straightforward as it possibly can. The program will show 3 items at the top of the screen and depending on which one you click a different message will be displayed on the screen. This should give you enough information to know how to work with the mouse in FreeBASIC.

In FreeBASIC, there's basically 2 commands that you need to worry about when trying to handle the mouse in your projects. Here they are with their syntax explained as per the documentation.

--------------------------------------------------------------------------------

Syntax

result = GetMouse (x, y [, [ wheel ] [, [ buttons ] [, [ clip ]]]])

Description

GETMOUSE retrieves the mouse position and button status.

Mouse position is stored in X and Y when the function is called. If the mouse is not in the program window, X and Y will be -1.

'wheel' is the mouse wheel counter. Rotating the wheel away from you makes the count to increase, rotating towards you makes it to decrease. If mouse is not present or out of the program window, wheel will hold -1.

'buttons' stores the button status. On function termination, this will return a bitmask holding buttons status. Bit 0 is set if left mouse button is down; bit 1 is set if right mouse button is down; bit 2 is set if middle mouse button is down.

'clip' stores the mouse clipping status; if 1, the mouse is currently clipped to the graphics window; if 0, the mouse is not clipped.

*GETMOUSE is for use in graphics modes, set using the SCREEEN command ONLY.*

---------------------------------------------------------------------------------

Syntax

result = SetMouse([ x ] [, [ y ] [, [ visibility ] [, [ clip ]]]])

Description

SETMOUSE will set the X,Y coordinates of the mouse pointer, as well as setting it's visibility.

Mouse position is set using the X and Y parameters.

The mouse will be visible if visibility is set to 1, and invisible if visibility is set to 0.

clip - 1 indicates mouse is clipped to graphics window, 0 indicates no clipping

*SETMOUSE is intended for graphics modes initiated using the SCREEN statement only.*

--------------------------------------------------------------------------------

THE CODING BEGINS

Here are a set of constants that I declare at the beginning of the module. This is simply to gain a bit of clarity of code in the rest of the programming example.
    Const LEFTBUTTON = 1
    Const MIDDLEBUTTON = 4
    Const RIGHTBUTTON = 2
    Const SHOWMOUSE = 1
    Const HIDEMOUSE = 0

As a first step in this example, we will be declaring variables that we will be using throughout the example program. Of course you don't have to declare your variables, but me I like to do so because when you do so you know exactly why you're declaring your variables. To me that's good practice. Editors Note: In #lang FB you must declare variables before using them.
    Dim CurrentX As Integer
    Dim CurrentY As Integer
    Dim MouseButtons As Integer
    Dim CanExit As Integer
    Dim As String A,B,C

The idea here is to do everything within a loop so that we can also control how the program exits. So we'll create a loop that will exit when the "CanExit" variable is equal to 0. In the loop we'll interrogate the mouse and print some basic values. (This part is extracted from the example provided in the GETMOUSE syntax explanation in the gfxlib.txt file). Don't forget to set your graphics mode as it is a must to get valid return values from the mouse commands. We'll use Screen 12 for our example.
Screen 12
CanExit = 1

Do While CanExit <> 0
    GetMouse CurrentX, CurrentY, , MouseButtons
    If CurrentX < 0 Then
     Print "Mouse is out of context."
    Else
    If MouseButtons And LEFTBUTTON Then A="L"
    If MouseButtons And MIDDLEBUTTON Then B="M"
    If MouseButtons And RIGHTBUTTON Then C="R"
     Print Using "Mouse position: ###:### Buttons: &&&"; CurrentX; CurrentY;A;B;C
     A="":B="":C=""
    End If
Loop

This sample will basically continuously display information about Where the mouse is, if it's on the program window and which mouse button is pressed if any. The GETMOUSE statement basically puts the current X and Y coordinates in our CurrentX and CurrentY variables and the status of the mouse buttons in our MouseButtons variable. The Three If Statements will print L if the left button was pressed, M if the middle button (or the wheel) was pressed and R if the Right button was pressed.

For the next step, since we want to control a bit what's happening with the mouse, will display a few extra things at the beginning of the program and control what happens with them afterwards, in the loop. This is regular text being displayed, this could be replaced by a series of line commands or something to draw a button for the different options. But that is outside the scope of this tutorial. So far, by getting rid of the unwanted print statements from the code above, the loop should now look like this:
    Screen 12
    SetMouse 1, 1, 1
    CanExit = 1
    Locate 1,1
    Print " | FIRST | SECOND | THIRD | EXIT | "
    Do While CanExit <> 0
     Locate 1,1
     GetMouse CurrentX, CurrentY, , MouseButtons
    Loop

Basically we print the line that has " | FIRST | SECOND | THIRD | EXIT | " at the top of the screen. And we go into the loop that interrogates the mouse. Of course, right now nothing will happen if you press a button because there is no code for it. In our example, we'll add code that simple prints which option was selected. If the user selects the EXIT option, we'll print the Option and we'll exit the loop. We'll also add a print statement outside the loop with a sleep to tell the use that we are truly outside the loop and therefore the program is ended. With all this, the code should now look like this. I am putting the whole source file here so you can cut and paste it easily.
Const LEFTBUTTON   = 1
Const MIDDLEBUTTON = 4   ' UNUSED IN THIS DEMO
Const RIGHTBUTTON  = 2   ' UNUSED IN THIS DEMO
Const SHOWMOUSE    = 1
Const HIDEMOUSE    = 0

Dim CurrentX     As Integer
Dim CurrentY     As Integer
Dim MouseButtons As Integer
Dim CanExit      As Integer

Screen 12
SetMouse 1, 1, SHOWMOUSE
CanExit = 1
Locate 1,1
Print " | FIRST | SECOND | THIRD | EXIT | "

Do
   GetMouse CurrentX, CurrentY, , MouseButtons
   If MouseButtons And LEFTBUTTON Then
      If CurrentY <= 12 Then
         If CurrentX >= 0 And CurrentX <=75 Then
            Locate 12, 1
            Print "First Option Selected ";
         ElseIf CurrentX >= 76 And CurrentX <= 147 Then
            Locate 12, 1
            Print "Second Option Selected";
         ElseIf CurrentX >= 148 And CurrentX <=212 Then
            Locate 12, 1
            Print "Third Option Selected ";
         ElseIf CurrentX >= 213 And CurrentX <=268 Then
            Locate 12, 1
            Print "Last Option Selected  ";
            Exit Do
         End If
      End If
   End If
Loop While Inkey$ = ""

SetMouse 1, 1, HIDEMOUSE
Print
Print "AND NOW WE'RE OUT OF THE LOOP"
Sleep

You can see the many IF statements in this last piece of code. The numbers that are there have been measured as per SCREEN 12 returned coordinates. They should work in all graphics mode however because a pixel is a pixel in a Console Graphics Window. Each if represents where the different options are written on the screen. If you would have used a graphics button routine you could simply use the same width and height as you did to draw the button in these if statements to know which button was clicked.

IN CONCLUSION

As you can see, using the mouse has been made very simple in FreeBASIC. You can use simple statement like the print command to draw your screens or you can use graphics command like LINE to draw your screens graphically. No matter which way you choose to draw your screens with, the SETMOUSE and GETMOUSE statement will work the same way and return the very same values. All you have to do is get that information and make your programs do what you want them to do if they press a button, select an option, or even in the case of a game, you could easily make the main character move towards the location where you clicked on the screen as well. The choice is up to you.

As always, if you have any questions regarding this tutorial or any other I've written, feel free to email me and we'll see what we can do about solving your particular problem.

MystikShadows
Stéphane Richard
srichard@adaworld.com

Last Reviewed by Sancho3 on February 06, 2018


Unicode
A worldwide standard for storing, categorizing and interpreting characters

Unicode is an industry standard designed to allow text and symbols from all of the writing systems of the world to be consistently represented and manipulated by computers. Developed in tandem with the Universal Character Set standard and published in book form as The Unicode Standard, Unicode consists of a character repertoire, an encoding methodology and set of standard character encodings, a set of code charts for visual reference, an enumeration of character properties such as upper and lower case, a set of reference data computer files, and rules for normalization, decomposition, collation and rendering.

The Unicode Consortium, the non-profit organization that coordinates Unicode's development, has the ambitious goal of eventually replacing existing character encoding schemes with Unicode and its standard Unicode Transformation Format (UTF) schemes, as many of the existing schemes are limited in size and scope, and are incompatible with multilingual environments. Unicode's success at unifying character sets has led to its widespread and predominant use in the internationalization and localization of computer software. The standard has been implemented in many recent technologies, including XML, the Java programming language, and modern operating systems.

Common Unicode formats include:
- UTF-8
- UTF-16
- UTF-32


rdc
Rick Clark aka rdc
rickclark58@yahoo.com
http://rickclark58.bravehost.com/


x86 Microprocessor Architecture
x86 or 80x86 is the generic name of a microprocessor architecture first developed and manufactured by Intel.
More information can be obtained by reading this Wikipedia article.