Returning Arrays

General FreeBASIC programming questions.
Psyche_Demon
Posts: 46
Joined: Jul 30, 2015 8:07
Location: England
Contact:

Returning Arrays

Post by Psyche_Demon »

Hi there,
I think I mentioned at one point that I was making a huge high-level library to mirror and perhaps even extend the library built into a previous scripting language I used to create games. This includes versatile arrays, hashmap dictionaries, vectors, timers, sounds, calendars, networking, tone synthesisers, combination algorithms, serialisation, text-to-speech, interface management, input device access, file packaging and encryption just to name a few.

In one post I briefly mentioned about the possibility of returning arrays from a function. I'm still unclear as to how this can be done.

1. Is it possible to return an array directly from a function?

declare function test() as string()
declare function test2() as double(any)

I have tried several variations, but can't seem to do it.
The only thing I have been able to find on here is to pass an array as a byref parameter, which I would rather avoid if at all possible.
There are several functions in my library that would need this (string_split, find_files, find_directories, list_devices, list_voices, generate_combinations etc). I would rather not have to resort to using next/previous handles.

2. How practical is it to make a custom array object?

It would in fact be nice to create my own custom array type that had extra options, such as automatic resizing, reserving memory to optimise performance, search, sort, swap, shuffle etc.
I have seen a few examples of creating custom arrays with fixed datatypes, but I can't find anything that shows me how to make an array that could accept any datatype. In this example, I'm passing a datatype as the constructor. Naturally the below examples wouldn't work out of the box, but serve as examples of what I'm aiming for.

dim my_array as array(string)
dim my_second_array as array(double)
dim my_third_array as array(sound) ''uses a UDT
my_array.insert_last("First element")
print my_array(0)
my_second_array.resize(10)
my_second_array(0)=5.5
my_third_array.resize(1)
my_third_array(0).load("test.wav")

Using UDTs would especially be nice, since this would theoretically allow for multidimensional arrays. Again, I haven't had chance to experiment with any of this as I'm stuck at the first hurdle of datatype access.

dim my_2d_array as array(array(string))
my_2d_array.resize(10)
for x as long=0 to my_2d_array.length-1
my_2d_array(x).resize(10)
for y as long=0 to my_2d_array(x).length-1
my_2d_array(x)(y)=x*y
next
next

In other examples, it also appears you have to use a property to access the array:

my_array.item(0)=1

It would be nice to be able to access the data as you would an ordinary array. I'm guessing that's not possible though since the compiler won't know the difference between the builtin and your custom array?

Again, I would rather not have to create multiple array types:

type array_double
...
end type

type array_string
...
end type

Since all the types will essentially have the same functions, methods, indexing, operator overloads etc. Not only that, but given the number of datatypes and custom datatypes my library has, it would be impractical to maintain a huge list of different array types.

One thing I did consider was to use an any pointer, but then there's the question of how to get the array to cast that to the appropriate datatype when accessing indexes. Otherwise you'd have to cast it yourself when using the array, which I think is rather ugly. Certainly not desirable for a high-level library.

One of my friends says that in C++, templates are good for this sort of thing. Is there an equivalent in FB?

Cheers.
fxm
Moderator
Posts: 12106
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Returning Arrays

Post by fxm »

That reminds me of previous discussions like this one:
Return an array from a Function
greenink
Posts: 200
Joined: Jan 28, 2016 15:45

Re: Returning Arrays

Post by greenink »

Yeh, I avoid arrays now for any kind of library code or code that I intend to reuse. I simply work with pointers. Often that means passing an extra length parameter to sub's and functions. The question is - do pointers generally increase or decrease the overall number of lines of code and the complexity of the code? I often find a reduction in both mainly because you can use pointer arithmetic to simplify indexing manipulations.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Returning Arrays

Post by MrSwiss »

Psyche_Demon wrote:One of my friends says that in C++, templates are good for this sort of thing. Is there an equivalent in FB?
No, there is nothing like that in FB.

About the question of different Data-Types, of Arrays:
you could implement a call-back mechanism, which then uses the correct procedure, to manipulate the array.
The actual handler-procedures (overloaded), are then doing the actual processing (behind the scenes).
Psyche_Demon
Posts: 46
Joined: Jul 30, 2015 8:07
Location: England
Contact:

Re: Returning Arrays

Post by Psyche_Demon »

you could implement a call-back mechanism, which then uses the correct procedure, to manipulate the array.
The actual handler-procedures (overloaded), are then doing the actual processing (behind the scenes).
Not sure how that would work. The callback would still need to know the datatype to know what to call, would it not?
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Returning Arrays

Post by MrSwiss »

Psyche_Demon wrote:The callback would still need to know the datatype to know what to call, would it not?
Yes, that's usually the first param. to the callback.

Simpler approach: just 'overload' the procedures, and let the compiler decide (based on param-type), which one to use.
Psyche_Demon
Posts: 46
Joined: Jul 30, 2015 8:07
Location: England
Contact:

Re: Returning Arrays

Post by Psyche_Demon »

