• ### Article comments

#### User defined types #2: Glory and shame of dot notation

I would never of thought of this. As Michael said very informative.

kryton9 08-05-2017 03:20
• # User defined types #2: Glory and shame of dot notation

 Target audience: beginner to intermediate, applicable version: 1.9.16.16 and newer

The first article in the series provided some basic motivation for usage of user defined types (UDT): their ability to encapsulate multiple fields and straightforward memory consumption tracking.

The second part in the series will introduce you to dot notation, its benefits and pitfalls.

Dot notation
Designing the data model with UDT is very straightforward, as the first article suggested. Each field of the UDT can be then accessed via dotted syntax:
```type vector2d
x as single
y as single
end type

dim myPoint as vector2d
myPoint.x = 1
myPoint.y = 2
```
The fields of the UDT may be one of the primitive types or even another UDT.
```type line2d
pointA as vector2d
pointB as vector2d
end type

dim xAxis as line2d

xAxis.pointA.x = -10
xAxis.pointA.y = 0

xAxis.pointB.x = 10
xAxis.pointB.y = 0
```
Field inheritance
You can even inherit the fields of one UDT in another. This can be done in two ways:
```type vector3d
vector2d
z as single
end type

' -- or equivalent

type vector3d extends vector2d
z as single
end type
```
In both cases, the vector3d will be equipped with x, y and z fields.

The first approach allows specifying even multiple types to inherit fields from.
The second one allows inheriting from just one specified type, whose fields will be always inserted as first in the new type.

Memory representation
Another interesting property of user defined types is how they are stored in memory. The following example will give you little hint:
```uses "console"

type vector2d
x as single
y as single
end type

type vector3d extends vector2d
z as single
end type

dim v as vector3d
printL "v:   " + varPtr(v)
printL "v.x: " + varPtr(v.x)
printL "v.y: " + varPtr(v.y)
printL "v.z: " + varPtr(v.z)

printL

single x, y, z
printL "x:   " + varPtr(x)
printL "y:   " + varPtr(y)
printL "z:   " + varPtr(z)
waitKey
```
By running the code above you'll be able to make a few interesting observations:
1. the pointer to the first field of UDT equals pointer to the UDT variable itself
2. by default, the fields are tightly packed in memory, one after another
3. the memory order of the fields is the same as the order in the declaration
4. the memory order of separate x, y, z variables is completely random

The third point is very important, if you want to access the fields via pointers. While it is tempting to presume .x is available at +0 offset, .y at +4 and .z at +8, please do not hardcode these.

Never. Ever.

Instead, use UDT_ElementOffset function to calculate the offsets dynamically. This will make your code more maintainable in case you would like to reorder the fields in the UDT later, for aesthetical purposes, for example.
```uses "console"

type vector3d
x as single
y as single
z As Single
end type

dim v(3) as vector3d

dword v_pointer
long  i

printL "Bad practice - try changing order of fields in vector3d" in 12
v_pointer = varptr(v(1))
for i = 1 to countof(v)
poke(single, v_pointer + 0, 1 * i)
poke(single, v_pointer + 4, 2 * i)
poke(single, v_pointer + 8, 3 * i)

printL v(i).x, v(i).y, v(i).z

v_pointer += sizeOf(vector3d)
next

printL

printL "Good practice - changing the order in vector3d does not affect result" in 10
dword x_offset  = udt_elementoffset(v(1).x)
dword y_offset  = udt_elementoffset(v(1).y)
dword z_offset  = udt_elementoffset(v(1).z)

printL "x offset: " + x_offset
printL "y offset: " + y_offset
printL "z offset: " + z_offset
printL

v_pointer = varptr(v(1))
for i = 1 to countof(v)
poke(single, v_pointer + x_offset, 1 * i)
poke(single, v_pointer + y_offset, 2 * i)
poke(single, v_pointer + z_offset, 3 * i)

printL v(i).x, v(i).y, v(i).z

v_pointer += sizeOf(vector3d)
next

waitKey
```
You may wonder - why should I care about accessing the fields via pointers at all? Well, there is a reason which might interest you.
While the dotted syntax is very clear to follow, and de-facto standard even in other languages, it comes at performance price.

