How a function return can alone provide an array

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

How a function return can alone provide an array

Post by fxm »

How a function return can alone provide a var-len array, via a reference to the descriptor of that array

To simplify the code, the arrays must first be declared fully unsized (even the number of dimensions) so that their descriptors are allocated with the maximum size (the one corresponding to their type).

From fbc version 1.08, the user can easily access any array descriptor.
The function returns by reference the descriptor of a local but static array.
This array descriptor is then used to temporarily update that of a new not yet sized array.
At the end, this new array will find its initial descriptor (before update)

See the commented code below:

Code: Select all

' Only for a fbc version >= 1.08

#include once "fbc-int/array.bi"

Function returnArray() Byref As FBC.FBARRAY
    ' defining a local array with static storage
    Static As String array0()  '' fully unsized array, so the size allocated for the array descriptor is maximum
    Redim array0(1 To 9, 1 To 5)
    For I As Integer = Lbound(array0, 1) To Ubound(array0, 1)
        For J As Integer = Lbound(array0, 2) To Ubound(array0, 2)
            array0(I, J) = Str(I * 10 + J)
        Next J
    Next I
    ' returning the array descriptor
    Return *FBC.ArrayDescriptorPtr(array0())
End Function

' defining a new fully unsized array and updating its descriptor with the one returned
Dim As String array()  '' fully unsized array to maximize the size allocated for the array descriptor
If (FBC.ArrayDescriptorPtr(array())->flags And FBC.FBARRAY_FLAGS_DIMENSIONS) = FBC.FB_MAXDIMENSIONS And (returnArray().flags And FBC.FBARRAY_FLAGS_DIMENSIONS) = FBC.FB_MAXDIMENSIONS Then
    Swap returnArray(), *FBC.ArrayDescriptorPtr(array())  '' the two descriptors have the same size (the max size)
    ' printing the returned array
    For I As Integer = Lbound(array, 1) To Ubound(array, 1)
        For J As Integer = Lbound(array, 2) To Ubound(array, 2)
            Print "'" & array(I, J) & "'",
        Next J
        Print
    Next I
    ' restoring the array descriptor (before update)
    Swap returnArray(), *FBC.ArrayDescriptorPtr(array())
Else
    Print "Incompatible arrays"
End If

Sleep
Last edited by fxm on Sep 26, 2021 8:07, edited 2 times in total.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How a function return can alone provide an array

Post by fxm »

How a function return can alone provide any array, via a reference to the descriptor of that array

All types of arrays can be declared but with the same number of dimensions, and the target array must be dynamic (in order to have the same descriptor allocated size).
As the allocated size of the descriptors may be lower than the one of their type, the SWAP keyword is no longer usable (otherwise, access to non allocated data).
So a specific memory swapping (descriptorSwap()) must be defined.

From fbc version 1.08, the user can easily access any array descriptor.
The function returns by reference the descriptor of a local but static array.
This array descriptor is then used to temporarily update that of a new not yet sized array.
At the end, this new array will find its initial descriptor (before update)

See the commented code below:

Code: Select all

' Only for a fbc version >= 1.08

#include once "fbc-int/array.bi"

Function descriptorSize(Byref descriptor As FBC.FBARRAY) As Integer
    Return Sizeof(FBC.FBARRAY) - (FBC.FB_MAXDIMENSIONS - descriptor.dimensions) * Sizeof(FBC.FBARRAYDIM)
End Function

Function descriptorSwap(Byref descriptor1 As FBC.FBARRAY, Byref descriptor2 As FBC.FBARRAY) As Boolean
    If descriptor1.dimensions = descriptor2.dimensions Then
        Dim As Integer N = descriptorSize(descriptor1)
        Dim As Byte Ptr p = Allocate(N)
        fb_memcopy(Byval p, descriptor2, N)
        fb_memcopy(descriptor2, descriptor1, N)
        fb_memcopy(descriptor1, Byval p, N)
        Deallocate(p)
        Return True
    End If
    Return False
End Function

Function returnArray() Byref As FBC.FBARRAY
    ' defining a local array with static storage
    Static As String array0(1 To 9, 1 To 5)  '' two-dimension fixed-len array
    For I As Integer = Lbound(array0, 1) To Ubound(array0, 1)
        For J As Integer = Lbound(array0, 2) To Ubound(array0, 2)
            array0(I, J) = Str(I * 10 + J)
        Next J
    Next I
    ' returning the array descriptor
    Return *FBC.ArrayDescriptorPtr(array0())
End Function

' defining a new array and updating its descriptor with the one returned
Dim As String array(Any, Any)  '' two-dimension var-len array
If descriptorSwap(returnArray(), *FBC.ArrayDescriptorPtr(array())) Then
    ' printing the returned array
    For I As Integer = Lbound(array, 1) To Ubound(array, 1)
        For J As Integer = Lbound(array, 2) To Ubound(array, 2)
            Print "'" & array(I, J) & "'",
        Next J
        Print
    Next I
    ' restoring the array descriptor (before update)
    descriptorSwap(returnArray(), *FBC.ArrayDescriptorPtr(array()))
