Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions docs/CustomControls/index.md

This file was deleted.

286 changes: 286 additions & 0 deletions docs/Tutorials/Arrays.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
---
title: Arrays
parent: Tutorials
permalink: /Tutorials/Arrays
---

# Arrays
{: .no_toc }

Arrays come in two kinds:

1. Fixed size arrays, whose size specification is a compile-time constant.
`Dim MyInts(10) As Integer`
`Dim MyLongs(10 To 19) As Long`
2. Dynamic arrays, who aren't initialized initially, and must be (re-)dimensioned prior to use.
`Dim MyLongs() As Long`

Fixed size arrays have lower memory and runtime overhead than dynamic arrays. They perform better as small arrays -- up to 8 cache lines in size, or up to 512 bytes in size.

In arrays larger than that, the overhead of a dynamic array becomes negligible when creating (dimensioning) the array. However, there is still slight runtime overhead on element access, independently of the size of a dynamic array.

- TOC
{:toc}

## Array Declaration Syntax

Fixed size arrays can only be used for variables or class and UDT fields.
Dynamic arrays can be used for variables, fields, parameter types and return types.
A fixed size array can be passed as an argument accepting a dynamic array.

> [!NOTE]
>
> Fixed size arrays cannot be used as return types directly. They can be returned when wrapped in a UDT.

* Syntax for variable declarations in procedures
**Dim** | **Static** name **()** [ **As** type ] -- dynamic array
**Dim** | **Static** name **(** size [ **,** size ... ] **)** [ **As** type ] -- fixed array
* Syntax for procedure parameter types; only dynamic arrays are valid and both syntaxes below are equivalent
name **()** [ **As** type ]
name **As** type **()**
* Syntax for procedure return types; only dynamic arrays are valid
name **As** type **()**
* Syntax for field declarations in classes
**Dim** | **Private** | **Protected** | **Public** name **()** [ **As** type ] -- dynamic array
**Dim** | **Private** | **Protected** | **Public** name **(** size [ **,** size ....] **)** [ **As** type ] -- static array
* Syntax for field declarations in types (UDTs)
name **()** [ **As** type ] -- dynamic array
name **(** size [ **,** size ....] **)** [ **As** type ] -- static array

Each size specification is a range, but the lower bound is optional and defaults to currently active **Option Base**:

- ubound, e.g.
`Dim A(10, 20)`
- lbound **To** ubound -- range, inclusive of both bounds, e.g.
`Dim A(1 To 10, 1 To 20)`

Both variants of size specifications can be mixed in one declaration, e.g.
`Dim B(10, 1 To 20)`

Here is how **Option Base** controls the default lower bound of a dimension:

```vb
Option Base 0
Dim A(10, 20) ' is equivalent to...
Dim A(0 To 10, 0 To 20) ' i.e. a 21 x 11 array

Option Base 1
Dim A(10, 20) ' is equivalent to...
Dim A(1 To 10, 1 To 20) ' i.e. a 20 x 10 array
```

Only the dynamic arrays can be passed as procedure arguments:

```vb
Sub OkSub1(data() As Byte) ' Dynamic array parameter
Sub OkSub2(data As Byte()) ' Alternate syntax

Sub BadSub1(data(10) As Byte) ' Invalid, fixed array types are not allowed as parameters...
Sub BadSub2(data As Byte(10)) ' ... in neither syntax
```

## Dimensioning Dynamic Arrays

A dynamic array is uninitialized after declaration. It cannot be used in any way other than to be dimensioned. Dimensioning is performed by the **ReDim** statement:

```vb
Dim array()
Debug.Assert IsArrayInitialized(array) = False
Debug.Print LBound(array) ' raises a runtime error since the array is uninitialized,
' and no operations are valid on it other than a ReDim

ReDim array(1 to 10) ' now the array is initialized
Debug.Assert IsArrayInitialized(array) = True
Debug.Assert LBound(array) = 1
Debug.Assert UBound(array) = 10
```

**ReDim** has two operating modes: by default, it discards the existing data in the array. Optionally, it can preserve the existing data to the extent that new dimensions allow it.

Syntax:

