DIM myArray AT (syntax request)

General FreeBASIC programming questions.
Post Reply
cbruce
Posts: 166
Joined: Sep 12, 2007 19:13
Location: Dallas, Texas

DIM myArray AT (syntax request)

Post by cbruce »

Folks, I was wondering if anyone else thinks that this would be a good idea for a new FB feature...

DIM AT overlays an array on top of another variable's allocated space.

'dim a 20 character string...
Dim As String s1 * (4 * 5)

'dim a 5 integer array on top of the string space...
Dim As Integer aInt(1 to 5) AT StrPtr(s1)

It's a PowerBasic syntax that I use all the time.

I know that I can (and do) use Cast() and CPtr() in FB to basically achieve the same thing, but there are advantages to the PB built-in:
  • - It's very obvious what the programmer is doing
    - The DIM'd AT array works like any standard array, except PB does not initialize its space; and if it's ERASE'd, it does not release the underlying allocated space
cbruce
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Post by notthecheatr »

Try unions... they can do something like that. Take a look at the manual page: http://www.freebasic.net/wiki/wikka.php ... KeyPgUnion

Here's an example of accessing the bytes of a string with unions. There is one drawback which is that strings in unions must be fixed length:

Code: Select all

Union mystr
  As Byte mybytes(13)
  As String*13 mystr
End Union

Dim As mystr m
m.mystr = "Hello, World!"
Dim As Integer i

For i = 0 to Len(m.mystr)-1
  Print Chr(m.mybytes(i));
Next i
Sleep
Unions allow you to group a bunch of variables together in the same space.


In the case of strings, however, there is also the [] syntax. You can use [] after the name of a string to index into the string... it will return a uByte. So, for example, the first character in the string mystr has the ASCII value mystr[0]. Here's a piece of code that demonstrates this:

Code: Select all

Dim As String mystr = "Hello, World!"
Dim As Integer i

For i = 0 to Len(mystr)-1
  Print Chr(mystr[i]);
Next i
Sleep
cbruce
Posts: 166
Joined: Sep 12, 2007 19:13
Location: Dallas, Texas

Post by cbruce »

Thanks, cheatr(not)... I know about those features...

I actually cheat though - I use a string instead of malloc() so that my mem usage gets cleaned up when the FB app exits - I just write code to "allocate" out of the string and overlay those "allocated" spaces with arrays of the requisite type of data structure at the time.

I also do a lot of Union type data handling, but my way is fully dynamic at run-time... so my routines can adapt to needs, instead of everything being hard-coded up front.

I appreciate your thoughts on the subject,
cbruce
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Post by notthecheatr »

*grinds teeth*

No offense, but that's a really bad way to write code, mainly because dynamic strings are pretty inefficient (that is, when they're actually used dynamically - it's OK to use dynamic strings, but if the size of the string is changing constantly it's probably better to do it some other way). Static is always faster, no matter what variable type you use.

Far preferable would be to use OOP... have you heard of RAII? cha0s did a tutorial on it once. RAII says that you do all the allocation in the constructor, and all deallocation in the destructor of the object. Both of those are called automatically, so there's no need to worry about allocating/deallocating the memory in your program. The object does that all by itself. Then you just have to worry about it being dynamic - that's pretty simple too. You can overload operators or just use member functions to add or remove items in your object. The object itself keeps track of whether anything needs to be allocated or deallocated. It's very nice really.

Of course, it's your choice... but it's a lot harder to do what you're doing that way.

By the way, if you want to store integers in a string, here's one way to do it (implemented like a stack):

Code: Select all

Sub push_integer(ByRef mystr As String, myint As Integer)
Dim As Byte Ptr myb = CPtr(Byte Ptr, @myint)
  mystr = mystr + Chr(myb[0])
  mystr = mystr + Chr(myb[1])
  mystr = mystr + Chr(myb[2])
  mystr = mystr + Chr(myb[3])
End Sub

Function pop_integer(ByRef mystr As String) As Integer
Dim As Integer myint
Dim As Integer lms = Len(mystr)
Dim As Byte Ptr myb = CPtr(Byte Ptr, @myint)
  If Len(mystr) = 0 Then Return 0
  myb[0] = mystr[lms-4]
  myb[1] = mystr[lms-3]
  myb[2] = mystr[lms-2]
  myb[3] = mystr[lms-1]
  mystr = Left(mystr, lms-4)
  Return myint
End Function

Dim As String mystr

push_integer(mystr, 1)
push_integer(mystr, 2)
push_integer(mystr, 3)

Print Str(pop_integer(mystr))
Print Str(pop_integer(mystr))
Print Str(pop_integer(mystr))
Print Str(pop_integer(mystr))

Sleep
I do not recommend coding this way, but there's a solution if you want to. You can replace integer here with uinteger or single (for double you need 8 bytes instead of 4) - I assume you can figure out what to do with all this.
Zippy
Posts: 1295
Joined: Feb 10, 2006 18:05

Post by Zippy »

@notthecheatr

I sorta MISSED your OOP example? The simple one?

Would you define "efficient", please?

@cbruce

IMO the alternatives to "dim n() AT" are too simple to warrant "AT" implementation. I just tried to ascertain where/how to re-point numeric arrays here:

