FreeBASIC Manual ------------------------------------------------------------ This document last compiled : 2026/05/14 16:04:19 from https://www.freebasic.net/wiki/ -------------------------------------------------------------- PrintToc ---- Table of Contents Welcome to FreeBASIC Getting Help with FreeBASIC Language Documentation . ... _ __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__ __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 AndAlso And (Graphics Put) Any Append Arraylen Arraysize As Asc Asin Asm Assert AssertWarn Atan2 Atn Audio B Balance Base (Initialization) 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 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 Getxpad Generated Sound Commands GoSub Goto H Hex HiByte HiWord Hour I If...Then IIf ImageConvertRow ImageCreate ImageDestroy ImageInfo Imp Implements Import Inkey Inp 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 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 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 (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 Rest 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 Short Shr Sin Single SizeOf Sleep Sound Space Spc Sqr Static Static (Member) stdcall Step Stick Stop Str Strig Stream 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 (Graphics Put) Y Year Z ZString Operator List Variables and Data Types Variable Declarations User Defined Types Standard Data Types Standard Data Type Limits Converting Between Data Types Assignment operators Operator =[>] (Assignment) Operator &= (Concatenate And Assign) Operator += (Add And Assign) Operator -= (Subtract And Assign) Operator *= (Multiply And Assign) Operator /= (Divide And Assign) Operator \= (Integer Divide And Assign) Operator ^= (Exponentiate And Assign) Operator Let (Assignment) Operator Let() (Assignment) Operator Mod= (Modulus And Assign) Operator And= (Conjunction And Assign) Operator Eqv= (Equivalence And Assign) Operator Imp= (Implication And Assign) Operator Or= (Inclusive Disjunction And Assign) Operator Xor= (Exclusive Disjunction And Assign) Operator Shl= (Shift Left And Assign) Operator Shr= (Shift Right And Assign) Arithmetic operators Operator + (Add) Operator - (Subtract) Operator * (Multiply) Operator / (Divide) Operator \ (Integer Divide) Operator ^ (Exponentiate) Operator Mod (Modulus) Operator - (Negate) Operator Shl (Shift Left) Operator Shr (Shift Right) Conditional operators Operator = (Equal) Operator <> (Not Equal) Operator < (Less Than) Operator <= (Less Than Or Equal) Operator >= (Greater Than Or Equal) Operator > (Greater Than) Operator Is (Run-Time Type Information) Logical operators Operator And (Conjunction) Operator Eqv (Equivalence) Operator Imp (Implication) Operator Not (Complement) Operator Or (Inclusive Disjunction) Operator Xor (Exclusive Disjunction) Short circuit operators Operator Andalso (Short Circuit Conjunction) Operator Orelse (Short Circuit Inclusive Disjunction) Indexing operators Operator () (Array Index) Operator [] (String Index) Operator [] (Pointer Index) String operators Operator + (String Concatenation) Operator & (String Concatenation With Conversion) Operator Strptr (String Pointer) Preprocessor operators Operator # (Stringize) Operator ## (Concatenation) Operator ! (Escaped String Literal) Operator $ (Non-Escaped String Literal) Pointer operators Operator Varptr (Variable Pointer) Operator Strptr (String Pointer) Operator Procptr (Procedure Pointer And Vtable Index) Operator @ (Address Of) Operator * (Value Of) Type or Class operators Operator . (Member Access) Operator -> (Pointer To Member Access) Operator Is (Run-Time Type Information) Memory operators Operator New Expression Operator New Overload Operator Placement New Operator Delete Statement Operator Delete Overload Iterating operators Operator For Operator Step Operator Next 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 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 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 Precedence Control Flow Statements Procedures Procedures Overview Passing Arguments to Procedures Returning a Value 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 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 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 Community Tutorials Community Code Library External Libraries Index Graphical/test-based user interfaces CGUI Curses GTK+ IUP wxC Windows API X11 Graphics Allegro DUGL caca Cairo DISLIN freeglut FreeImage Freetype2 GD GIFLIB GLUT GLFW GRX IL (DevIL) japi jpeglib JPGalleg libpng OpenGL PDFlib SDL TinyPTC Music/Sound, Audio/Video BASS BASSMOD Flite FMOD MediaInfo mpg123 Ogg OpenAL PortAudio sfx sndfile VLC Vorbis Database GDBM MySQL PostgreSQL SQLite Development Helpers CUnit GDSL gettext (includes libintl) GNU ASpell libbfd Embeddable Languages JNI json-c libffi libjit Lua SpiderMonkey Cryptography cryptlib UUID Mathematics big_int Chipmunk GMP GSL Newton ODE Networking cgi-util curl FastCGI ZeroMQ eXtensible Markup Language (XML) Expat libxml libxslt Mini-XML Regular Expressions PCRE TRE Compression bzip2 libzip liblzma LZO QuickLZ zlib System APIs C Runtime Library DOS API disphelper GLib Windows API X11 Using the FreeBASIC compiler Installing FreeBASIC Requirements Running FreeBASIC Using the Command Line Command Line Options @< file > -a < name > -arch < type > -asm < format > -b < name > -buildprefix -c -C -d < name=val > -dll -dylib -e -earray -earraydims -eassert -edebug -edebuginfo -elocation -entry < name > -enullptr -eunwind -ex -exx -export -forcelang -fpmode < type > -fpu < type > -g -gen < backend > -i < name > -include < name > -l < name > -lang < name > -lib -m < name > -map < name > -maxerr < val > -mt -nodeflibs -nolib < name(s) > -noerrline -noobjinfo -nostrip -o < name > -O < level > -p < name > -pic -pp -prefix < path > -print < option > -profgen -profile -r -R -rr -RR -s < name > -showincludes -static -strip -sysroot < path > -t < value > -target < platform > -v -vec < level > -version -w < value > -Wa < opt > -Wc < opt > -Wl < opt > -x < name > -z < value > 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 ------------------------------------------------------- CompilerWelcome ---- 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! ---------------------------------------------------------- CompilerHelp ---- 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. ============================================================================ LANGUAGE DOCUMENTATION ---------------------- ============================================================================ . ------------------------------------------------------------- KeyPgDots ---- ... (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 ============================================================================ _ ----------------------------------------------------------- KeyPgDddate ---- __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 -------------------------------------------------------- KeyPgDddateiso ---- __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 -------------------------------------------------------- KeyPgDdfb64bit ---- __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 ------------------------------------------------------ KeyPgDdfbandroid ---- __FB_ANDROID__ Intrinsic define set by the compiler Syntax __FB_ANDROID__ Description Define without a value created at compile time when targeting android platforms, when the -target android command line option is used. It can be used to compile parts of the program only for a such target. Example #ifdef __FB_ANDROID__ '...instructions only for android target... #else '...instructions not for android target... #endif Version * Since fbc 1.20.0 Differences from QB * New to FreeBASIC See also * Compiling FB for Android ----------------------------------------------------- KeyPgDdfbargcount ---- __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__ --------------------------------------------------- KeyPgDdfbargextract ---- __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__ ---------------------------------------------------- KeyPgDdfbargleftof ---- __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__ ------------------------------------------------ KeyPgDdfbarglistexpand ---- __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__ --------------------------------------------------- KeyPgDdfbargrightof ---- __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__ --------------------------------------------------------- KeyPgDdfbargc ---- __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 --------------------------------------------------------- KeyPgDdfbargv ---- __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 ---------------------------------------------------------- KeyPgDdfbarm ---- __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 ---------------------------------------------------------- KeyPgDdfbasm ---- __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 ------------------------------------------------------ KeyPgDdFBBackend ---- __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 ---------------------------------------------------- KeyPgDdFBBigEndian ---- __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 ---------------------------------------------------- KeyPgDdFBBuildDate ---- __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__ ------------------------------------------------- KeyPgDdfbbuilddateiso ---- __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__ -------------------------------------------------- KeyPgDdfbbuildforkid ---- __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 ---------------------------------------------------- KeyPgDdfbbuildsha1 ---- __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 ------------------------------------------------------- KeyPgDdfbcygwin ---- __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 ------------------------------------------------------- KeyPgDdfbdarwin ---- __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 -------------------------------------------------------- KeyPgDdfbdebug ---- __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 ---------------------------------------------------------- KeyPgDdfbdos ---- __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 ---------------------------------------------------------- KeyPgDdfberr ---- __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 --------------------------------------------------------- KeyPgDdfbeval ---- __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__ ------------------------------------------------------- KeyPgDdfbfpmode ---- __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 ---------------------------------------------------------- KeyPgDdfbfpu ---- __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 ------------------------------------------------------ KeyPgDdfbfreebsd ---- __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 ---------------------------------------------------------- KeyPgDdfbgcc ---- __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 ---------------------------------------------------------- KeyPgDdfbgui ---- __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 ---------------------------------------------------------- KeyPgDdfbiif ---- __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 --------------------------------------------------------- KeyPgDdfbjoin ---- __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) ----------------------------------------------------------- KeyPgDdfbjs ---- __FB_JS__ Intrinsic define set by the compiler Syntax __FB_JS__ Description Define without a value created at compile time in the emscripten version of the compiler, when the -target js-asmjs command line option is used. It can be used to compile parts of the program only for a such target. Example #ifdef __FB_JS__ '...instructions only for emscripten target... #else '...instructions not for emscripten target... #endif Differences from QB * New to FreeBASIC See also * Compiling FB with Emscripten --------------------------------------------------------- KeyPgDdfblang ---- __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 -------------------------------------------------------- KeyPgDdfblinux ---- __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 -------------------------------------------------- KeyPgDdfbloongarch64 ---- __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 --------------------------------------------------------- KeyPgDdFBMain ---- __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 --------------------------------------------------- KeyPgDdFBMinVersion ---- __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 ----------------------------------------------------------- KeyPgDdfbmt ---- __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 ------------------------------------------------------- KeyPgDdfbnetbsd ---- __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 ------------------------------------------------------ KeyPgDdfbopenbsd ---- __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 ----------------------------------------------------- KeyPgDdfboptimize ---- __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 -------------------------------------------------- KeyPgDdfboptionbyval ---- __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 ------------------------------------------------ KeyPgDdfboptiondynamic ---- __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 ------------------------------------------------- KeyPgDdfboptionescape ---- __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 ----------------------------------------------- KeyPgDdfboptionexplicit ---- __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 -------------------------------------------------- KeyPgDdfboptiongosub ---- __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 ------------------------------------------------ KeyPgDdfboptionprivate ---- __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 ------------------------------------------------ KeyPgDdfboptionprofile ---- __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 ------------------------------------------------------- KeyPgDdfboutdll ---- __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 ------------------------------------------------------- KeyPgDdfboutexe ---- __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__ ------------------------------------------------------- KeyPgDdfboutlib ---- __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 ------------------------------------------------------- KeyPgDdfboutobj ---- __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__ --------------------------------------------------------- KeyPgDdfbpcos ---- __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 ---------------------------------------------------------- KeyPgDdfbppc ---- __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 ------------------------------------------------------ KeyPgDdfbprofile ---- __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 -------------------------------------------------- KeyPgDdfbquerysymbol ---- __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 -------------------------------------------------------- KeyPgDdfbquote ---- __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__ ------------------------------------------------------ KeyPgDdfbriscv64 ---- __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 -------------------------------------------------------- KeyPgDdfbs390x ---- __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 ---------------------------------------------------- KeyPgDdfbsignature ---- __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__ ---------------------------------------------------------- KeyPgDdfbsse ---- __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 ----------------------------------------------------- KeyPgDdfbuniqueid ---- __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__ -------------------------------------------------- KeyPgDdfbuniqueidpop ---- __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__ ------------------------------------------------- KeyPgDdfbuniqueidpush ---- __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__ --------------------------------------------------------- KeyPgDdfbunix ---- __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 ------------------------------------------------------ KeyPgDdfbunquote ---- __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__ ---------------------------------------------------- KeyPgDdfbvectorize ---- __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 ----------------------------------------------------- KeyPgDdFBVerMajor ---- __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__ ----------------------------------------------------- KeyPgDdFBVerMinor ---- __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__ ----------------------------------------------------- KeyPgDdFBVerPatch ---- __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__ ------------------------------------------------------ KeyPgDdfbversion ---- __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__ -------------------------------------------------------- KeyPgDdfbwin32 ---- __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 ---------------------------------------------------------- KeyPgDdfbx86 ---- __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 --------------------------------------------------------- KeyPgDdfbxbox ---- __FB_XBOX__ Intrinsic define set by the compiler Syntax __FB_XBOX__ Description Define without a value created at compile time when the -target xbox command line option is used. It can be used to compile parts of the program only if the target is Xbox. Example #ifdef __FB_XBOX__ '...instructions only for Xbox... #else '...instructions not for Xbox... #endif Differences from QB * New to FreeBASIC See also * __FB_LINUX__ * __FB_WIN32__ * Compiler Option: -target ----------------------------------------------------------- KeyPgDdfile ---- __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__ --------------------------------------------------------- KeyPgDdfilenq ---- __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__ ------------------------------------------------------- KeyPgDdfunction ---- __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__ ----------------------------------------------------- KeyPgDdfunctionnq ---- __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__ ----------------------------------------------------------- KeyPgDdline ---- __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__ ----------------------------------------------------------- KeyPgDdpath ---- __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__ ----------------------------------------------------------- KeyPgDdtime ---- __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 ============================================================================ # --------------------------------------------------------- KeyPgPpassert ---- #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 -------------------------------------------------------- KeyPgPpcmdline ---- #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 --------------------------------------------------------- KeyPgPpdefine ---- #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 ----------------------------------------------------------- KeyPgPpelse ---- #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 --------------------------------------------------------- KeyPgPpelseif ---- #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 ------------------------------------------------------ KeyPgPpelseifdef ---- #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 ----------------------------------------------------- KeyPgPpelseifndef ---- #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 ---------------------------------------------------------- KeyPgPpendif ---- #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 ---------------------------------------------------------- KeyPgPpmacro ---- #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 ---------------------------------------------------------- KeyPgPperror ---- #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 ...#endif, so the compiler can reach #error only if 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 ------------------------------------------------------------- KeyPgPpif ---- #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 ---------------------------------------------------------- KeyPgPpifdef ---- #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 --------------------------------------------------------- KeyPgPpifndef ---- #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 ----------------------------------------------------------- KeyPgInclib ---- #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 ---------------------------------------------------------- KeyPgInclude ---- #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 ----------------------------------------------------------- KeyPgPplang ---- #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 -------------------------------------------------------- KeyPgPplibpath ---- #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 ----------------------------------------------------------- KeyPgPpline ---- #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__ ---------------------------------------------------------- KeyPgPpmacro ---- #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 --------------------------------------------------------- KeyPgPpPragma ---- #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 -------------------------------------------------- KeyPgPpPragmaReserve ---- #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 ---------------------------------------------------------- KeyPgPpprint ---- #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 ---------------------------------------------------------- KeyPgPpundef ---- #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 ============================================================================ $ ------------------------------------------------------ KeyPgMetaDynamic ---- $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 ------------------------------------------------------ KeyPgMetaInclude ---- $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 --------------------------------------------------------- KeyPgMetaLang ---- $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 ------------------------------------------------------- KeyPgMetaStatic ---- $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) '< 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 ----------------------------------------------------------- KeyPgAccess ---- 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 ------------------------------------------------------------- KeyPgAcos ---- 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 ----------------------------------------------------------- KeyPgAddGfx ---- 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) ------------------------------------------------------------ KeyPgAlias ---- 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) ---------------------------------------------------- KeyPgAliasModifier ---- 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) --------------------------------------------------------- KeyPgAllocate ---- 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 --------------------------------------------------------- KeyPgAlphaGfx ---- 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 ------------------------------------------------------------ KeyPgOpAnd ---- 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 -------------------------------------------------------- KeyPgOpAndAlso ---- 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 ----------------------------------------------------------- KeyPgAndGfx ---- 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) -------------------------------------------------------------- KeyPgAny ---- 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 ----------------------------------------------------------- KeyPgAppend ---- 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 # --------------------------------------------------------- KeyPgArrayLen ---- 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) -------------------------------------------------------- KeyPgArraySize ---- 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) --------------------------------------------------------------- KeyPgAs ---- 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 -------------------------------------------------------------- KeyPgAsc ---- 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!) dim a as wstring * 12 a = "Привет, мир" print "the Unicode of the second char of " & a & " is: " & asc(a, 2) will produce the output: the Unicode of the second char of Привет, мир is: 1088 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 ------------------------------------------------------------- KeyPgAsin ---- 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 -------------------------------------------------------------- KeyPgAsm ---- 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 ----------------------------------------------------------- KeyPgAssert ---- 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 ------------------------------------------------------- KeyPgAssertwarn ---- 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 ------------------------------------------------------------ KeyPgAtan2 ---- 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 -------------------------------------------------------------- KeyPgAtn ---- 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 ============================================================================ B ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference --------------------------------------------------------- KeyPgBaseInit ---- 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 ------------------------------------------------------------- KeyPgBase ---- 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 ------------------------------------------------------------- KeyPgBeep ---- 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 -------------------------------------------------------------- KeyPgBin ---- 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 ----------------------------------------------------------- KeyPgBinary ---- 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 -------------------------------------------------------------- KeyPgBit ---- 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 --------------------------------------------------------- KeyPgBitreset ---- 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 ----------------------------------------------------------- KeyPgBitset ---- 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 ------------------------------------------------------------ KeyPgBload ---- 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 ---------------------------------------------------------- KeyPgBoolean ---- 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 ------------------------------------------------------------ KeyPgBsave ---- 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 ------------------------------------------------------------ KeyPgByref ---- 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) ---------------------------------------------------- KeyPgByrefFunction ---- 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) --------------------------------------------------- KeyPgByrefVariables ---- 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) ------------------------------------------------------------- KeyPgByte ---- 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 ------------------------------------------------------------ KeyPgByval ---- 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 ============================================================================ C ------------------------------------------------------------- KeyPgCall ---- 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 -------------------------------------------------------- KeyPgCallocate ---- 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 ------------------------------------------------------------- KeyPgCase ---- Case Control flow statement Syntax Case expression Example See example at Select Case. Differences from QB * None See also * Select Case ------------------------------------------------------------- KeyPgCast ---- 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 ---------------------------------------------------------- KeyPgCapture ---- Capture Controls sfxlib audio capture. 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 when the active backend supports capture. Capture Start begins recording, pause/resume/stop control the capture state, Capture Available reports how many frames can be read, Capture Read copies captured samples into a Single buffer, and Capture Save writes the buffered capture data to a file. Capture Status returns 0 for stopped, 1 for running, and 2 for paused. Example See examples/sfxlib/capture-start.bas, examples/sfxlib/capture-read.bas, examples/sfxlib/capture-save.bas, and examples/sfxlib/showcase.bas. Differences from QB * New to FreeBASIC. See also * Device * Audio ------------------------------------------------------------ KeyPgCbool ---- 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 ------------------------------------------------------------ KeyPgCbyte ---- 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 ------------------------------------------------------------- KeyPgCdbl ---- 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 ------------------------------------------------------------ KeyPgCdecl ---- 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 ------------------------------------------------------------ KeyPgChain ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ------------------------------------------------------------ KeyPgChdir ---- 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 -------------------------------------------------------------- KeyPgChr ---- 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 ------------------------------------------------------------- KeyPgCint ---- CInt Converts a numeric or string expression to an Integer or an Integer Syntax Declare Function CInt ( ByVal expression As datatype ) As Integer Declare Function CInt ( ByVal expression As datatype ) As Integer< bits> Type typename Declare Operator Cast ( ) As Integer Declare Operator Cast ( ) As Integer 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 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 parameter was not allowed in QB See also * Cast * CByte * CUByte * CShort * CUShort * CUInt * CLng * CULng * CLngInt * CULngInt * CSng * CDbl * Integer ----------------------------------------------------------- KeyPgCircle ---- 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 ------------------------------------------------------------ KeyPgClass ---- 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 ------------------------------------------------------------ KeyPgClear ---- 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 ------------------------------------------------------------- KeyPgClng ---- 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 ---------------------------------------------------------- KeyPgClngint ---- 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 ------------------------------------------------------------ KeyPgClose ---- 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 -------------------------------------------------------------- KeyPgCls ---- 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) ------------------------------------------------------------ KeyPgColor ---- 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) ---------------------------------------------------------- KeyPgCommand ---- 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 ----------------------------------------------------------- KeyPgCommon ---- 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 ---------------------------------------------------- KeyPgCondBroadcast ---- 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 ------------------------------------------------------- KeyPgCondCreate ---- 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 " : 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 ------------------------------------------------------ KeyPgCondDestroy ---- 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 ------------------------------------------------------- KeyPgCondSignal ---- 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 --------------------------------------------------------- KeyPgCondWait ---- 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 ------------------------------------------------------------ KeyPgConst ---- 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 ------------------------------------------------------ KeyPgConstMember ---- 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 --------------------------------------------------- KeyPgConstQualifier ---- 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 ------------------------------------------------------ KeyPgConstructor ---- 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 ------------------------------------------------ KeyPgModuleConstructor ---- 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 --------------------------------------------------------- KeyPgContinue ---- 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 -------------------------------------------------------------- KeyPgCos ---- 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 ------------------------------------------------------------- KeyPgCptr ---- 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 ----------------------------------------------------------- KeyPgCshort ---- 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 ------------------------------------------------------------ KeyPgCsign ---- 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 ------------------------------------------------------------- KeyPgCsng ---- 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 ----------------------------------------------------------- KeyPgCsrlin ---- 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 ----------------------------------------------------------- KeyPgCubyte ---- 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 ------------------------------------------------------------ KeyPgCuint ---- CUInt Converts numeric or string expression to a UInteger or UInteger Syntax Declare Function CUInt ( ByVal expression As datatype ) As UInteger Declare Function CUInt ( ByVal expression As datatype ) As UInteger Type typename Declare Operator Cast ( ) As UInteger Declare Operator Cast ( ) As UInteger 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 value datatype any numeric, string, or pointer data type typename a user defined type Return Value A UInteger or UInteger 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 ------------------------------------------------------------ KeyPgCulng ---- 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 --------------------------------------------------------- KeyPgCulngint ---- 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 ------------------------------------------------------------ KeyPgCunsg ---- 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 ----------------------------------------------------------- KeyPgCurdir ---- 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 ---------------------------------------------------------- KeyPgCushort ---- 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 -------------------------------------------------------- KeyPgCustomgfx ---- 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) ----------------------------------------------------------- KeyPgCvaArg ---- 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 ---------------------------------------------------------- KeyPgCvaCopy ---- 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 ----------------------------------------------------------- KeyPgCvaEnd ---- 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 ---------------------------------------------------------- KeyPgCvaList ---- 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 --------------------------------------------------------- KeyPgCvaStart ---- 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 -------------------------------------------------------------- KeyPgCvd ---- 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 -------------------------------------------------------------- KeyPgCvi ---- 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 ( expr As DataType ) As Integer Usage result = CVI( sng ) result = CVI( str ) result = CVI( 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. Return Value An Integer or Integer 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 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 parameter. See also * MKI * CVShort * CVL * CVLongInt * Integer -------------------------------------------------------------- KeyPgCvl ---- 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 -------------------------------------------------------- KeyPgCvlongint ---- 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 -------------------------------------------------------------- KeyPgCvs ---- 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 ---------------------------------------------------------- KeyPgCvshort ---- 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 ============================================================================ D ------------------------------------------------------------- KeyPgData ---- 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 ------------------------------------------------------------- KeyPgDate ---- 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 ---------------------------------------------------------- KeyPgDateAdd ---- 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 --------------------------------------------------------- KeyPgDateDiff ---- 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 --------------------------------------------------------- KeyPgDatePart ---- 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 ------------------------------------------------------- KeyPgDateSerial ---- 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 -------------------------------------------------------- KeyPgDateValue ---- 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 -------------------------------------------------------------- KeyPgDay ---- 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 ------------------------------------------------------- KeyPgDeallocate ---- 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 ---------------------------------------------------------- KeyPgDeclare ---- 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) ---------------------------------------------------------- KeyPgDefbyte ---- 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 ----------------------------------------------------------- KeyPgDefdbl ---- 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 ---------------------------------------------------------- KeyPgDefined ---- 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 ----------------------------------------------------------- KeyPgDefint ---- 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 ----------------------------------------------------------- KeyPgDeflng ---- 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 ------------------------------------------------------- KeyPgDeflongint ---- 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 --------------------------------------------------------- KeyPgDefshort ---- 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 ----------------------------------------------------------- KeyPgDefsng ---- 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 ----------------------------------------------------------- KeyPgDefstr ---- 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 --------------------------------------------------------- KeyPgDefubyte ---- 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 ---------------------------------------------------------- KeyPgDefuint ---- 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 ------------------------------------------------------ KeyPgDefulongint ---- 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 -------------------------------------------------------- KeyPgDefushort ---- 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 --------------------------------------------------------- KeyPgOpDelete ---- 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 ------------------------------------------------------- KeyPgDestructor ---- 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 ------------------------------------------------- KeyPgModuleDestructor ---- 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 ----------------------------------------------------------- KeyPgDevice ---- Device Lists, selects, and queries sfxlib output devices. Syntax Device List result = Device Select( device ) Device Info [ device ] Description Device List asks sfxlib to enumerate the output drivers compiled into the runtime. Device Select selects a device by index. Device Info prints diagnostic information for the current device or for the supplied device index. Device availability is platform dependent. The null output driver may be selected for headless tests and diagnostics. Example See examples/sfxlib/device-list.bas, examples/sfxlib/device-select.bas, and examples/sfxlib/device-info.bas. Differences from QB * New to FreeBASIC. See also * Audio * Capture -------------------------------------------------------------- KeyPgDim ---- 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 -------------------------------------------------------------- KeyPgDir ---- 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 --------------------------------------------------------------- KeyPgDo ---- 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 ----------------------------------------------------------- KeyPgDoloop ---- 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 ----------------------------------------------------------- KeyPgDouble ---- 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 ------------------------------------------------------------- KeyPgDraw ---- 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 ------------------------------------------------------- KeyPgDrawString ---- 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 -------------------------------------------------------- KeyPgDylibfree ---- 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 -------------------------------------------------------- KeyPgDylibload ---- 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: ".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 ------------------------------------------------------ KeyPgDylibsymbol ---- 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 ============================================================================ E ------------------------------------------------------------- KeyPgElse ---- 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 ----------------------------------------------------------- KeyPgElseif ---- 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 --------------------------------------------------------- KeyPgEncoding ---- 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 --------------------------------------------------------- KeyPgEndblock ---- 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 -------------------------------------------------------------- KeyPgEnd ---- 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 ------------------------------------------------------------ KeyPgEndif ---- 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 ------------------------------------------------------------- KeyPgEnum ---- 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 ------------------------------------------------------- KeyPgSetenviron ---- 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 ---------------------------------------------------------- KeyPgEnviron ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference -------------------------------------------------------------- KeyPgEoc ---- 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 -------------------------------------------------------------- KeyPgEof ---- 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 ------------------------------------------------------------ KeyPgOpEqv ---- 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 ------------------------------------------------------------ KeyPgErase ---- 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 ------------------------------------------------------------- KeyPgErfn ---- 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 -------------------------------------------------------------- KeyPgErl ---- 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 ------------------------------------------------------------- KeyPgErmn ---- 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 -------------------------------------------------------------- KeyPgErr ---- 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 ------------------------------------------------------------ KeyPgError ---- 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 ------------------------------------------------------------ KeyPgEvent ---- 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 ------------------------------------------------------------- KeyPgExec ---- 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 ---------------------------------------------------------- KeyPgExepath ---- 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 ------------------------------------------------------------- KeyPgExit ---- 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 -------------------------------------------------------------- KeyPgExp ---- 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) ----------------------------------------------------------- KeyPgExport ---- 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 ---------------------------------------------------------- KeyPgExtends ---- 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 --------------------------------------------------- KeyPgExtendsWstring ---- 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 --------------------------------------------------- KeyPgExtendsZstring ---- 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(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(Trim(v) & " ") Print "Left justified" Case Type(" " & 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(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 ----------------------------------------------------------- KeyPgExtern ---- 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) ------------------------------------------------------ KeyPgExternBlock ---- 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 ============================================================================ F ------------------------------------------------------------ KeyPgFalse ---- 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 --------------------------------------------------------- KeyPgFastcall ---- __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 -------------------------------------------------------- KeyPgFBMemcopy ---- 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 --------------------------------------------------- KeyPgFBMemcopyclear ---- 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 -------------------------------------------------------- KeyPgFBMemmove ---- 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 ---------------------------------------------------------- KeyPgFBArray ---- Fbarray (Array Descriptor Structure And Access) Pre-defined structure (UDT) and procedure declarations from the fbc-int/arra y.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 ------------------------------------------------------- KeyPgFbNoGfxlib ---- 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 ------------------------------------------------------- KeyPgFbNoSfxlib ---- 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 ------------------------------------------------------------ KeyPgField ---- 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 --------------------------------------------------------- KeyPgFileattr ---- 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 --------------------------------------------------------- KeyPgFilecopy ---- 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 ----------------------------------------------------- KeyPgFiledatetime ---- 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 ------------------------------------------------------- KeyPgFileexists ---- 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 -------------------------------------------------------- KeyPgFileflush ---- 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 ---------------------------------------------------------- KeyPgFilelen ---- 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 ------------------------------------------------------- KeyPgFileseteof ---- 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 -------------------------------------------------------------- KeyPgFix ---- 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 ------------------------------------------------------------- KeyPgFlip ---- 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 -------------------------------------------------------------- KeyPgFor ---- 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 ---------------------------------------------------------- KeyPgFornext ---- 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 ----------------------------------------------------------- KeyPgFormat ---- 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 ------------------------------------------------------------- KeyPgFrac ---- 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 -------------------------------------------------------------- KeyPgFre ---- 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 --------------------------------------------------------- KeyPgFreefile ---- 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 --------------------------------------------------------- KeyPgFunction ---- 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 --------------------------------------------------- KeyPgMemberFunction ---- 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 ------------------------------------------------------ KeyPgFunctionPtr ---- 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 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 = 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) ============================================================================ G ------------------------------------------------------ KeyPgGetgraphics ---- 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 -------------------------------------------------------- KeyPgGetfileio ---- 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 ------------------------------------------------------ KeyPgGetjoystick ---- 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 ----------------------------------------------------------- KeyPgGetkey ---- 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 --------------------------------------------------------- KeyPgGetmouse ---- 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 ---------------------------------------------------------- KeyPgGetxpad ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ------------------------------------------------------------ KeyPgGosub ---- 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 ------------------------------------------------------------- KeyPgGoto ---- 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 ============================================================================ H -------------------------------------------------------------- KeyPgHex ---- 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 ----------------------------------------------------------- KeyPgHibyte ---- 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 ----------------------------------------------------------- KeyPgHiword ---- 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 ------------------------------------------------------------- KeyPgHour ---- 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 ============================================================================ I ----------------------------------------------------------- KeyPgIfthen ---- 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 -------------------------------------------------------------- KeyPgIif ---- 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 -------------------------------------------------- KeyPgImageConvertRow ---- 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 ------------------------------------------------------ KeyPgImagecreate ---- 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 ----------------------------------------------------- KeyPgImageDestroy ---- 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 -------------------------------------------------------- KeyPgImageInfo ---- 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 ------------------------------------------------------------ KeyPgOpImp ---- 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 ------------------------------------------------------- KeyPgImplements ---- 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 ----------------------------------------------------------- KeyPgImport ---- 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 ------------------------------------------------------------ KeyPgInkey ---- 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 -------------------------------------------------------------- KeyPgInp ---- 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 ------------------------------------------------------------ KeyPgInput ---- 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 ---------------------------------------------------- KeyPgInputfilemode ---- 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 ---------------------------------------------------------- KeyPgInputPp ---- 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) --------------------------------------------------------- KeyPgInputnum ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ------------------------------------------------------------ KeyPgInstr ---- 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 'A Unicode example: dim text as wstring*20 text = "Привет, мир!" print instr(text,"ет") ' displays 5 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) --------------------------------------------------------- KeyPgInstrrev ---- 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 'A Unicode example: dim text as wstring*20 text = "Привет, мир!" print instrrev(text,"ет") ' displays 5 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) -------------------------------------------------------------- KeyPgInt ---- 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 ---------------------------------------------------------- KeyPgInteger ---- 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 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 --------------------------------------------------------------- KeyPgIs ---- 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 ------------------------------------------------------------- KeyPgOpIs ---- 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 ----------------------------------------------------------- KeyPgIsDate ---- 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 ----------------------------------------------------- KeyPgIsredirected ---- 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) ============================================================================ K ------------------------------------------------------------- KeyPgKill ---- 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 ============================================================================ L ----------------------------------------------------------- KeyPgLbound ---- 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 ------------------------------------------------------------ KeyPgLcase ---- 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 ------------------------------------------------------------- KeyPgLeft ---- 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: dim text as wstring*20 text = "Привет, мир!" print left(text, 6) 'displays "Привет" 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) -------------------------------------------------------------- KeyPgLen ---- 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 -------------------------------------------------------------- KeyPgLet ---- 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 -------------------------------------------------------------- KeyPgLib ---- 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 ----------------------------------------------------- KeyPgLinegraphics ---- 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) -------------------------------------------------------- KeyPgLineinput ---- 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 ------------------------------------------------------ KeyPgLineinputPp ---- 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) ----------------------------------------------------------- KeyPgLoByte ---- 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 -------------------------------------------------------------- KeyPgLoc ---- 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 ------------------------------------------------------------ KeyPgLocal ---- 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 ----------------------------------------------------------- KeyPgLocate ---- 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 * ? ------------------------------------------------------------- KeyPgLock ---- 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 -------------------------------------------------------------- KeyPgLof ---- 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 -------------------------------------------------------------- KeyPgLog ---- 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 ------------------------------------------------------------- KeyPgLong ---- 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 ---------------------------------------------------------- KeyPgLongint ---- 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 ------------------------------------------------------------- KeyPgLoop ---- Loop Control flow statement for looping. Syntax Do [ statement block ] Loop [ { Until | While } condition ] Example See example at Do...Loop. See also * Do...Loop ----------------------------------------------------------- KeyPgLoWord ---- 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 ------------------------------------------------------------- KeyPgLpos ---- 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 ----------------------------------------------------------- KeyPgLprint ---- 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 ------------------------------------------------------------- KeyPgLset ---- 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 ------------------------------------------------------------ KeyPgLtrim ---- 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 ============================================================================ M ------------------------------------------------------------- KeyPgMidi ---- Midi Controls MIDI output through sfxlib. Syntax result = Midi Open( device ) result = Midi Close() result = Midi Play( filename ) result = Midi Stop() result = Midi Pause() result = Midi Resume() result = Midi Send( status, data1, data2 ) Description Midi opens a MIDI output device, plays MIDI files where supported, and sends raw three-byte MIDI messages through Midi Send. MIDI support depends on the target and the active sfxlib backend. Programs should be prepared for a MIDI device or MIDI file playback to be unavailable. Example See examples/sfxlib/midi-open.bas, examples/sfxlib/midi-send.bas, examples/sfxlib/midi-play.bas, and examples/sfxlib/showcase.bas. Differences from QB * New to FreeBASIC. See also * Device * Music ----------------------------------------------------- KeyPgMidstatement ---- 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) ------------------------------------------------------ KeyPgMidfunction ---- 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. dim text as wstring * 20 text = "Привет, мир!" print mid(text, 6, 4) ' displays "т, м" 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 ----------------------------------------------------------- KeyPgMinute ---- 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 -------------------------------------------------------------- KeyPgMkd ---- 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 ------------------------------------------------------------ KeyPgMkdir ---- 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 -------------------------------------------------------------- KeyPgMki ---- 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 ( ByVal number As Integer ) As String Usage result = MKI[$]( number ) result = MKI[$]( number ) Parameters number A Integer or Integer 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 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. This function is useful to write numeric values to buffers without using a Type definition. MKI supports an optional 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, 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 -------------------------------------------------------------- KeyPgMkl ---- 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 -------------------------------------------------------- KeyPgMklongint ---- 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 -------------------------------------------------------------- KeyPgMks ---- 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 ---------------------------------------------------------- KeyPgMkshort ---- 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 -------------------------------------------------------- KeyPgOpModulus ---- 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 ------------------------------------------------------------ KeyPgMonth ---- 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 -------------------------------------------------------- KeyPgMonthname ---- 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 ------------------------------------------------------------ KeyPgMusic ---- Music Loads and controls the current music file through sfxlib. Syntax result = Music Load( filename ) result = Music Play() result = Music Play( filename ) result = Music Loop() result = Music Loop( filename ) Music Pause Music Resume Music Stop status = Music Status() position = Music Position() Description Music is the one-current-track file playback interface. Music Load loads a file as the current music and replaces any previous music. Music Play and Music Loop start the current music when called without a filename, or load and start the supplied filename directly. Music Pause, Music Resume, and Music Stop control the current music. Music Status returns 0 for stopped, 1 for playing, and 2 for paused. Music Position returns the current playback position in frames. Example See examples/sfxlib/music-load.bas, examples/sfxlib/music-play.bas, examples/sfxlib/music-loop.bas, and examples/sfxlib/showcase.bas. Differences from QB * New to FreeBASIC. See also * Sfx --------------------------------------------------------- KeyPgMultikey ---- 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 ------------------------------------------------------ KeyPgMutexCreate ---- 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 " : 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 ----------------------------------------------------- KeyPgMutexDestroy ---- 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 -------------------------------------------------------- KeyPgMutexLock ---- 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 " or : 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 ------------------------------------------------------ KeyPgMutexUnlock ---- 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 ============================================================================ N ------------------------------------------------------------ KeyPgNaked ---- 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 ------------------------------------------------------------- KeyPgName ---- 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 -------------------------------------------------------- KeyPgNamespace ---- 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) ------------------------------------------------------------ KeyPgOpNew ---- 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 --------------------------------------------------- KeyPgOpPlacementNew ---- 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 ------------------------------------------------------------- KeyPgNext ---- 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 ------------------------------------------------------- KeyPgResumenext ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ------------------------------------------------------------ KeyPgOpNot ---- 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 -------------------------------------------------------------- KeyPgNow ---- 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 ============================================================================ O ----------------------------------------------------------- KeyPgObject ---- 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 -------------------------------------------------------------- KeyPgOct ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference --------------------------------------------------------- KeyPgOffsetof ---- 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 ---------------------------------------------------------- KeyPgOnerror ---- 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 ---------------------------------------------------------- KeyPgOngosub ---- 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 ----------------------------------------------------------- KeyPgOngoto ---- 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 ---------------------------------------------------------- KeyPgInclude ---- #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 ------------------------------------------------------------- KeyPgOpen ---- 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 ---------------------------------------------------------- KeyPgOpenCom ---- 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 --------------------------------------------------------- KeyPgOpenCons ---- 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 ---------------------------------------------------------- KeyPgOpenErr ---- 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 ---------------------------------------------------------- KeyPgOpenLpt ---- 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 --------------------------------------------------------- KeyPgOpenPipe ---- 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 --------------------------------------------------------- KeyPgOpenScrn ---- 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 ---------------------------------------------------------- KeyPgOpenTcp ---- 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 ---------------------------------------------------- KeyPgOpenTcpServer ---- 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 --------------------------------------------------------- KeyPgOperator ---- 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( 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( 1.2, 3.4 ) Dim b As Vector2D = Type( 8.9, 6.7 ) Dim c As Vector2D = Type( 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 ----------------------------------------------------------- KeyPgOption ---- 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 ------------------------------------------------------- KeyPgOptionbase ---- 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 ------------------------------------------------------ KeyPgOptionbyval ---- 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__ ---------------------------------------------------- KeyPgOptiondynamic ---- 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 ----------------------------------------------------- KeyPgOptionescape ---- 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 --------------------------------------------------- KeyPgOptionexplicit ---- 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__ ------------------------------------------------------ KeyPgOptiongosub ---- 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) ---------------------------------------------------- KeyPgOptionnogosub ---- 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) -------------------------------------------------- KeyPgOptionnokeyword ---- 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 ---------------------------------------------------- KeyPgOptionprivate ---- 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 ----------------------------------------------------- KeyPgOptionstatic ---- 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 ------------------------------------------------------------- KeyPgOpOr ---- 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 ------------------------------------------------------------ KeyPgOrGfx ---- 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) --------------------------------------------------------- KeyPgOpOrElse ---- 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 -------------------------------------------------------------- KeyPgOut ---- 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 ----------------------------------------------------------- KeyPgOutput ---- 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 --------------------------------------------------------- KeyPgOverload ---- 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 --------------------------------------------------------- KeyPgOverride ---- 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 ============================================================================ P ------------------------------------------------------------ KeyPgPaint ---- 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) ---------------------------------------------------------- KeyPgPalette ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ----------------------------------------------------------- KeyPgPascal ---- 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 ------------------------------------------------------------ KeyPgPcopy ---- 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) ------------------------------------------------------------- KeyPgPeek ---- 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) ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ------------------------------------------------------------- KeyPgPmap ---- 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) ------------------------------------------------------------ KeyPgPoint ---- 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 ------------------------------------------------------- KeyPgPointCoord ---- 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 -------------------------------------------------------------- KeyPgPtr ---- (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 ------------------------------------------------------------- KeyPgPoke ---- 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 -------------------------------------------------------------- KeyPgPos ---- 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 --------------------------------------------------------- KeyPgPreserve ---- 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 ----------------------------------------------------------- KeyPgPreset ---- 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 ------------------------------------------------------------ KeyPgPrint ---- (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 Print ed. See also * Spc * Tab * Print # * ? # * Print Using * ? Using * Write * Draw String * Input ---------------------------------------------------------- KeyPgPrintPp ---- (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 Print ed. See also * Print Using * ? Using * Print * ? * Write # * Open ------------------------------------------------------- KeyPgPrintusing ---- (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 ---------------------------------------------------------- KeyPgPrivate ---- 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 ------------------------------------------------------- KeyPgVisPrivate ---- 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 -------------------------------------------------------- KeyPgOpProcptr ---- 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 --------------------------------------------------------- KeyPgProperty ---- 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 ----------------------------------------------------- KeyPgVisProtected ---- 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 ------------------------------------------------------------- KeyPgPset ---- 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 ---------------------------------------------------------- KeyPgPsetGfx ---- 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) -------------------------------------------------------------- KeyPgPtr ---- (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 ----------------------------------------------------------- KeyPgPublic ---- 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 -------------------------------------------------------- KeyPgVisPublic ---- 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 ------------------------------------------------------ KeyPgPutgraphics ---- 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 -------------------------------------------------------- KeyPgPutfileio ---- 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 ============================================================================ R ----------------------------------------------------------- KeyPgRandom ---- 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 # -------------------------------------------------------- KeyPgRandomize ---- 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 ------------------------------------------------------------- KeyPgRead ---- 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 --------------------------------------------------------- KeyPgReadFile ---- 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 ---------------------------------------------------- KeyPgReadWriteFile ---- 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 ------------------------------------------------------- KeyPgReallocate ---- 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 ------------------------------------------------------------ KeyPgRedim ---- 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 -------------------------------------------------------------- KeyPgRem ---- 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 ------------------------------------------------------------ KeyPgReset ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ---------------------------------------------------------- KeyPgRestore ---- 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 ----------------------------------------------------------- KeyPgResume ---- 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 ------------------------------------------------------- KeyPgResumenext ---- 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 ----------------------------------------------------------- KeyPgReturn ---- 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) ------------------------------------------------------ KeyPgReturnGosub ---- 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) -------------------------------------------------------------- KeyPgRgb ---- 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 ------------------------------------------------------------- KeyPgRgba ---- 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 ------------------------------------------------------------ KeyPgRight ---- 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: dim text as wstring*20 text = "Привет, мир!" print right(text, 5) 'displays " мир!" 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) ------------------------------------------------------------ KeyPgRmdir ---- 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 -------------------------------------------------------------- KeyPgRnd ---- 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 ------------------------------------------------------------- KeyPgRset ---- 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 ------------------------------------------------------------ KeyPgRtrim ---- 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 -------------------------------------------------------------- KeyPgRun ---- 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 ============================================================================ S ------------------------------------------------------------- KeyPgSadd ---- 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 ------------------------------------------------------------ KeyPgScope ---- 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) --------------------------------------------------- KeyPgScreengraphics ---- 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 ------------------------------------------------------- KeyPgScreenCons ---- 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 ------------------------------------------------------- KeyPgScreencopy ---- 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 ---------------------------------------------------- KeyPgScreencontrol ---- 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) ------------------------------------------------------ KeyPgScreenevent ---- 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 ----------------------------------------------------- KeyPgScreenglproc ---- 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 ------------------------------------------------------- KeyPgScreeninfo ---- 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) ------------------------------------------------------- KeyPgScreenlist ---- 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 ------------------------------------------------------- KeyPgScreenlock ---- 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 -------------------------------------------------------- KeyPgScreenptr ---- 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 -------------------------------------------------------- KeyPgScreenres ---- 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 -------------------------------------------------------- KeyPgScreenset ---- 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 ------------------------------------------------------- KeyPgScreensync ---- 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 ----------------------------------------------------- KeyPgScreenunlock ---- 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 ----------------------------------------------------------- KeyPgSecond ---- 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 ---------------------------------------------------------- KeyPgSeekset ---- 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 ------------------------------------------------------- KeyPgSeekreturn ---- 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 ------------------------------------------------------- KeyPgSelectcase ---- 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 ---------------------------------------------------------- KeyPgSetdate ---- 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 ------------------------------------------------------- KeyPgSetenviron ---- 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 --------------------------------------------------------- KeyPgSetmouse ---- 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 ---------------------------------------------------------- KeyPgSettime ---- 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 -------------------------------------------------------------- KeyPgSgn ---- 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 -------------------------------------------------------------- KeyPgSfx ---- Sfx Loads and controls short 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 handles short reusable samples. Sfx Load places a decoded sample in an id slot. Sfx Play plays it once, while Sfx Loop repeats it. The channel forms use the channel number first and the loaded effect id second. The optional pitch argument scales playback speed. With no argument, Sfx Stop, Sfx Pause, and Sfx Resume affect all active sound effects. Sfx Status returns 0 for stopped, 1 for playing, and 2 for paused. Example See examples/sfxlib/sfx-load.bas, examples/sfxlib/sfx-play.bas, examples/sfxlib/sfx-loop.bas, examples/sfxlib/sfx-pitch.bas, and examples/sfxlib/showcase.bas. Differences from QB * New to FreeBASIC. See also * Music * Audio * Generated Sound Commands ----------------------------------------------------------- KeyPgShared ---- 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) ------------------------------------------------------------ KeyPgShell ---- 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 ------------------------------------------------------ KeyPgOpShiftLeft ---- 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 ------------------------------------------------------------ KeyPgShort ---- 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 ----------------------------------------------------- KeyPgOpShiftRight ---- 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 -------------------------------------------------------------- KeyPgSin ---- 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 ----------------------------------------------------------- KeyPgSingle ---- 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 ----------------------------------------------------------- KeyPgSizeof ---- 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 ------------------------------------------------------------ KeyPgSleep ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ------------------------------------------------------------ KeyPgSpace ---- 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) -------------------------------------------------------------- KeyPgSpc ---- 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 * ? -------------------------------------------------------------- KeyPgSqr ---- 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 ----------------------------------------------------------- KeyPgStatic ---- 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 ----------------------------------------------------- KeyPgStaticMember ---- 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 ---------------------------------------------------------- KeyPgStdcall ---- 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 ------------------------------------------------------------- KeyPgStep ---- 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 ------------------------------------------------------------ KeyPgStick ---- 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 ------------------------------------------------------------- KeyPgStop ---- 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) -------------------------------------------------------------- KeyPgStr ---- 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 ------------------------------------------------------------ KeyPgStrig ---- 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 --------------------------------------------------- KeyPgStringFunction ---- 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 ----------------------------------------------------------- KeyPgString ---- 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 --------------------------------------------------------- KeyPgOpStrptr ---- 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 -------------------------------------------------------------- KeyPgSub ---- 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 -------------------------------------------------------- KeyPgMemberSub ---- 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 ----------------------------------------------------------- KeyPgSubPtr ---- 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 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 = 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) ------------------------------------------------------------- KeyPgSwap ---- 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) ----------------------------------------------------------- KeyPgSystem ---- 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) ============================================================================ T -------------------------------------------------------------- KeyPgTab ---- 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 * ? -------------------------------------------------------------- KeyPgTan ---- 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 -------------------------------------------------------- KeyPgTcpAccept ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ------------------------------------------------------------- KeyPgThen ---- 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 ------------------------------------------------------------- KeyPgThis ---- 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 --------------------------------------------------------- KeyPgThiscall ---- __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 ------------------------------------------------------- KeyPgThreadCall ---- 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 ----------------------------------------------------- KeyPgThreadCreate ---- 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) ----------------------------------------------------- KeyPgThreadDetach ---- 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 ------------------------------------------------------- KeyPgThreadSelf ---- 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 ------------------------------------------------------- KeyPgThreadWait ---- 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 ------------------------------------------------------------- KeyPgTime ---- 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 ------------------------------------------------------------ KeyPgTimer ---- 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 ------------------------------------------------------- KeyPgTimeserial ---- 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 -------------------------------------------------------- KeyPgTimeValue ---- 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 --------------------------------------------------------------- KeyPgTo ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference --------------------------------------------------------- KeyPgTransGfx ---- 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 ------------------------------------------------------------- KeyPgTrim ---- 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 ------------------------------------------------------------- KeyPgTrue ---- 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 -------------------------------------------------------- KeyPgTypeAlias ---- 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) --------------------------------------------------------- KeyPgTypeTemp ---- Type (Temporary) Creates a temporary copy of a user defined type Syntax result = Type( initializers, ... ) or result = Type( 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( 1, 2 ) ) test( Type( 1, 2 ) ) test( Type( 1 ) ) Differences from QB * New to FreeBASIC See also * Type...End Type * Type (Alias) ------------------------------------------------------------- KeyPgType ---- 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 (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 ----------------------------------------------------------- KeyPgTypeof ---- 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 ============================================================================ U ----------------------------------------------------------- KeyPgUbound ---- 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 ------------------------------------------------------------ KeyPgUbyte ---- 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 ------------------------------------------------------------ KeyPgUcase ---- 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 --------------------------------------------------------- KeyPgUinteger ---- 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 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 ------------------------------------------------------------ KeyPgUlong ---- 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 --------------------------------------------------------- KeyPgUlongint ---- 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 ------------------------------------------------------------ KeyPgUnion ---- 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 ----------------------------------------------------------- KeyPgUnlock ---- 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 --------------------------------------------------------- KeyPgUnsigned ---- 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 ------------------------------------------------------------ KeyPgUntil ---- 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 ----------------------------------------------------------- KeyPgUshort ---- 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 ------------------------------------------------------- KeyPgPrintusing ---- (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 ------------------------------------------------------------ KeyPgUsing ---- 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 ============================================================================ V ------------------------------------------------------------ KeyPgVaArg ---- 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 ---------------------------------------------------------- KeyPgVaFirst ---- 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 ----------------------------------------------------------- KeyPgVaNext ---- 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 -------------------------------------------------------------- KeyPgVal ---- 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 ----------------------------------------------------------- KeyPgVallng ---- 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 ----------------------------------------------------------- KeyPgValint ---- 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 ---------------------------------------------------------- KeyPgValuint ---- 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 ---------------------------------------------------------- KeyPgValulng ---- 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 -------------------------------------------------------------- KeyPgVar ---- 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 --------------------------------------------------------- KeyPgOpVarptr ---- 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 --------------------------------------------------------- KeyPgViewtext ---- 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 ----------------------------------------------------- KeyPgViewgraphics ---- 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 ---------------------------------------------------------- KeyPgVirtual ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ============================================================================ W ------------------------------------------------------------- KeyPgWait ---- 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 ----------------------------------------------------- KeyPgSfxGenerated ---- Generated Sound Commands Commands for generated sound, PLAY strings, and sfxlib mix settings. Syntax Beep [ duration, pitch ] Sound frequency, duration Sound channel, frequency, duration, volume Tone channel, frequency, duration Noise channel, duration, volume Noise channel, frequency, duration, volume Note name, octave, duration Note channel, name, octave, duration Play music Play channel, music Play music1, music2 Play music1, music2, music3 Tempo bpm bpm = Tempo() Channel channel channel = Channel() Octave octave octave = Octave() Voice instrument instrument = Voice() Vol level Volume level level = Volume() Volume channel, level level = Volume( channel ) Balance position position = Balance() Pan channel, position position = Pan( channel ) Wave id, waveform Envelope id, attack, decay, sustain, release Instrument id, wave_id, envelope_id Instrument channel, instrument_id Description These commands synthesize sound directly through sfxlib. Sound, Tone, Noise, Note, and Play create voices. Tempo, Channel, Octave, and Voice set defaults used by PLAY-string and generated-sound commands. Volume, Vol, Balance, and Pan control mix levels and stereo placement. Sound accepts the high-impact classic form Sound frequency, duration and the explicit channel form Sound channel, frequency, duration, volume. When both arguments are integers, duration is interpreted as the PC BASIC timer-tick value. Wave defines a waveform id. The currently supported waveform values are: * 0 sine * 1 square * 2 triangle * 3 saw * 4 noise Envelope defines an ADSR envelope using seconds for attack, decay, and release, and a 0.0 to 1.0 sustain level. Instrument can define an instrument from a waveform and envelope, or assign an instrument to a channel. The channel-oriented commands use sfxlib's channel table. Channels are zero-based. Example See examples/sfxlib/basics-01-generated-sound.bas, examples/sfxlib/basics-02-play-strings.bas, examples/sfxlib/basics-03-channels-mix.bas, examples/sfxlib/basics-04-instruments.bas, and examples/sfxlib/showcase.bas. Platform Differences * On headless or diagnostic runs, SFXLIB_DRIVER=null can be set before program startup to use the null output driver. Differences from QB * New to FreeBASIC. See also * Beep * Music * Sfx * Sound Library Reference ------------------------------------------------------------- KeyPgWbin ---- 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 ------------------------------------------------------------- KeyPgWchr ---- 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: The character represented by the UNICODE code of 934 is: Φ Multiple UNICODE characters: ΥΦΧ 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 ---------------------------------------------------------- KeyPgWeekday ---- 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 ------------------------------------------------------ KeyPgWeekdayname ---- 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 ------------------------------------------------------------- KeyPgWend ---- 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 ------------------------------------------------------------ KeyPgWhile ---- 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 -------------------------------------------------------- KeyPgWhilewend ---- 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 ------------------------------------------------------------- KeyPgWhex ---- 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 ------------------------------------------------------------ KeyPgWidth ---- 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 ----------------------------------------------------------- KeyPgWindow ---- 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 ------------------------------------------------------ KeyPgWindowtitle ---- 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 ----------------------------------------------------------- KeyPgWinput ---- 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 ------------------------------------------------------------- KeyPgWith ---- 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 ------------------------------------------------------------- KeyPgWoct ---- 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 ------------------------------------------------------------ KeyPgWrite ---- 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 * ? ---------------------------------------------------------- KeyPgWritePp ---- 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 # -------------------------------------------------------- KeyPgWriteFile ---- 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 ----------------------------------------------------------- KeyPgWspace ---- 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 ------------------------------------------------------------- KeyPgWstr ---- 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 ---------------------------------------------------------- KeyPgWstring ---- 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-chars that never overflows if the size is known at compile-time. It has no descriptor, and does never resize unless it's a pointer and Allocate/Reallocate/Deallocate are used directly. 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. The end of the string is marked by the character 0 automatically added by the FreeBASIC string handling functions, so that character must never be part of a WString or the content will be truncated. The character 0 will be appended when the string is created, and the length will be calculated by scanning the string for the first null character. In a WString, Len returns the size of the contained string and SizeOf returns the space allocated to the WString. SizeOf only works if the size is known by the compiler, i.e. a fixed-size WString variable is passed directly, not as a dereferenced pointer or a ByRef function argument. This type is provided for support non-Latin based alphabets. Any intrinsic string function like Left will work with WStrings too, as will any string operator. When processing source files, FreeBASIC can parse ASCII files with Unicode escape sequences (\u), or UTF-8, UTF-16LE, UTF-16BE, UTF-32LE and UTF-32BE files, as long as they were saved with Byte Order Mark (BOM). Note: The most reliable cross-platform code is obtained by encoding without BOM in ASCII/UTF-8 characters. The FreeBASIC text file functions can read and write Unicode files in different encodings, provided the Encoding is specified when the file is opened. The text is automatically converted to the internal encoding at read and converted back to the file encoding at write. SizeOf( WString ) returns the number of bytes used by a WString character in the current platform. When allocating dynamic memory for a WString, 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 WString). Note : When any operand of a binary operator (as assignment, equal, +, *, ...) consists in dereferencing a 'Wstring Ptr' pointer ('pw'), this can give a 'Wstring' string or a 'Numeric' variable, depending on the other operand. If the other operand is numeric, so the dereferenced ' Wstring Ptr' pointer ('*pw') will be treated as a 'Numeric' reference to the one character pointed. If a 'Wstring' pointer indexing '[]' operator is used as dereferencing syntax ('pw[n]'), it is basically a short-cut version of the 'String' indexing '[]' operator ('(*pw)[n]'). Example Dim As WString * 13 str1 => "hello, world" Print str1 Print Len(str1) 'returns 12, the length of the string it contains Print SizeOf(str1) 'returns 13 * sizeof(wstring), the number of bytes used by the variable Dim As WString Ptr str2 str2 = Allocate( 13 * Len(WString) ) *str2 = "hello, world" Print *str2 Print Len(*str2) 'returns 12, the length of the string it points to Platform Differences Support for wstrings relies in the C runtime library available in the platform and the internal format may vary. * Unicode is not supported in the DOS port of FreeBASIC. In this port a character takes up always 1 byte and Wstrings will behave as standard ASCII Zstrings * On Win32, FreeBASIC wstrings are encoded in UCS-2 which uses two bytes (16 bits) for each character and, as such, can only encode the first 65,536 code points of Unicode, that is, the Basic Multilingual Plane (BMP). FreeBASIC does not support Win32 UTF-16 4-byte surrogate encoding beyond the BMP. * On Linux wstrings are encoded in UCS-4 and a character takes up 4 bytes. 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 -------------------------------------------------- KeyPgWstringFunction ---- 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 ΦΦΦΦ IIIII 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) ============================================================================ X ------------------------------------------------------------ KeyPgOpXor ---- 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 ----------------------------------------------------------- KeyPgXorGfx ---- 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) ============================================================================ Y ------------------------------------------------------------- KeyPgYear ---- 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 ============================================================================ Z ---------------------------------------------------------- KeyPgZstring ---- 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 ---------------------------------------------------------- CatPgOpIndex ---- 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 ============================================================================ Variables and Data Types -------------------------------------------------------- CatPgVariables ---- 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. ----------------------------------------------------- CatPgUserDefTypes ---- 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 ----------------------------------------------------- CatPgStdDataTypes ---- 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 ----------------------------------------------------------- TblVarTypes ---- 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 Usage of suffixes for variables Usage of suffixes for literals / numbers ---------------------------------------------------------- CatPgCasting ---- 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. ============================================================================ Assignment operators ----------------------------------------------------- KeyPgOpAssignment ---- 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 -------------------------------------------------- KeyPgOpCombineConcat ---- 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) ----------------------------------------------------- KeyPgOpCombineAdd ---- 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 ----------------------------------------------------- KeyPgOpCombineSub ---- 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 ------------------------------------------------ KeyPgOpCombineMultiply ---- 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 -------------------------------------------------- KeyPgOpCombineDivide ---- 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 ------------------------------------------- KeyPgOpCombineIntegerDivide ---- 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 -------------------------------------------- KeyPgOpCombineExponentiate ---- 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 ------------------------------------------------------------ KeyPgOpLet ---- 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("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 -------------------------------------------------------- KeyPgOpLetlist ---- 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) ------------------------------------------------- KeyPgOpCombineModulus ---- 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 ----------------------------------------------------- KeyPgOpCombineAnd ---- 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 ----------------------------------------------------- KeyPgOpCombineEqv ---- 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 ----------------------------------------------------- KeyPgOpCombineImp ---- 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 ------------------------------------------------------ KeyPgOpCombineOr ---- 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 ----------------------------------------------------- KeyPgOpCombineXor ---- 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 ----------------------------------------------- KeyPgOpCombineShiftLeft ---- 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 ---------------------------------------------- KeyPgOpCombineShiftRight ---- 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 ============================================================================ Arithmetic operators ------------------------------------------------------------ KeyPgOpAdd ---- 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 ------------------------------------------------------- KeyPgOpSubtract ---- 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 ------------------------------------------------------- KeyPgOpMultiply ---- 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 --------------------------------------------------------- KeyPgOpDivide ---- 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 -------------------------------------------------- KeyPgOpIntegerDivide ---- 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 --------------------------------------------------- KeyPgOpExponentiate ---- 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 -------------------------------------------------------- KeyPgOpModulus ---- 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 --------------------------------------------------------- KeyPgOpNegate ---- 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 ------------------------------------------------------ KeyPgOpShiftLeft ---- 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 ----------------------------------------------------- KeyPgOpShiftRight ---- 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 ============================================================================ Conditional operators ---------------------------------------------------------- KeyPgOpEqual ---- 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) ------------------------------------------------------- KeyPgOpNotEqual ---- 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) ------------------------------------------------------- KeyPgOpLessThan ---- 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) ------------------------------------------------ KeyPgOpLessThanOrEqual ---- 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) --------------------------------------------- KeyPgOpGreaterThanOrEqual ---- 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) ---------------------------------------------------- KeyPgOpGreaterThan ---- 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) ------------------------------------------------------------- KeyPgOpIs ---- 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 ============================================================================ Logical operators ------------------------------------------------------------ KeyPgOpAnd ---- 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 ------------------------------------------------------------ KeyPgOpEqv ---- 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 ------------------------------------------------------------ KeyPgOpImp ---- 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 ------------------------------------------------------------ KeyPgOpNot ---- 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 ------------------------------------------------------------- KeyPgOpOr ---- 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 ------------------------------------------------------------ KeyPgOpXor ---- 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 ============================================================================ Short circuit operators -------------------------------------------------------- KeyPgOpAndAlso ---- 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 --------------------------------------------------------- KeyPgOpOrElse ---- 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 ============================================================================ Indexing operators ----------------------------------------------------- KeyPgOpArrayIndex ---- 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) ---------------------------------------------------- KeyPgOpStringIndex ---- 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 ------------------------------------------------------- KeyPgOpPtrIndex ---- 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 ============================================================================ String operators --------------------------------------------------------- KeyPgOpConcat ---- 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 -------------------------------------------------- KeyPgOpConcatConvert ---- 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 --------------------------------------------------------- KeyPgOpStrptr ---- 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 ============================================================================ Preprocessor operators ---------------------------------------------------- KeyPgOpPpStringize ---- 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 ------------------------------------------------------- KeyPgOpPpConcat ---- 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 ------------------------------------------------------- KeyPgOpPpEscape ---- 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 ----------------------------------------------------- KeyPgOpPpNoescape ---- 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 ============================================================================ Pointer operators --------------------------------------------------------- KeyPgOpVarptr ---- 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 --------------------------------------------------------- KeyPgOpStrptr ---- 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 -------------------------------------------------------- KeyPgOpProcptr ---- 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 ------------------------------------------------------------- KeyPgOpAt ---- 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 -------------------------------------------------------- KeyPgOpValueOf ---- 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 ============================================================================ Type or Class operators --------------------------------------------------- KeyPgOpMemberAccess ---- 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 ------------------------------------------------ KeyPgOpPtrMemberAccess ---- 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) ------------------------------------------------------------- KeyPgOpIs ---- 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 ============================================================================ Memory operators ------------------------------------------------------------ KeyPgOpNew ---- 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 ---------------------------------------------------- KeyPgOpNewOverload ---- 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 --------------------------------------------------- KeyPgOpPlacementNew ---- 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 --------------------------------------------------------- KeyPgOpDelete ---- 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 ------------------------------------------------- KeyPgOpDeleteOverload ---- 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 ============================================================================ Iterating operators ------------------------------------------------------------ KeyPgOpFor ---- 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 ----------------------------------------------------------- KeyPgOpStep ---- 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 ----------------------------------------------------------- KeyPgOpNext ---- 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 ---------------------------------------------------------- OpPrecedence ---- 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 -------------------------------------------------------------- TblTruth ---- 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| +------+-+-+ ============================================================================ Statements ------------------------------------------------------ CatPgControlFlow ---- 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. ------------------------------------------------------- CatPgProcedures ---- 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 ----------------------------------------------------- CatPgModularizing ---- Modularizing Keywords helpful when writing modular programs. * Common * DyLibFree * DyLibLoad * DyLibSymbol * Export * Extern * Extern...End Extern * Import * Namespace * Private * Public * Using (Namespaces) ============================================================================ Other ------------------------------------------------------- CatPgPreProcess ---- 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 ---------------------------------------------------- TblEscapeSequences ---- 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 ------------------------------------------------- CatPgCompilerSwitches ---- 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 -------------------------------------------------------- CatPgDddefines ---- 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__ 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. ---------------------------------------------------- ProPgErrorHandling ---- 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 -------------------------------------------------------------- KeyPgAsm ---- 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 ============================================================================ RUNTIME LIBRARY REFERENCE ------------------------- ------------------------------------------------------------ CatPgArray ---- 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). ------------------------------------------------------------- CatPgBits ---- 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. ---------------------------------------------------------- CatPgConsole ---- 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. ------------------------------------------------------------- CatPgDate ---- 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. ------------------------------------------------------------ CatPgError ---- 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 ------------------------------------------------------------- CatPgFile ---- 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. 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. ------------------------------------------------------------- CatPgMath ---- 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). ----------------------------------------------------------- CatPgMemory ---- 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 ------------------------------------------------------------ CatPgOpsys ---- 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. ----------------------------------------------------------- CatPgString ---- 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. -------------------------------------------------------- CatPgThreading ---- 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. ------------------------------------------------------------ CatPgInput ---- 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. ============================================================================ GRAPHICS LIBRARY REFERENCE -------------------------- ------------------------------------------------------------ CatPgGfx2D ---- 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 --------------------------------------------------------- CatPgGfxInput ---- User Input Functions (Graphics Library) Procedures (from graphics library) for working with mice, gaming devices and keyboards. Description These procedures provide access to external devices such as keyboards, mice and gamepads. Mouse and Joystick Input Procedures that provide state information of the mouse or joystick. Keyboard Input Procedures that provide keyboard state information. Mouse and Joystick Input GetMouse Gets button and axis information for the mouse. SetMouse Sets position and visibility of the mouse cursor. 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. -------------------------------------------------------- CatPgGfxScreen ---- 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 ---- GfxLib - FreeBASIC graphics library overview The libary named GfxLib is the built-in graphics library included in FreeBAS IC. As well as re-creating every QuickBASIC graphics command, GfxLib has built-in commands to handle input from the keyboard and mouse. Major contributors of the library are Lillo, coderJeff and DrV. The library supports various drivers depending on the platform: * All: * Null Does nothing, allows to use graphics functions on in-memory buffers and such, without anything being displayed in a graphics window. (gfxlib2/gfx_driver_null.c) * Windows: * Direct2D the default selection of FB GfxLib. For fbc built on WinXP + mingw.org (very old tool chain now), the Direct2D driver is disabled in the gfxlib. * DirectX is fallback if Direct2D can't be initialized. May not be available on old Windows installations. (gfxlib2/win32/gfx_driver_ddraw.c) * GDI as last resort, the "safest" one, available in all Windows versions. Bug note: broken in FB versions 0.20 to 0.24 (crash), and minor problems 0.18.5, and 0.90.x and 1.xx ("banding effects", try extra SCREENUNLOCK), (forum discussion: p=106600) (gfxlib2/win32/gfx_driver_gdi.c) * OpenGL (gfxlib2/win32/gfx_driver_opengl.c) * Linux & others: * X11 The default on Unix systems (gfxlib2/unix/gfx_driver_x11.c) * OpenGL (on top of X11) (gfxlib2/unix/gfx_driver_opengl_x11.c) * FBDev Linux framebuffer device -- fallback in case X11 is disabled (gfxlib2/linux/gfx_driver_fbdev.c) * DOS: * BIOS (gfxlib2/dos/gfx_driver_bios.c) * ModeX "tuned" 320x240x8bpp VGA mode (gfxlib2/dos/gfx_driver_modex.c) * VESA banked compatible with very old VESA 1.x implementations (gfxlib2/dos/gfx_driver_vesa_bnk.c) * VESA linear needs VESA version at least 2.0, usually faster than banked VESA (gfxlib2/dos/gfx_driver_vesa_lin.c) * VGA (gfxlib2/dos/gfx_driver_vga.c) * Bug note: Palette doesn't work well (forum discussion: t=12691 2008) (forum discussion: t=19980 2012) ScreenControl can be used (SET_DRIVER_NAME 103) to override the default driver preferences. Platform Differences * In DOS, GfxLib will create and "manage" a mouse arrow if a mouse driver is detected. There is no "official" way to disable this. Also note that the arrow doesn't react to mouse movements while the screen is locked. * In DOS, Windowing and OpenGL related commands and switches are not available (they exist but do nothing, or return some values with no meaning) * In DOS, the refresh rate setting is not available (some VESA cards do support it, but FreeBASIC for now doesn't) * In DOS, the resolution must match one supported by the graphics card. GfxLib will try to find an appropriate mode from VGA modes, ModeX or VESA, preferring VESA LFB interface if available, or banked VESA otherwise. Unsupported resolutions may currently crash the program (if you fail to check SCREENPTR for ZERO before using it), though in future GfxLib may try to find a close match instead. For optimal compatibility, you should support "safe" resolutions like 640x480 and 800x600, and maybe 1024x768. There are various additional modes like 768x576 around, but they are vendor specific and lacking on many other cards. Also modes 1024x768 and above are not available on older cards and laptops. * It has been observed that SCREEN and SCREENRES may fail to clear the screen in DOS, actually this is probably a BIOS bug that GfxLib currently doesn't workaround. Differences from QB * Graphics support was internally redesigned. QB used VGA graphics modes, and wrote directly into the VGA RAM. Multiple pages were available as long as the card supported them. FB uses backbuffers, one per defined page, and copies them to the video RAM (VGA (DOS), VESA (DOS), DirectX (Win32), ...) in the background. Graphics commands do work as they used to in QB, but a few notable differences are present: * The background screen updating eats a considerable amount of CPU performance. * There is a thread (Win32 and Linux) or ISR (DOS, uses the PIT) active for this. * Mixing FB's graphics support with low-level screen accesses (VGA) is not supported, even in DOS. However direct screen memory access is possible using Screenptr and Screenlock and is fully portable. In DOS VGA and VESA are still available, but can't be mixed with FB's graphics support. See also * GFX Functions Index * Screen (Graphics) The QB-like way to set graphics mode * 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 ---------------------------------------------------------- GfxScancodes ---- 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 -------------------------------------------------------- GfxDefPalettes ---- 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. ============================================================================ TUTORIALS --------- ============================================================================ Getting Started ------------------------------------------------------- ProPgHelloWorld ---- 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 ---------------------------------------------------------- ProPgPrimer1 ---- 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 ------------------------------------------------------ ProPgSourceFiles ---- 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) ------------------------------------------------------ ProPgHeaderFiles ---- 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 ------------------------------------------------ ProPgPrebuiltLibraries ---- 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 FreeB ASIC, 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) --------------------------------------------- ProPgSourceCompiledModule ---- 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 ============================================================================ Lexical Conventions --------------------------------------------------------- ProPgComments ---- 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 -------------------------------------------------- ProPgIdentifierRules ---- 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 --------------------------------------------------------- ProPgLiterals ---- 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 ----------------------------------------------------------- ProPgLabels ---- 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 ------------------------------------------------- ProPgLineContinuation ---- 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 ---------------------------------------------------- ProPgLineSeparator ---- 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 ============================================================================ Variables and Datatypes ----------------------------------------------------- ProPgEnumerations ---- Constants and Enumerations Enumeration, a special User-Defined Type composed of a number of named const ants. 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 ----------------------------------------------------- ProPgNumericTypes ---- 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 ----------------------------------------------------- ProPgStringsTypes ---- 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 --------------------------------------------------- ProPgDataConversion ---- 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 -------------------------------------------------------- ProPgConstants ---- 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 -------------------------------------------------------- ProPgVariables ---- 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 ----------------------------------------------------------- ProPgArrays ---- 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 ----------------------------------------------------- ProPgFixLenArrays ---- 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}} ----------------------------------------------------- ProPgVarLenArrays ---- 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) ------------------------------------------------------- ProPgArrayIndex ---- 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 ---------------------------------------------------- ProPgPassingArrays ---- 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 --------------------------------------------------------- ProPgPointers ---- 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 ---------------------------------------------------- ProPgPtrArithmetic ---- 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 ============================================================================ References ----------------------------------------------------- ProPgFromPtrToRef ---- 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 ------------------------------------------------------- ProPgReferences ---- 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 ============================================================================ Declarations --------------------------------------------- ProPgImplicitdeclarations ---- 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 --------------------------------------------------- ProPgInitialization ---- 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[]( 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[]( 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(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(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(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(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(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(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(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(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(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(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(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 --------------------------------------------------- ProPgStorageClasses ---- 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 ---------------------------------------------------- ProPgVariableScope ---- 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 ------------------------------------------------- ProPgVariableLifetime ---- 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 --------------------------------------------------- ProPgObjectLifetime ---- 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 ------------------------------------------------------- ProPgNamespaces ---- 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 --------------------------------------------------- ProPgVarProcLinkage ---- 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 ------------------------------------------------------------- ProPgUDTs ---- 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 ------------------------------------------------------ ProPgTypeAliases ---- 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 ) --------------------------------------------------------- KeyPgTypeTemp ---- Type (Temporary) Creates a temporary copy of a user defined type Syntax result = Type( initializers, ... ) or result = Type( 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( 1, 2 ) ) test( Type( 1, 2 ) ) test( Type( 1 ) ) Differences from QB * New to FreeBASIC See also * Type...End Type * Type (Alias) -------------------------------------------------------- ProPgTypeUnion ---- 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 ------------------------------------------------------- ProPgCtorsDtors ---- 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) ------------------------------------------------- ProPgMemberProcedures ---- 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. ------------------------------------------------------- ProPgProperties ---- 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. ----------------------------------------------- ProPgVariableLengthData ---- 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) ----------------------------------------------- ProPgMemberAccessRights ---- 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 ---------------------------------------------- ProPgOperatorOverloading ---- 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 ---------------------------------------------------- ProPgTypeIterators ---- 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 -------------------------------------------------------- ProPgNewDelete ---- New and Delete The different operators New (Implicit/Overload/Expression/Placement) and Del ete (Implicit/Overload/Statement), and all their array-versions New[] and De lete[] 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 ------------------------------------------------------ ProPgTypeObjects ---- 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 ----------------------------------------------- ProPgCompoAggregInherit ---- 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(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 ----------------------------------------------------- ProPgPolymorphism ---- 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 ------------------------------------------------- ProPgCtorsAssignDtors ---- 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) ------------------------------------------------ ProPgCtorsAssignDtors2 ---- 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) ============================================================================ Statements and Expressions -------------------------------------------- ProPgExpressionsStatements ---- Differences between Expressions and Statements To introduce the expressions and statements (syntactic entities) in FreeBASI C. 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 ---------------------------------------------- ProPgConstantExpressions ---- 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 ------------------------------------------------------ ProPgAssignments ---- 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 ---------------------------------------------------------- OpPrecedence ---- 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 ------------------------------------------------------ ProPgControlFlow ---- 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 ------------------------------------------------------- ProPgProcedures ---- 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 ------------------------------------------------- ProPgPassingArguments ---- 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) ------------------------------------------------------ ProPgReturnValue ---- 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) -------------------------------------------------- ProPgProcedureScopes ---- 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 ----------------------------------------------- ProPgCallingConventions ---- 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 -------------------------------------------------------- ProPgRecursion ---- 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 --------------------------------------------------------- ProPgCallback ---- 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 --------------------------------------------------- ProPgProcCtorsDtors ---- 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 ------------------------------------------------ ProPgProcedurePointers ---- 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 ------------------------------------------------ ProPgVariadicArguments ---- 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(123), @Type(456), @Type(789), CPtr(Integer Ptr, 0)) printNumericList("Double Ptr", @Type(1.2), @Type(3.4), @Type(5.6), @Type(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(123), @Type(456), @Type(789), CPtr(Integer Ptr, 0)) printNumericList("Double Ptr", @Type(1.2), @Type(3.4), @Type(5.6), @Type(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 ---------------------------------------------------------------- GfxLib ---- GfxLib - FreeBASIC graphics library overview The libary named GfxLib is the built-in graphics library included in FreeBAS IC. As well as re-creating every QuickBASIC graphics command, GfxLib has built-in commands to handle input from the keyboard and mouse. Major contributors of the library are Lillo, coderJeff and DrV. The library supports various drivers depending on the platform: * All: * Null Does nothing, allows to use graphics functions on in-memory buffers and such, without anything being displayed in a graphics window. (gfxlib2/gfx_driver_null.c) * Windows: * Direct2D the default selection of FB GfxLib. For fbc built on WinXP + mingw.org (very old tool chain now), the Direct2D driver is disabled in the gfxlib. * DirectX is fallback if Direct2D can't be initialized. May not be available on old Windows installations. (gfxlib2/win32/gfx_driver_ddraw.c) * GDI as last resort, the "safest" one, available in all Windows versions. Bug note: broken in FB versions 0.20 to 0.24 (crash), and minor problems 0.18.5, and 0.90.x and 1.xx ("banding effects", try extra SCREENUNLOCK), (forum discussion: p=106600) (gfxlib2/win32/gfx_driver_gdi.c) * OpenGL (gfxlib2/win32/gfx_driver_opengl.c) * Linux & others: * X11 The default on Unix systems (gfxlib2/unix/gfx_driver_x11.c) * OpenGL (on top of X11) (gfxlib2/unix/gfx_driver_opengl_x11.c) * FBDev Linux framebuffer device -- fallback in case X11 is disabled (gfxlib2/linux/gfx_driver_fbdev.c) * DOS: * BIOS (gfxlib2/dos/gfx_driver_bios.c) * ModeX "tuned" 320x240x8bpp VGA mode (gfxlib2/dos/gfx_driver_modex.c) * VESA banked compatible with very old VESA 1.x implementations (gfxlib2/dos/gfx_driver_vesa_bnk.c) * VESA linear needs VESA version at least 2.0, usually faster than banked VESA (gfxlib2/dos/gfx_driver_vesa_lin.c) * VGA (gfxlib2/dos/gfx_driver_vga.c) * Bug note: Palette doesn't work well (forum discussion: t=12691 2008) (forum discussion: t=19980 2012) ScreenControl can be used (SET_DRIVER_NAME 103) to override the default driver preferences. Platform Differences * In DOS, GfxLib will create and "manage" a mouse arrow if a mouse driver is detected. There is no "official" way to disable this. Also note that the arrow doesn't react to mouse movements while the screen is locked. * In DOS, Windowing and OpenGL related commands and switches are not available (they exist but do nothing, or return some values with no meaning) * In DOS, the refresh rate setting is not available (some VESA cards do support it, but FreeBASIC for now doesn't) * In DOS, the resolution must match one supported by the graphics card. GfxLib will try to find an appropriate mode from VGA modes, ModeX or VESA, preferring VESA LFB interface if available, or banked VESA otherwise. Unsupported resolutions may currently crash the program (if you fail to check SCREENPTR for ZERO before using it), though in future GfxLib may try to find a close match instead. For optimal compatibility, you should support "safe" resolutions like 640x480 and 800x600, and maybe 1024x768. There are various additional modes like 768x576 around, but they are vendor specific and lacking on many other cards. Also modes 1024x768 and above are not available on older cards and laptops. * It has been observed that SCREEN and SCREENRES may fail to clear the screen in DOS, actually this is probably a BIOS bug that GfxLib currently doesn't workaround. Differences from QB * Graphics support was internally redesigned. QB used VGA graphics modes, and wrote directly into the VGA RAM. Multiple pages were available as long as the card supported them. FB uses backbuffers, one per defined page, and copies them to the video RAM (VGA (DOS), VESA (DOS), DirectX (Win32), ...) in the background. Graphics commands do work as they used to in QB, but a few notable differences are present: * The background screen updating eats a considerable amount of CPU performance. * There is a thread (Win32 and Linux) or ISR (DOS, uses the PIT) active for this. * Mixing FB's graphics support with low-level screen accesses (VGA) is not supported, even in DOS. However direct screen memory access is possible using Screenptr and Screenlock and is fully portable. In DOS VGA and VESA are still available, but can't be mixed with FB's graphics support. See also * GFX Functions Index * Screen (Graphics) The QB-like way to set graphics mode * 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 --------------------------------------------------- ProPgAntiFlickering ---- 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 " 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 ---------------------------------------------------- GfxInternalFormats ---- Internal graphics formats Information on the internal formats used by FreeBASIC to represent graphics. Pixel formats When a graphics mode is set via the Screen (Graphics) or ScreenRes functions, GfxLib creates also a framebuffer in standard system memory and sets an appropriate internal pixel format for the mode. There are basically three internal pixel formats, selected depending on the screen depth, as described in the following table: +------------+------------------------+-------------+--------------------------------+ |Screen depth|Internal bytes per pixel|Range bitmask|Pixel format | |1bpp | 1 | &h1 |palette color index | |2bpp | 1 | &h3 |palette color index | |4bpp | 1 | &hF |palette color index | |8bpp | 1 | &hFF |palette color index | |15/16bpp | 2 | &hFFFF |RRRRRGGGGGGBBBBB | |24/32bpp | 4 | &hFFFFFFFF |AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB| +------------+------------------------+-------------+--------------------------------+ All drawing operations work on this RAM framebuffer; when the actual display needs to be updated, GfxLib copies the contents of the framebuffer to the real display memory, automatically converting in the process from the current internal pixel format to whatever pixel format the real display uses. By limiting the internal pixel formats to 3, the library prevents you having to deal with the plethora of real display formats. Color values When calling a graphics primitive that accepts a color, this can be specified in two ways. In 8bpp or less modes, the color value must be a direct 8 bits color index in the current palette, and this matches the internal pixel format for those modes. In higher color depths, the color value should always have the form &hAARRGGBB. This is what the RGB and RGBA macros return, and is equivalent to the 24/32bpp internal pixel format representation. If the current color depth is 24 or 32bpp, this means the color value passes in unaltered. If a 15/16bpp mode is in use, internally each primitive automatically converts the color from the &hAARRGGBB form into the RRRRRGGGGGGBBBBB internal pixel format (note that in this process the alpha channel is lost, as 15/16bpp modes do not support it). Once the color value is in one of the three pixel formats, the primitive limits its range to the range supported by the current color depth, by using a bitwise And operation with a range bitmask. So if in 8bpp, the color value passed is Anded by &hFF for example. Notes on transparency For 8bpp or less modes, color index 0 is always treated as the transparent color for the Put modes that support transparency. For higher depths, RGB(255, 0, 255) always represents the transparent color. In 15/16bpp modes, this translates to the internal value &hF81F, whereas in 24/32bpp modes it becomes &hFFFF00FF. Note that in 24/32bpp modes, Put identifies the transparent color by looking just at the red, green and blue components of the color value, while the alpha value can assume any value. This means that in 24/32bpp modes, &h00FF00FF, &h10FF00FF, &hABFF00FF and &hFFFF00FF for example all represent the transparent color, since the lower 24 bits are always &hFF00FF. Buffer formats 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. Old-style chunk header consists of 4 bytes (32 bits, or 4 bytes). The first 3 bits contain the image color depth in bytes per pixel (8-bit color depth -> 1; 16-bit color depth -> 2; 32-bit color depth -> 4). The next 13 bits contain the image width. The last 16 bits contain the image's height. Please note the intrinsic nature of the header allows only for sizes up to 8191 * 65535 pixels: ' inside FB namespace (extracted from fbgfx.bi) Type _OLD_HEADER Field = 1 bpp : 3 As UShort Width : 13 As UShort height As UShort End Type The actual pixel data follows the header, and is compacted one row of pixels after another; no data alignment is assumed. The final size of the chunk can then be computed using the formula: size = 4 + ( width * height * bytes_per_pixel ) New-style chunk header consists of 32 bytes. The first dword (32 bits) must be equal to the value 7, allowing GfxLib to identify the new type of chunk. The second dword contains the image color depth in bytes per pixel. The third and fourth dwords contain the image width and height respectively, effectively removing the image size limit enforced by the old-style image chunks. The fifth dword contains the pixel row pitch in bytes; this tells how many bytes a row of pixels in the image takes up. The pitch in new-style chunks is always padded to a multiple of 16, to allow pixels' row data to be aligned on the paragraph boundary. The remaining 3 dwords (total 12 bytes) of the header are currently unused and reserved for future use: ' inside FB namespace (extracted from fbgfx.bi) Type IMAGE Field = 1 '' in FB namespace 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 End Type The final size of the image is: size = 32 + ( ( ( ( width * bytes_per_pixel ) + &hF ) and not &hF ) * height ) The format of images created by ImageCreate and Get depend on the dialect used. In the -lang fb dialect, images will be created with the new-style header. In the -lang fblite and -lang qb dialects, the old-style image header is created. All graphics primitives can work with both old-style and new-style image chunks. For easy access to image information, ImageInfo can be used to obtain useful properties of an image buffer - such as its dimensions, color depth, pitch, and a pointer to the pixel data - whichever format is used. It is also possible to access the image header directly to access this information. For more information on acessing the header structure, please refer to this example. See also * Screen (Graphics) * ScreenRes * Get (Graphics) * Put (Graphics) * ImageCreate * ImageInfo * Trans * Alpha -------------------------------------------------- ProPgExternalFormats ---- External Graphics File Formats (page to be completed) The external graphics file formats supported by rtlib through Bload and Bsave. Here is a rough starting point, though many bits still to add in... (from coderJeff) Files used with BLOAD/BSAVE First byte of file: &hFD QB BSAVED BLOCK &hFE FB BSAVED BLOCK &h42 Windows Bitmap (&h42 = Asc("B")) For the QB/FB saved block, &hFD and &hFE identify the file as a QB/FB format and the next bytes determine the SIZE of the data contained in the file, followed by DATA and optionally PALETTE. For the Windows Bitmap, should be binary compatible with standard Windows BMP files. When loading, &h42 only identifies the file as possibly a windows bitmap and other checks on the header may cause it to fail loading. QB BSAVED Block OFS Len DESCRIPTION 0 1 QB BSAVED BLOCK Identifier (&hFD) 1 4 Ignored 5 2 Unsigned 16 Bit SIZE, little endian order 7 ... Data (expected To be SIZE bytes Long) ? ... Palette COLORS BSAVE and QB/FB Save Block BSave( filename, source [,[ size ][,{ pal | pal, bitsperpixel }]] ) BSAVE always writes using the FB format (&hFE) DATA bytes written is the smaller of the SIZE parameter and screen * pitch PALETTE is written either from pal parameter or from current screen's palette BLOAD and QB/FB Save Block BLoad filename [, dest [, [pal]]] If dest is not given, then DATA is loaded directly to screen memory byte for byte. If SIZE exceeds the current screen * pitch, then DATA is truncated to fit the size of the current graphics mode. If dest is given, then DATA is loaded directly to the memory location pointed to by 'dest'. RLE (Run-Length Encoding) not supported. So only uncompressed formats. BLOAD/BSAVE and Windows bitmap BLOAD - if first byte is &H42 then file is attempted to be loaded as a bitmap BSAVE - if filename has '.bmp' extension, then is saved as bitmap ..... ..... See also * BLoad * BSave * ..... ----------------------------------------------------- ProPgLiteRegulate ---- 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 - 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 " : 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 " : 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: - for True/False for image skipping - for Scroll/Remove image skipped - for Calibration phase - 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 " or : Normal resolution" Print " or : High resolutiion" Print #endif Print " <+> : Increase FPS" Print " <-> : Decrease FPS" Print Print " Optional parameter :" Print " or : True for image skipping" If SkipImage = True Then Print " or : Remove image skipped" Print " or : Scroll image skipped" End If Print " or : False for image skipping" Print " or : Calibration phase" Print Print " : 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 " or : Increment tc" Print " or : Decrement tc" Print Print " Optional parameter :" Print " or : True for image skipping" If SkipImage = True Then Print " or : Remove image skipped" Print " or : Scroll image skipped" End If Print " or : False for image skipping" Print Print " : 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 '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(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(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(1,1,1)) As vector3d #define cr 0.0174532925199433 Angle=Type(Angle.x*cr,Angle.y*cr,Angle.z*cr) #macro Rotate(a1,a2,b1,b2,d) temp=Type((a1)*Cos(Angle.d)+(a2)*Sin(Angle.d),(b1)*Cos(Angle.d)+(b2)*Sin(Angle.d)) #endmacro Dim As vector3d p=Type(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((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((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.xxres-border Then kx=-kx If disp.yyres-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)," or : True for image skipping activation" If Skipping = True Then Draw String (16,144)," or : Scroll image skipped" Draw String (16,160)," or : Remove image skipped" Draw String (16,176)," or : False for image skipping activation" Draw String (16,208)," : Quit" Else Draw String (16,144)," or : False for image skipping activation" Draw String (16,176)," : 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)," or : True for image skipping activation" If Skipping = True Then Draw String (16,144)," or : Scroll image skipped" Draw String (16,160)," or : Remove image skipped" Draw String (16,176)," or : False for image skipping activation" Draw String (16,208)," : Quit" Else Draw String (16,144)," or : False for image skipping activation" Draw String (16,176)," : 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 ---------------------------------------------------- ProPgDelayRegulate ---- 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 " : 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 " or : Normal resolution" Print " or : High resolutiion" Print #endif Print " <+> : Increase FPS" Print " <-> : Decrease FPS" Print Print " Optional parameter :" Select Case res Case "N" Print " or : Increase NR threshold" Print " or : Decrease NR threasold" Draw String (320, 280), "(optimal value : 32)" Case "H" Print " or : Increase HR threshold" Print " or : Decrease HR threasold" Draw String (320, 280), "(optimal value : 2)" End Select Print Print " : 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 --------------------------------------------------- ProPgMultiThreading ---- 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 -------------------------------------------------------- ProPgMtThreads ---- 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 ------------------------------------------------ ProPgMtMutualExclusion ---- 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 ------------------------------------------- ProPgMtConditionalVariables ---- Conditional Variables The built-in procedures that create, wait-for/signal, and destroy Conditiona l 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 ----------------------------------------------- ProPgMtCriticalSections ---- 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 -------------------------------------------- ProPgMtCriticalSectionsFAQ ---- 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 " : 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 " : 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 " : 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 ============================================================================ Making Binaries ------------------------------------------------------ ProPgExecutables ---- 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 -------------------------------------------------- ProPgStaticLibraries ---- 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(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 -------------------------------------------------- ProPgSharedLibraries ---- 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 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 ---------------------------------------------- ProPgProfilingFbProfiler ---- 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 .prf (where 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 .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 .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 .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 .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 ----------------------------------------------- ProPgProfilingGmonGprof ---- 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 ----------------------------------------------------- ProPgPreprocessor ---- 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 ------------------------------------------- ProPgConditionalCompilation ---- 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 ----------------------------------------------------------- ProPgMacros ---- 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 ============================================================================ Other Topics -------------------------------------------------------------- CptAscii ---- 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. 0 0 16 10 32 20 48 30 0 64 40 @ 80 50 P 96 60 `112 70 p 1 1 17 11 33 21 ! 49 31 1 65 41 A 81 51 Q 97 61 a113 71 q 2 2 18 12 34 22 " 50 32 2 66 42 B 82 52 R 98 62 b114 72 r 3 3 19 13 35 23 # 51 33 3 67 43 C 83 53 S 99 63 c115 73 s 4 4 20 14 36 24 $ 52 34 4 68 44 D 84 54 T100 64 d116 74 t 5 5 21 15 37 25 % 53 35 5 69 45 E 85 55 U101 65 e117 75 u 6 6 22 16 38 26 & 54 36 6 70 46 F 86 56 V102 66 f118 76 v 7 7 23 17 39 27 ' 55 37 7 71 47 G 87 57 W103 67 g119 77 w 8 8 24 18 40 28 ( 56 38 8 72 48 H 88 58 X104 68 h120 78 x 9 9 25 19 41 29 ) 57 39 9 73 49 I 89 59 Y105 69 i121 79 y 10 A 26 1A 42 2A * 58 3A : 74 4A J 90 5A Z106 6A j122 7A z 11 B 27 1B 43 2B + 59 3B ; 75 4B K 91 5B [107 6B k123 7B { 12 C 28 1C 44 2C , 60 3C < 76 4C L 92 5C \108 6C l124 7C | 13 D 29 1D 45 2D - 61 3D = 77 4D M 93 5D ]109 6D m125 7D } 14 E 30 1E 46 2E . 62 3E > 78 4E N 94 5E ^110 6E n126 7E ~ 15 F 31 1F 47 2F / 63 3F ? 79 4F O 95 5F _111 6F o127 7F  128 80 144 90 160 A0 176 B0 192 C0 208 D0 г224 E0 240 F0 129 81 145 91 161 A1 177 B1 193 C1 209 D1 ѳ225 E1 241 F1 130 82 146 92 162 A2 178 B2 194 C2 ³210 D2 ҳ226 E2 242 F2 131 83 147 93 163 A3 179 B3 195 C3 ó211 D3 ӳ227 E3 243 F3 132 84 148 94 164 A4 180 B4 196 C4 ij212 D4 Գ228 E4 244 F4 133 85 149 95 165 A5 181 B5 197 C5 ų213 D5 ճ229 E5 245 F5 134 86 150 96 166 A6 182 B6 198 C6 Ƴ214 D6 ֳ230 E6 246 F6 135 87 151 97 167 A7 183 B7 199 C7 dz215 D7 ׳231 E7 247 F7 136 88 152 98 168 A8 184 B8 200 C8 ȳ216 D8 س232 E8 248 F8 137 89 153 99 169 A9 185 B9 201 C9 ɳ217 D9 ٳ233 E9 249 F9 138 8A 154 9A 170 AA 186 BA 202 CA ʳ218 DA ڳ234 EA 250 FA 139 8B 155 9B 171 AB 187 BB 203 CB ˳219 DB ۳235 EB 251 FB 140 8C 156 9C 172 AC 188 BC 204 CC ̳220 DC ܳ236 EC 252 FC 141 8D 157 9D 173 AD 189 BD 205 CD ͳ221 DD ݳ237 ED 253 FD 142 8E 158 9E 174 AE 190 BE 206 CE γ222 DE ޳238 EE 254 FE 143 8F 159 9F 175 AF 191 BF 207 CF ϳ223 DF ߳239 EF 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. ------------------------------------------------------------ ProPgDates ---- 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" ---------------------------------------------------------- ProPgRadians ---- 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. ---------------------------------------------------- ProPgErrorHandling ---- 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 ---------------------------------------------------- ProPgEventHandling ---- 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 -------------------------------------------------------- CatPgDddefines ---- 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__ 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. ------------------------------------------------- ProPgIdentifierLookup ---- 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.root End Sub End Namespace End Namespace ' "N.P.test()" calls the unqualified identifier "duplicateType" ' "Print Type.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.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.root End Sub End Namespace End Namespace ' "N.P.test()" calls the unqualified identifier "duplicateType" ' "Print Type.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.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.root End Sub End Namespace End Namespace ' "N.P.test()" calls the unqualified identifier "duplicateType" ' "Print Type.root" calls the qualified identifier "N.P.duplicateType" Print "From Namespace:" N.P.test() '' "..duplicateSub" expected : in [3] parent namespaces (by nesting) Print Type.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.root End Sub End Namespace End Namespace ' "N.P.test()" calls the unqualified identifier "duplicateType" ' "Print Type.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.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 ============================================================================ Technical Articles -------------------------------------------------------------- KeyPgAsm ---- 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 --------------------------------------------------------- ProPgCruntime ---- 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 ----------------------------------------------------------- ProPgFileIO ---- 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) ---------------------------------------------------- ProPgDynamicMemory ---- 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 ----------------------------------------------- ProPgRecursionIteration ---- 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 ------------------------------------------------------- ProPgObjectRtti ---- 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 --------------------------------------------------- ProPgDataExecutable ---- 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 ----------------------------------------------------- ProPgUseNewDelete ---- 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 ----------------------------------------------------- ProPgEmulateTlsTp ---- 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 ---------------------------------------------------- CommunityTutorials ---- 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 ----------------------------------------------------------- CodeLibrary ---- 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 External Library Documentation Sourceforge FreeBASIC Games Directory 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 LIBRARIES INDEX ------------------------ ============================================================================ Graphical/test-based user interfaces ------------------------------------------------------------ ExtLibcgui ---- CGUI Library for making GUIs in a simple way. Website: http://cgui.sourceforge.net/index.html, http://www.allegro.cc/resou rce/Libraries/GUI/CGUI Platforms supported: Win32, Linux Headers to include: cgui.bi Header version: 2.0.4 Example Usage: yes, in examples/GUI/CGUI/ ---------------------------------------------------------- ExtLibcurses ---- Curses Standardized console user interface library Website: http://pdcurses.sourceforge.net/ and http://www.gnu.org/software/nc urses/ 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() ------------------------------------------------------------- ExtLibgtk ---- 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() ------------------------------------------------------------- ExtLibIUP ---- 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/ ------------------------------------------------------------- ExtLibwxc ---- 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/ --------------------------------------------------------- ExtLibwindows ---- 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/ ------------------------------------------------------------- ExtLibX11 ---- 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 ============================================================================ Graphics --------------------------------------------------------- ExtLiballegro ---- 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/ ------------------------------------------------------------ ExtLibDUGL ---- 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 ------------------------------------------------------------ ExtLibcaca ---- 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/ ----------------------------------------------------------- ExtLibcairo ---- 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 ---------------------------------------------------------- ExtLibdislin ---- 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 -------------------------------------------------------- ExtLibfreeglut ---- Free alternative to the OpenGL Utility Toolkit Just like GLUT, freeglut is a helper library that can be used to create Open GL 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 ------------------------------------------------------- ExtLibfreeimage ---- 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 ------------------------------------------------------- ExtLibfreetype2 ---- 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 -------------------------------------------------------------- ExtLibgd ---- 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/ ---------------------------------------------------------- ExtLibgiflib ---- 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/ ------------------------------------------------------------ ExtLibGLUT ---- 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/ ------------------------------------------------------------ ExtLibGLFW ---- 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/ ------------------------------------------------------------- ExtLibgrx ---- 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/ -------------------------------------------------------------- ExtLibIL ---- 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) ------------------------------------------------------------ ExtLibjapi ---- 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 --------------------------------------------------------- ExtLibjpeglib ---- 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 -------------------------------------------------------- ExtLibjpgalleg ---- 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 ---------------------------------------------------------- ExtLiblibpng ---- 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, 1 5, 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). ---------------------------------------------------------- ExtLibOpenGL ---- 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/ ---------------------------------------------------------- ExtLibpdflib ---- 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/ ------------------------------------------------------------- ExtLibSDL ---- 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/ --------------------------------------------------------- ExtLibtinyptc ---- 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/ ============================================================================ Music/Sound, Audio/Video ------------------------------------------------------------ ExtLibbass ---- 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() --------------------------------------------------------- ExtLibbassmod ---- 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() ----------------------------------------------------------- ExtLibFlite ---- 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 ------------------------------------------------------------ ExtLibfmod ---- 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() ------------------------------------------------------- ExtLibMediaInfo ---- 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 ---------------------------------------------------------- ExtLibmpg123 ---- 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 ------------------------------------------------------------- ExtLibogg ---- 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 -------------------------------------------------------------- ExtLibal ---- 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 OpenA L. 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/ ------------------------------------------------------- ExtLibPortAudio ---- 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 ------------------------------------------------------------- ExtLibsfx ---- 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 WaveHeaderTy pe 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 --------------------------------------------------------- ExtLibsndfile ---- 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 ------------------------------------------------------------- ExtLibVLC ---- 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 ---------------------------------------------------------- ExtLibvorbis ---- 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 ============================================================================ Database ------------------------------------------------------------ ExtLibGDBM ---- 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 ----------------------------------------------------------- ExtLibmysql ---- 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/ ------------------------------------------------------ ExtLibpostgresql ---- 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/ ---------------------------------------------------------- ExtLibsqlite ---- 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/ ============================================================================ Development Helpers ----------------------------------------------------------- ExtLibCUnit ---- 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/ ------------------------------------------------------------ ExtLibgdsl ---- 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/ --------------------------------------------------------- ExtLibgettext ---- 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 ---------------------------------------------------------- ExtLibaspell ---- 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) ------------------------------------------------------------- ExtLibbfd ---- 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" ============================================================================ Embeddable Languages ------------------------------------------------------------- ExtLibjni ---- 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 ----------------------------------------------------------- ExtLibjsonc ---- 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) ------------------------------------------------------------- ExtLibFfi ---- 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) ------------------------------------------------------------- ExtLibJit ---- 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) ------------------------------------------------------------- ExtLibLua ---- 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/ ---------------------------------------------------- ExtLibspidermonkey ---- SpiderMonkey Embeddable javascript engine. Website: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMon key 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) ============================================================================ Cryptography -------------------------------------------------------- ExtLibcryptlib ---- 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 ------------------------------------------------------------ ExtLibUUID ---- 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 ============================================================================ Mathematics ---------------------------------------------------------- ExtLibbigint ---- 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) -------------------------------------------------------- ExtLibChipmunk ---- 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 ------------------------------------------------------------- ExtLibgmp ---- 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) ------------------------------------------------------------- ExtLibgsl ---- 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.so urceforge.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 ---------------------------------------------------------- ExtLibnewton ---- 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/ ------------------------------------------------------------- ExtLibode ---- 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/ ============================================================================ Networking --------------------------------------------------------- ExtLibcgiutil ---- 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 ------------------------------------------------------------ ExtLibcurl ---- 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 ) --------------------------------------------------------- ExtLibFastCGI ---- 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 "FastCGI Hello!" Print "

