Modify Function Pointer to Take Additional Parameters

2016-02-18 Thread jmh530 via Digitalmars-d-learn
I'm trying to write a function that will adjust the parameters of 
a function pointer.


In the code below, my goal is to call the function qux with a 
variety of different function pointers (in the actual 
application, I don't have the ability to modify qux). I created a 
function foo that I thought would adjust it properly. The problem 
is that the foo function converts the function pointer into a 
delegate.


I was able to get something that works in this simple example by 
introducing a delegate alias and an alternate definition of qux 
that takes a delegate. However, in my actual application, I can't 
modify what the equivalent of qux would take as parameters.


So I was just curious if there was any other alternative.


alias fp1 = int function(int x);
alias fp2 = int function(int x, int y);

auto foo(T)(T f)
{
static if (is(T == fp2))
return f;
else static if (is(T == fp1))
{
return (int x, int y) => f(x);
}
else
return 0;
}

int bar(int x)
{
return x;
}

int baz(int x, int y)
{
return x + y;
}

int qux(int x, int y, fp2 f)
{
return f(x, y);
}

void main()
{
import std.stdio : writeln;

auto foo_bar = foo(&bar);

writeln(qux(1, 2, foo_bar)); //compiler error
writeln(qux(1, 2, &baz));
}



Re: Modify Function Pointer to Take Additional Parameters

2016-02-19 Thread Nicholas Wilson via Digitalmars-d-learn

On Friday, 19 February 2016 at 05:41:01 UTC, jmh530 wrote:
I'm trying to write a function that will adjust the parameters 
of a function pointer.




I think the problem is that it defaults to a delegate not that it 
cannot be one

does clarifying this to the compiler work

Like

alias fp1 = int function(int x);
alias fp2 = int function(int x, int y);

auto foo(T)(T f)
{
static if (is(T == fp2))
return f;
else static if (is(T == fp1))
{
return int function(int x, int y) => f(x);
}
else
return 0;
}


?


Re: Modify Function Pointer to Take Additional Parameters

2016-02-19 Thread Kagamin via Digitalmars-d-learn

On Friday, 19 February 2016 at 05:41:01 UTC, jmh530 wrote:

void main()
{
import std.stdio : writeln;

auto foo_bar = foo(&bar);

writeln(qux(1, 2, foo_bar)); //compiler error
writeln(qux(1, 2, &baz));
}


int bar(int x)
{
return x;
}

int baz(int x, int y)
{
return bar(x);
}

void main()
{
import std.stdio : writeln;

int function(int x, int y) foo_bar = &baz;

writeln(foo_bar(1, 2));
}



Re: Modify Function Pointer to Take Additional Parameters

2016-02-19 Thread jmh530 via Digitalmars-d-learn
On Friday, 19 February 2016 at 11:26:56 UTC, Nicholas Wilson 
wrote:


Like

alias fp1 = int function(int x);
alias fp2 = int function(int x, int y);

auto foo(T)(T f)
{
static if (is(T == fp2))
return f;
else static if (is(T == fp1))
{
return int function(int x, int y) => f(x);
}
else
return 0;
}


?


This code doesn't compile for me. I have to adjust it to

return function int(int x, int y) => f(x);

and then it complains that it's a delegate.


Re: Modify Function Pointer to Take Additional Parameters

2016-02-19 Thread jmh530 via Digitalmars-d-learn

On Friday, 19 February 2016 at 14:21:26 UTC, Kagamin wrote:


int bar(int x)
{
return x;
}

int baz(int x, int y)
{
return bar(x);
}

void main()
{
import std.stdio : writeln;

int function(int x, int y) foo_bar = &baz;

writeln(foo_bar(1, 2));
}


This works.

But when I re-write foo to take that into account as in below, I 
get an error that I can't implicitly convert int function(int x) 
to int function(int x, int y).


auto foo(T)(T f)
{
static if (is(T == fp2))
{
return f;
}
else static if (is(T == fp1))
{
int function(int x, int y) f_ = f;
return f_;
}
else
{
return 0;
}
}


Re: Modify Function Pointer to Take Additional Parameters

2016-02-19 Thread jmh530 via Digitalmars-d-learn

On Friday, 19 February 2016 at 15:00:51 UTC, jmh530 wrote:


This works.

But when I re-write foo to take that into account as in below, 
I get an error that I can't implicitly convert int function(int 
x) to int function(int x, int y).



I don't think I had looked at what you had done carefully enough. 
Basically, you just define a new function and take a function 
pointer of that. That might be a brute force solution.


I tried to use a cast (below) to modify the function pointer, but 
it is printing the second number instead of the first. I find 
this behavior strange...


int foo(int x)
{
return x;
}

void main()
{
import std.stdio : writeln;

auto foo_ = cast(int function(int x, int y)) &foo;

writeln(foo_(1, 200)); //prints 200
}