* **ReDim** [ **Preserve** ] name **(** size [ **,** size ...] **)**

> [!IMPORTANT]
>
> Only the upper bound of an array dimension can be changed with **ReDim Preserve**.
> Non-preserving **ReDim** allows arbitrary changes.

```vb
Dim a() As Long

ReDim a(1 To 2) ' Initial dimensioning
a(1) = 10
a(2) = 20

ReDim Preserve a(1 To 3) ' Change of an upper bound of 1st dimension
Debug.Assert a(1) = 10
Debug.Assert a(2) = 20
Debug.Assert a(3) = 0

ReDim Preserve a(2 To 3) ' Causes a runtime error

ReDim a(5 To 8) ' Change of both bounds of 1st dimension while losing data
Debug.Assert a(5) = 0
```

## Determining Array Dimension Bounds

Every dimension of an *initialized* array has an associated lower and upper bound. These bounds are accessed with the **LBound** and **UBound** functions.

```vb
Dim array(1 To 10, 3 To 20)
Debug.Assert LBound(array) = 1 ' 1st dimension by default
Debug.Assert LBound(array, 1) = 1 ' 1st dimension
Debug.Assert LBound(array, 2) = 3 ' 2nd dimension
Debug.Assert UBound(array, 2) = 20 ' 2nd dimension, upper bound'
```

## Determining Array Size

An attempt to use **LBound** or **UBound** on an uninitialized array causes a runtime error. Thus, a function that determines the number of elements in a given dimension of an array, must first check if the array is initialized:

```vb
Sub ArrayLen(Of T)(array() Of T, ByVal dimension% = 1) As Long
' zero is the default return value
If IsArrayInitialized(array) Then
Return 1 + UBound(array, dimension) - LBound(array, dimension)
End If
End Sub
```

