Union

Forum for discussion about the documentation project.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Union

Post by Tourist Trap »

fxm wrote: Note:
In your above example, the nested unnamed TYPE is useless because it contains only one element.
Hi,

yes I was only playing with syntax. The goal of the test is both:
  • verifying that whatever switchable field within the union (unionable field?) is used, the address is always unique, and
  • observing that when I change the size of the array of bytes of any of the 2 switchable, the size of the union is also unique and equal to the greatest possible size
About the new version proposal, it's very fine and of you to have made it so fast. It's very more accurate than the previous paragraph. I have made some changes below to show how I see things that I still feel a bit difficult to guess with 100% certainty, or that I just do not understand at all.
fxm wrote:UNION description update proposal
Unions are similar to a Type structure, except that
the elements of a union occupy a common space in memory (same memory address for all elements of the union).
The size of the Union is the size of the largest data item.
A data item can be an unnamed Type.
Since they occupy a common memory space, only one element can usually be used at a given time.
--> maybe better add a sentence to say what is in a union rather than adding this with the 'global' size description?

[Unions share characteristics with user defined types, and also have differences.]
[*]LikeUser Defined Type, Union can use the optional Field = number specifier ---> I dont see what this means (that the fields can have default values?)
and supports also inheritance through the use of the Extends keyword.
[*]Unlike User Defined Types, Union can not contain variable-length strings and arrays, and more generally fields (or can not have bases) with constructors or destructors (or can not have bases...-->?). Therefore, Union does not support to inherit from the Object built-in type. (maybe a link to Object page here?)

The size of the Union is the size of the largest data item. A data item can be an unnamed Type. Since they occupy the same a common memory space, only one element can usually ??be used at a given time.

Unions support member procedures including Constructor, Destructor, Function, Operator, Property and Sub. All members of a union are public and access control access modifiers areis not supported.

Nested unnamed type or union cannot can NOT have procedure members or static data members (same restriction for local ->meaning?? named type/union).

A Union can be passed as in a similar way of a user defined type to overloaded operator functionsprocedures.
This part below is not too much clear. Maybe it should be put not as a simple not, but as a paragraph about inheritance and unions. Or, a link to a dedicated place in the inheritance page, so it could come with examples and details that would make it perfectly sound.
fxm wrote:
Note: When Union extends a base type, 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 fields 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 fields from the derived UDT.
Since Union is not allowed to have complex fields (i.e. UDTs with constructor/destructor, or dynamic strings), a derived Union cannot be allowed to have (contain) a complex base.
And to finish, I would like to know if those 2 sentences below are true. If they are true could something like this be said along the introductory text, because it is one of the difficult point with unions: how to write them.
The Union is a group of data that can take 2 forms, either the form of a single data field or, the form of a group of data types embedded in a nested Type bloc.

Only one data member, either as a single data field or as a complex data field group within a Type bloc, can be used at a given time. When a given data field is written, the other data fields sharing the common union space may be overwritten or mutilated.

Nested type blocs in the context of Union are not user defined types, they are just blocs gathering data together.
Thanks again!
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Union

Post by Tourist Trap »

To summarize, what I can see to be very important to be described fully and distinctly in Unions is :
  • purpose of the union (ability to switch between data in a common space?)
  • types of switchable data, single or grouped as nested 'Types' blocs
  • treat the possible confusion of nested types blocs with nested User Defined Types that doesn't even exist in FB
  • union memory address
  • union size in memory
  • comparison with namespaces, enums
  • comparison with UDTs as containers (UDTs can hold dynamic arrays etc...that Unions can not)
  • about unions' member procedures (maybe union is just a namespace in this context?)
  • more involved stuff about inheritance and/or things melted between Unions and Udts, the Object built-in type and so on
  • usage of unions!?
That's a lot of stuff. From my side right now, I just undestand the 5 first points, the memory structure of the union when it holds just pure data....
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: Union

Post by srvaldez »

Tourist Trap wrote:To summarize, what I can see to be very important to be described fully and distinctly in Unions is :
  • usage of unions!?
That's a lot of stuff. From my side right now, I just undestand the 5 first points, the memory structure of the union when it holds just pure data....
I find unions useful when accessing data using different types is needed.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Union

Post by Tourist Trap »

srvaldez wrote: I find unions useful when accessing data using different types is needed.
My first use of a Union ever. Based upon your suggestion, I send a data of a given datatype to the union, and it returns it as a byte array... I had to write the typename beyond the byte array last index!

Edit: third version, where is my last byte for the letter H of my zstring*8?

Code: Select all