Re: Modify Function Pointer to Take Additional Parameters

2016-02-19 Thread Yuxuan Shui via Digitalmars-d-learn

On Friday, 19 February 2016 at 20:45:23 UTC, jmh530 wrote:

On Friday, 19 February 2016 at 15:00:51 UTC, jmh530 wrote:


This works.

But when I re-write foo to take that into account as in below, 
I get an error that I can't implicitly convert int 
function(int x) to int function(int x, int y).



I don't think I had looked at what you had done carefully 
enough. Basically, you just define a new function and take a 
function pointer of that. That might be a brute force solution.


I tried to use a cast (below) to modify the function pointer, 
but it is printing the second number instead of the first. I 
find this behavior strange...


int foo(int x)
{
return x;
}

void main()
{
import std.stdio : writeln;

auto foo_ = cast(int function(int x, int y)) &foo;

writeln(foo_(1, 200)); //prints 200
}


I don't think it's safe to convert between function pointer with 
different number of arguments... It's possible to mess up the 
stack frame.


Also '(int x, int y)=>f(x)' is clearly a delegate because it 
refers to local variable 'f', you can't just cast it to 'int 
function(int, int)'.


Re: Modify Function Pointer to Take Additional Parameters

2016-02-19 Thread Chris Wright via Digitalmars-d-learn
On Fri, 19 Feb 2016 20:45:23 +, jmh530 wrote:

> I tried to use a cast (below) to modify the function pointer, but it is
> printing the second number instead of the first. I find this behavior
> strange...

If you want to cast function pointers successfully, you have to know the 
D calling convention.

See: https://dlang.org/spec/abi.html

Notably: "The last parameter is passed in EAX [a CPU register] rather 
than being pushed on the stack". So foo expected an argument in EAX, and 
it dealt with that. Calling foo_ pushes '1' onto the stack, sets EAX to 
'200', and then jumps to the function address.

(But note also: "The callee cleans the stack." So if you pass, say, a 
struct that has a destructor instead of an integer, that means the struct 
destructor won't be called. I was a little surprised that the stack 
pointer is correctly restored.)

If you had more arguments, you'd find similar results -- the last 
argument always goes to EAX, previous arguments are pushed on the stack 
in order, so you're always ignoring a prefix of the arguments. But it's a 
byte-wise prefix, so if you change the types, you'll see more significant 
changes.

Casting function pointers is "here be dragons" territory. Unfortunately, 
it's got the same syntax as routine stuff like integer truncation and 
class casts, so it looks deceptively safe.


Re: Modify Function Pointer to Take Additional Parameters

2016-02-19 Thread jmh530 via Digitalmars-d-learn

On Friday, 19 February 2016 at 22:07:25 UTC, Chris Wright wrote:


If you want to cast function pointers successfully, you have to 
know the D calling convention.


[snip]


I figured there was an explanation. Definitely "here be dragons" 
territory. I hope I can figure out a better solution, but the 
behavior I'm trying to get is really just a nice to have.


Re: Modify Function Pointer to Take Additional Parameters

2016-02-19 Thread Chris Wright via Digitalmars-d-learn
On Fri, 19 Feb 2016 21:57:46 +, Yuxuan Shui wrote:

> I don't think it's safe to convert between function pointer with
> different number of arguments... It's possible to mess up the stack
> frame.

I tested this a fair bit today, and I haven't been able to do any of the 
nefarious things I expected to be able to do. No overwriting variables in 
the caller's scope, no smashing stack pointers, etc.

I was surprised by this result, but in retrospect, it's relatively 
obvious. The caller pushes variables onto the stack and sets the stack 
pointer for the callee. It wouldn't send a stack pointer that pointed 
into its own stack frame.


Re: Modify Function Pointer to Take Additional Parameters

2016-02-19 Thread jmh530 via Digitalmars-d-learn

On Friday, 19 February 2016 at 22:34:48 UTC, Chris Wright wrote:


I tested this a fair bit today, and I haven't been able to do 
any of the nefarious things I expected to be able to do. No 
overwriting variables in the caller's scope, no smashing stack 
pointers, etc.


I was surprised by this result, but in retrospect, it's 
relatively obvious. The caller pushes variables onto the stack 
and sets the stack pointer for the callee. It wouldn't send a 
stack pointer that pointed into its own stack frame.


Thanks for taking the time to test.

The more I've thought about it, the more I wonder if there should 
be a restriction so that casts of function pointers/delegate 
maintain the same number of parameters. Even though you haven't 
been able to do nefarious things, it's giving a completely wrong 
answer than you would expect. The result of the answer might 
cause bad things to happen in a program. Further, to even 
understand what's going wrong you have to understand how the 
compiler is generating assembly. I've been using D for like a 
year or so, and I would never have been able to figure out the 
reason by myself.


Or at least in safe code you shouldn't be able to do this.