So I would have to make an overloaded function for every conceivable datatype?
My ideal would be:
type array
''Constructor/destructor, functions, properties etc

item(any) as any ''we don't know the datatype until the constructor is called.
end type

constructor array(item_type as datatype)
redim item(any) as item_type
end constructor

That way, I could say:

dim my_array as array(string)

Even if I have to use pointers on the low-level end, I still can't find a way to avoid them the high level end, nor a way to accommodate for all types.
Really sorry about this. I feel like the solution is way more simpler than it seems, it's probably staring me in the face and I'm completely overlooking it somehow. It's very furstrating. I sure do appreciate your help, and especially your patience.
Cheers.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: Returning Arrays

Post by MrSwiss »

Well, I don't, at the moment, see a solution to that dilemma ...
But I'm not the person to speak to, when it comes to OO in FB. I've successfully avoided it, so far.
Psyche_Demon wrote:So I would have to make an overloaded function for every conceivable datatype?
There are not so many of those, since:
  • U-/Integer (don't use in a LIB, size dependent on compiler type used, 4/8 Byte lenght)
    U-/Byte (8bit)
    U-/Short (16bit)
    U-/Long (32bit)
    U-/LongInt (64bit)
    Single (32bit float)
    Double (64bit)
    String
    ZString * lenght or ZString Ptr
Last edited by MrSwiss on Feb 08, 2017 18:18, edited 1 time in total.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Returning Arrays

Post by dodicat »

Just overloaded methods are enough to demonstrate.
These methods (functions) transfer data into each array field, double, long and string.

Code: Select all


'A udt containing only re sizeable arrays

type array
    as double d(any)
    as long i(any)
    as string s(any)
    declare sub print
    declare function transfer overload(a() as double) as array
    declare function transfer overload(a() as long)   as array
    declare function transfer overload(a() as string) as array
end type

sub array.print
    dim as long n
    for n=lbound(d) to ubound(d)
        ..print d(n);
    next
    ..print
     for n=lbound(i) to ubound(i)
        ..print i(n);
    next
    ..print
     for n=lbound(s) to ubound(s)
        ..print s(n);
    next
    ..print
    end sub

function array.transfer overload(a() as double) as array
    redim d(lbound(a) to ubound(a))
    for z as long=lbound(a) to ubound(a)
        d(z)=a(z)
    next
    return this
end function

function array.transfer overload(a() as long) as array
    redim i(lbound(a) to ubound(a))
    for z as long=lbound(a) to ubound(a)
        i(z)=a(z)
    next
    return this
end function

function array.transfer overload(a() as string) as array
    redim s(lbound(a) to ubound(a))
    for z as long=lbound(a) to ubound(a)
        s(z)=a(z)
    next
    return this
end function

dim as double d(...)={1.5,2.5,3.5}
dim as long L(...)={1,2,3,4,5,6,7,8,9,0}
dim as string s(...)={"Free","BASIC"}

dim as array x
'use the methods like subs
x.transfer(d())
x.transfer(L())
x.transfer(s())
x.print

x.constructor 'reset all x 
'use a method as a function
dim as array y=x.transfer(s())
y.print
sleep




 
Psyche_Demon
Posts: 46
Joined: Jul 30, 2015 8:07
Location: England
Contact:

Re: Returning Arrays

Post by Psyche_Demon »

Hi,
That is still limited to only 3 datatypes. If I wanted a fully customised, fully flexible, usable array type, how would I go about that? For instance, how could I declare arrays of both intrinsic and custom datatypes? What if I wanted to have arrays of strings, doubles, files, devices, pages, lists, calendars, timers, sounds, dictionaries, combinations, positions, directions, maps, characters, items, weapons, scenes, states ... the list could go on and on. As my library grows, it would become more and more impractical to have definition lists for every single type of possible array, not only for size but also for maintenance reasons.
Cheers.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Returning Arrays

Post by dodicat »

That's a tall order Psyche_Demon.

Are you a Physicist?, you should be.
Looking for that elusive theory of everything in one structure.
I can only suggest the most general of datatypes (any pointer) and the most general of procedures (macros)

Code: Select all

type udt
    as single f
end type

type array
    as any ptr p(any)
end type

#define value(U,datatype,UDT_Field) *cast(datatype ptr,u.p(n)) UDT_Field

#macro show(u,datatype,UDT_Field)
    for n as long=lbound(u.p) to ubound(u.p)
        print value(u,datatype,UDT_Field);
    next
    print
#endmacro


#macro transfer(u,a,UDT_Field)
redim (u.p)(lbound(a) to ubound(a)) as typeof(a)
    for z as long=lbound(a) to ubound(a)
        u.p(z)=@a(z) UDT_Field
    next
#endmacro


dim as double d(...)={1.5,2.5,3.5}
dim as long L(...)={1,2,3,4,5,6,7,8,9,0}
dim as string s(...)={"Free","BASIC"}
dim as udt g(...)={(13.3),(14.4)}


dim as array x(1 to 4)

transfer(x(1),d,)
transfer(x(2),l,)
transfer(x(3),s,)
transfer(x(4),g,.f)



show(x(1),double,)
show(x(2),long,)
show(x(3),string,)
show(x(4),udt,.f)

sleep 
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Returning Arrays

Post by Tourist Trap »

Maybe you would be interested on variant arrays?
http://www.freebasic.net/forum/viewtopi ... ay#p223304
greenink
Posts: 200
Joined: Jan 28, 2016 15:45

Re: Returning Arrays

Post by greenink »

Are you looking for generic types? Have you tried using macros? Does the code need to be thread safe?
I guess some of the tools available are:
defines
macros
namespaces
pointers
types
scope
Psyche_Demon
Posts: 46
Joined: Jul 30, 2015 8:07
Location: England
Contact:

Re: Returning Arrays

Post by Psyche_Demon »

Dodicat,
That's an interesting solution, but these still wouldn't be able to be accessed like ordinary arrays. Similarly the procedures deal with individual elements on a global type, rather than acting inside the array.
my_array.insert_at(1, "test") ''Insert a string at position 1. Of course this would only work if the array was a string array.
What is very interesting about this though, and something I'd never thought of, is that your solution would allow for arrays of different datatypes per element. This could come in very handy for the dictionary table (key/value pair, associative array, hashmaps or whatever they call them these days). My current array though, is simply looking to be a standard type that can be indexed, reused etc as much as need be. I'm looking to implement an array of a fixed value type, but whose datatype is not known until the constructor is called.
dim my_array as array ''Error, because datatype unknown.
dim my_array1 as array(string) ''Valid, because we specify that the datatype held by the array should be a string.
dim my_array2 as array(array(sound)) ''A 2d array of sounds.
my_array2.resize(5) ''Resize the array with five array elements.
my_array2(0).resize(5) ''Resize array element 0 with 5 sounds.
my_array2(0)(0).load("test.wav" ''Load a sound into 0 0.
my_array1.insert_last("Hi!") ''Insert a string into the next free slot in the array.
my_array1.insert_last("Bye!")
my_array1.sort_ascending() ''Now, 0 will be Bye! and 1 will be Hi!
my_array1.reverse() ''Back to how it was before, coincidentally!
dim position1 as long=my_array1.find("Bye!") ''Returns 1.
dim position2 as long=my_array1.find("Error") ''Returns -1, it's not there.

As you can see, everything is being done from the array itself.
That's the ideal situation.

Tourist Trap:
I took a quick glance of your variant array class, but again it only seems to do integer, single, and string (or ZString). Also it seems like the code used to access these has to make use of pointers, something which I'd rather avoid at the high level layer if at all possible. Like other array examples, it seems to keep a cache of all available intrinsic datatypes.
Again, the datatype isn't so much that it is variant, it's that it is unknown (uninitialised, if you like), until the constructor initialises it.

type array
declare constructor(my_type as datatype)
item(any) as unknown
end type

constructor array(my_type as datatype)
redim item(any) as my_type
end constructor

Again, I know the compiler will flag Unknown as, funnily enough, unknown datatype, but that's the sort of access I'm after. As you see, the array is initialising the items to the appropriate datatype.

Greenink:
I don't know of any "generic datatypes" except for any pointer. I did consider something like the following:

type array
declare constructor(datatype as string)
item as any pointer
end type

As you can see, the datatype in the constructor is specified as a string. But then that would mean an if statement for every single object...Slightly better than managing multiple arrays, but still very hacky in my opinion.
As for macros and threads, those are two areas of programming I haven't yet explored. The scripting languages I came from don't allow for things like that. Personally I thought macros were for replacing text, not writing functions?
My idea is for array to be a type. That way, I could have an array of arrays if necessary.
dim yes as array
This would be as part of my namespace gigalector.storage
dim yes as gigalector.storage.array
or,
using gigalector.storage
dim yes as array
I'm guessing that for my array I would need to somehow use an any pointer somewhere. But the array would have to cast that somehow, so that you could safely say, yes(0)="hello" and that would be converted and assigned to the pointer internally etc.
As I said, I'm getting slightly better with pointers now, so I don't mind writing them to bring something low level to a higher level. However the reason for me writing this high level library in the first place is because pointers is not something I want to have to deal with all the time.
Cheers.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: Returning Arrays

Post by Tourist Trap »

Psyche_Demon wrote: constructor array(my_type as datatype)
redim item(any) as my_type
end constructor

Again, I know the compiler will flag Unknown as, funnily enough, unknown datatype, but that's the sort of access I'm after. As you see, the array is initialising the items to the appropriate datatype.
Freebasic doesn't allow to pass Types as parameters , or only in one special case when you use macros. But it's more about text replacement then.
If you want to pass a fake type, it will have to be a string for instance (typenamestring), and then you will have to use <select case typenamestring> and trigger the correct action depending on the name. But you will have to cover all the possible cases before the code is compiled. FB doesn't allow dynamic typing (like ruby would for instance, and I'm not sure if it has not some restrictions that you may find excessive or not).

At least I can't see another workaround, but who knows?
Post Reply