Else
    Print "Incompatible arrays"
End If

Sleep
Last edited by fxm on Sep 26, 2021 8:07, edited 2 times in total.
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: How a function return can alone provide an array

Post by dafhi »

parts from post 1

Code: Select all

'' maybe mention fbc ver. in code.  it looks like that's important b/c u mention it

Function returnArray() Byref As FBC.FBARRAY
    ' unsized array provides maximum allocated descriptor size
    Static As String array0() '' found myself checking for 'array' argument,
                              '' and remembering my early English class days of "no 0 without a 1"
    Redim array0(1 To 9, 1 To 5)
    For I As Integer = Lbound(array0, 1) To Ubound(array0, 1)
        For J As Integer = Lbound(array0, 2) To Ubound(array0, 2)
            array0(I, J) = Str(I * 10 + J)
        Next J
    Next I
    ' return descriptor
    Return *FBC.ArrayDescriptorPtr(array0())
End Function

' empty array to match function
Dim As String array()
Swap returnArray(), *FBC.ArrayDescriptorPtr(array())

' printing the result
For I As Integer = Lbound(array, 1) To Ubound(array, 1)
    For J As Integer = Lbound(array, 2) To Ubound(array, 2)
        Print "'" & array(I, J) & "'",
    Next J
    Print
Next I

'' restoring the array descriptor (before update)
' why is this necessary?
Swap returnArray(), *FBC.ArrayDescriptorPtr(array())

from post 2

Code: Select all

function fxm_ArrayDesc_size(desc as FBC.FBARRAY) as integer
    return Sizeof(FBC.FBARRAY) - (FBC.FB_MAXDIMENSIONS - (desc.flags And FBC.FBARRAY_FLAGS_DIMENSIONS)) * Sizeof(FBC.FBARRAYDIM)
end function

Function descriptorSwap(Byref descriptor1 As FBC.FBARRAY, Byref descriptor2 As FBC.FBARRAY) As Boolean
    Dim As Integer N = fxm_ArrayDesc_size(descriptor1)
    If N = fxm_ArrayDesc_size(descriptor2) then
        Dim As Byte Ptr p = Allocate(N)
        fb_memcopy(*p, descriptor2, N)
        fb_memcopy(descriptor2, descriptor1, N)
        fb_memcopy(descriptor1, *p, N)
        Deallocate(p)
        Return True
    End If
    Return False
End Function

Function functionedArrayDesc() Byref As FBC.FBARRAY
    Static As String array0(1 To 9, 1 To 5) ' fixed size
    For I As Integer = Lbound(array0, 1) To Ubound(array0, 1)
        For J As Integer = Lbound(array0, 2) To Ubound(array0, 2)
            array0(I, J) = Str(I * 10 + J)
        Next J
    Next I
    Return *FBC.ArrayDescriptorPtr(array0())
End Function


' swapping array descriptor with functioned
Dim As String array(Any, Any)
If descriptorSwap(functionedArrayDesc(), *FBC.ArrayDescriptorPtr(array())) Then
    ' printing
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How a function return can alone provide an array

Post by fxm »

