Thread overview | ||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
July 07, 2015 Understanding Safety of Function Pointers vs. Addresses of Functions | ||||
---|---|---|---|---|
| ||||
I'm not sure I understand the safety of function pointers vs. the addresses of functions. The code below illustrates the issue. I was under the impression that pointers are not allowed in safe code. Naturally, I took that to also mean that function pointers are not allowed in safe code. Indeed, I haven't been able to pass a function pointer to a safe function. However, I am able to take the address of a function and pass that as a parameter. It seems to work fine for taking the address of functions and templates (so long as I !) import std.stdio : writeln; import std.traits; import std.math; void function_safety(T)(T fp) { if (functionAttributes!fp & FunctionAttribute.safe) writeln("fp is safe"); else if (functionAttributes!fp & FunctionAttribute.trusted) writeln("fp is trusted"); else if (functionAttributes!fp & FunctionAttribute.system) writeln("fp is system"); else writeln("fp is neither safe nor trusted nor system"); } void main() { function_safety(&cbrt); //prints fp is trusted real function(real) fp = &cbrt; function_safety(fp); //prints fp is system } One other related issue. The code above works for some parts of std.math, but it doesn't for all of it. I thought it was that some of them are defined in other places, so I used static import and tried function_safety(&std.math.cos); but I'm still getting this error (other people seem to have an issue with linking and getting it, but I'm not making any other change and shouldn't need to link anything). OPTLINK (R) for Win32 Release 8.00.17 Copyright (C) Digital Mars 1989-2013 All rights reserved. http://www.digitalmars.com/ctg/optlink.html [path]\AppData\Local\Temp\.rdmd\rdmd-testing_fp_template_purity.d-A5DD C99AC50E1539BAB8627648C9740B\objs\testing_fp_template_purity.exe.obj(testing_fp_ template_purity.exe) Error 42: Symbol Undefined _D3std4math3cosFNaNbNiNfeZe --- errorlevel 1 |
July 07, 2015 Re: Understanding Safety of Function Pointers vs. Addresses of Functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to jmh530 | On Tuesday, 7 July 2015 at 19:54:19 UTC, jmh530 wrote: > I'm not sure I understand the safety of function pointers vs. the addresses of functions. The code below illustrates the issue. > > I was under the impression that pointers are not allowed in safe code. No, pointers are fine. It's pointer arithmetic that's considered unsafe. > Naturally, I took that to also mean that function pointers are not allowed in safe code. Indeed, I haven't been able to pass a function pointer to a safe function. However, I am able to take the address of a function and pass that as a parameter. It seems to work fine for taking the address of functions and templates (so long as I !) So long as you exclamation mark? Huh? > import std.stdio : writeln; > import std.traits; > import std.math; > > void function_safety(T)(T fp) > { > if (functionAttributes!fp & FunctionAttribute.safe) > writeln("fp is safe"); > else if (functionAttributes!fp & FunctionAttribute.trusted) > writeln("fp is trusted"); > else if (functionAttributes!fp & FunctionAttribute.system) > writeln("fp is system"); > else > writeln("fp is neither safe nor trusted nor system"); > } > > void main() > { > function_safety(&cbrt); //prints fp is trusted > real function(real) fp = &cbrt; You're explicitly typing that as `real function(real)` which is not an @safe type. Add @safe and you're good to go: real function(real) @safe fp = &cbrt; function_safety(fp); /* prints "fp is safe" */ Or let the compiler infer things: auto fp = &cbrt; function_safety(fp); /* prints "fp is trusted" */ > function_safety(fp); //prints fp is system > } |
July 07, 2015 Re: Understanding Safety of Function Pointers vs. Addresses of Functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to jmh530 | On Tue, Jul 07, 2015 at 07:54:18PM +0000, jmh530 via Digitalmars-d-learn wrote: > I'm not sure I understand the safety of function pointers vs. the addresses of functions. The code below illustrates the issue. Function pointers and addresses of functions are the same thing. There is no difference between them. > I was under the impression that pointers are not allowed in safe code. This is incorrect. Pointers are allowed in safe code as long as they are not manipulated in unsafe ways (e.g., assigning a literal value to them, pointer arithmetic, casting to a pointer to an incompatible type, using a pointer to call an un-@safe function from @safe code, etc.). > Naturally, I took that to also mean that function pointers are not allowed in safe code. Indeed, I haven't been able to pass a function pointer to a safe function. However, I am able to take the address of a function and pass that as a parameter. It seems to work fine for taking the address of functions and templates (so long as I !) The reason for this is that function pointers have attributes associated with them, as part of the type system. The address of a @safe function is a @safe function pointer, meaning that it's legal to call the function through this pointer in @safe code. The address of a @system function is a @system function pointer, which *cannot* be used to call the function from @safe code. However, while it is not allowed to assign a @system function pointer to a @safe function pointer (since that could be used to bypass the @safe restriction on calling @system functions), it is OK to assign a @safe function pointer to a @system function pointer (this is called covariance). This is allowed because a @system function pointer can only be dereferenced in @system code, and @system code can freely call any function with no restrictions. Where this might become a problem, though, is when you inadvertently assign a @safe function pointer to a @system function pointer, and then attempt to call it from @safe code -- the compiler will reject that. This is what happened with your sample code: [...] > void main() > { > function_safety(&cbrt); //prints fp is trusted > real function(real) fp = &cbrt; Here, fp is a @system function pointer. While &cbrt is a @safe function pointer, it is covariant with a @system function pointer, so the assignment is allowed. > function_safety(fp); //prints fp is system [...] The result, however, is now @system, so you can no longer call cbrt from @safe code through this particular function pointer (though you can obviously call it through a @safe function pointer). The correct syntax for declaring a @safe function pointer would be: real function(real) @safe fp = ...; Either that, or use `auto` to let the compiler figure out the correct function pointer attributes for you: auto fp = &cbrt; pragma(msg, typeof(fp).stringof); The compiler prints: real function(real x) nothrow @nogc @trusted So you see here that two other attributes, nothrow and @nogc, are also inferred by the compiler, which is a good thing because otherwise your function pointer wouldn't be usable from nothrow or @nogc code, respectively. T -- Music critic: "That's an imitation fugue!" |
July 07, 2015 Re: Understanding Safety of Function Pointers vs. Addresses of Functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to anonymous | On Tuesday, 7 July 2015 at 20:13:08 UTC, anonymous wrote:
>
> So long as you exclamation mark? Huh?
>
Thanks for the detailed answer. All I meant here is that if I have some
T foo(T)(T x), then to take the address, sometimes I've needed to &foo!int or &foo!real, etc.
|
July 07, 2015 Re: Understanding Safety of Function Pointers vs. Addresses of Functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to H. S. Teoh | On Tuesday, 7 July 2015 at 20:23:08 UTC, H. S. Teoh wrote: > > The reason for this is that function pointers have attributes associated with them, as part of the type system. The address of a @safe function is a @safe function pointer, meaning that it's legal to call the function through this pointer in @safe code. The address of a @system function is a @system function pointer, which *cannot* be used to call the function from @safe code. > > However, while it is not allowed to assign a @system function pointer to a @safe function pointer (since that could be used to bypass the @safe restriction on calling @system functions), it is OK to assign a @safe function pointer to a @system function pointer (this is called covariance). This is allowed because a @system function pointer can only be dereferenced in @system code, and @system code can freely call any function with no restrictions. Where this might become a problem, though, is when you inadvertently assign a @safe function pointer to a @system function pointer, and then attempt to call it from @safe code -- the compiler will reject that. This is what happened with your sample code: > This, and the rest, does such a good job explaining why I have been so confused. > > Either that, or use `auto` to let the compiler figure out the correct function pointer attributes for you: > > auto fp = &cbrt; > pragma(msg, typeof(fp).stringof); > I've tried using auto, but I get errors when I use auto with a function that has been overloaded. For instance, creal function(creal) pure nothrow @nogc @safe fp = &conj; compiles, but auto fp = &conj; does not. |
July 07, 2015 Re: Understanding Safety of Function Pointers vs. Addresses of Functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to jmh530 | On Tuesday, 7 July 2015 at 20:32:49 UTC, jmh530 wrote:
> Thanks for the detailed answer. All I meant here is that if I have some
> T foo(T)(T x), then to take the address, sometimes I've needed to &foo!int or &foo!real, etc.
Ah, sure. Templates don't have addresses. Function templates are not exempt from that. I think you always have to instantiate them in order take an address, not just sometimes.
|
July 08, 2015 Re: Understanding Safety of Function Pointers vs. Addresses of Functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to jmh530 | I'm still not sure why I'm getting the error I mention in the original post. For instance, the code below is giving that Error 42 Symbol Undefined error. Seems very mystifying... import std.math : cos; real foo(T)(T fp, real x) { return fp(x); } void main() { real x = 0; real y = foo(&cos, x); } |
July 08, 2015 Re: Understanding Safety of Function Pointers vs. Addresses of Functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to jmh530 | On Wednesday, 8 July 2015 at 03:31:43 UTC, jmh530 wrote: > I'm still not sure why I'm getting the error I mention in the original post. For instance, the code below is giving that Error 42 Symbol Undefined error. Seems very mystifying... > > import std.math : cos; > > real foo(T)(T fp, real x) > { > return fp(x); > } > > void main() > { > real x = 0; > real y = foo(&cos, x); > } It seems std.math.cos is an intrinsic function (i.e. one that the compiler implements when needed, or generates the appropriate asm for): https://github.com/D-Programming-Language/phobos/blob/master/std/math.d#L630 The compiler should probably refuse to take the address of such a function. I've found this bug report: https://issues.dlang.org/show_bug.cgi?id=4541 |
July 08, 2015 Re: Understanding Safety of Function Pointers vs. Addresses of Functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Marc Schütz | On Wednesday, 8 July 2015 at 11:15:12 UTC, Marc Schütz wrote:
>
> It seems std.math.cos is an intrinsic function (i.e. one that the compiler implements when needed, or generates the appropriate asm for):
> https://github.com/D-Programming-Language/phobos/blob/master/std/math.d#L630
>
> The compiler should probably refuse to take the address of such a function. I've found this bug report:
> https://issues.dlang.org/show_bug.cgi?id=4541
Thanks. I was worried I was doing something wrong.
|
July 08, 2015 Re: Understanding Safety of Function Pointers vs. Addresses of Functions | ||||
---|---|---|---|---|
| ||||
Posted in reply to jmh530 | On Wednesday, 8 July 2015 at 14:37:23 UTC, jmh530 wrote:
> Thanks. I was worried I was doing something wrong.
It seems like if you wrap the intrinsic function in another function than it works fine (below). Easy enough work-around, I suppose.
If there is no intention to fix the pointer to assembly function issue (I have zero clue how to go about it, maybe there is a good reason it can't be fixed), this work-around could be implemented in the std.math library for those functions. I.e., put the assembly code in separate functions and then have the existing intrinsic functions act as wrappers to those.
import std.math : cos;
real cos_wrap(real x) pure nothrow @nogc @safe
{
return cos(x);
}
void main()
{
auto fp = &cos_wrap;
real x = 0;
real y = fp(x);
}
|
Copyright © 1999-2021 by the D Language Foundation