Each time a dot is found, a calculation takes place to find the referenced field. The effect gets more serious, the more dots you have in your expression.
This is something many data modeling maniacs do not take in count, bringing them to wonder later.

With thinBasic, this can be completely eliminated with usage of pointers (and you can impress girls by living dangerously).
Have a look at this very synthetic benchmark to demonstrate the time difference:
```uses "console"

type vector3d
x as single
y as single
z As Single
end type

dim v(1000000) as vector3d
long  i

hiResTimer_Init
quad t1, t2

print "Classic dotted aproach: "
t1 = hiResTimer_Get
for i = 1 to countOf(v)
v(i).x = 1 * i
v(i).y = 2 * i
v(i).z = 3 * i
next
t2 = hiResTimer_Get
printL format\$((t2 - t1)\1000) + " ms" in 12 ' -- If you wonder, this is forced integer division

'
dword v_pointer
dword x_offset  = udt_elementoffset(v(1).x)
dword y_offset  = udt_elementoffset(v(1).y)
dword z_offset  = udt_elementoffset(v(1).z)
'

print "Virtual variable:       "
t1 = hiResTimer_Get

single x at 0
single y at 0
single z at 0

v_pointer = varptr(v(1))
for i = 1 to countOf(v)
setAt(x, v_pointer + x_offset)
setAt(y, v_pointer + y_offset)
setAt(z, v_pointer + z_offset)
x = 1 * i
y = 2 * i
z = 3 * i

v_pointer += sizeOf(vector3d)
next
t2 = hiResTimer_Get
printL format\$((t2 - t1)\1000) + " ms" in 14

print "Poking around:          "
t1 = hiResTimer_Get
v_pointer = varptr(v(1))
for i = 1 to countOf(v)
poke(single, v_pointer + x_offset, 1 * i)
poke(single, v_pointer + y_offset, 2 * i)
poke(single, v_pointer + z_offset, 3 * i)

v_pointer += sizeOf(vector3d)
next
t2 = hiResTimer_Get
printL format\$((t2 - t1)\1000) + " ms" in 10

waitKey
```
Conclusion
User defined types provide great flexibility for data modeling, exposed via dot notation, in line with industry expected approach. While this syntactic sugar offers
great readability, it has its performance impact.

The article demonstrates possible approach to overcome this limitation for cases when performance is needed. While the approach is pointer based, it can be made safer by using UDT_ElementOffset function, ensuring proper field addressing.
2 Comments
1. Michael Hartlef -
Great writing and very informative.
1. kryton9 -
I would never of thought of this. As Michael said very informative.

• ### Recent Activity

#### Accesing this forum

Thread Starter: Kuron

I keep getting this screen with Firefox when I try and visit the forums.

Last Post By: ErosOlmi Yesterday, 17:12

#### searching for Curves and Surfaces in the space

Thread Starter: primo

suppose someone tell you to plot this equation Pow(x,4) + Pow(y,4) + Pow(z,4) - Pow(x*x + y*y + z*z,2) + 3 * (x*x + y*y + z*z) = 0 and you don't...

Last Post By: ErosOlmi 17-10-2017, 15:25

#### The power ob GRAB\$ function

Thread Starter: ErosOlmi

A script example showing the power of GRAB\$ function. A simple function that can be used in many situations where you have data structured with...

Last Post By: primo 15-10-2017, 15:05

#### Software Protection...

Thread Starter: Kuron

The old software protection method I used for years was Armadillo. Handled registrations good, but was bloated and became increasingly insecure for...

Last Post By: ErosOlmi 12-10-2017, 12:38

#### When should I use the tbasicu file extension?

Thread Starter: Michael Hartlef

@Eros or @Petr: When should I use the .tbasicu file extension instead of the normal .tbasic one?

Last Post By: Michael Hartlef 12-10-2017, 08:13

#### Combinations(N,K) error (?)

Thread Starter: LechU

Why 'Combinations(49,6)' from 'Math' returns 1.38888888888888889E-3 I think valid answer should be 13983816

Last Post By: LechU 11-10-2017, 07:17