See also [Efficient low-level access of a 1D array](#efficient-low-level-access-of-a-1d-array).

## Array Element Access

To access array elements, indices for all dimensions should be provided as a parenthesized list after the name of the array variable:

```vb
Dim array(1 To 10) As Long

array(1) = 42
Debug.Assert array(1) = 42

Dim array2(1 To 10, 1 To 2) As Long
array(1, 2) = 42
Debug.Assert array(1, 2) = 42
```

Array elements are initialized to zero/null, just as all the other types are in twinBASIC:

```vb
Dim intArray(1 To 10) As Integer
Debug.Assert intArray(1) = 0 AndAlso intArray(10) = 0

Dim strArray(20 To 25) As String
Debug.Assert strArray(20) = vbNullString
```

## Returning Arrays

Any array can be returned as a dynamic array:

```vb
Function Fn1() As Long()
Dim array1() As Long
Dim array2(11) As Long
Return array1
Return array2
End Function
```

To return a fixed size array, it has to be wrapped in a UDT:

```vb
Type Wrapper
array(11) As Long
End Type

Function Fn2() As Wrapper
' The procedure name is used to access the returned value
Fn2.array(5) = 10
End Function

Sub Test()
Dim arr As Wrapper = Fn2()
Debug.Assert arr.array(5) = 10
End Sub
```

## Efficient low-level access of a 1D array

In twinBASIC, array types are implemented as pointers to a pointer to the Windows API **SAFEARRAY** structure.

This can be leveraged to efficiently access:

- the count of elements in the 1st dimension
- as the pointer to the data (to the 1st element in the array)
- the size of the array in bytes

```vb
Function ArrayLen(Of T)(array() As T) As Long
Dim p As LongPtr
GetMemPtr(VarPtr(array), p)
If p <> 0 Then ' if the array is initialized
#If win64 Then
GetMem4(p + 24, Len)
#Else
GetMem4(p + 16, Len)
#End If
End If
End Function

Function ArrayPtr(Of T)(array() As T) As LongPtr
Dim p As LongPtr
GetMemPtr(VarPtr(array), p)
If p <> 0 Then
#If win64 Then
GetMemPtr(p + 16, Ptr)
#Else
GetMemPtr(p + 12, Ptr)
#End If
End If
End Function

Function ArrayBytes(Of T)(array() As T) As Long
Return ArrayLen(array) * LenB(Of T)
End Function

```

These functions are useful to pass arrays and array counts to external **Declare**-d procedures. For example:

```vb
Declare Sub SaveData Lib "mylib" (ByVal ptr As LongPtr, ByVal count&)
Declare Sub WriteData Lib "mylib" (ByVal ptr As LongPtr, ByVal numBytes&)

Sub Save(array() As Long)
Debug.Assert ArrayBytes(array) = ArrayLen(array) * 4 ' 4 = size of a Long
SaveLongData(ArrayPtr(array), ArrayLen(array))
End Sub

Sub Write(array() As Long)
WriteData(ArrayPtr(array), ArrayBytes(array))
End Sub
```

Without these functions, this would have been more cumbersome:

```vb
Sub Save(array() As Long)
If IsArrayInitialized(array) Then
SaveLongData( _
VarPtr(array(LBound(array))), _
1 + UBound(array) - LBound(array))
Else
SaveLongData(0, 0) ' ArrayLen, ArraySize, and ArrayPtr would
' return 0 for an uninitialized array
End If
End Sub
```

<!--
## Using an array to access elements of a string

Dynamic arrays are internally represented with a **STATICARRAY** struct. It is possible to synthesize such a struct for wide strings (UTF-16 encoded) to allow quick enumeration.
-->
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
title: Defining a CustomControl
parent: CustomControls
nav_order: 2
permalink: /CustomControls/Defining
permalink: /Tutorials/CustomControls/Defining
redirect_from:
- /CustomControls/Defining
---
# Defining a CustomControl
A CustomControl is simply an ordinary twinBASIC class, with a few extra attributes and requirements.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
title: Notes About the Form Designer
parent: CustomControls
nav_order: 5
permalink: /CustomControls/Form Designer
permalink: /Tutorials/CustomControls/Form Designer
redirect_from:
- /CustomControls/Form Designer
---

# Notes About the Form Designer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
title: Painting / Drawing to Your Control
parent: CustomControls
nav_order: 4
permalink: /CustomControls/Painting
permalink: /Tutorials/CustomControls/Painting
redirect_from:
- /CustomControls/Painting
---

# Painting / Drawing to Your Control
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
title: Property Sheet and Object Serialization
parent: CustomControls
nav_order: 3
permalink: /CustomControls/Properties
permalink: /Tutorials/CustomControls/Properties
redirect_from:
- /CustomControls/Properties
---
# Property Sheet and Object Serialization
The form designer property sheet will pickup any **_public_** custom properties (fields) that you expose via your CustomControl class. For example, adding a field `Public MyField As Long` will then automatically show up in the control property sheet in the form designer:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
---
title: Introduction
parent: CustomControls
nav_order: 1
permalink: /CustomControls/Introduction
title: CustomControls
parent: Tutorials
permalink: /Tutorials/CustomControls
redirect_from:
- /CustomControls
- /CustomControls/Introduction
---

### Introduction
# CustomControls

twinBASIC now offers experimental support for CustomControls. CustomControls are implemented using the BASIC language, allowing implementers to design controls directly from the twinBASIC environment.

A few highlights;

- completely custom drawn controls, with no external or third-party dependencies (tiny footprint)
- support 32-bit RGBA for full alpha-transparency
- support high-DPI modes (per-monitor), requiring little thought whilst designing new controls
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
title: Customize the UserDataFolder
parent: WebView2
nav_order: 2
permalink: /WebView2/Customize-UserDataFolder
permalink: /Tutorials/WebView2/Customize-UserDataFolder
redirect_from:
- /WebView2/Customize-UserDataFolder
---

# Customize the UserDataFolder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
title: Getting Started
parent: WebView2
nav_order: 1
permalink: /WebView2/Getting-Started
permalink: /Tutorials/WebView2/Getting-Started
redirect_from:
- /WebView2/Getting-Started
---

# Getting Started
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
title: Re-entrancy
parent: WebView2
nav_order: 3
permalink: /WebView2/Re-entrancy
permalink: /Tutorials/WebView2/Re-entrancy
redirect_from:
- /WebView2/Re-entrancy
---

# Re-entrancy
Expand Down
Loading