3

Went back to good old qbasic for nostalgic reasons and have never used types and functions in qbasic before as I was very young that time.

TYPE Vector2
    x AS SINGLE
    y AS SINGLE
END TYPE

FUNCTION Vector2Mag (a AS Vector2)
    Vector2Mag = SQR((a.x * a.x) + (a.y * a.y))
END FUNCTION

FUNCTION Vector2Add (a AS Vector2, b AS Vector2)
    DIM r AS Vector2
    r.x = a.x + b.x
    r.y = a.y + b.y
    Vector2Add = r
END FUNCTION

But i get

Illegal SUB/FUNCTION parameter on current line

using qb64 in both first function lines. Google didn't help as it looks like I am doing everything right. I checked passing multiple variables, specifying a type for a parameter, how to use types but nothing really helped.

Thanks guys.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
Silberling
  • 160
  • 17
  • From the title I thought this was going to be how to pass the type name Vector2 into the function. I was looking for a way to do something like that a while ago. Also passing a function/sub pointer into another function/sub. – majinnaibu Apr 14 '23 at 08:14

4 Answers4

3

Defining or using user-defined variables inside functions is illegal in QB. It doesn't matter if the function is declared by DEF FNname ... END DEF or FUNCTION ... END FUNCTION

What you can do is send a pointer to the address of a user-defined variable, then the function/sub uses the address to read it directly from memory. The elements of a userdefined variable are stored exactly in the order they are defined. In this example, a (two byte integer) will be stored first, in big endian format, then b, a total of four bytes.

TYPE xtyp
DIM a AS INTEGER
DIM b AS INTEGER
END TYPE
DIM var AS xtyp
var.a = 5
var.b = 7
DEF SEG = VARSEG(var)
PRINT "The value of var.a and var.b multiplied is"; mpl(VARPTR(var))
END
'-------------------------- End of main program, function begins here    -------
FUNCTION mpl(addr)
factor1 = PEEK(addr) + PEEK(addr + 1) * 256
factor2 = PEEK(addr + 2) + PEEK(addr + 3) * 256
mpl = factor1 * factor2
END FUNCTION

DEF SEGis used to set the current segment, and VARSEG()returns the segment of a numeric or user defined variable. PEEK()is used to read a byte from a certain memory-position, and VARPTR()returns the address of a numeric or user-defined variable in it's segment. Note that the above code assumes both factors are unsigned. If they are signed, the transforming of individual bytes into integer numbers must be slightly different.

Alpha_Pi
  • 383
  • 2
  • 13
1

It has been a while, but I believe the problem is actually that you can't return a UDT (user defined type, a.k.a. "any type that is not built in"). What you need to do is pass a third argument to Vector2Add and make it a SUB. For example:

SUB Vector2Add (r AS Vector2, a AS Vector2, b AS Vector2)
    r.x = a.x + b.x
    r.y = a.y + b.y
END SUB

The SUB is almost an exact translation with equivalent C code, aside from syntax differences. My reasoning is that you usually add a type suffix to the name of a FUNCTION in QB or it will use its default type, which may have been overridden by DEFxxx M-N (or _DEFINE in QB64; and no you can't use _DEFINE with UDTs). For example, returning a string:

'Default type for all identifiers beginning with 'S' is STRING.
' Type suffixes, "AS xxxx" clauses and future "DEFxxx" items override the behavior.
DEFSTR S-S

FUNCTION swapFirstLast$ (s)
    swapFirstLast$ = RIGHT$(s, 1) + MID$(s, 2, LEN(s) - 2) + LEFT$(s, 1)
END FUNCTION

QB64 is kinda limited in this regard since it aims to be as compatible with the syntax used by QuickBASIC 4.5 as possible. FreeBASIC, another language based on QB, has no such restriction:

'Notice the "AS Vector2" at the end of this next line and the RETURN statement
' that FB introduced for FUNCTIONs (originally it was used to return from an
' "old-style" subroutine that was invoked via the GOSUB statement).
FUNCTION Vector2Add (a AS Vector2, b AS Vector2) AS Vector2
    DIM r AS Vector2
    r.x = a.x + b.x
    r.y = a.y + b.y
    RETURN r
END FUNCTION

The important point to remember is that QB64 is basically still QB, except it will compile code to run on modern operating systems (rather than DOS). FreeBASIC, on the other hand, chose to sacrifice some compatibility in favor of creating a more "modern" language that retains QB's syntax.

  • 1
    I already thought of using a c-style third parameter but i tried finding another solution first. I'm going to try both solutions so I get a little bit more familiar with the different basic dialects. thank you for your good explanation. – Silberling Feb 07 '16 at 17:42
  • I emulate OOP classes by creating a UDT to hold the data and then functions/subs that take that UDT as the first parameter like for a constructor I `Dim ms as MenuStyle` and then `Call NewMenuStyle(ms, ...)` since the parameter is passed by reference you don't need to return it. In this case the ms parameter is an "out" param not an "in" one. It's kinda like the alloc:init pattern in Objective-C. The main downside is having to use Call to call subs and having to assign the return value of a function to something even if I don't need it. It's pretty much what C++ actually does under the hood. – majinnaibu Apr 14 '23 at 08:08
1

have the function write to a shared variable and have it return -1 to indicate success

TYPE testType
    x AS INTEGER
    y AS INTEGER
END TYPE
DIM SHARED ret AS testType
DIM a AS testType,b as testType
a.x=5
b.x=7
IF add(a,b) THEN a=ret
FUNCTION add(a AS testType, b AS testType)
ret.x=a.x+b.x
ret.y=a.y+b.y
add=-1
END FUNCTION

this allows for easier handling of non-fatal errors than making it a SUB

SkyCharger
  • 11
  • 1
0

Instead of trying to return a UDT to a function name, use the global UDT:

TYPE Vector2
    x AS SINGLE
    y AS SINGLE
END TYPE

DIM SHARED r AS Vector2

FUNCTION Vector2Mag (a AS Vector2)
Vector2Mag = SQR((a.x * a.x) + (a.y * a.y))
END FUNCTION

FUNCTION Vector2Add (a AS Vector2, b AS Vector2)
r.x = a.x + b.x
r.y = a.y + b.y
'Vector2Add = r
END FUNCTION
eoredson
  • 1,167
  • 2
  • 14
  • 29