Request: CTYPE and CARRAY

BlitzMax Forums/BlitzMax Programming/Request: CTYPE and CARRAY

Noel,

This is not just an interoperability issue, this is a fundamental and performant language feature, only to benefit BMax and it's community. I'm not sure I understand how it "over" complicates the code - it indeed does the opposite, by simplifying it. See my previous examples above.

In essence it is syntatically very similar to a regular "Type" declaration in bmax. I don't even see a need to elevate this to an 'object' type, I think it is fine to treat it like any other first class data type (int, float, byte, etc).


I think you've made a point in favour of a 'C' style struct by suggesting that if you have the need to write code that works with C (or infact Delphi, .Net). To elaborate, by using pointers or writing a type that must create memory blocks that imitate the layour of a complex data structure (I've dropped the 'c' name, because interoping could invole any language with this ability), you've considerably over-complicated your code, reduced readability and maintainability.
You've now introduced additional memory requirements to create this 'fake' structure (which will be accessed by either memcopy's or peeks/pokes), overhead in copying to and from this structure and therefore performance.

Here is a more realistic example:

Noel, this is what you would have to do, and I think you may agree that adding a 'C' structured type is far less of an 'overcomplication' than the hoops required in this example ;-)

Type colourargb
   Field a:Byte
   Field b:Byte
   Field g:Byte
   Field r:Byte
End Type
' struct size 4 bytes

Type vert3 
   Field x:Double
   Field y:Double
   Field z:Double
   Field normal:Double

   Field colour:colourargb
End Type
' struct size 36 bytes

Type cube
	Field name:Byte[32]
	Field verts:vert3[8]
End Type
' struct size 320 bytes

Print "colourargb size: " + SizeOf(colourargb)
Print "vert3 size: " + SizeOf(vert3)
Print "cube size: " + SizeOf cube 


Local mycube:cube = New cube
Print "mycube size: " + SizeOf mycube

For i = 0 Until mycube.verts.length
	mycube.verts[i] = New vert3
	mycube.verts[i].colour = New colourargb
Next



' cube size is 320 bytes
' layout:
' 0..31    : name
' 32..319  : vert3[8]

' vert3 size is 36 bytes
' layout:
' 0..7     : x
' 8..15    : y
' 16..23   : z
' 24..31   : normal
' 32..6    : colourargb

' colourargb size is 4 bytes
' layout:
' 0				 : a
' 1				 : g
' 2				 : b
' 3				 : r

' create bank:

Local cubemem:TBank = CreateBank(320)

' copy cube.name
For i = 0 Until mycube.name.length
  PokeByte(cubemem, i, mycube.name[i])
Next

vert3size = 36
vert3start = 32
offset = 0
' copy cube.verts
For i = 0 Until mycube.verts.length
  PokeDouble(cubemem, i * vert3size + offset, mycube.verts[i].x)
	offset :+ 8

  PokeDouble(cubemem, i * vert3size + offset, mycube.verts[i].y)
	offset :+ 8

  PokeDouble(cubemem, i * vert3size + offset, mycube.verts[i].z)
	offset :+ 8

  PokeDouble(cubemem, i * vert3size + offset, mycube.verts[i].normal)
  offset :+ 8

  PokeByte(cubemem, i * vert3size + offset, mycube.verts[i].colour.a)
  offset :+ 1
  
  PokeByte(cubemem, i * vert3size + offset, mycube.verts[i].colour.b)
  offset :+ 1
  
  PokeByte(cubemem, i * vert3size + offset, mycube.verts[i].colour.g)
  offset :+ 1
  
  PokeByte(cubemem, i * vert3size + offset, mycube.verts[i].colour.r)
  
  offset = 0
  
Next

' now we can pass packed data structure off to external library
' call some lib function

' upon return, where the data may have been modified, we must copy
' back to our local objects

offset = 0
' copy cube.verts
For i = 0 Until mycube.verts.length
  mycube.verts[i].x = PeekDouble(cubemem, i * vert3size + offset)
	offset :+ 8

  mycube.verts[i].y = PeekDouble(cubemem, i * vert3size + offset)
	offset :+ 8

  mycube.verts[i].z = PeekDouble(cubemem, i * vert3size + offset)	
  offset :+ 8

  mycube.verts[i].normal = PeekDouble(cubemem, i * vert3size + offset)
  offset :+ 8

  mycube.verts[i].colour.a = PeekByte(cubemem, i * vert3size + offset)
  offset :+ 1
  
  mycube.verts[i].colour.b = PeekByte(cubemem, i * vert3size + offset)
  offset :+ 1
  
  mycube.verts[i].colour.g = PeekByte(cubemem, i * vert3size + offset)
  offset :+ 1
  
  mycube.verts[i].colour.r = PeekByte(cubemem, i * vert3size + offset)
  
  offset = 0
  
Next


With a 'C' style or complex data type, it would be as simple as:

CType colourargb
   Field a:Byte
   Field b:Byte
   Field g:Byte
   Field r:Byte
End CType
' struct size 4 bytes

CType vert3 
   Field x:Double
   Field y:Double
   Field z:Double
   Field normal:Double

   Field colour:colourargb
End CType
' struct size 36 bytes

CType cube
	Field name:Byte[32]
	Field verts:vert3[8]
End CType
' struct size 320 bytes

Print "colourargb size: " + SizeOf(colourargb)
Print "vert3 size: " + SizeOf(vert3)
Print "cube size: " + SizeOf cube 


Local mycube:cube = New cube

' call external lib function, passing mycube pointer directly

' no need to copy back to anything, as it was already modified in place.


So, aside from reducing the complexity, we have reduced:

Original:

mem allocs : 18
mem pokes (copy to bank) : 96
mem peeks (copy from bank): 64

Proposed 'c' style:
mem allocs : 1
mem pokes : 0
mem peeks : 0

That is 145 memory operations down to 1. Not bad. This also doesn't change based on the number of vertices in our 'object', cube in this case.

Let's extrapolate that out a little, to some arbitrary object with 1000 vertices:

Original:

mem allocs : 2002
mem peeks : 16032
mem pokes : 16000

Proposed 'c' style:
mem allocs : 1
mem pokes : 0
mem peeks : 0

Quite a difference.


Cheers,

Stu