Why typed const not allowed to put in a binary file ?

General FreeBASIC programming questions.
Post Reply
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Why typed const not allowed to put in a binary file ?

Post by D.J.Peters »

Why are typed const not allowed to put in a binary file ?
I would accept it for a typeless const but in this case it's 4 byte ulong.

Joshy

Code: Select all

type TGEO_HEADERID as ulong
const as TGEO_HEADERID GEO_ID = &H47443348 ' "H3DG"
chdir exepath()
var hFile = FreeFile()
if open("test.geo",for binary,access write,as #hFile) then
  print "error: can't create file 'test.geo' !"
  beep: sleep: end 1
end if
put #hFile,,GEO_ID
' ...
close #hFile
as a workaround I use a define
here are the real example with many const's

Code: Select all

type TGEO_HEADERID       as ulong
type TGEO_VERSION        as ulong
type TGEO_STREAM_COUNT   as ulong
type TGEO_STREAM_ID      as ulong
type TGEO_ELEMENT_SIZE   as ulong
type TGEO_ELEMENT_COUNT  as ulong
type TGEO_TRIANGLE_INDEX as ulong
type TGEO_CHUNKID        as ulong 

const as TGEO_HEADERID GEO_ID = &H47443348 ' "H3DG"
const as TGEO_VERSION  GEO_VERSION =  5

const as TGEO_STREAM_ID GEO_POSITON         = 0
const as TGEO_STREAM_ID GEO_NORMAL          = 1
const as TGEO_STREAM_ID GEO_TANGENT         = 2
const as TGEO_STREAM_ID GEO_BITANGENTS      = 3
const as TGEO_STREAM_ID GEO_JOINT_INDEX     = 4
const as TGEO_STREAM_ID GEO_WHEIGHTS        = 5
const as TGEO_STREAM_ID GEO_TEXTURE_COORDS0 = 6
const as TGEO_STREAM_ID GEO_TEXTURE_COORDS1 = 7

const as TGEO_ELEMENT_SIZE GEO_POSITON_SIZE         = 12 ' 3 * sizeof(single) (x,y,z)
const as TGEO_ELEMENT_SIZE GEO_NORMAL_SIZE          =  6 ' 3 * sizeof(short) (x,y,z)
const as TGEO_ELEMENT_SIZE GEO_TANGENT_SIZE         =  6 ' 3 * sizeof(short) (x,y,z)
const as TGEO_ELEMENT_SIZE GEO_BITANGENTS_SIZE      =  6 ' 3 * sizeof(short) (x,y,z)
const as TGEO_ELEMENT_SIZE GEO_JOINT_INDEX_SIZE     =  4 ' 4 * sizeof(ubyte) or 0 if not used
const as TGEO_ELEMENT_SIZE GEO_WHEIGHTS_SIZE        =  4 ' 4 * sizeof(ubyte)
const as TGEO_ELEMENT_SIZE GEO_TEXTURE_COORDS0_SIZE =  8 ' 2 * sizeof(single) (u,v)
const as TGEO_ELEMENT_SIZE GEO_TEXTURE_COORDS1_SIZE =  8 ' 2 * sizeof(single) (u,v)

' a simple textured cube
const as TGEO_ELEMENT_COUNT nStreams         =  3 ' ( in this case 3 vertex streams: position, normal and texture coords set0)
const as TGEO_ELEMENT_COUNT nVertices        = 24 ' 6 quads * 4 vertices
const as TGEO_ELEMENT_COUNT nTriangles       = 12 ' 6 quads * 2 triangles
const as TGEO_ELEMENT_COUNT nTriangleIndices = 36 ' 12 triangles * 3 indicies

type TGEO_JOINT_INVERSE_BIND_MATRICE
  as single m4x4(15)
end type

type TGEO_POSITION
  as single x,y,z
end type

type TGEO_NORMAL
  as short x,y,z  ' single stored as short / 32767.0  
end type

type TGEO_TANGENT
  as short x,y,z  ' single stored as short/ 32767.0
end type

type TGEO_BITANGENTS
  as short x,y,z  ' single stored as short/ 32767.0
end type

type TGEO_JOINT_INDEX
  as ubyte a,b,c,d ' can be 0=unused
end type

type TGEO_JOINT_WIGHTS
  as ubyte a,b,c,d ' single stored as byte / 255
end type

type TGEO_TEXTURE_COORDS
  as single u,v
end type


dim as TGEO_POSITION Positions(nVertices-1) => { _
  (-0.5, 0.5, 0.5), (-0.5,-0.5, 0.5), ( 0.5,-0.5, 0.5), ( 0.5, 0.5, 0.5), _ ' 4 vertices build the front face of the cube
  ( 0.5, 0.5, 0.5), ( 0.5,-0.5, 0.5), ( 0.5,-0.5,-0.5), ( 0.5, 0.5,-0.5), _ ' 4 vertices build the right face of the cube
  ( 0.5, 0.5,-0.5), ( 0.5,-0.5,-0.5), (-0.5,-0.5,-0.5), (-0.5, 0.5,-0.5), _ ' 4 vertices build the back face of the cube
  (-0.5, 0.5,-0.5), (-0.5,-0.5,-0.5), (-0.5,-0.5, 0.5), (-0.5, 0.5, 0.5), _ ' 4 vertices build the left face of the cube
  (-0.5, 0.5,-0.5), (-0.5, 0.5, 0.5), ( 0.5, 0.5, 0.5), ( 0.5, 0.5,-0.5), _ ' 4 vertices build the top face of the cube
  (-0.5,-0.5, 0.5), (-0.5,-0.5,-0.5), ( 0.5,-0.5,-0.5), ( 0.5,-0.5, 0.5)}   ' 4 vertices build the bottom face of the cube

#define one 32767 
dim as TGEO_NORMAL Normals(nVertices-1) => { _
  (0,0, one), (0,0, one), (0,0, one), (0,0, one), _ ' 4 normals of the front face
  ( one,0,0), ( one,0,0), ( one,0,0), ( one,0,0), _ ' 4 normals of the right face
  (0,0,-one), (0,0,-one), (0,0,-one), (0,0,-one), _ ' 4 normals of the back face
  (-one,0,0), (-one,0,0), (-one,0,0), (-one,0,0), _ ' 4 normals of the left face
  (0, one,0), (0, one,0), (0, one,0), (0, one,0), _ ' 4 normals of the top face
  (0,-one,0), (0,-one,0), (0,-one,0), (0,-one,0)}   ' 4 normals of the bottom face
#undef one

dim as TGEO_TEXTURE_COORDS TextureCoords0(nVertices-1) => { _
  (0,1),(0,0),(1,0),(1,1), _ ' texture coords from front face
  (0,1),(0,0),(1,0),(1,1), _ ' texture coords from right face
  (0,1),(0,0),(1,0),(1,1), _ ' texture coords from back face
  (0,1),(0,0),(1,0),(1,1), _ ' texture coords from left face
  (0,1),(0,0),(1,0),(1,1), _ ' texture coords from top face
  (0,1),(0,0),(1,0),(1,1) }  ' texture coords from bottom face

dim as TGEO_TRIANGLE_INDEX Indices(nTriangleIndices-1) => { _
   0, 1, 2,  2, 3, 0, _ ' 2 triangles build the front face
   4, 5, 6,  6, 7, 4, _ ' 2 triangles build the right face
   8, 9,10, 10,11, 8, _ ' 2 triangles build the back face
  12,13,14, 14,15,12, _ ' 2 triangles build the left face
  16,17,18, 18,19,16, _ ' 2 triangles build the top face
  20,21,22, 22,23,20}   ' 2 triangles build the bottom face


ChDir ExePath()

' write file: cube.geo in Horde3D/Content/models/cube/

var hFile = FreeFile()
if open("cube.geo",for binary,access write,as #hFile) then
  print "error: can't create 'cube.geo' !"
  beep : sleep: end 1
end if

dim as ulong u32 ' a helper FreeBASIC can#t write typed const#s in binary file !
#define WRITE_U32(value) u32=value : put #hFile,,u32

WRITE_U32(GEO_ID)      ' write geo file id
WRITE_U32(GEO_VERSION) ' write geo file version

WRITE_U32(0)           ' write number of joints in this file
' write nJoints * 16 single inverse matrices if any
'for i as integer=0 to nJoints-1
'  put #hFile,,Matrices(i)
' next

WRITE_U32(nStreams)    ' write number of vertex strams in this file 3 in this case

WRITE_U32(nVertices)   ' write number of vertices in this file

' write position chunk 1
WRITE_U32(GEO_POSITON)
WRITE_U32(GEO_POSITON_SIZE)
put #hFile,,Positions()

' write normal chunk 2
WRITE_U32(GEO_NORMAL)
WRITE_U32(GEO_NORMAL_SIZE)
put #hFile,,Normals()

' write tangent chunk if any
'WRITE_U32(GEO_TANGENT)
'WRITE_U32(GEO_TANGENT_SIZE)
'put #hFile,,Tangents()

' write bitangents chunk if any
'WRITE_U32(GEO_BITANGENTS)
'WRITE_U32(GEO_BITANGENTS_SIZE)
'put #hFile,,Bitangents()

' write joint indicies if an y
'WRITE_U32(GEO_JOINT_INDEX)
'WRITE_U32(GEO_JOINT_INDEX_SIZE)
'put #hFile,,Jointindicies()

' write joint wheights if any
'WRITE_U32(GEO_WHEIGHTS)
'WRITE_U32(GEO_WHEIGHTS_SIZE)
'put #hFile,,Wheights()

' write texturecoord set0 chunk 3
WRITE_U32(GEO_TEXTURE_COORDS0)
WRITE_U32(GEO_TEXTURE_COORDS0_SIZE)
put #hFile,,TextureCoords0()

' write texturecoord set1 if any
'WRITE_U32(GEO_TEXTURE_COORDS1)
'WRITE_U32(GEO_TEXTURE_COORDS1_SIZE)
'put #hFile,,TextureCoords1()

' write triangle indicies
WRITE_U32(nTriangleIndices)
put #hFile,,Indices()

WRITE_U32(0) ' write number of morph targets

' close the file
close #hFile
sancho2
Posts: 547
Joined: May 17, 2015 6:41

Re: Why typed const not allowed to put in a binary file ?

Post by sancho2 »

I don't have an answer, but just as a heads up: No constants can be written directly to a binary file except it seems String constants.
The manual says any "buffer variable" can be written. But I dont find any info as to what a buffer variable is.

Code: Select all

Type TGEO_HEADERID as String

const geo_ID As tgeo_headerid = "123r56"  '"string" ' "H3DG"
ChDir exepath()
var hFile = FreeFile()
if open("test.geo",for binary,access write,as #hFile) then
  print "error: can't create file 'test.geo' !"
  beep: sleep: end 1
end if
put #hFile,,geo_id
' ...
close #hFile
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Why typed const not allowed to put in a binary file ?

Post by fxm »

Only the "buffer variables":
The character data of Const strings are wrote in memory (with a Zstring format) but no equivalent for the numerical constants.

Code: Select all

Const As Integer Ci = 1
Const As String Cs = "1"

'Print @Ci  '' error 24: Invalid data types, for @ or VARPTR in 'Print @Ci'
Print @Cs

Sleep
codeFoil
Posts: 256
Joined: Dec 22, 2011 4:45
Location: United States
Contact:

Re: Why typed const not allowed to put in a binary file ?

Post by codeFoil »

I think you've answered your own question.
I suspect that the call to fb_FilePut requires a memory address. Integral constants are emitted literally to the backend, so they are neither in static storage, the stack, or the heap.

There is another work around however. You can use a variable with the "const" modifier. These do get memory allocated, so they have an address to pass.

Code: Select all

TYPE T AS ULONG
STATIC SHARED AS CONST T  C = &h47443348
CHDIR exepath()
VAR F = FreeFile
IF Open ( "test.txt", FOR BINARY, AS F ) THEN
    Print "Could not open file."
ELSE
    PUT #F , , C
    CLOSE F
END IF

D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: Why typed const not allowed to put in a binary file ?

Post by D.J.Peters »

A stanadard dataype (not UDT) const or a var is for the code emitter a label with a type
e.g. name: .long 1245

the label 'name:' is the address the only difference is
a const label can optional be stored (read only) in the code segement.

Joshy
codeFoil
Posts: 256
Joined: Dec 22, 2011 4:45
Location: United States
Contact:

Re: Why typed const not allowed to put in a binary file ?

Post by codeFoil »

I apologize in advance. I'm going to use the term "constant variable" below. I know that's an oxymoron.

For this sample:

Code: Select all

STATIC SHARED AS integer aVar = &hCF
STATIC SHARED AS CONST integer aConstVar = &hCE
CONST AS integer aConst = &hCD
Print &hCC
Print aConst
Print aConstVar
Print aVar
In the first two print statements, wherein the first uses a literal and the second uses a constant,
for the gas back end in version 1.04.0, we get literal integers in the assembly language.

Code: Select all

##Print &hCC
	push 1
	push 204
	push 0
	call _fb_PrintInt@12
.stabn 68,0,4,.Lt_0005-_fb_ctor__test
.Lt_0006:
##Print aConst
	push 1
	push 205
	push 0
	call _fb_PrintInt@12
.stabn 68,0,5,.Lt_0006-_fb_ctor__test
For the following two cases, using a constant variable and a variable, labels are dereferenced.

Code: Select all

##Print aConstVar
	push 1
	push dword ptr [_ACONSTVAR]
	push 0
	call _fb_PrintInt@12
.stabn 68,0,6,.Lt_0007-_fb_ctor__test
.Lt_0008:
##Print aVar
	push 1
	push dword ptr [_AVAR]
	push 0
	call _fb_PrintInt@12
.stabn 68,0,7,.Lt_0008-_fb_ctor__test
I don't know the compile code very, but I've always taken these results as representative of the different ways it treats a symbols declared CONST AS TYPE and AS CONST TYPE.

When I look at the code generated for a PUT statement, I saw that is passes by reference. That' make sense, and explains why both literals and constants are not accepted. That's not to say that it wouldn't be possible, just that it appears the compiler is not written to handle these cases.

For the more general and common case of a SUB or FUNCTION that takes a parameter by reference,
it appears that space is allocated on the stack to store the value of the constant so that the address can be passed.

Code: Select all

CONST aConst = 1
SUB Test( BYREF i AS integer ) 
  i += 1
END SUB
Test aConst

Code: Select all

##Test aConst
       mov dword ptr [ebp-4], 1
	lea eax, [ebp-4]
	push eax
	call _TEST@4
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Why typed const not allowed to put in a binary file ?

Post by fxm »

codeFoil wrote:When I look at the code generated for a PUT statement, I saw that is passes by reference. That' make sense, and explains why both literals and constants are not accepted. That's not to say that it wouldn't be possible, just that it appears the compiler is not written to handle these cases.
This is not the general rule.
For a procedure (declared with a parameter passed byref), the compiler authorizes to passed a literal expression or a constant:
instead of a normal reference passed, a temporary variable initialized to the literal expression or to the constant value is passed itself by reference.

Code: Select all

Sub foo (Byref I As Integer)
   Print I,
   I += 1
   Print I,
End Sub

Dim As Integer I = 1
Const As Integer I0 = 1

foo(I) : Print I
foo(I0) : Print I0
foo(1)

Sleep
By cons, declare "Dim As Const Integer ..." well induces a compiler error when passing the constant Integer to a such procedure.
sancho2
Posts: 547
Joined: May 17, 2015 6:41

Re: Why typed const not allowed to put in a binary file ?

Post by sancho2 »

There is another way to work around the issue. You can write your own Put command.

Code: Select all

Sub PutConst(ByVal fNum As Integer, ByVal cnst As ULong)
	Put #fNum,, cnst
End Sub
type TGEO_HEADERID as ulong

const as TGEO_HEADERID GEO_ID = &H47443348 ' "H3DG"

chdir exepath()
var hFile = FreeFile()
if open("c:\freebasic stuff\test.geo",for binary,access write,as #hFile) then
  print "error: can't create file 'test.geo' !"
  beep: sleep: end 1
end if
'put #hFile,,GEO_ID
PutConst(hFile, Geo_id)
' ...
close #hFile
Post Reply