Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Wednesday, 15 July 2015 at 11:45:00 UTC, Laeeth Isharc wrote: Now - is there a way to rewrite my code without mixins? Not sure that is possible. It would be interesting if someone could figure it out though. I'm more focused on making the "givemeabettername" a bit more general. Someone above had sort of asked why bother for the simple case. True enough, but if I can write something generic enough to work on a wide variety of function types, then I would consider it a win. E.g., below. template givemeabettername(alias fun) { static if (arity!fun == 1) { T givemeabettername(T)(T x) if (isDynamicArray!(T)) { return x.map!fun.array; } T givemeabettername(T)(T x) if (isStaticArray!(T)) { T result = x.dup; foreach(ref elem; result) { elem = fun(elem); } return result; } } }
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Tuesday, 14 July 2015 at 17:24:41 UTC, anonymous wrote: This fails with "Error: None of the overloads of 'cos' are callable using argument types (int[])". The problem is that template mixins cannot add to existing overload sets. The spec says: "If the name of a declaration in a mixin is the same as a declaration in the surrounding scope, the surrounding declaration overrides the mixin one" [1]. That means, the `cos` from `alias cos = std.math.cos;` completely overrides the one from `mixin t!();`. I guess this is a measure against function hijacking again. I'm not sure if it's supposed to work like it does when the alias is removed, two implicitly imported/generated symbols forming an overload set. But I can't immediately see a problem with it either. [1] http://dlang.org/template-mixin.html - see "Mixin Scope" Now - is there a way to rewrite my code without mixins?
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Tuesday, 14 July 2015 at 17:24:41 UTC, anonymous wrote: This fails with "Error: None of the overloads of 'cos' are callable using argument types (int[])". The problem is that template mixins cannot add to existing overload sets. The spec says: "If the name of a declaration in a mixin is the same as a declaration in the surrounding scope, the surrounding declaration overrides the mixin one" [1]. That means, the `cos` from `alias cos = std.math.cos;` completely overrides the one from `mixin t!();`. I guess this is a measure against function hijacking again. I'm not sure if it's supposed to work like it does when the alias is removed, two implicitly imported/generated symbols forming an overload set. But I can't immediately see a problem with it either. [1] http://dlang.org/template-mixin.html - see "Mixin Scope" That's a great explanation. It doesn't seem like a bug or anything. It's just alias and template mixins are two areas of D that I'm less familiar with. Combining them together pushes my understanding a bit.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Tuesday, 14 July 2015 at 14:02:46 UTC, jmh530 wrote: Thanks for posting that. I figured out the issue. Before you had recommended that I use alias cos = std.math.cos; I had kept that text in. When I removed it, everything worked just fine. I'm still not sure I grasp the subtleties of alias when used with templates. Oh that's another goodie. So you had a situation like this: import std.math; alias cos = std.math.cos; mixin template t() { void cos(real[] arr) {/* ... */} } mixin t!(); void main() { cos([1, 2, 3]); } This fails with "Error: None of the overloads of 'cos' are callable using argument types (int[])". The problem is that template mixins cannot add to existing overload sets. The spec says: "If the name of a declaration in a mixin is the same as a declaration in the surrounding scope, the surrounding declaration overrides the mixin one" [1]. That means, the `cos` from `alias cos = std.math.cos;` completely overrides the one from `mixin t!();`. I guess this is a measure against function hijacking again. I'm not sure if it's supposed to work like it does when the alias is removed, two implicitly imported/generated symbols forming an overload set. But I can't immediately see a problem with it either. [1] http://dlang.org/template-mixin.html - see "Mixin Scope"
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Tuesday, 14 July 2015 at 12:12:41 UTC, anonymous wrote: Works for me. Please show the complete code and the error message you get. Here's what works for me: Thanks for posting that. I figured out the issue. Before you had recommended that I use alias cos = std.math.cos; I had kept that text in. When I removed it, everything worked just fine. I'm still not sure I grasp the subtleties of alias when used with templates.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Tuesday, 14 July 2015 at 01:05:21 UTC, jmh530 wrote: Note: some of the above seemed to only work when I kept the std.math.cos, std.math.sin text in there. When I take it out, I get warnings about recursive aliases. Yeah, you can't do `alias cos = givemeabettername!cos;`. That would define the new cos in terms of itself. But, I can't seem to use a foreach loop. The recursive mixin template seems to be required. You can only use the implicitly static foreach where the normal, dynamic foreach is allowed, i.e. in function bodies. This is a syntactic restriction. If we had `static foreach` (like we have `static if`), that would presumably work everywhere. So this is the last thing I did (that didn't work). Adding foo back to the alias works. mixin template callThemAll(functions...) { mixin("alias " ~__traits(identifier, functions[0]) ~ " = givemeabettername!(std.math."~__traits(identifier, functions[0]) ~ ");"); static if(functions.length >1) mixin callThemAll!(functions[1..$]); } mixin callThemAll!(cos, sin); Works for me. Please show the complete code and the error message you get. Here's what works for me: import std.algorithm: map; import std.math; import std.array: array; template givemeabettername(alias fun) { T givemeabettername(T : U[], U)(T x) { return x.map!fun.array; } } mixin template callThemAll(functions...) { mixin("alias " ~__traits(identifier, functions[0]) ~ " = givemeabettername!(std.math."~__traits(identifier, functions[0]) ~ ");"); static if(functions.length >1) mixin callThemAll!(functions[1..$]); } mixin callThemAll!(cos, sin); void main() { real[] a = [1, 2, 3]; auto c = cos(a); auto s = sin(a); }
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Monday, 13 July 2015 at 02:52:11 UTC, Laeeth Isharc wrote: I am venturing in territory still new to me, but I think that was his point - foreach with tuples looks like it is looping, but really it is unpacking them statically at compile time. And similarly with the recursive version. I don't know if you can avoid the mixin, but you can make it a little tidier. import std.math; import std.stdio; import std.traits; mixin template callThemAll(functions...) { mixin("alias foo"~__traits(identifier,functions[0])~"="~__traits(identifier,functions[0])~";"); static if(functions.length >1) mixin callThemAll!(functions[1..$]); } void main() { mixin callThemAll!(sin,cos,tan); writefln("%s",foosin(1)); writefln("%s",foocos(1)); writefln("%s",footan(1.0)); } Not sure if you knew this already and still found it too messy. I had considered mixins as messy, but my original one was way messier than this. By comparison, it's not so bad. Beyond the mixins, what I find most interesting was the __traits(identifier,functions[0]). Hadn't seen that before. I was only familiar with __FUNCTION__. It definitely simplifies it Anyway, the downside of the approach so far is that I can't quite understand why some versions have worked, but not others. For your foo version (with a few modifications to better match what I'm doing), I was able to get it working regardless of whether it was in main or not. That's good. Further, when I got rid of the foo, but kept it in main, it still worked (after changing the function names). However, when I moved it back out of main I was getting messages about there being no cos/sin available for those types. Note: some of the above seemed to only work when I kept the std.math.cos, std.math.sin text in there. When I take it out, I get warnings about recursive aliases. But, I can't seem to use a foreach loop. The recursive mixin template seems to be required. So this is the last thing I did (that didn't work). Adding foo back to the alias works. mixin template callThemAll(functions...) { mixin("alias " ~__traits(identifier, functions[0]) ~ " = givemeabettername!(std.math."~__traits(identifier, functions[0]) ~ ");"); static if(functions.length >1) mixin callThemAll!(functions[1..$]); } mixin callThemAll!(cos, sin);
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
I don't know what exactly you're after, but you can use foreach on a whatever-they're-called-now tuple (there's been a discussion about the name which I haven't followed; I mean the kind you get from a TemplateTupleParameter): void f1() {} void f2() {} void callThemAll(functions ...)() { foreach(f; functions) /* The loop is unrolled at compile time. */ { f(); } } void main() { callThemAll!(f1, f2)(); } As usual, recursion is an alternative: void callThemAll(functions ...)() { static if(functions.length > 0) { functions[0](); callThemAll!(functions[1 .. $])(); } } Sorry, I don't think I made myself clear enough. Your code allows you to do something like call multiple functions in a loop. I'm talking about the fact that alias cos = givemeabettername!(std.math.cos); alias sin = givemeabettername!(std.math.sin); are just two functions of many in std.math. Suppose I wanted to write it so that every function in std.math had an array version generated by this code. I would be repeating this alias line once each time. My point is that I don't see a way to do this in a loop. I don't think I can do something like void callThemAll(functions ...)() { foreach(f; functions) { alias __Function__ = givemeabettername!(f); //where __Function__ is the name of the function for f, not callThemAll } } callThemAll(std.math.cos, std.math.sin); void main() { real[] x = [0, PI]; auto y = cos(x); auto z = sin(x); } I am venturing in territory still new to me, but I think that was his point - foreach with tuples looks like it is looping, but really it is unpacking them statically at compile time. And similarly with the recursive version. I don't know if you can avoid the mixin, but you can make it a little tidier. import std.math; import std.stdio; import std.traits; mixin template callThemAll(functions...) { mixin("alias foo"~__traits(identifier,functions[0])~"="~__traits(identifier,functions[0])~";"); static if(functions.length >1) mixin callThemAll!(functions[1..$]); } void main() { mixin callThemAll!(sin,cos,tan); writefln("%s",foosin(1)); writefln("%s",foocos(1)); writefln("%s",footan(1.0)); } Not sure if you knew this already and still found it too messy.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Sunday, 12 July 2015 at 22:26:44 UTC, anonymous wrote: You don't need the lambda, do you? -> return x.map!fun.array; You're right. I don't know what exactly you're after, but you can use foreach on a whatever-they're-called-now tuple (there's been a discussion about the name which I haven't followed; I mean the kind you get from a TemplateTupleParameter): void f1() {} void f2() {} void callThemAll(functions ...)() { foreach(f; functions) /* The loop is unrolled at compile time. */ { f(); } } void main() { callThemAll!(f1, f2)(); } As usual, recursion is an alternative: void callThemAll(functions ...)() { static if(functions.length > 0) { functions[0](); callThemAll!(functions[1 .. $])(); } } Sorry, I don't think I made myself clear enough. Your code allows you to do something like call multiple functions in a loop. I'm talking about the fact that alias cos = givemeabettername!(std.math.cos); alias sin = givemeabettername!(std.math.sin); are just two functions of many in std.math. Suppose I wanted to write it so that every function in std.math had an array version generated by this code. I would be repeating this alias line once each time. My point is that I don't see a way to do this in a loop. I don't think I can do something like void callThemAll(functions ...)() { foreach(f; functions) { alias __Function__ = givemeabettername!(f); //where __Function__ is the name of the function for f, not callThemAll } } callThemAll(std.math.cos, std.math.sin); void main() { real[] x = [0, PI]; auto y = cos(x); auto z = sin(x); }
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Sunday, 12 July 2015 at 21:07:34 UTC, jmh530 wrote: private template givemeabettername(alias fun) { T givemeabettername(T : U[], U)(T x) if (isArray!(T)) { return x.map!(a => fun(a)).array; You don't need the lambda, do you? -> return x.map!fun.array; } } Very cool. I guess I'm still left with the issue that I don't know how to loop through a bunch of different functions at compile time, but this is so elegant that it doesn't seem like that big a deal. I don't know what exactly you're after, but you can use foreach on a whatever-they're-called-now tuple (there's been a discussion about the name which I haven't followed; I mean the kind you get from a TemplateTupleParameter): void f1() {} void f2() {} void callThemAll(functions ...)() { foreach(f; functions) /* The loop is unrolled at compile time. */ { f(); } } void main() { callThemAll!(f1, f2)(); } As usual, recursion is an alternative: void callThemAll(functions ...)() { static if(functions.length > 0) { functions[0](); callThemAll!(functions[1 .. $])(); } }
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Sunday, 12 July 2015 at 20:31:20 UTC, jmh530 wrote: On Sunday, 12 July 2015 at 17:11:04 UTC, anonymous wrote: And personally, I'd probably just type out `x.map!fun.array` every time. [1] http://dlang.org/hijack.html Thanks for the comments. After thinking it over, I think you're absolutely right about the function pointers. I made a slight change to just this: private template givemeabettername(alias fun) { T givemeabettername(T : U[], U)(T x) if (isArray!(T)) { return x.map!(a => fun(a)).array; } } Very cool. I guess I'm still left with the issue that I don't know how to loop through a bunch of different functions at compile time, but this is so elegant that it doesn't seem like that big a deal. I'll admit, I still don't think I really have a handle on alias (i.e., it's not something I immediately think of; I understand what it does). It seems like you can do so many things with it. It's just not something that I used in other languages much.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Sunday, 12 July 2015 at 17:11:04 UTC, anonymous wrote: And personally, I'd probably just type out `x.map!fun.array` every time. [1] http://dlang.org/hijack.html Thanks for the comments.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Sunday, 12 July 2015 at 16:34:17 UTC, jmh530 wrote: I've been playing around with this a little more. I wrote this function to encapsulate a simple operation on arrays. U array_fun(T, U)(T fp, U x) if (isFunctionPointer!(T) && isArray!(U)) { return x.map!(a => fp(a)).array; } Then I wrote a cos function that calls it on using a function pointer. T cos(T : U[], U)(T x) if (isArray!(T)) { auto fp = (U a) => cos(a); return array_fun(fp, x); } This seems to work just fine, but for some reason this only seems to work when I import std.math : cos. It doesn't work when I just do import std.math. Bug? Your function is called cos, too. So you need your local cos and std.math.cos to form an overload set. This isn't automatically done with implicitly imported functions to avoid hijacking [1] (accidentally calling the wrong function). Selectively imported functions are added to the overload set, as you (the programmer) are obviously aware of them and want them in the overload set. An alternative would be to use an alias: import std.math; alias cos = std.math.cos; Nevertheless, if I want to implement it for another function, then I have to write it all over again. So I wrote a mixin that would encapsulate that idea (I put in the \n, \t for formatting purposes because I like to be able to writeln it out and see that it matches the original). template mixin_builder(string function_name) { const char[] mixin_builder = "T " ~ function_name ~ "(T : U[], U)(T x)\n" ~ "\tif (isArray!(T))\n" ~ "{\n" ~ "\tauto fp = (U a) => " ~ function_name ~ "(a);\n" ~ "\treturn array_fun(fp, x);\n" ~ "}"; } Then I can just call mixin(mixin_builder!("cos")); mixin(mixin_builder!("sin")); While it works, it feels a bit hackish. I'm not sure I can think of a generic way to do this without mixins. Take the function via an alias parameter: template givemeabettername(alias fun) { T givemeabettername(T : U[], U)(T x) if (isArray!(T)) { auto fp = (U a) => fun(a); return array_fun(fp, x); } } alias cos = givemeabettername!(std.math.cos); alias sin = givemeabettername!(std.math.sin); But turning a statically known function to a function pointer only to pass it to `map` seems pointless to me. So: template givemeabettername(alias fun) { T givemeabettername(T : U[], U)(T x) if (isArray!(T)) /* With the specialization above, this is redundant, isn't it? */ { return x.map!fun.array; } } And personally, I'd probably just type out `x.map!fun.array` every time. [1] http://dlang.org/hijack.html
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Wednesday, 8 July 2015 at 18:31:00 UTC, Steven Schveighoffer wrote: You can use a function lambda: auto fp = (real a) => cos(a); Note, I had to put (real a) even though I would have expected "a => cos(a)" to work. -Steve I've been playing around with this a little more. I wrote this function to encapsulate a simple operation on arrays. U array_fun(T, U)(T fp, U x) if (isFunctionPointer!(T) && isArray!(U)) { return x.map!(a => fp(a)).array; } Then I wrote a cos function that calls it on using a function pointer. T cos(T : U[], U)(T x) if (isArray!(T)) { auto fp = (U a) => cos(a); return array_fun(fp, x); } This seems to work just fine, but for some reason this only seems to work when I import std.math : cos. It doesn't work when I just do import std.math. Bug? Nevertheless, if I want to implement it for another function, then I have to write it all over again. So I wrote a mixin that would encapsulate that idea (I put in the \n, \t for formatting purposes because I like to be able to writeln it out and see that it matches the original). template mixin_builder(string function_name) { const char[] mixin_builder = "T " ~ function_name ~ "(T : U[], U)(T x)\n" ~ "\tif (isArray!(T))\n" ~ "{\n" ~ "\tauto fp = (U a) => " ~ function_name ~ "(a);\n" ~ "\treturn array_fun(fp, x);\n" ~ "}"; } Then I can just call mixin(mixin_builder!("cos")); mixin(mixin_builder!("sin")); While it works, it feels a bit hackish. I'm not sure I can think of a generic way to do this without mixins.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On 7/9/15 4:44 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= " wrote: On Wednesday, 8 July 2015 at 21:04:27 UTC, jmh530 wrote: On Wednesday, 8 July 2015 at 18:31:00 UTC, Steven Schveighoffer wrote: You can use a function lambda: auto fp = (real a) => cos(a); Note, I had to put (real a) even though I would have expected "a => cos(a)" to work. Interesting. You have to put real because there is also a float and double version of cos. Even if there are no overloads, you'd still need to specify the type. That's because `a => cos(a)` is not a function, but a function template, and therefore can't be store in a variable. I didn't know this! I thought it would infer the type if there wasn't any overloads (or in this case, I thought there was a weird issue with the fact that cos is intrinsic). This is good to know. -Steve
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Wednesday, 8 July 2015 at 21:04:27 UTC, jmh530 wrote: On Wednesday, 8 July 2015 at 18:31:00 UTC, Steven Schveighoffer wrote: You can use a function lambda: auto fp = (real a) => cos(a); Note, I had to put (real a) even though I would have expected "a => cos(a)" to work. -Steve Interesting. You have to put real because there is also a float and double version of cos. Even if there are no overloads, you'd still need to specify the type. That's because `a => cos(a)` is not a function, but a function template, and therefore can't be store in a variable.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Wednesday, 8 July 2015 at 18:31:00 UTC, Steven Schveighoffer wrote: You can use a function lambda: auto fp = (real a) => cos(a); Note, I had to put (real a) even though I would have expected "a => cos(a)" to work. -Steve Interesting. You have to put real because there is also a float and double version of cos.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On 7/8/15 11:49 AM, jmh530 wrote: 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); } You can use a function lambda: auto fp = (real a) => cos(a); Note, I had to put (real a) even though I would have expected "a => cos(a)" to work. -Steve
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
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); }
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
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.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
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
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
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); }
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
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.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
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.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
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.
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
On Tue, Jul 07, 2015 at 07:54:18PM +, 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!"
Re: Understanding Safety of Function Pointers vs. Addresses of Functions
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 }