field within union possible?

General FreeBASIC programming questions.
Post Reply
dafhi
Posts: 1726
Joined: Jun 04, 2005 9:51

field within union possible?

Post by dafhi »

Compiler throws an error if I do this:

Code: Select all

Union
    Type Field = 2
        As UShort           t_BH
        As UInteger         t_A
        As UShort           t_BL
    End Type
    Type
        ...
TJF
Posts: 3809
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: field within union possible?

Post by TJF »

Code: Select all

TYPE udt1 FIELD = 2
  AS USHORT           t_BH
  AS UINTEGER         t_A
  AS USHORT           t_BL
END TYPE

TYPE udt2
  AS INTEGER i1, i2
END TYPE

UNION uni
  AS udt1 u1
  AS udt2 u2
END UNION
dafhi
Posts: 1726
Joined: Jun 04, 2005 9:51

Re: field within union possible?

Post by dafhi »

I was hoping to avoid that route. Ah well. Thanks TJF
dafhi
Posts: 1726
Joined: Jun 04, 2005 9:51

Re: field within union possible?

Post by dafhi »

Found the solution I was hoping for .. the Union I actually had inside a type, so I am doing this

Code: Select all

Type SDS Field = 2
    Union
        Type
            As UShort           SD_BH
            As UInteger         SD_A
            As UShort           SD_BL
        End Type
        Type
            As UInteger         D_A
            As UInteger         D_B
        End Type
        ..
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: field within union possible?

Post by counting_pine »

I think that might be a bug/oversight in FB. Even if it isn't allowed, then ideally we should still give a more helpful error message.
But I think it it probably an oversight, and there just hasn't been made any allowance for 'type field=' because the usual syntax requires a type name in the middle. I don't know.
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Re: field within union possible?

Post by MichaelW »

I can’t see how a field statement could be useful/meaningful for a union, other than as a way to patch a compiler problem. The union is supposed to be aligned at the largest alignment requirement of any of its members, and all of its members are supposed to start at the same location in memory.
dkl
Site Admin
Posts: 3235
Joined: Jul 28, 2005 14:45
Location: Germany

Re: field within union possible?

Post by dkl »

If you've got something like this:

Code: Select all

type T [field = N]
	union
		...
	end union
end type
you can also do:

Code: Select all

union T [field = N]
	...
end union
which I think should be exactly the same.


Nested anonymous structures inherit their parent's field alignment, which I think should be enough to handle all cases, but nevertheless, I wrote a patch to allow Field = N to be used on them to override the default same-as-parent:

Code: Select all

diff --git a/changelog.txt b/changelog.txt
index 78809bc..dd2e6fc 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -27,6 +27,7 @@ Version 0.24.0:
 - 'fbc -static' option to tell the linker to link against static libraries if installed
 - CONST may now be specified in front of the bodies of CONST methods, not just in the declaration, same as for STATIC
 - "Real" Rnd() algorithm (activate via 'Randomize(, 5)') using Win32 Crypto APIs or Linux /dev/urandom
+- FIELD = N can now be used on nested anonymous TYPEs/UNIONs
 
 [fixed]
 - Subtracting pointers from numbers, e.g. (i-p) was being allowed, rearranging to (p-i)
diff --git a/compiler/parser-decl-struct.bas b/compiler/parser-decl-struct.bas
index bff85fe..0ca0b5f 100644
--- a/compiler/parser-decl-struct.bas
+++ b/compiler/parser-decl-struct.bas
@@ -564,6 +564,49 @@ private function hTypeAdd _
 
 end function
 