http://www.freebasic.net/forum/viewtopic.php?t=9852

with no success. I would use the "AT" form if it was available but I can't rationalize requesting it when there are multiple truly-simple alternatives. Then, requesting a feature that only two users (you and me) might use would probably rightfully be rejected (unless the solution was really simple..).

Welcome to the forum, BTW. There are reasonable folk here (I'm not claiming to be reasonable).
notthecheatr
Posts: 1759
Joined: May 23, 2007 21:52
Location: Cut Bank, MT
Contact:

Post by notthecheatr »

I'm just stating it as possibility. What would you like an OOP example for? I might have time for a quickie hack...
cbruce
Posts: 166
Joined: Sep 12, 2007 19:13
Location: Dallas, Texas

Post by cbruce »

Thanks a lot folks - that's why I asked - to ascertain if anyone else would find it helpful.

notthecheatr - I'm basically doing my own memory management, so RAII doesn't accomplish my goal directly. I Dim 3 or 4 heap strings and null fill the first one to the size I think I will need for the app. Then I overlay all of my different structures over the heap string. I do this in subroutines that often have to allocate a few thousand structures. I can do exactly the same thing with large malloc()'s, but since PB cleans up the strings for me, I end up with fewer memory leaks that I have to track down.

Like I said, I can get by with Cast() and CPtr() - Dim AT would just be cleaner.

Thanks for listening,
cbruce
jofers
Posts: 1525
Joined: May 27, 2005 17:18

Post by jofers »

Here's a QB compatible way while we're throwing around alternative means to filling string data with integers:

Code: Select all

Dim myString As String * 20

myString = "Hello y'all"

' Set set integer index 1
Mid(myString, 1*4+1, 4) = Mkl(&habcdef12)

' Get integer index 1
Print Cvl(Mid(myString, (1)*4+1, 4))

' Show defiled string
Print myString
But if you want something that looks nice in code, use a macro:

Code: Select all

#define IntArray(x) (CPtr(Integer Ptr, StrPtr(x)))

Dim myString As String * 20

myString = "Hello y'all"

IntArray(myString)[1] = &habcdef12

Print IntArray(myString)[1]
Print myString
yetifoot
Posts: 1710
Joined: Sep 11, 2005 7:08
Location: England
Contact:

Post by yetifoot »

Here's a dirty hack that may break in the future if array internals change, but is fun for now :)

It basically just provides you with two macros, map and unmap that do the work.

Code: Select all

type FBARRAYDIM
	as integer elements
	as integer _lbound
	as integer _ubound
end type

type FBARRAY
	as any ptr    _data       ' ptr + diff, must be at ofs 0!
	as any ptr    _ptr
	as integer    size
	as integer    element_len
	as integer    dimensions
	'as FBARRAYDIM dimTB(0     ' dimtb[dimensions]
end type

sub array_debug _
	( _
		byval array as FBARRAY ptr _
	)

	dim as FBARRAYDIM ptr TB = cptr( FBARRAYDIM ptr, cptr( ubyte ptr, array ) + sizeof( FBARRAY ) )

	print "_data             : " & array->_data
	print "_ptr              : " & array->_ptr
	print "element_len       : " & array->element_len
	print "dimensions        : " & array->dimensions
	for i as integer = 0 to array->dimensions - 1
		print "dimTB(" & i & ").elements : " & TB[i].elements
		print "dimTB(" & i & ")._lbound  : " & TB[i]._lbound
		print "dimTB(" & i & ")._ubound  : " & TB[i]._ubound
	next i

end sub

sub array_set _
	( _
		byval array as FBARRAY ptr, _
		byval p     as any ptr, _
		byval size  as integer, _
		byval count as integer _
	)

	dim as FBARRAYDIM ptr TB = cptr( FBARRAYDIM ptr, cptr( ubyte ptr, array ) + sizeof( FBARRAY ) )

	array->_data = p
	array->_ptr = p
	array->element_len = size
	array->dimensions = 1

	TB[0].elements = count
	TB[0]._lbound = 0
	TB[0]._ubound = count - 1

end sub

declare function getarray cdecl alias "getarray" _
	( _
		foo() as any _
	) as any ptr

' hacky function to get the array descriptor address...
asm
	jmp .dummy
	getarray:
		mov eax, dword ptr [esp+4]
		ret
	.dummy:
end asm

#macro map( __name__, __type__, __count__, __p__ )
	redim as __type__ __name__()
	array_set( getarray( __name__() ), __p__, sizeof( __name__ ), __count__ )
	array_debug( getarray( __name__() ) )
#endmacro

#macro unmap( __name__ )
	scope
		dim as FBARRAY ptr __temp__ = getarray( __name__() )
		__temp__->_data = 0
		__temp__->_ptr = 0
		__temp__->element_len = 0
		__temp__->dimensions = 0
	end scope
#endmacro

''''
' test code...

dim as integer ptr moo = callocate( 10 * sizeof( integer ) )
moo[0] = 64

map( foo, integer, 10, moo )

print foo(0)

' either do both or none of these next two.  with both it has to clear the array details
' or the cleanup for the array will double free.  If you have neither of these then
' the cleanup will free the memory
unmap( foo )
deallocate( moo )
Post Reply