dafhi wrote:'' maybe mention fbc ver. in code. it looks like that's important b/c u mention it
OK
dafhi wrote:'' restoring the array descriptor (before update)
' why is this necessary?
Indeed, as there is an exchange of descriptors and not duplication, that would perhaps not be mandatory to restore them although the two arrays are declared in different scopes.
But I still have the habit, when I hack internal data, to always restore the original situation after use (for more safety when the arrays are finally destroyed at program end, because I don't know the associated code from the compiler).
dafhi wrote:function fxm_ArrayDesc_size(desc as FBC.FBARRAY) as integer
return Sizeof(FBC.FBARRAY) - (FBC.FB_MAXDIMENSIONS - (desc.flags And FBC.FBARRAY_FLAGS_DIMENSIONS)) * Sizeof(FBC.FBARRAYDIM)
end function
OK to define a function to calculate the size actually allocated to the array descriptor.

The first two posts have been updated accordingly.
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: How a function return can alone provide an array

Post by dafhi »

fxm wrote:when the arrays are finally destroyed at program end, because I don't know the associated code from the compiler
i can understand that.

i have a habit of assuming environment completeness. rarely fun to be proven wrong
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: How a function return can alone provide an array

Post by D.J.Peters »

@fxm a little bit offtopic but I forgot how to declare an two dimensional array as argument ?
Or is it the same array1d() vs array2d() ?
thank you.

Joshy

Code: Select all

#include once "fbc-int/array.bi"

#ifndef float
type float as double
#endif

function array1d(array() as float) Byref As FBC.FBARRAY
  ' ...
end function
' here are how to declare array() as 2D array(X,Y) ?
function array2d(array(,) as float) Byref As FBC.FBARRAY
  ' ...
end function
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How a function return can alone provide an array

Post by fxm »

D.J.Peters wrote: Jul 27, 2022 13:31 I forgot how to declare an two dimensional array as argument ?

Code: Select all

#include once "fbc-int/array.bi"

#ifndef float
type float as double
#endif

' to refer to an AnyD array(...) as argument
function arrayAnyD(array() as float) Byref As FBC.FBARRAY
  ' ...
end function

' to refer to a 1D array(X) as argument
function array1D(array(Any) as float) Byref As FBC.FBARRAY
  ' ...
end function

' to refer to a 2D array(X, Y) as argument
function array2D(array(Any, Any) as float) Byref As FBC.FBARRAY
  ' ...
end function

' to refer to a 3D array(X, Y, Z) as argument
function array3D(array(Any, Any, Any) as float) Byref As FBC.FBARRAY
  ' ...
end function
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How a function return can alone provide an array

Post by fxm »

@Luxan, @dodicat

Actually your vectors/matrices subject has nothing to do with this thread ('How a function return can alone provide an array').
Ii is better to continue in Easy matrix. I already moved the concerned posts there (from viewtopic.php?p=293589#p293589 up to viewtopic.php?p=293641#p293641).
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: How a function return can alone provide an array

Post by dodicat »

Thanks, better over there.
A quick copier of arrays.

Code: Select all


#include once "fbc-int/array.bi"


#macro copyFromTo(array1,array2)
    *FBC.ArrayDescriptorPtr(array2())=*FBC.ArrayDescriptorPtr(array1())
#endmacro


dim as double d(1 to 3,2 to 4)={{7,8,9}, _
                                {-8.5,2,6}, _
                                {9,9,2}}
                                
dim as string s(3,1 to 2)={{"a","b"}, _
                           {"c","d"}, _
                           {"e","f"}, _
                           {"g","h"}}
                               

dim as double e()
dim as string g()


copyFromTo(d,e)

for n as long=lbound(e,1) to ubound(e,1)
    for m as long=lbound(e,2) to ubound(e,2)
        print e(n,m);" ";
    next
    print
next
print
print lbound(e,1); " to ";ubound(e,1)
print lbound(e,2); " to ";ubound(e,2)
print
print "----------------------"

copyFromTo(s,g)

for n as long=lbound(g,1) to ubound(g,1)
    for m as long=lbound(g,2) to ubound(g,2)
        print g(n,m);" ";
    next
    print
next

print
print lbound(g,1); " to ";ubound(g,1)
print lbound(g,2); " to ";ubound(g,2)
print
sleep

 
A macro is more general than a function.
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How a function return can alone provide an array

Post by fxm »

This is not an array copy, rather it is a descriptor duplication:
- If the e() array is then modified, the d() array will be modified as well.
- At the end of the program, the same array data will be destroyed/deallocated 2 times.

Moreover, if the number of dimensions is known at compile time (case of d() and s() arrays, not e() and g() arrays), then only the number of necessary dimensions is allocated for the array descriptor. Thus, the allocated FBARRAY data may be smaller than the declared FBARRAY structure.
Thus, when the descriptor is duplicated, access to non-allocated data may occur and induce a crash.
(see KeyPgFBArray → fxm [added formula to calculate size of allocated FBARRAY data (may be smaller compared to declared FBARRAY structure)])
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: How a function return can alone provide an array

Post by dodicat »

I know that is not an independent entity.
rather like

dim as double d
dim byref as double d2=d


dim as double x( 5,3,1 to 6, etc.. )
dim byref e()=x()
Where the compiler would say:
Array of references - not supported yet.
{except for a hack}
fxm
Moderator
Posts: 12082
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How a function return can alone provide an array

Post by fxm »

Under the hood, a reference to an entity is just an auto-dereferenced pointer to this entity.
Even for a var-len string, a reference to a var-len string is just an auto-dereferenced pointer to the unique string descriptor. There is no string descriptor duplication.

A reference to an array would just be an auto-dereferenced pointer to the unique array descriptor.

I disagree with the users who say that a reference to an array could be an array of elementary references to the elements of the first array, because in that case there would be two dependent array descriptors to manage.
Luxan
Posts: 222
Joined: Feb 18, 2009 12:47
Location: New Zealand

Re: How a function return can alone provide an array

Post by Luxan »

Generally I'd like to know how to generate an array, say a1(0 to nx, 0 to ny, 0 to p);
where for each index k , a1(0 to nx, 0 to ny, k) the dimensions (nx,ny) are different.

I 'm fairly certain one BASIC language has this feature, I was hoping to encounter this
in FreeBasic too.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: How a function return can alone provide an array

Post by dodicat »

These sort of jagged arrays are not a freebasic feature.
You could get round this problem for 2D arrays by fixing their size (large to begin with) and use only parts of the arrays.
You will have to use the zero'th elements to hold the number of rows and columns, because in matrices you would naturally use only 1 to ... elements
for the calculations.
You will have a spare element (0,0) which you could use for indexing arrays.
I will put an example in the Easy matrix thread.
Luxan
Posts: 222
Joined: Feb 18, 2009 12:47
Location: New Zealand

Re: How a function return can alone provide an array

Post by Luxan »

See my recent reply at Beginners, Easy matrix; for one interpretation of what needs to be
done.
Post Reply