Skip to content

Commit adb1358

Browse files
authored
Merge pull request #67 from KubaO/staging
Improve Tutorials
2 parents 41224a6 + 0b90625 commit adb1358

36 files changed

+337
-38
lines changed

docs/CustomControls/index.md

Lines changed: 0 additions & 7 deletions
This file was deleted.

docs/Tutorials/Arrays.md

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
---
2+
title: Arrays
3+
parent: Tutorials
4+
permalink: /Tutorials/Arrays
5+
---
6+
7+
# Arrays
8+
{: .no_toc }
9+
10+
Arrays come in two kinds:
11+
12+
1. Fixed size arrays, whose size specification is a compile-time constant.
13+
`Dim MyInts(10) As Integer`
14+
`Dim MyLongs(10 To 19) As Long`
15+
2. Dynamic arrays, who aren't initialized initially, and must be (re-)dimensioned prior to use.
16+
`Dim MyLongs() As Long`
17+
18+
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.
19+
20+
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.
21+
22+
- TOC
23+
{:toc}
24+
25+
## Array Declaration Syntax
26+
27+
Fixed size arrays can only be used for variables or class and UDT fields.
28+
Dynamic arrays can be used for variables, fields, parameter types and return types.
29+
A fixed size array can be passed as an argument accepting a dynamic array.
30+
31+
> [!NOTE]
32+
>
33+
> Fixed size arrays cannot be used as return types directly. They can be returned when wrapped in a UDT.
34+
35+
* Syntax for variable declarations in procedures
36+
**Dim** | **Static** name **()** [ **As** type ] -- dynamic array
37+
**Dim** | **Static** name **(** size [ **,** size ... ] **)** [ **As** type ] -- fixed array
38+
* Syntax for procedure parameter types; only dynamic arrays are valid and both syntaxes below are equivalent
39+
name **()** [ **As** type ]
40+
name **As** type **()**
41+
* Syntax for procedure return types; only dynamic arrays are valid
42+
name **As** type **()**
43+
* Syntax for field declarations in classes
44+
**Dim** | **Private** | **Protected** | **Public** name **()** [ **As** type ] -- dynamic array
45+
**Dim** | **Private** | **Protected** | **Public** name **(** size [ **,** size ....] **)** [ **As** type ] -- static array
46+
* Syntax for field declarations in types (UDTs)
47+
name **()** [ **As** type ] -- dynamic array
48+
name **(** size [ **,** size ....] **)** [ **As** type ] -- static array
49+
50+
Each size specification is a range, but the lower bound is optional and defaults to currently active **Option Base**:
51+
52+
- ubound, e.g.
53+
`Dim A(10, 20)`
54+
- lbound **To** ubound -- range, inclusive of both bounds, e.g.
55+
`Dim A(1 To 10, 1 To 20)`
56+
57+
Both variants of size specifications can be mixed in one declaration, e.g.
58+
`Dim B(10, 1 To 20)`
59+
60+
Here is how **Option Base** controls the default lower bound of a dimension:
61+
62+
```vb
63+
Option Base 0
64+
Dim A(10, 20) ' is equivalent to...
65+
Dim A(0 To 10, 0 To 20) ' i.e. a 21 x 11 array
66+
67+
Option Base 1
68+
Dim A(10, 20) ' is equivalent to...
69+
Dim A(1 To 10, 1 To 20) ' i.e. a 20 x 10 array
70+
```
71+
72+
Only the dynamic arrays can be passed as procedure arguments:
73+
74+
```vb
75+
Sub OkSub1(data() As Byte) ' Dynamic array parameter
76+
Sub OkSub2(data As Byte()) ' Alternate syntax
77+
78+
Sub BadSub1(data(10) As Byte) ' Invalid, fixed array types are not allowed as parameters...
79+
Sub BadSub2(data As Byte(10)) ' ... in neither syntax
80+
```
81+
82+
## Dimensioning Dynamic Arrays
83+
84+
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:
85+
86+
```vb
87+
Dim array()
88+
Debug.Assert IsArrayInitialized(array) = False
89+
Debug.Print LBound(array) ' raises a runtime error since the array is uninitialized,
90+
' and no operations are valid on it other than a ReDim
91+
92+
ReDim array(1 to 10) ' now the array is initialized
93+
Debug.Assert IsArrayInitialized(array) = True
94+
Debug.Assert LBound(array) = 1
95+
Debug.Assert UBound(array) = 10
96+
```
97+
98+
**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.
99+
100+
Syntax:
101+
102+
* **ReDim** [ **Preserve** ] name **(** size [ **,** size ...] **)**
103+
104+
> [!IMPORTANT]
105+
>
106+
> Only the upper bound of an array dimension can be changed with **ReDim Preserve**.
107+
> Non-preserving **ReDim** allows arbitrary changes.
108+
109+
```vb
110+
Dim a() As Long
111+
112+
ReDim a(1 To 2) ' Initial dimensioning
113+
a(1) = 10
114+
a(2) = 20
115+
116+
ReDim Preserve a(1 To 3) ' Change of an upper bound of 1st dimension
117+
Debug.Assert a(1) = 10
118+
Debug.Assert a(2) = 20
119+
Debug.Assert a(3) = 0
120+
121+
ReDim Preserve a(2 To 3) ' Causes a runtime error
122+
123+
ReDim a(5 To 8) ' Change of both bounds of 1st dimension while losing data
124+
Debug.Assert a(5) = 0
125+
```
126+
127+
## Determining Array Dimension Bounds
128+
129+
Every dimension of an *initialized* array has an associated lower and upper bound. These bounds are accessed with the **LBound** and **UBound** functions.
130+
131+
```vb
132+
Dim array(1 To 10, 3 To 20)
133+
Debug.Assert LBound(array) = 1 ' 1st dimension by default
134+
Debug.Assert LBound(array, 1) = 1 ' 1st dimension
135+
Debug.Assert LBound(array, 2) = 3 ' 2nd dimension
136+
Debug.Assert UBound(array, 2) = 20 ' 2nd dimension, upper bound'
137+
```
138+
139+
## Determining Array Size
140+
141+
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:
142+
143+
```vb
144+
Sub ArrayLen(Of T)(array() Of T, ByVal dimension% = 1) As Long
145+
' zero is the default return value
146+
If IsArrayInitialized(array) Then
147+
Return 1 + UBound(array, dimension) - LBound(array, dimension)
148+
End If
149+
End Sub
150+
```
151+
152+
See also [Efficient low-level access of a 1D array](#efficient-low-level-access-of-a-1d-array).
153+
154+
## Array Element Access
155+
156+
To access array elements, indices for all dimensions should be provided as a parenthesized list after the name of the array variable:
157+
158+
```vb
159+
Dim array(1 To 10) As Long
160+
161+
array(1) = 42
162+
Debug.Assert array(1) = 42
163+
164+
Dim array2(1 To 10, 1 To 2) As Long
165+
array(1, 2) = 42
166+
Debug.Assert array(1, 2) = 42
167+
```
168+
169+
Array elements are initialized to zero/null, just as all the other types are in twinBASIC:
170+
171+
```vb
172+
Dim intArray(1 To 10) As Integer
173+
Debug.Assert intArray(1) = 0 AndAlso intArray(10) = 0
174+
175+
Dim strArray(20 To 25) As String
176+
Debug.Assert strArray(20) = vbNullString
177+
```
178+
179+
## Returning Arrays
180+
181+
Any array can be returned as a dynamic array:
182+
183+
```vb
184+
Function Fn1() As Long()
185+
Dim array1() As Long
186+
Dim array2(11) As Long
187+
Return array1
188+
Return array2
189+
End Function
190+
```
191+
192+
To return a fixed size array, it has to be wrapped in a UDT:
193+
194+
```vb
195+
Type Wrapper
196+
array(11) As Long
197+
End Type
198+
199+
Function Fn2() As Wrapper
200+
' The procedure name is used to access the returned value
201+
Fn2.array(5) = 10
202+
End Function
203+
204+
Sub Test()
205+
Dim arr As Wrapper = Fn2()
206+
Debug.Assert arr.array(5) = 10
207+
End Sub
208+
```
209+
210+
## Efficient low-level access of a 1D array
211+
212+
In twinBASIC, array types are implemented as pointers to a pointer to the Windows API **SAFEARRAY** structure.
213+
214+
This can be leveraged to efficiently access:
215+
216+
- the count of elements in the 1st dimension
217+
- as the pointer to the data (to the 1st element in the array)
218+
- the size of the array in bytes
219+
220+
```vb
221+
Function ArrayLen(Of T)(array() As T) As Long
222+
Dim p As LongPtr
223+
GetMemPtr(VarPtr(array), p)
224+
If p <> 0 Then ' if the array is initialized
225+
#If win64 Then
226+
GetMem4(p + 24, Len)
227+
#Else
228+
GetMem4(p + 16, Len)
229+
#End If
230+
End If
231+
End Function
232+
233+
Function ArrayPtr(Of T)(array() As T) As LongPtr
234+
Dim p As LongPtr
235+
GetMemPtr(VarPtr(array), p)
236+
If p <> 0 Then
237+
#If win64 Then
238+
GetMemPtr(p + 16, Ptr)
239+
#Else
240+
GetMemPtr(p + 12, Ptr)
241+
#End If
242+
End If
243+
End Function
244+
245+
Function ArrayBytes(Of T)(array() As T) As Long
246+
Return ArrayLen(array) * LenB(Of T)
247+
End Function
248+
249+
```
250+
251+
These functions are useful to pass arrays and array counts to external **Declare**-d procedures. For example:
252+
253+
```vb
254+
Declare Sub SaveData Lib "mylib" (ByVal ptr As LongPtr, ByVal count&)
255+
Declare Sub WriteData Lib "mylib" (ByVal ptr As LongPtr, ByVal numBytes&)
256+
257+
Sub Save(array() As Long)
258+
Debug.Assert ArrayBytes(array) = ArrayLen(array) * 4 ' 4 = size of a Long
259+
SaveLongData(ArrayPtr(array), ArrayLen(array))
260+
End Sub
261+
262+
Sub Write(array() As Long)
263+
WriteData(ArrayPtr(array), ArrayBytes(array))
264+
End Sub
265+
```
266+
267+
Without these functions, this would have been more cumbersome:
268+
269+
```vb
270+
Sub Save(array() As Long)
271+
If IsArrayInitialized(array) Then
272+
SaveLongData( _
273+
VarPtr(array(LBound(array))), _
274+
1 + UBound(array) - LBound(array))
275+
Else
276+
SaveLongData(0, 0) ' ArrayLen, ArraySize, and ArrayPtr would
277+
' return 0 for an uninitialized array
278+
End If
279+
End Sub
280+
```
281+
282+
<!--
283+
## Using an array to access elements of a string
284+
285+
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.
286+
-->

docs/CustomControls/Defining a CustomControl.md renamed to docs/Tutorials/CustomControls/Defining a CustomControl.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
title: Defining a CustomControl
33
parent: CustomControls
44
nav_order: 2
5-
permalink: /CustomControls/Defining
5+
permalink: /Tutorials/CustomControls/Defining
6+
redirect_from:
7+
- /CustomControls/Defining
68
---
79
# Defining a CustomControl
810
A CustomControl is simply an ordinary twinBASIC class, with a few extra attributes and requirements.
File renamed without changes.

docs/CustomControls/Images/ccClassIdAttribute.png renamed to docs/Tutorials/CustomControls/Images/ccClassIdAttribute.png

File renamed without changes.
File renamed without changes.

docs/CustomControls/Images/ccCustomControlAttribute.png renamed to docs/Tutorials/CustomControls/Images/ccCustomControlAttribute.png

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)