+'' [FIELD '=' ConstExpression]
+private function cFieldAlignmentAttribute( ) as integer
+	'' FIELD
+	if( lexGetToken( ) <> FB_TK_FIELD ) then
+		return 0
+	end if
+
+	lexSkipToken( )
+
+	'' '='
+	if( hMatch( FB_TK_ASSIGN ) = FALSE ) then
+		errReport( FB_ERRMSG_SYNTAXERROR )
+	end if
+
+	'' ConstExpression
+	dim as ASTNODE ptr expr = cExpression( )
+	if( expr = NULL ) then
+		errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
+		'' error recovery: fake an expr
+		expr = astNewCONSTi( 0, FB_DATATYPE_INTEGER )
+	end if
+
+	if( astIsCONST( expr ) = FALSE ) then
+		errReport( FB_ERRMSG_EXPECTEDCONST )
+		'' error recovery: fake an expr
+		astDelTree( expr )
+		expr = astNewCONSTi( 0, FB_DATATYPE_INTEGER )
+	end if
+
+	'' follow the GCC 3.x ABI
+	dim as integer align = astGetValueAsInt( expr )
+	astDelNode( expr )
+	if( align < 0 ) then
+		align = 0
+	elseif( align > FB_INTEGERSIZE ) then
+		align = 0
+	elseif( align = 3 ) then
+		align = 2
+	end if
+
+	return align
+end function
+
 '':::::
 ''TypeBody      =   ( (UNION|TYPE Comment? SttSeparator
 ''					   ElementDecl
@@ -638,7 +681,8 @@ private function hTypeBody _
 		case FB_TK_TYPE, FB_TK_UNION
 			'' isn't it a field called TYPE|UNION?
 			select case as const lexGetLookAhead( 1 )
-			case FB_TK_EOL, FB_TK_EOF, FB_TK_COMMENT, FB_TK_REM
+			case FB_TK_EOL, FB_TK_EOF, FB_TK_COMMENT, FB_TK_REM, _
+			     FB_TK_FIELD
 
 decl_inner:		'' it's an anonymous inner UDT
 				isunion = lexGetToken( ) = FB_TK_UNION
@@ -658,8 +702,14 @@ decl_inner:		'' it's an anonymous inner UDT
 
 				lexSkipToken( )
 
+				'' [FIELD '=' ConstExpression]
+				dim as integer align = cFieldAlignmentAttribute( )
+				if( align = 0 ) then
+					align = symbGetUDTAlign( s )
+				end if
+
 				'' create a "temp" one
-				inner = hTypeAdd( s, NULL, NULL, isunion, symbGetUDTAlign( s ) )
+				inner = hTypeAdd( s, NULL, NULL, isunion, align )
 				if( inner = NULL ) then
 					exit function
 				end if
@@ -786,11 +836,10 @@ function cTypeDecl _
 		byval attrib as FB_SYMBATTRIB _
 	) as integer
 
-    static as zstring * FB_MAXNAMELEN+1 id
-    dim as ASTNODE ptr expr = any
-    dim as integer align, isunion, checkid = any
-    dim as FBSYMBOL ptr sym = any
-   	dim as FB_CMPSTMTSTK ptr stk = any
+	static as zstring * FB_MAXNAMELEN+1 id
+	dim as integer isunion = any, checkid = any
+	dim as FBSYMBOL ptr sym = any
+	dim as FB_CMPSTMTSTK ptr stk = any
 
 	function = FALSE
 
@@ -875,42 +924,8 @@ function cTypeDecl _
 		end if
 	end if
 
-	'' (FIELD '=' Expression)?
-	if( lexGetToken( ) = FB_TK_FIELD ) then
-		lexSkipToken( )
-
-		if( hMatch( FB_TK_ASSIGN ) = FALSE ) then
-			errReport( FB_ERRMSG_SYNTAXERROR )
-		end if
-
-		expr = cExpression( )
-		if( expr = NULL ) then
-			errReport( FB_ERRMSG_EXPECTEDEXPRESSION )
-			'' error recovery: fake an expr
-			expr = astNewCONSTi( 0, FB_DATATYPE_INTEGER )
-		end if
-
-		if( astIsCONST( expr ) = FALSE ) then
-			errReport( FB_ERRMSG_EXPECTEDCONST )
-			'' error recovery: fake an expr
-			astDelTree( expr )
-			expr = astNewCONSTi( 0, FB_DATATYPE_INTEGER )
-		end if
-
-  		'' follow the GCC 3.x ABI
-  		align = astGetValueAsInt( expr )
-  		astDelNode( expr )
-  		if( align < 0 ) then
-  			align = 0
-  		elseif( align > FB_INTEGERSIZE ) then
-  			align = 0
-  		elseif( align = 3 ) then
-  			align = 2
-  		end if
-
-	else
-		align = 0
-	end if
+	'' [FIELD '=' ConstExpression]
+	dim as integer align = cFieldAlignmentAttribute( )
 
 	'' start a new compound, or any EXTERN..END EXTERN used around this struct
 	'' would turn-off function mangling depending on the mode passed
However, when would this be useful?
dafhi
Posts: 1726
Joined: Jun 04, 2005 9:51

Re: field within union possible?

Post by dafhi »

I distinctly remember trying Field on a Union and not having it work. Perhaps it was an issue with the IDE at the time. Anyway, I just tried it now and found that it does work.

@dkl - as you pointed out, there seems to be no need for Field on the nested UDT now. I'll have a peek at what you posted because I will probably learn something.

[edit: just had a look] AHH MY EYES

Here is my old UDT

Code: Select all

Type SDS Field = 2 'short-dword-short
    Union
        Type
            As UShort           D_DH ''Most significant bit is to the 'right'
            As UInteger         D_C
            As UShort           D_DL
        End Type
        Type
            As UInteger         D_B
            As UInteger         D_A
        End Type
    End Union
    Declare Property            D_D as UInteger
    Declare Property            D_D( ByVal Input_ as UInteger )
End Type

Property SDS.D_D                As UInteger
    D_D = D_DL or  D_DH shl 16
End Property

Property SDS.D_D( ByVal Input_ as UInteger )
    D_DL = Input_ and &HFFFF
    D_DH = Input_ Shl 16
End Property
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: field within union possible?

Post by counting_pine »

For what it's worth, I think this patch might as well be applied unless it has any disadvantages, just because you would intuitively expect it to work.
People can already use nested types in unions for any old crazy thing, so (in my opinion) they may as well have fields as well.
On the other hand there's probably a case for disallowing any nested types, since in either case there's the easy (but perhaps less elegant) workaround of making it as a named type beforehand.
fxm
Moderator
Posts: 12508
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: field within union possible?

Post by fxm »

Why this is not allowed?

Code: Select all

Union StringDescriptor
  Dim As String S
  Type
    Dim As Zstring Ptr PointerToCharacter
    Dim As Integer StringLength
    Dim As Integer MemorySize
  End Type
End Union
error 149: Var-len strings cannot be part of UNION's or nested TYPE's in 'Dim As String S'

Var-len string is allowed inside a Type but not inside an Union!
In the two cases, that should correspond only to the string descriptor (fixed memory block of 12 bytes)!
TJF
Posts: 3809
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Re: field within union possible?

Post by TJF »

fxm wrote:Why this is not allowed?
Why should it get allowed? (IMHO it's not useful.)
fxm
Moderator
Posts: 12508
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: field within union possible?

Post by fxm »

TJF wrote:
fxm wrote:Why this is not allowed?
Why should it get allowed? (IMHO it's not useful.)
Don't focalized on my previous example which only describes the descriptor structure using an Union block.

Else, it can be used for returning different types of data in one structure.

Code: Select all

Union ReturnValue
  Dim As String S
  Dim As Integer I
  Dim As Double D
End Union

Scope
  Dim As ReturnValue rv
  rv.S = "Hello!"
  Print "'" & rv.S & "'", Len(rv.S)
  rv.S = ""
End Scope
I know that Union does not support field with constructor/destructor (a string being a pseudo object).
But we could consider that object declared inside an Union is not automatically built or destroyed, but only from the user program to do so (the string descriptor might be just initialized with its 12 bytes to 0 as a numeric field).
fxm
Moderator
Posts: 12508
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: field within union possible?

Post by fxm »

fxm wrote:
TJF wrote:
fxm wrote:Why this is not allowed?
Why should it get allowed? (IMHO it's not useful.)
Don't focalized on my previous example which only describes the descriptor structure using an Union block.

Else, it can be used for returning different types of data in one structure.

Code: Select all

Union ReturnValue
  Dim As String S
  Dim As Integer I
  Dim As Double D
End Union

Scope
  Dim As ReturnValue rv
  rv.S = "Hello!"
  Print "'" & rv.S & "'", Len(rv.S)
  rv.S = ""
End Scope
I know that Union does not support field with constructor/destructor (a string being a pseudo object).
But we could consider that object declared inside an Union is not automatically built or destroyed, but only from the user program to do so (the string descriptor might be just initialized with its 12 bytes to 0 as a numeric field).
A workaround to use a string in an Union:
- Declare bytes for the string destructor (private array of bytes in an unamed type).
- Declare and define a property 'S' for user interface with the private internal string descriptor.
- Declare and define a destructor to destroy the string character data.

From user, 'S' is like a var-len string field inside the Union:

Code: Select all

Union ReturnValue
  Declare Property S () As String
  Declare Property S (Byref value As String)
  Dim As Integer I
  Dim As Double D
  Declare Destructor ()
  Type
    Private:
      Dim As Byte StringDescriptor(0 To Sizeof(String) - 1)
  End Type
End Union

Property ReturnValue.S () As String
  Property = *Cast(String Ptr, @This.StringDescriptor(0))
End property

Property ReturnValue.S (Byref value As String)
  *Cast(String Ptr, @This.StringDescriptor(0)) = value
End property

Destructor ReturnValue ()
  This.S = ""
End Destructor


Scope
  Dim As ReturnValue rv
  rv.S = "Hello!"
  Print "'" & rv.S & "'", Len(rv.S)
End Scope
Sleep
Variant using a function returning by reference instead of property:

Code: Select all

Union ReturnValue
  Declare Function S () Byref As String
  Dim As Integer I
  Dim As Double D
  Declare Destructor ()
  Type
    Private:
      Dim As Byte StringDescriptor(0 To Sizeof(String) - 1)
  End Type
End Union

Function ReturnValue.S () Byref As String
  Function = *Cast(String Ptr, @This.StringDescriptor(0))
End Function

Destructor ReturnValue ()
  This.S = ""
End Destructor


Scope
  Dim As ReturnValue rv
  rv.s = "Hello!"
  Print "'" & rv.s & "'", Len(rv.s)
End Scope
Sleep
[Edit]
- String descriptor size defined by 'Sizeof(String)' instead of 12 bytes (counting_pine).
- Added a destructor.
Last edited by fxm on Apr 11, 2013 20:54, edited 5 times in total.
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Re: field within union possible?

Post by counting_pine »

To avoid making an assumption about the descriptor size, I suggest changing it to: 'Dim As Byte StringDescriptor(0 To sizeof(string)-1)'
fxm
Moderator
Posts: 12508
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: field within union possible?

Post by fxm »

counting_pine wrote:To avoid making an assumption about the descriptor size, I suggest changing it to: 'Dim As Byte StringDescriptor(0 To sizeof(string)-1)'
Yeah, I updated my two workarounds!
Post Reply