Jump to page: 1 2 3
Thread overview
Understanding Safety of Function Pointers vs. Addresses of Functions
Jul 07, 2015
jmh530
Jul 07, 2015
anonymous
Jul 07, 2015
jmh530
Jul 07, 2015
anonymous
Jul 07, 2015
H. S. Teoh
Jul 07, 2015
jmh530
Jul 08, 2015
jmh530
Jul 08, 2015
Marc Schütz
Jul 08, 2015
jmh530
Jul 08, 2015
jmh530
Jul 08, 2015
jmh530
Jul 09, 2015
Marc Schütz
Jul 12, 2015
jmh530
Jul 12, 2015
anonymous
Jul 12, 2015
jmh530
Jul 12, 2015
jmh530
Jul 12, 2015
anonymous
Jul 13, 2015
jmh530
Jul 13, 2015
Laeeth Isharc
Jul 14, 2015
jmh530
Jul 14, 2015
anonymous
Jul 14, 2015
jmh530
Jul 14, 2015
anonymous
Jul 14, 2015
jmh530
Jul 15, 2015
Laeeth Isharc
Jul 15, 2015
jmh530
July 07, 2015
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
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
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
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
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
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
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
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
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
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);
}


« First   ‹ Prev
1 2 3