FastCGI Hello!

" Print Using "Request number ### running on host &"; count; *getenv("SERVER_NAME"); Wend ---------------------------------------------------------- ExtLibZeroMQ ---- 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 ============================================================================ eXtensible Markup Language (XML) ----------------------------------------------------------- ExtLibexpat ---- 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 " 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) ---------------------------------------------------------- ExtLiblibxml ---- 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() --------------------------------------------------------- ExtLiblibxslt ---- 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 ------------------------------------------------------------ ExtLibmxml ---- 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 ============================================================================ Regular Expressions ------------------------------------------------------------ ExtLibpcre ---- 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/ ------------------------------------------------------------- ExtLibtre ---- 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/ ============================================================================ Compression ----------------------------------------------------------- ExtLibbzip2 ---- 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/ ------------------------------------------------------------- ExtLibZip ---- 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 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 = "......6...3..2" myZipFile = "26.zip" myFile = "file3.txt" zipAddFileFromString(myZipFile, myFile, myText) myFile = "BIMBO/file3.txt" zipAddFileFromString(myZipFile, myFile, myText) Sleep ------------------------------------------------------------ ExtLibLzma ---- 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/ ------------------------------------------------------------- ExtLibLzo ---- 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) --------------------------------------------------------- ExtLibquicklz ---- 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 ------------------------------------------------------------ ExtLibzlib ---- 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 ============================================================================ System APIs ------------------------------------------------------------- ExtLibCRT ---- 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 Micro soft 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.as px ------------------------------------------------------------- ExtLibdos ---- 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/ ------------------------------------------------------ ExtLibdisphelper ---- 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) ------------------------------------------------------------ ExtLibGLib ---- 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/ --------------------------------------------------------- ExtLibwindows ---- 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/ ------------------------------------------------------------- ExtLibX11 ---- 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 ============================================================================ USING THE FREEBASIC COMPILER ---------------------------- ---------------------------------------------------- CompilerInstalling ---- 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 -------------------------------------------------- CompilerRequirements ---- 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 ------------------------------------------------------- CompilerRunning ---- 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 ------------------------------------------------------- CompilerCmdLine ---- 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 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 ============================================================================ COMMAND LINE OPTIONS -------------------- ---------------------------------------------------- CompilerOptoptfile ---- 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 ---------------------------------------------------------- CompilerOpta ---- 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 ------------------------------------------------------- CompilerOptarch ---- 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 -------------------------------------------------------- CompilerOptasm ---- 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 ---------------------------------------------------------- CompilerOptb ---- 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 ------------------------------------------------ CompilerOptbuildprefix ---- 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 = / - normal : prefix = /../ * Override default prefix: - if the '-prefix ' 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 ' command line option given, overrides the default target * Default buildprefix: - always empty * Override default buildprefix: - if the '-buildprefix ' 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 = /bin// - toolname = + + [.exe] - include path = /inc - library path = /lib/ normal fbc: - default binary path: - binary path = /bin/ - or if '-buildprefix' given: - binary path = /bin/ - toolname = + [.exe] - If binary tool not found, then rely on PATH environment variable to find: - toolname = + - 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 ' 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 ---------------------------------------------------------- CompilerOptc ---- 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 ------------------------------------------------------- CompilerOptcupp ---- 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 ---------------------------------------------------------- CompilerOptd ---- 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 -------------------------------------------------------- CompilerOptdll ---- 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.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_il.a" file name See also * __FB_OUT_DLL__ * Shared Libraries * Using the Command Line ------------------------------------------------------ CompilerOptdylib ---- 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 ---------------------------------------------------------- CompilerOpte ---- 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 ----------------------------------------------------- CompilerOptearray ---- 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 ------------------------------------------------- CompilerOptearraydims ---- 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 ---------------------------------------------------- CompilerOpteassert ---- 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 ----------------------------------------------------- CompilerOptedebug ---- 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 ------------------------------------------------- CompilerOptedebuginfo ---- 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 -------------------------------------------------- CompilerOptelocation ---- 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 ------------------------------------------------------ CompilerOptentry ---- 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 --------------------------------------------------- CompilerOptenullptr ---- 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 ---------------------------------------------------- CompilerOpteunwind ---- 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 --------------------------------------------------------- CompilerOptex ---- 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 -------------------------------------------------------- CompilerOptexx ---- 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 ----------------------------------------------------- CompilerOptexport ---- 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 -------------------------------------------------- CompilerOptforcelang ---- 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 ----------------------------------------------------- CompilerOptfpmode ---- 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__ -------------------------------------------------------- CompilerOptfpu ---- 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__ ---------------------------------------------------------- CompilerOptg ---- 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 -------------------------------------------------------- CompilerOptgen ---- 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 ---------------------------------------------------------- CompilerOpti ---- 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 ---------------------------------------------------- CompilerOptinclude ---- 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 ---------------------------------------------------------- CompilerOptl ---- 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 ------------------------------------------------------- CompilerOptlang ---- 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 -------------------------------------------------------- CompilerOptlib ---- 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 ---------------------------------------------------------- CompilerOptm ---- 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 -------------------------------------------------------- CompilerOptmap ---- 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 ----------------------------------------------------- CompilerOptmaxerr ---- 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 --------------------------------------------------------- CompilerOptmt ---- 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 -------------------------------------------------- CompilerOptnodeflibs ---- 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 ------------------------------------------------------ CompilerOptnolib ---- 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 -------------------------------------------------- CompilerOptnoerrline ---- 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 -------------------------------------------------- CompilerOptnoobjinfo ---- 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 ---------------------------------------------------- CompilerOptnostrip ---- 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 ---------------------------------------------------------- CompilerOpto ---- 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 ----------------------------------------------- CompilerOptoptimization ---- 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 ---------------------------------------------------------- CompilerOptp ---- 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 -------------------------------------------------------- CompilerOptpic ---- 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 --------------------------------------------------------- CompilerOptpp ---- 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 ----------------------------------------------------- CompilerOptprefix ---- 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 ------------------------------------------------------ CompilerOptprint ---- 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__ ---------------------------------------------------- CompilerOptprofgen ---- 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 .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 ---------------------------------------------------- CompilerOptprofile ---- 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 ---------------------------------------------------------- CompilerOptr ---- 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 ------------------------------------------------------- CompilerOptrupp ---- 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 --------------------------------------------------------- CompilerOptrr ---- 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 ------------------------------------------------------ CompilerOptrrupp ---- 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 ---------------------------------------------------------- CompilerOpts ---- 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 ----------------------------------------------- CompilerOptshowincludes ---- 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 ----------------------------------------------------- CompilerOptstatic ---- 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 ------------------------------------------------------ CompilerOptstrip ---- 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 ---------------------------------------------------- CompilerOptsysroot ---- 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 ---------------------------------------------------------- CompilerOptt ---- 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 ----------------------------------------------------- CompilerOpttarget ---- Compiler Option: -target Set the target platform for cross compilation Syntax -target < platform > Parameters platform The target platform. Recognized values: * android * dos * win32 * win64 * xbox * - can be one of: * android * linux * cygwin * darwin * freebsd * netbsd * openbsd can be one of: * arm * aarch64 * powerpc or ppc * powerpc64 or ppc64 * powerpc64le or ppc64le * x86 * x86_64 Examples: * android-arm * android-x86 * freebsd-x86 * freebsd-x86_64 * linux-arm * linux-aarch64 * linux-x86 * linux-x86_64 * ... * For backwards compatibility, the following values are recognized. They will select the corresponding operating system, together with the compiler's default architecture (same as the host), because these values do not specify an architecture explicitly. * linux * cygwin * darwin * freebsd * netbsd * openbsd * fbc (e.g. FB-linux release) additionally recognizes GNU triplets, for example: * i686-w64-mingw32 * x86_64-w64-mingw32 * i686-pc-linux-gnu * arm-linux-gnueabihf * i386-pc-msdosdjgpp * arm-linux-android * armv7a-linux-android * armv7a-linux-androideabi * armv5te-linux-android * i686-linux-android * x86_64-linux-android * ... Description The -target compiler option can be used to create an executable for a platform which is different from the host on which the source code is being compiled and linked. Appropriate libraries and cross compilation tools (assembler, linker) must be installed for cross compilation to work (also see FB and cross-compiling). If -target is given, the compiler will compile programs more or less as if they were compiled on the given platform. This affects which __FB_*__ operating-system-specific symbol will be pre-defined, the default calling convention, the object and executable file format (e.g. ELF/COFF), the available runtime libraries and functions, etc. With a standalone FB setup such as the FB-dos or FB-win32 releases: * Specifying -target causes the compiler to use the compiler tools in the bin// directory, and target-specific libraries in the lib// directory. For example, -target win32 causes the compiler to compile for Win32 and use tools from bin/win32/ and libraries from lib/win32/. * It is unnecessary (but safe) to specify a -target option that matches the host (for example -target win32 on win32). It does not make a difference to the compilation process. * If -target is not specified, the compiler defaults to compiling for the native system. It will then use the compiler tools and libraries from the bin/ and lib/ directories corresponding to the native system. * -target may be specified as a gnu triplet as an alternate method for specifying the platform target and architecture. For example, -target i586-pc-mdosdjgpp will identify as -arch 586 -target dos and will look for binaries in bin/dos/ and libraries in lib/dos/. The platform string is not prefixed to names of binary tools. With a normal FB setup such as the FB-linux release: * Specifying -target causes the compiler to prefix the < platform>- string to the executable names of binutils and gcc. For example, specifying -target i686-w64-mingw32 causes the compiler to invoke i686-w64-mingw32-ld instead of ld (same for other tools besides the linker). This allows fbc to integrate with binutils/gcc cross-compiler toolchains and matches how cross-compiling tools are typically installed on Linux distributions. * Note that specifying something like -target win32 does not usually make sense here. It causes the compiler to try to use win32-ld which usually does not exist, because binutils/gcc toolchains for cross-compilation to Windows typically have names such as i686-pc-mingw32, not just win32. Thus, it is necessary to specify something like -target i686-pc-mingw32 instead of -target win32. * For backwards compatibility, if the given platform string describes the host and is an FB target name (the values accepted by the -target option with a standalone FB setup) instead of a GNU triplet, then the -target option will be ignored, and the - string will not be prefixed to compiler tools. For example, this allows -target linux to work with the FB-linux release. It will be ignored instead of causing the compiler to try to use linux-ld instead of ld. * If -target is not specified, the compiler defaults to compiling for the native system, and it will invoke binutils/gcc without a target-specific prefix. This allows fbc to integrate with usual Linux (and similar) systems where binutils/gcc for native compilation are installed without any target-specific prefix. * Libraries besides FB's own runtime libraries are located by running gcc -print-file-name=... (or -gcc -print-file-name=...). This allows fbc to use the system and gcc libraries installed on Linux and similar systems without knowing the exact installation directories. When using triplets they generally have the form os -- but can vary a lot between build systems or over time between different versions of a build system or toolchain. fbc also accepts the form < arch>- and is fairly permissive in what will be accepted and typically will not throw an error if at least part of the gnu triplet is recognized For more details, see on page -buildprefix, the paragraph: "Detailed behavior if using -buildprefix, -prefix, -target command line options". Version * Before fbc 1.20.0, android was not recognized as a target * Before fbc 1.10.0, gnu triplets were not allowed in standalone builds, and '-target i386-pc-msdosdjgpp' and similar were not allowed. See also * -arch * -buildprefix * -prefix * Using the Command Line * FB and cross-compiling ---------------------------------------------------------- CompilerOptv ---- 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 -------------------------------------------------------- CompilerOptvec ---- 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 ---------------------------------------------------- CompilerOptversion ---- 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 ---------------------------------------------------------- CompilerOptw ---- 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 --------------------------------------------------------- CompilerOptWa ---- 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 --------------------------------------------------------- CompilerOptWc ---- 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 --------------------------------------------------------- CompilerOptWl ---- 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 ---------------------------------------------------------- CompilerOptx ---- 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 ---------------------------------------------------------- CompilerOptz ---- 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 ------------------------------------------------------- DebuggerRunning ---- 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. -------------------------------------------------------- CompilerErrMsg ---- 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' 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' * 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 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 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 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. --------------------------------------------------------- CompilerTools ---- 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). "" 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/ * /topdir/bin/ * gcc is queried for missing libraries (currently on linux/freebsd only) If cross compiling, fbc looks in the these locations: * /topdir/inc * /topdir/lib/ * /topdir/bin/ * 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). "" 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/ * /prefix/bin/freebasic/ * 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/ * /prefix/bin/freebasic/ * 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/ , or ./bin/ for the standalone version, then use it. * On Linux, if the tool could not be found in prefix/bin/freebasic/< target>, or ./bin/ for the standalone version, fbc tries to invoke it anyway as it may be installed on the system and located on the PATH. "" 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 ------------------------------------------------- CompilerInstallingGcc ---- 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 g cc.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 ============================================================================ FREEBASIC DIALECTS AND QBASIC ----------------------------- ------------------------------------------------------------ CompilerQB ---- 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. ---------------------------------------------------------------- LangQB ---- 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. ------------------------------------------------------ CompilerDialects ---- 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. ============================================================================ FAQS ---- ----------------------------------------------------------- CompilerFAQ ---- 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 ---------------------------------------------------------- FaqPggfxlib2 ---- 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 ------------------------------------------------------------ FaqPgrtlib ---- 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 ------------------------------------------------------------- FaqPgxbox ---- Frequently Asked Questions FreeBASIC on Xbox general questions - Can FreeBASIC really make Xbox games? - How was the FreeBASIC Xbox port made? - How about a port for Xbox 360? - How about a port for PlayStation or another console? - Why don't you use an emulator until a developer gets a modded Xbox? - Why don't you use the Microsoft XDK? - Why don't you use the Microsoft debugger to fix it? - Isn't this illegal? Can't Microsoft sue you? Getting Started with FreeBASIC on Xbox questions - What do I need to compile Xbox games with FreeBASIC? - How would you get input? - Does it only run on certain Xboxes? - Is another language (eg C or ASM) needed for the job? - Do you need a special lib? - Can you use premade functions (inkey, line etc)? - What else should I know? FreeBASIC on Xbox general questions Can FreeBASIC really make Xbox games? In theory, yes. A copy of FreeBASIC 0.13 was ported to the Xbox in July 2005, and produced working executables. However, changes to the compiler for the 0.14 release broke compatiblity. The Xbox port is currently in zombie mode; nobody in the project team has the console at the moment - the original port was done by SJ Zero, but it got broken with the runtime library modifications done in v0.14. The port is on hold until the GCC backend port is complete, because it is believed that this port will fix the Xbox port. How was the FreeBASIC Xbox port made? FreeBASIC for Xbox is possible because of the efforts of Open Source developers who created OpenXDK, the legal software development kit for Xbox. OpenXDK is created for a unixish environment, which is quite compatible with the FreeBASIC source. The port was created by forcing the FreeBASIC runtime library to use the OpenXDK version of Glibc instead of the mingw32 version. When compiled with the correct flags, this creates what looks like a standard EXE file. CXBE then strips the Windows PE header on this executable file and replaces it with an Xbox header. In effect, all the port really does is change the runtime library and link in a certain way to allow the CXBE utility to create an Xbox executable. How about a port for Xbox 360? The Xbox is an Intel Pentium 3 running a derivative of the NVIDIA nForce chipset, with an NVIDIA video chip and an NVIDIA SoundStorm sound card. This is why the Xbox port was possible and relatively straightforward to do. The Xbox 360, on the other hand, uses an alien CPU, and similarly alien hardware. FreeBASIC cannot presently be made to produce executables for the Xbox 360. Another problem is the lack of an equivilent to OpenXDK for the Xbox 360. This would force any port to use the Xbox 360 XDK, a copyrighted piece of software created by Microsoft. This would be illegal, immoral, and would put FreeBASIC in legal jeparody. Therefore, a port to the Xbox 360 is to be considered impossible at this time. How about a port for PlayStation or another console? The Xbox is an Intel Pentium 3 running a derivative of the NVIDIA nForce chipset, with an NVIDIA video chip and an NVIDIA SoundStorm sound card. This is why the Xbox port was possible and relatively straightforward to do. The PlayStation, on the other hand, uses a RISC chip, which FreeBASIC cannot currently produce code for. Almost all consoles utilize non x86 processors, stopping development using FreeBASIC from being possible. Another problem is the lack of an equivilent to OpenXDK for many consoles. This would force any port to use the commercial software development kit, a copyrighted piece of software created by the console manufacturer. This would be illegal, immoral, and would put FreeBASIC in legal jeparody. Therefore, a port to other consoles are to be considered impossible at this time. However, many ports to consoles and other platforms with legally available development kits will be possible when the GCC backend port is complete. Why don't you use an emulator until a developer gets a modded Xbox? No known Xbox emulator is capable of running FreeBASIC code. A legitimate hardware console is required to run the programs made. This makes an emulator completely useless for development. Why don't you use the Microsoft XDK? There are two main reasons not to use the Microsoft XDK. 1) Microsoft's XDK is a piece of copyrighted software, and utilizing it would be illegal and immoral, putting FreeBASIC at risk of legal action. Furthermore, no member of the FreeBASIC team has ever had any access to the Microsoft XDK, to prevent "tainting" FreeBASIC legally. 2) OpenXDK is developed around gcc and UNIX-style systems such as MinGW or Cygwin. This means that it can be integrated into FreeBASIC with very little effort. Microsoft's XDK, on the other hand, is developed around Microsoft based compilers, and thus would not easily integate into the source code of FreeBASIC. NOTE: PROTECTION OF MICROSOFT'S COPYRIGHT, AND BY PROXY OF FREEBASIC, IS OF PRIMARY IMPORTANCE IN THIS PROJECT. WE DO NOT WANT HELP FROM ANYONE WITH THE XDK, NOR DO WE WANT HELP FROM ANYONE WITH A DEBUGGER XBOX. ANY ATTEMPT TO OFFER THE XDK OR XDK RELATED HELP SHALL BE FORWARDED TO THE PROPER LAW ENFORCEMENT AGENCIES. Why don't you use the Microsoft debugger to fix it? There are two very good reasons not to use the Microsoft debugger. 1) Microsoft's XDK is a piece of copyrighted software, and utilizing it would be illegal and immoral, putting FreeBASIC at risk of legal action. Furthermore, no member of the FreeBASIC team has ever had any access to the Microsoft XDK, to prevent "tainting" FreeBASIC legally. 2) Microsoft's debugger requires a specially modified Xbox which neither SJ Zero nor any development team member has, and frankly, nobody who has worked on the port believes the debugger would work with FreeBASIC executables -- just as Microsoft's debugger can't read FreeBASIC debugger files, we doubt the Xbox debugger could read FreeBASIC debugger files. Regardless, point #1 trumps any attempt. NOTE: PROTECTION OF MICROSOFT'S COPYRIGHT, AND BY PROXY OF FREEBASIC, IS OF PRIMARY IMPORTANCE IN THIS PROJECT. WE DO NOT WANT HELP FROM ANYONE WITH THE XDK, NOR DO WE WANT HELP FROM ANYONE WITH A DEBUGGER XBOX. ANY ATTEMPT TO OFFER THE XDK OR XDK RELATED HELP SHALL BE FORWARDED TO THE PROPER LAW ENFORCEMENT AGENCIES. Isn't this illegal? Can't Microsoft sue you? Copyright is important for the protection of both commercial firms like Microsoft, and for small projects such as FreeBASIC. Without copyright, neither could enforce any rights over the code (In our case, such as the GPL). Generally speaking, it is copyright issues which are most often the cause of problems for open source projects attempting to do things like this. Because the FreeBASIC Xbox port is created using software tools whose legality has already been established, themselves often derived from other sources whose legality has been established, FreeBASIC for Xbox is not illegal. Careful care has been taken to protect FreeBASIC from using any Microsoft copyrighted code, and diligence is and will be followed to prevent access to copyrighted code. Getting Started with FreeBASIC on Xbox questions What do I need to compile Xbox games with FreeBASIC? The port isn't currently working, but when it is ready, you will only need a copy of FreeBASIC for Xbox. How would you get input? Initially, input will be acquired through SDL, as a gfxlib port is not yet complete. One of the developers is working on a generic SDL version of gfxlib, however, and it will provide full gfxlib functionality to the Xbox port. Does it only run on certain Xboxes? FreeBASIC for Xbox executables will only run on modded Xboxes. However, modding an Xbox is often as simple as loading a savegame in a certain game. More information is available on the Xbox-Linux website. Is another language (eg C or ASM) needed for the job? No. FreeBASIC for Xbox is the only thing needed. Do you need a special lib? No. FreeBASIC for Xbox will come with all supported libraries. Can you use premade functions (inkey, line etc)? Currently, input and output commands such as inkey and line aren't available, but all other functions, including file I/O, are. One of the developers is working on a generic SDL version of gfxlib, however, and if it functions, it will provide full gfxlib functionality to the xbox port. What else should I know? Executables created by FreeBASIC for Xbox are free of copyrighted Microsoft code, making them legal for distribution. Windows and Linux source files which are designed to use SDL and rtlib will be capable of compiling for Xbox out of the box. While the Xbox does have keyboard support through the gamepad ports (proprietary USB connection), the input scheme will have to be altered to account for a gamepad. ---------------------------------------------------------------- FaqDOS ---- 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 bef ore 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 ------------------------------------------------------------ FaqPgWin32 ---- 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 \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 Compiler FAQ FB Runtime Library FAQ ------------------------------------------------------------ FaqPgLinux ---- 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 Compiler FAQ FB Runtime Library FAQ ============================================================================ MISCELLANEOUS ------------- --------------------------------------------------------- ObsoletedKwds ---- 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. --------------------------------------------------------- GlossaryIndex ---- Glossary Brief definitions and explanations for words and phrases used in the FreeBAS IC 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 ------------------------------------------------------------- CatPgMisc ---- 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() --------------------------------------------------------- ProPgCruntime ---- 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 -------------------------------------------------------------- CptAscii ---- 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. 0 0 16 10 32 20 48 30 0 64 40 @ 80 50 P 96 60 `112 70 p 1 1 17 11 33 21 ! 49 31 1 65 41 A 81 51 Q 97 61 a113 71 q 2 2 18 12 34 22 " 50 32 2 66 42 B 82 52 R 98 62 b114 72 r 3 3 19 13 35 23 # 51 33 3 67 43 C 83 53 S 99 63 c115 73 s 4 4 20 14 36 24 $ 52 34 4 68 44 D 84 54 T100 64 d116 74 t 5 5 21 15 37 25 % 53 35 5 69 45 E 85 55 U101 65 e117 75 u 6 6 22 16 38 26 & 54 36 6 70 46 F 86 56 V102 66 f118 76 v 7 7 23 17 39 27 ' 55 37 7 71 47 G 87 57 W103 67 g119 77 w 8 8 24 18 40 28 ( 56 38 8 72 48 H 88 58 X104 68 h120 78 x 9 9 25 19 41 29 ) 57 39 9 73 49 I 89 59 Y105 69 i121 79 y 10 A 26 1A 42 2A * 58 3A : 74 4A J 90 5A Z106 6A j122 7A z 11 B 27 1B 43 2B + 59 3B ; 75 4B K 91 5B [107 6B k123 7B { 12 C 28 1C 44 2C , 60 3C < 76 4C L 92 5C \108 6C l124 7C | 13 D 29 1D 45 2D - 61 3D = 77 4D M 93 5D ]109 6D m125 7D } 14 E 30 1E 46 2E . 62 3E > 78 4E N 94 5E ^110 6E n126 7E ~ 15 F 31 1F 47 2F / 63 3F ? 79 4F O 95 5F _111 6F o127 7F  128 80 144 90 160 A0 176 B0 192 C0 208 D0 г224 E0 240 F0 129 81 145 91 161 A1 177 B1 193 C1 209 D1 ѳ225 E1 241 F1 130 82 146 92 162 A2 178 B2 194 C2 ³210 D2 ҳ226 E2 242 F2 131 83 147 93 163 A3 179 B3 195 C3 ó211 D3 ӳ227 E3 243 F3 132 84 148 94 164 A4 180 B4 196 C4 ij212 D4 Գ228 E4 244 F4 133 85 149 95 165 A5 181 B5 197 C5 ų213 D5 ճ229 E5 245 F5 134 86 150 96 166 A6 182 B6 198 C6 Ƴ214 D6 ֳ230 E6 246 F6 135 87 151 97 167 A7 183 B7 199 C7 dz215 D7 ׳231 E7 247 F7 136 88 152 98 168 A8 184 B8 200 C8 ȳ216 D8 س232 E8 248 F8 137 89 153 99 169 A9 185 B9 201 C9 ɳ217 D9 ٳ233 E9 249 F9 138 8A 154 9A 170 AA 186 BA 202 CA ʳ218 DA ڳ234 EA 250 FA 139 8B 155 9B 171 AB 187 BB 203 CB ˳219 DB ۳235 EB 251 FB 140 8C 156 9C 172 AC 188 BC 204 CC ̳220 DC ܳ236 EC 252 FC 141 8D 157 9D 173 AD 189 BD 205 CD ͳ221 DD ݳ237 ED 253 FD 142 8E 158 9E 174 AE 190 BE 206 CE γ222 DE ޳238 EE 254 FE 143 8F 159 9F 175 AF 191 BF 207 CF ϳ223 DF ߳239 EF 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. ------------------------------------------------------ TblRuntimeErrors ---- 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 -------------------------------------------------------- TblComparisonC ---- 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 ; : 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 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.hfoo.bas, foo.bi typical compiler command to create an executable gcc foo.c -o foofbc foo.bas Last reviewed by MrSwiss on April 6, 2018 Note: changed Integer to Long (where needed) --------------------------------------------------- TblComparisonCTypes ---- Comparison of integer data types: FreeBASIC vs. C/C++ (using GCC) +-------------------+------+------------------+-------------+--------+-----------+-----------+ | | C int| C long long [int]| C long [int]| FB Long| FB LongInt| FB Integer| | 32bit win32 | 32 | 64 | 32 (ILP32) | 32 | 64 | 32 | | 32bit linux-x86 | 32 | 64 | 32 (ILP32) | 32 | 64 | 32 | | 64bit win64 | 32 | 64 | 32 (LLP64) | 32 | 64 | 64 | | 64bit linux-x86_64| 32 | 64 | 64 (LP64) | 32 | 64 | 64 | +-------------------+------+------------------+-------------+--------+-----------+-----------+ Some public symbols (like namespaces, and user defined type member procedures) will have data type information encoded in to the public name (also known as name mangling or name decoration). Specific mapping of data types between FreeBASIC and gcc is required when linking fbc compiled object modules with gcc compiled object modules. +-------------+-------------------+-------------------------+-----+ |Target | fbc data type | c data type | bits| | all | byte | signed char | 8 | | all | ubyte | unsigned char | 8 | | all | byte alias "char" | char | 8 | | all | ubyte alias "char"| char | 8 | | all | short | [signed] short [int] | 16 | | all | ushort | unsigned short [int] | 16 | | all | long | [signed] int | 32 | | all | ulong | unsigned int | 32 | | x86 | integer | [signed] long [int] | 32 | | x86 | uinteger | unsigned long [int] | 32 | | win64 | long alias "long" | [signed] long [int] | 32 | | win64 | ulong alias "long"| unsigned long [int] | 32 | | win64 | integer | INTEGER | 64 | | win64 | uinteger | UINTEGER | 64 | | linux-x86_64| integer | [signed] long [int] | 64 | | linux-x86_64| uinteger | unsigned long [int] | 64 | | all | longint | [signed] long long [int]| 64 | | all | ulongint | unsigned long long [int]| 64 | +-------------+-------------------+-------------------------+-----+ See also * Creating FB bindings for C libraries - How to translate C data types to FB ============================================================================ HACKING ON FREEBASIC -------------------- ---------------------------------------------------------------- DevToc ---- Information for hacking on FreeBASIC This area of the Wiki is for documenting everything about the compiler and the runtime libraries. It is, however, incomplete. If you find that information provided here does not match what the source is doing then please update the relevant pages here. New pages and articles may be added freely, provided they help understanding what's going on inside FB. Developing FreeBASIC Itself Compiling a Development Version of FreeBASIC Getting the source code Compiling FB for DOS Compiling FB on Linux Compiling FB on Windows Compiling FB with Emscripten Getting source code updates and recompiling FB Debugging FB FB build configuration options Known problems when compiling FB GCC toolchain choice Running the FreeBASIC test suite Normal vs. Standalone Glossary Notes on the creation of FB releases FB and cross-compiling Bootstrapping/cross-compiling fbc Creating FB bindings for C libraries C Header Translation Tutorial Header Style Guidelines External Libraries Index (header status) Compiler internals Quick overview of all modules The objinfo feature Memory management Lexer & preprocessor Parser & compiler (fb, parser, symb, rtl) Purpose Top level parsing process Symbols Representation of data types Arrays SELECT CASE Profiling with fb's profiler Profiling for gmon/gprof Structure packing/field alignment Run-time (rtlib) and Graphics (gfxlib2) Libraries Keyboard input: inkey(), multikey(), etc. Overview of drivers (backends) Pixel formats