www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Understanding Safety of Function Pointers vs. Addresses of Functions

reply "jmh530" <john.michael.hall gmail.com> writes:
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
Jul 07 2015
next sibling parent reply "anonymous" <anonymous example.com> writes:
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
 }
Jul 07 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
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.
Jul 07 2015
parent "anonymous" <anonymous example.com> writes:
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.
Jul 07 2015
prev sibling next sibling parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
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!"
Jul 07 2015
parent "jmh530" <john.michael.hall gmail.com> writes:
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.
Jul 07 2015
prev sibling parent reply "jmh530" <john.michael.hall gmail.com> writes:
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);
}
Jul 07 2015
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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
Jul 08 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
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.
Jul 08 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
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); }
Jul 08 2015
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Jul 08 2015
next sibling parent reply "jmh530" <john.michael.hall gmail.com> writes:
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.
Jul 08 2015
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
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.
Jul 09 2015
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/9/15 4:44 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>" 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
Jul 09 2015
prev sibling parent reply "jmh530" <john.michael.hall gmail.com> writes:
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.
Jul 12 2015
parent reply "anonymous" <anonymous example.com> writes:
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
Jul 12 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
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.
Jul 12 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
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.
Jul 12 2015
parent reply "anonymous" <anonymous example.com> writes:
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 .. $])(); } } ----
Jul 12 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
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); }
Jul 12 2015
parent reply "Laeeth Isharc" <laeethnospam nospamlaeeth.com> writes:
 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.
Jul 12 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
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);
Jul 13 2015
parent reply "anonymous" <anonymous example.com> writes:
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); } ----
Jul 14 2015
parent reply "jmh530" <john.michael.hall gmail.com> writes:
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.
Jul 14 2015
parent reply "anonymous" <anonymous example.com> writes:
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"
Jul 14 2015
next sibling parent "jmh530" <john.michael.hall gmail.com> writes:
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.
Jul 14 2015
prev sibling parent reply "Laeeth Isharc" <laeethnospam nospamlaeeth.com> writes:
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?
Jul 15 2015
parent "jmh530" <john.michael.hall gmail.com> writes:
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; } } }
Jul 15 2015