Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
SUB & FUNCTION when declaring dynamic library
#1
I just ran across something that is confusing me.

The User32 function SetCursorPos as outlined here in the Microsoft docs:

https://learn.microsoft.com/en-us/window...tcursorpos

is a function. However, it's being declared as a SUB in the Wiki example code here:

https://qb64phoenix.com/qb64wiki/index.p...Mouse_Area

At first I though this was a typo but the code works. Is this a typo that somehow works? Or is it possible to declare FUNCTIONs as SUBs in certain circumstances?
New to QB64pe? Visit the QB64 tutorial to get started.
QB64 Tutorial
Reply
#2
Terry, the return value is simply discarded, but SetCursorPos  should be declared as a function
Code: (Select All)

DECLARE DYNAMIC LIBRARY "User32"
  FUNCTION SetCursorPos~& (BYVAL x AS LONG, BYVAL y AS LONG) 'move cursor position
END DECLARE

btw Terry, you probably won't be able to see my post as I seem to be on your ignore list Sad
Reply
#3
In c, there really is no SUB or FUNCTION.   The main difference is that what basic considers to be a SUB would basically be a Function with a return; with no value attached to it.

For example, let's look at the simple sub_preset:
Code: (Select All)
void sub_preset(float x, float y, uint32 col, int32 passed) {
    if (is_error_pending())
        return;
    if (!(passed & 2)) {
        col = write_page->background_color;
        passed |= 2;
    }
    sub_pset(x, y, col, passed);
    return;
}

See the return there, with no value attached.  That's basically what we'd call a SUB.

And compare that to the FUNCTION Console:
Code: (Select All)
int32 func__console() {
    if (is_error_pending())
        return -1;
    return console_image;
}

See where we return the screen handle for the console there?

That's basically the difference in what we consider to be SUB vs FUNCTION, on the c-side of things.



So, the person who wrote the example for us basically decided that they didn't need the error return value, and they'd rather call it as a SUB than as an errorcode = FUNCTION type routine.

Unless you need the return value, for most c-style routines (like the windows API), you can use SUB instead of FUNCTION. 

Now, if you want that return value, then you're definitely going to want to use FUNCTION when you reference it.  Wink
Reply
#4
The short answer is that there are cases where the declaration does not have to be exact and it will still work. You're much better off writing accurate declarations though, declaring it as a `FUNCTION` is correct.

The long answer involves discussion of x86 calling conventions, which are the underlying rules involved in calling functions. In this case it works out because (assuming 64-bit) the RAX register will be used to store the return value, and RAX is also a caller-saved register. This means that when calling the `GetCursorPos()` function the caller has to assume the function may mess with the value of RAX and back it up beforehand if it needs to be kept (regardless of whether the function actually returns anything useful in it). The `GetCursorPos()` will then place the return value in `RAX`, and the calling code will simply ignore whatever value is left in there.

Note that this does not always work, you can see in the Windows section that if a structure is too large to fit into RAX then a different convention of the caller allocating space for the result and passing it to the function is used. In this case, if you did not list the correct return value then things would go very wrong (but then, you can't actually declare such a function in QB64, so it doesn't matter that much).

The different kinds of `DECLARE LIBRARY` are also different levels of forgiving in this regard, because while `Dynamic` will force the function to be the declared type, others like regular `Declare Library` simply insert a call to the specified function and rely in the definition already being provided in the C++ code (via a header or elsewhere).
Reply
#5
In other words: DECLARE LIBRARY is messy.    Big Grin
Reply
#6
(06-09-2024, 05:33 PM)TerryRitchie Wrote: I just ran across something that is confusing me.

The User32 function SetCursorPos as outlined here in the Microsoft docs:

https://learn.microsoft.com/en-us/window...tcursorpos

is a function. However, it's being declared as a SUB in the Wiki example code here:

https://qb64phoenix.com/qb64wiki/index.p...Mouse_Area

At first I though this was a typo but the code works. Is this a typo that somehow works? Or is it possible to declare FUNCTIONs as SUBs in certain circumstances?

In the world o' BASIC, we have functions and subroutines.  Functions for when we want a value returned, subroutine when we don't want a value returned (I.e. we just want to call the subroutine.)

In the Windows API, we have functions but we do not have subroutines.

When you don't care about the return value for any Windows API function, go ahead and declare it (and use it) as a SUB.  (If the return value is a boolean, as in for SUCCESS or FAILURE, you decide whether or not you want to check that for error handling;  if so, then declare as a function;  if not, then SUB is fine.)

When you do care about the return value for any Windows API function, go ahead and declare it (and use it) as a FUNCTION.

Aside: I'm a Gupta Team Developer programmer by trade.  It has functions but does not have subroutines.  Every function can be used in an expression when we expect a value returned from it.  Every function can also be directly called (like a BASIC subroutine), and the return value is ignored.  (Functions can be defined as either returning a value or not returning any value.)
Reply
#7
(06-09-2024, 06:02 PM)Jack Wrote: btw Terry, you probably won't be able to see my post as I seem to be on your ignore list Sad

Huh, you are not on my ignore list? The only one I've ever ignored on a QB64 forum was Clippy. Smile

Thanks for the clarification everyone. I'll stick with declaring them as a function.
New to QB64pe? Visit the QB64 tutorial to get started.
QB64 Tutorial
Reply
#8
I miss Clippy Big Grin , yes my political  views were opposite his but I enjoyed Pete's interaction with him.
besides, I have learned not to take politics too seriously.
Reply
#9
I think there's something worth clarifying here in regards to `Declare Library` that is non-obvious. In C++ there's a big difference between the two lines in the `mycode` function here:

Code: (Select All)
int externalfunc(int);

int mycode() {
    externalfunc(2); // The return value of externalfunc(2) is ignored

    ((void(*)(int))externalfunc) (2); // externalfunc is treated as a function with no return value, and then called.
}

The first line of `mycode()` is perfectly valid C++ where the return value of a function is ignored. The second line is invalid (but still compiles and runs) where the external function is treated as having no return value at all. C++ makes no guarantees that the second one works, and indeed there are cases where the function will be called incorrectly.

For `Declare Library`, this becomes relevant. If you use regular `Declare Library` to call a function that's already accessible, then the call turns into a line like the first line in `mycode()`. Declaring it as a `SUB` does not matter because the declaration is still correct and the return value simply gets ignored.

If you use `Declare Dynamic Library` or `Declare CustomType Library` then your code is actually turned into something similar to the second line, where the `DECLARE` line is used to generate the function signature. This is where you can get into trouble if you introduce a mismatch.
Reply
#10
I often declare external functions as both SUBs and FUNCTIONs (QB64 does not care if you have a SUB and a FUNCTION with the same name), depending on whether or not I'll need to keep the return value. Rather than just make a throwaway temp variable for the function, I'll use it as a SUB and get the same functionality. It boils down to preference on some stuff.
Tread on those who tread on you

Reply




Users browsing this thread: 7 Guest(s)