October 30, 2025, 12:50:44 PM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


Function pointer syntax

Started by Parker, January 17, 2006, 05:14:14 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Parker

Just thinking out loud here, these are a couple of possible syntaxes I thought of for indirect function calls:
sub(int x, int y),int MyFunction;
MyFunction = &Something;
z = MyFunction(1,2);

or
sub *MyFunction(int x, int y),int;
MyFunction = &Something;
z = MyFunction(1,2);


I'm just trying for something that lets us get around the !<> syntax which I never really liked.

Zen

i didnt mind it although i would like something that i can use to create dynamic function templates with.

Lewis

Parker

Here's a hack you can use right now. Use a project. In one source file put this:
global __imp_FnPtr;
unsigned int __imp_FnPtr;

global sub SetFnPtr(unsigned int fnptr)
{
    __imp_FnPtr = fnptr;
}


To call the function, do something like this:
declare import, FirstFunction alias FnPtr(int i);
declare import, SecondFunction alias FnPtr(string s, int x),int;
declare extern SetFnPtr(unsigned int fnptr);

...
SetFnPtr(&Something);
FirstFunction(1);
SetFnPtr(&SomethingElse);
SecondFunction("Hello", 0);

Zen

Cool. Ill give it a go. Im waiting on the real thing for my plugin system and maybe do something for another common class for dynamic loading of DLL files.

Lewis

Ionic Wind Support Team

The second one was what I was planning on doing.

Paul.
Ionic Wind Support Team

Zen

Can either Paul or Parker exaplain what is happening in the second example. I dont quite think i am understanding it. I know the first line is the function declaration, just like it was in IBasic. What is the MyFunction = &Something about? Is that a handle to the function returned by GetProcAddress?

Lewis

Parker

January 17, 2006, 09:56:59 PM #6 Last Edit: January 17, 2006, 10:06:32 PM by Parker
It's not GetProcAddress, you can actually use it with anything. It doesn't even have to be a function, although SP2 will kill your program if you try to execute data. But yes, you will be able to do something like this:
sub *ExitProcess(int dwExitCode);
ExitProcess = GetProcAddress(hKernel32, "ExitProcess");
ExitProcess(0);


Edit - to clear it up a little more, & is the address of operator. It can be used on any variable or subroutine, or anything that has an address really. When you declare a function pointer, you have to give it an address before you call it. Calling 0 would have some strange effect and it would probably be stopped since address 0 is some windows code, I would think.

I've never written a linker so I can't say exactly, but I think that linkers clean up all these labels and resolve them into addresses, which windows does further when it loads your executable. So let's pretend you have a function (MyFunction) that is located at address 0xFFFF. And let's say you have a variable (MyVar) containing the value 0xFFFF. You can call that in two ways. One of which is to directly call the function
call MyFunction ; gets resolved by the linker into: call 0xFFFF
or to call the address contained in the variable
call [MyVar] ; MyVar contains the address of the function, 0xFFFF

It's also important to know that whenever you reserve space for a variable in assembly, the label that you have is an address. So while in Aurora you might do MyVar(), in assembly you have to dereference it first. Using sub* as the type instead of UINT might also give you a better feeling that it's being dereferenced instead of just calling a variable.

Zen

I guessed as much. I just wanted to make sure.

Lewis

sapero

yet another call - version for stdcall://extern int farcall(...);
declare farcall(...),int;

extern MessageBeep as int; // :D
extern Sleep as int;

global sub main
{
farcall(&myFunction1,"blah\n",2);
farcall(&myFunction2,"again ", "blah\n",2);
farcall(&MessageBeep, 16); // speakers on!
farcall(&Sleep, 500);
MessageBox(0, "paused...","");
return;
}


sub myFunction1(string s)
{
openconsole(); writeln("myFunction1 :"+s);
return;
}
sub myFunction2(string s, string d)
{
openconsole(); writeln("myFunction2 :"+s+d);
return;
}



// for stdcall
#asm
global farcall

; extern int farcall(...);
; ret=farcall(&MyFunction, 3, "string", lpPoint ...);

farcall:
enter 0,0
push  esi

mov   esi,[ebp+4] ; get argc [in bytes]
mov   ecx,[esi+2]

sub   ecx,4       ; -1 argument
; * save ecx for cdecl *
jz   .call
lea   esi,[ebp+12-4+ecx]; last parameter
shr   ecx,2       ; [in dwords]
.next
push  dword[esi] ; push all parameters from right to left
sub   esi,4
dec   ecx
jnz  .next
.call
call [ebp+8] ; and call stdcall function
; * add esp,saved ecx for cdecl *
.quit
pop   esi
leave
ret
#endasm

Zen

Thanks a lot sapero. This helped a lot. Also leart a little bit too ;)

Lewis

Ionic Wind Support Team

Parker will appreciate this.

This works now:


declare *test(int a),int;

global sub main()
{
test = &MyFunction;
writeln(str$(test(1)) + "\n");
while GetKey() = "";
}

sub MyFunction(int a),int
{
return a;
}
Ionic Wind Support Team

Parker

Yay :D
Will the compiler do type checking on function pointers? They're probably the ones that need typechecking the most, since it could crash if you pass a function with the wrong number of parameters. I don't know how passing parameters would work yet though.

I actually was needing this earlier today and resorted to my "hack" that I posted above. But it'll be nice to have it in the language. I'll have to update the dictionary class to not call with asm now when this update is released.

Ionic Wind Support Team

Well it checks the parameters if that is what you meant.  The pointer itself is just an address.

Ionic Wind Support Team