#macro _SHOWASBYTEARRAY(value, vartype)
    #undef U        ''doesn't work once U is a union... :(
    #undef uu
    ? sizeOf(vartype)
    union U
        'as zstring*(sizeOf(vartype)) z(sizeOf(vartype) + 1)
        as zstring*(sizeOf(vartype) + len(#vartype)) z(sizeOf(vartype) + 1)
        as vartype v
        type
            as byte b(sizeOf(vartype) - 1)
        end type
    end union
    dim as U uu
    uu.z(ubound(uu.z)) = #vartype
    clear (uu.b(lbound(uu.b)), 0, ubound(uu.b) - lbound(uu.b) + 1)
    uu.v = value
    ? value, "as "; uu.z(ubound(uu.z))
    for i as integer = lbound(uu.b) to ubound(uu.b) - 1
        ? uu.b(i);"::";
    next i
    ? uu.b(ubound(uu.b)), "as byte array"
#endMacro

'_SHOWASBYTEARRAY(129, byte)
_SHOWASBYTEARRAY("abcdEFGH", zstring*8)

getKey()

'output
'abcdEFGH      as zstring*8
' 97:: 98:: 99:: 100:: 69:: 70:: 71:: 0    as byte array

'---> last value missing!?
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Union

Post by Tourist Trap »

Interstingly, type forwarding is compatible with Unions. Doesn't it desserve a mention somewhere? Anyway here is a simple example:

Code: Select all

type x as u

type y
    declare constructor()
    as x ptr   xxx
end type


union u
    as integer i
end union

constructor y()
    THIS.xxx = new u
end constructor


dim as y yyy 
yyy.xxx->i = 100

? *yyy.xxx.i

sleep
fxm
Moderator
Posts: 12131
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Union

Post by fxm »

2nd UNION description update proposal:
Unions are similar to a Type structure, except that the The data elements of a Union occupy the same 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 unnamed 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, 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 can not have bases) with constructors or destructors. Therefore, Union does not support to inherit from the Object built-in type.
The size of the Union is the size of the largest data item. A data item can be an unnamed Type. Since they occupy a common memory space, only one element can usually be used at a given time.

Unions support member functions procedures including Constructor, Destructor, Function, Operator, Property and Sub. All members of a union are public and access control modifiers is are not supported.

Nested unnamed type or union cannot can not have procedure members or static data members (same restriction for local scope named type/union).

A Union can be passed as in a similar way of a user defined type to overloaded operator functions 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 fields 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 fields data elements from the derived UDT.
Since Union is not allowed to have complex fields data elements (i.e. UDTs with constructor/destructor, or dynamic strings), a derived Union cannot can not be allowed to have (contain) a complex base.
I added the Note in December 2017 because there was nothing before.
This note is just an introduction to the case of UNION + INHERITANCE which is not at all obvious but I think little or not at all used by coders.
I think this is enough without detailing the behavior for this case.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Union

Post by Tourist Trap »

fxm wrote:2nd UNION description update proposal
Clearly better. If seconded with talkative examples it is at least a much better starting point than it was before. (the topic stays a little obfuscated by nature I suppose)

I'm still stuck here:
Like Type, Union can use the optional Field = number specifier and supports also inheritance through the use of the Extends keyword.
Maybe this would make more sense:
Like Type, Union can use the optional declaration of a field with a value specified: 'Field = value', and supports also inheritance through the use of the Extends keyword.
By the way about inheritance. Have you here some examples? A union can it inherit a UDT, ok but for what purpose?

More generally there is a question that I'm thinking of right now. If Unions can't be built on the Object built-in stuff, does this mean that there is no vtable or most of those internal overhead that accompanies UDTs management internally? Or is it really equivalent to a UDT with a different storage management?

Thanks.

ps: just some tips in english as far as I remember, the "a" article" before "u" when "u" is pronounced "you" is not written "an". So a union, and not an union I believe. Just said "en passant", I didn't notice any fault. And I may be wrong at this also!
fxm
Moderator
Posts: 12131
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Union

Post by fxm »

Tourist Trap wrote:I'm still stuck here:
Like Type, Union can use the optional Field = number specifier and supports also inheritance through the use of the Extends keyword.
Maybe this would make more sense:
Like Type, Union can use the optional declaration of a field with a value specified: 'Field = value', and supports also inheritance through the use of the Extends keyword.
Did you understand that 'FIELD =' here is the specifier for field alignment.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Union

Post by Tourist Trap »

fxm wrote: Did you understand that 'FIELD =' here is the specifier for field alignment.
No I didn't know this keyword.
(Probably also the terminology came into conflict with terms like data field. Mentionning "alignement" and/or suppressing the "= number" may help to focus on the term as a possible keyword. Anyway thanks for explaining.)
Last edited by Tourist Trap on Dec 11, 2018 19:22, edited 2 times in total.
fxm
Moderator
Posts: 12131
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Union

Post by fxm »

Tourist Trap wrote:More generally there is a question that I'm thinking of right now. If Unions can't be built on the Object built-in stuff, does this mean that there is no vtable or most of those internal overhead that accompanies UDTs management internally? Or is it really equivalent to a UDT with a different storage management?
vptr / vtable are used exclusively for the types which extend OBJECT (directly or indirectly).
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Union

Post by Tourist Trap »

fxm wrote: vptr / vtable are used exclusively for the types which extend OBJECT (directly or indirectly).
Ok. Is there any difference internally below:

Code: Select all

type X
	as integer i1
	as integer i2
end type

union Y
	type
		as integer i1
		as integer i2
	end type
end union
By the way I really can't see the interest of this example of the doc:

Code: Select all

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
Of course you make some treatments with some IF conditions from this, but in my opinion, unless you have a reason to want to control very tighly the memory size of the object, this is not a very realistic example. Maybe if we were using a big array of CompType, we could see that we reach a better constant size, so better predictibility. I can't see another advantage. I don't know if you agree. It is still a syntactic sugar.

Code: Select all

dim as CompType   ct(1000)
In all cases:
sizeOf(ct) = 1000*(sizeOf(String*20) + sizeOf(UByte) + sizeOf(UInteger))
But we dont save any space.
A more interesting example in my opinion would be something that may save space. I propose this to feed the discussion:

Code: Select all

    union SPACESAVER
        as uinteger value
        type
            as ubyte b(sizeOf(uinteger))
            as zstring ptr  zPtr
        end type
        declare property ArrayInCompactForm() as zstring ptr
    end union
    property SPACESAVER.ArrayInCompactForm() as zstring ptr
        dim as string   s
        for i as integer = 0 to sizeOf(uinteger)
            s &= chr(b(i))
        next i
        THIS.zPtr = new zstring ptr
        *THIS.zPtr = s
        return THIS.zPtr
    end property
    
    dim as SPACESAVER spsa
    spsa.value = 888111999
    
   ? "initial value", spsa.value
    ? "initial length (in bytes)", len(spsa.value)
    ? "compacted via the union mechanism", *spsa.ArrayInCompactForm
    ? "final length (+ 1 byte for the zero terminator?)", len(*spsa.ArrayInCompactForm) + 1

Code: Select all

initial value 888111999
initial length (in bytes)    8
compacted via the union mechanism         ⌂â´4
final length (+ 1 byte for the zero terminator?)         5
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Union

Post by Tourist Trap »

It's even a lot simpler...

Code: Select all

    union SPACESAVER
            as uinteger value
            as zstring*9  zstr
    end union
   
    dim as SPACESAVER spsa
    spsa.value = 888111999
   
    ? "initial value", spsa.value
    ? "initial length (in bytes)", len(spsa.value)
    ? "compacted via the union mechanism", spsa.zstr
    ? "final length ", len(spsa.zstr)
    
    dim as string  strArray(0)
    strArray(0) = spsa.zstr
    ? "length taken in an array of string", len(strArray(0))
    
    ? "back to initial:"
    dim as SPACESAVER spsa2
    spsa2.zstr = strArray(0)
    ? spsa2.zstr
    ? spsa2.value
    
    getKey()

Code: Select all

initial value 888111999
initial length (in bytes)    8
compacted via the union mechanism         ⌂â´4
final length       4
length taken in an array of string         4
back to initial:
⌂â´4
888111999
Other version:

Code: Select all

    union SPACESAVER
            as uinteger value
            as zstring*9  zstr
        declare function SetValueGetString(byval as uinteger) as string
        declare function SetStringGetValue(byref as const string) as uinteger 
    end union
    function SPACESAVER.SetValueGetString(byval V as uinteger) as string
        THIS.value = V
        return THIS.zstr
    end function
    function SPACESAVER.SetStringGetValue(byref Z as const string) as uinteger
        THIS.zstr = Z
        return THIS.value
    end function
   
   dim as integer freememory
   freememory = fre()
   
    dim as SPACESAVER spsa
    dim as uinteger arrayOfUInt(1 to 1000)
    dim as string arrayOfStr(1 to 1000)
    
    dim as uinteger sumUintArray
    dim as uinteger sumStrArray
    for i as integer = 1 to 1000
        arrayOfUInt(i) = i
        arrayOfStr(i) = spsa.SetValueGetString( arrayOfUInt(i) )
        sumUintArray += len(i)
        sumStrArray += len( arrayOfStr(i) )
    next i
        
    ? "memory usage"
    ? sumUintArray
    ? sumStrArray
    
    ? "get the value at index 544 back"
    ? spsa.SetStringGetValue( arrayOfStr(544) )
    
    getKey()

Code: Select all

memory usage
8000
1739
get the value at index 544 back
544
Last edited by Tourist Trap on Dec 11, 2018 21:47, edited 1 time in total.
fxm
Moderator
Posts: 12131
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Union

Post by fxm »

As soon as a the uinteger longint contains a not higher null ubyte, that no longer works.
Simple example:
spsa.value = 256
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Union

Post by Tourist Trap »

fxm wrote:As soon as a the uinteger longint contains a not higher null ubyte, that no longer works.
Simple example:
spsa.value = 256
I noticed it beeped when I printed the list. How could we fix this problem easily?

If we restrict ourself to use the functions to return the values (second version), we could filter the 'null stuff' mod 256 or something like that, no?

Code: Select all

union SPACESAVER
            as uinteger value
            as zstring*9  zstr
        declare function SetValueGetString(byval as uinteger) as string   'add filter
        declare function SetStringGetValue(byref as const string) as uinteger   'add filter
end union
Here is an attempt with maybe a fix:

Code: Select all

wrong
edit:

Code: Select all

union SPACESAVER
        as uinteger     value
        as zstring*9    zstr
        as ubyte        nullvaluetracker
    declare function SetValueGetString(byval as uinteger) as string
    declare function SetStringGetValue(byref as const string) as uinteger 
end union
function SPACESAVER.SetValueGetString(byval V as uinteger) as string
    THIS.value = V
    select case THIS.nullvaluetracker
    case 0
        return str(V)
    case else
        return THIS.zstr
    end select
end function
function SPACESAVER.SetStringGetValue(byref Z as const string) as uinteger
    THIS.zstr = Z
    ? "****", Z
    ? "****", cUint(Z)
    select case (cUint(Z)<>0 andAlso (cUint(Z) mod 256 = 0))
    case TRUE
        return val(Z)
    case else
        return THIS.value
    end select
end function

dim as integer freememory
freememory = fre()

dim as SPACESAVER spsa
dim as uinteger arrayOfUInt(1 to 1000)
dim as string arrayOfStr(1 to 1000)

dim as uinteger sumUintArray
dim as uinteger sumStrArray
for i as integer = 1 to 1000
    arrayOfUInt(i) = i
    arrayOfStr(i) = spsa.SetValueGetString( arrayOfUInt(i) )
    sumUintArray += len(i)
    sumStrArray += len( arrayOfStr(i) )
next i
    
? "memory usage"
? sumUintArray
? sumStrArray

? "get the value at index 254 back"
? spsa.SetStringGetValue( arrayOfStr(254) )

? "get the value at index 512 back"
? spsa.SetStringGetValue( arrayOfStr(512) )

getKey()

Code: Select all

memory usage
8000
1748
get the value at index 254 back
****          ■
****          0
254
get the value at index 512 back
****          512
****          512
512
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Union

Post by Tourist Trap »

I have a problem that seems to come from the arrays boundaries in this code:

Code: Select all

dim as uinteger n       => 16       'size of the 2 arrays used below 2^n -1
dim as uinteger upper   => 2^n - 1

union SPACESAVER
        as uinteger     value
        as zstring*9    zstr
        as ubyte        firstbyte
    declare function SetValueGetString(byval as uinteger) as string
    declare function SetStringGetValue(byref as const string) as uinteger
    declare function ConsistencyCheck(byval as uinteger) as boolean
end union
function SPACESAVER.SetValueGetString(byval V as uinteger) as string
    THIS.value = V
    select case THIS.firstbyte
    case 0
        return str(V)
    case else
        return THIS.zstr
    end select
end function
function SPACESAVER.SetStringGetValue(byref Z as const string) as uinteger
    THIS.zstr = Z
    ? "****", Z
    ?"****", cUint(Z)
    select case ( (cUint(Z)<>0) andAlso (cUint(Z) mod 256 = 0))
    case TRUE
        return val(Z)
    case else
        return THIS.value
    end select
end function
function SPACESAVER.ConsistencyCheck(byval ArrayIndex as uinteger) as boolean
    if not THIS.SetStringGetValue(THIS.SetValueGetString(ArrayIndex))=ArrayIndex then 
        return FALSE
    else
        return TRUE
    end if
end function

dim as SPACESAVER spsa
redim as uinteger   arrayOfUInt(1 to upper)
redim as zstring*9  arrayOfStr(1 to upper)

dim as uinteger sumUintArray
dim as uinteger sumStrArray
for i as integer = 1 to upper
    arrayOfUInt(i) = i
    arrayOfStr(i) = spsa.SetValueGetString( arrayOfUInt(i) )
    sumUintArray += len(i)
    sumStrArray += len( arrayOfStr(i) )
next i
    
? "memory usage"
? sumUintArray
? sumStrArray

? "get the value at index 111 back"
? spsa.SetStringGetValue( arrayOfStr(111) )

? "get the value at index 1024 back"
? spsa.SetStringGetValue( arrayOfStr(1024) )

getKey()
When I set the number of items of my arrays up to 2^16 - 1, I get correct results (on the tested values). If I increment by only one unit or more, it's broken.

Do someone see some explanation to this phenomenom?
Post Reply