Input/Output multiple values from function
I have a function (say func1) that takes 1 input value (an integer number) and outputs 4 values (2 integers and 2 arrays of integers). Then inside another function (say func2) I provide the 1 input to func1 and then want to assign its 4 output values to their appropriate final variables that will use them. Conceptually I want to do below: func2 { ...; (a, b, c, d) = func1(i) } I tried using a struct but compiler keeps complaining. Thanks for help in advance.
Re: Input/Output multiple values from function
On Wednesday, 28 August 2019 at 04:19:49 UTC, Jabari Zakiya wrote: I have a function (say func1) that takes 1 input value (an integer number) and outputs 4 values (2 integers and 2 arrays of integers). Then inside another function (say func2) I provide the 1 input to func1 and then want to assign its 4 output values to their appropriate final variables that will use them. Conceptually I want to do below: func2 { ...; (a, b, c, d) = func1(i) } I tried using a struct but compiler keeps complaining. Thanks for help in advance. A struct should work just fine: https://run.dlang.io/is/aMBGD0 import std.stdio; struct Results { int a, b; int[] aa, ab; } Results func1() { return Results(1, 2, [0, 1, 2, 3], [10, 11, 12]); } void main() { writeln(func1); } What is the compiler complaining about?
Re: Input/Output multiple values from function
On Wednesday, 28 August 2019 at 04:39:23 UTC, Mike Parker wrote: On Wednesday, 28 August 2019 at 04:19:49 UTC, Jabari Zakiya wrote: I have a function (say func1) that takes 1 input value (an integer number) and outputs 4 values (2 integers and 2 arrays of integers). Then inside another function (say func2) I provide the 1 input to func1 and then want to assign its 4 output values to their appropriate final variables that will use them. Conceptually I want to do below: func2 { ...; (a, b, c, d) = func1(i) } I tried using a struct but compiler keeps complaining. Thanks for help in advance. A struct should work just fine: https://run.dlang.io/is/aMBGD0 import std.stdio; struct Results { int a, b; int[] aa, ab; } Results func1() { return Results(1, 2, [0, 1, 2, 3], [10, 11, 12]); } void main() { writeln(func1); } What is the compiler complaining about? Inside func2 I create an input value for func1 and then assign func1's 4 outputs to named variable. That's where the problems arise. func1 does some math based on the input and generates 4 outputs. I can't do (a, b, c,d) = func1(i) directly. What do I do to assign the output of func1 to the individual variables?
Re: Input/Output multiple values from function
On Wednesday, 28 August 2019 at 05:17:28 UTC, Jabari Zakiya wrote: On Wednesday, 28 August 2019 at 04:39:23 UTC, Mike Parker wrote: On Wednesday, 28 August 2019 at 04:19:49 UTC, Jabari Zakiya wrote: I have a function (say func1) that takes 1 input value (an integer number) and outputs 4 values (2 integers and 2 arrays of integers). Then inside another function (say func2) I provide the 1 input to func1 and then want to assign its 4 output values to their appropriate final variables that will use them. https://dlang.org/library/std/typecons/tuple.expand.html ?
Re: Input/Output multiple values from function
On Wednesday, 28 August 2019 at 05:17:28 UTC, Jabari Zakiya wrote: Inside func2 I create an input value for func1 and then assign func1's 4 outputs to named variable. That's where the problems arise. func1 does some math based on the input and generates 4 outputs. I can't do (a, b, c,d) = func1(i) directly. What do I do to assign the output of func1 to the individual variables? import std.meta : AliasSeq; import std.typecons : tuple; auto fun() { return tuple(1, "test"); } unittest { int a; string b; AliasSeq!(a, b) = fun; assert(a == 1); assert(b == "test"); } -- Simen
Re: Input/Output multiple values from function
On Wednesday, 28 August 2019 at 10:10:08 UTC, Simen Kjærås wrote: On Wednesday, 28 August 2019 at 05:17:28 UTC, Jabari Zakiya wrote: Inside func2 I create an input value for func1 and then assign func1's 4 outputs to named variable. That's where the problems arise. func1 does some math based on the input and generates 4 outputs. I can't do (a, b, c,d) = func1(i) directly. What do I do to assign the output of func1 to the individual variables? import std.meta : AliasSeq; import std.typecons : tuple; auto fun() { return tuple(1, "test"); } unittest { int a; string b; AliasSeq!(a, b) = fun; assert(a == 1); assert(b == "test"); } -- Simen When I do this: uint a; uint b; uint[] c; uint[] d; AliasSeq!(a, b, c, d) = genPGparameters(pg); modpg= a; res_0= b; restwins = c; resinvrs = d; the compiler (ldc2 1.17) says: D Projects ~/D/bin/ldc2 --release -O3 twinprimes_ssoznew1.d twinprimes_ssoznew1.d(170): Error: cannot implicitly convert expression c of type uint[] to shared(uint[]) twinprimes_ssoznew1.d(171): Error: cannot implicitly convert expression d of type uint[] to shared(uint[]) where modpg, res_0; restwins, resinvrs are shared (global) variables.
Re: Input/Output multiple values from function
On Wednesday, 28 August 2019 at 13:11:46 UTC, Jabari Zakiya wrote: When I do this: uint a; uint b; uint[] c; uint[] d; AliasSeq!(a, b, c, d) = genPGparameters(pg); modpg= a; res_0= b; restwins = c; resinvrs = d; the compiler (ldc2 1.17) says: D Projects ~/D/bin/ldc2 --release -O3 twinprimes_ssoznew1.d twinprimes_ssoznew1.d(170): Error: cannot implicitly convert expression c of type uint[] to shared(uint[]) twinprimes_ssoznew1.d(171): Error: cannot implicitly convert expression d of type uint[] to shared(uint[]) where modpg, res_0; restwins, resinvrs are shared (global) variables. Reduced example: unittest { int[] a; // cannot implicitly convert expression a of type int[] to shared(int[]) shared int[] b = a; } This is because an int[] is mutable and thread-local, while shared(int[]) is mutable and shared. Shared mutable data must be guarded closely, preferably behind a mutex or similar. Assigning the value of `a` to `b` above would leave a mutable reference to the shared data on a thread, and could easily lead to race conditions. In order to fix this issue, consider several things: Do modpg and friends really need to be shared? Removing shared() from them will still make them available to other parts of your code, but they will be thread-local instead. If you're not doing threaded work, that should be perfectly fine. Can they be immutable? If they're initialized once and never changed, this could be a good solution. If they need to be shared and mutable, have you protected them enough from race conditions? Are there possible situations where other threads may be accessing them while one thread is writing to them? Multi-threaded programming is hard, and requires a lot more knowledge than we have about your project from the code you've posted, so only you can answer these questions. If you're unsure, you can probably just remove shared() from modpg and friends. -- Simen
Re: Input/Output multiple values from function
On Thursday, 29 August 2019 at 09:04:17 UTC, Simen Kjærås wrote: On Wednesday, 28 August 2019 at 13:11:46 UTC, Jabari Zakiya wrote: [...] Reduced example: unittest { int[] a; // cannot implicitly convert expression a of type int[] to shared(int[]) shared int[] b = a; } This is because an int[] is mutable and thread-local, while shared(int[]) is mutable and shared. Shared mutable data must be guarded closely, preferably behind a mutex or similar. Assigning the value of `a` to `b` above would leave a mutable reference to the shared data on a thread, and could easily lead to race conditions. In order to fix this issue, consider several things: Do modpg and friends really need to be shared? Removing shared() from them will still make them available to other parts of your code, but they will be thread-local instead. If you're not doing threaded work, that should be perfectly fine. Can they be immutable? If they're initialized once and never changed, this could be a good solution. If they need to be shared and mutable, have you protected them enough from race conditions? Are there possible situations where other threads may be accessing them while one thread is writing to them? Multi-threaded programming is hard, and requires a lot more knowledge than we have about your project from the code you've posted, so only you can answer these questions. If you're unsure, you can probably just remove shared() from modpg and friends. -- Simen The values modpg, res_0, restwins, and resinvrs are constant (immutable) values that are generated at run time. They are global/shared and used inside threads. So this process is initializing them at the start of the program, based on the input values to it. The compiler only has a problem, it seems, with the arrays and not the single values.
Re: Input/Output multiple values from function
On Thursday, 29 August 2019 at 10:39:44 UTC, Jabari Zakiya wrote: The values modpg, res_0, restwins, and resinvrs are constant (immutable) values that are generated at run time. They are global/shared and used inside threads. So this process is initializing them at the start of the program, based on the input values to it. Great - then you can use shared(immutable(uint)[]). You should be able to convert from immutable(uint[]) to that without issue. There's a utility function in std.exception called assumeUnique that can be used for conversion to immutable that may be more informative than cast(immutable): shared(uint) modpg; shared(uint) res_0; shared(immutable(uint)[]) restwins; shared(immutable(uint)[]) resinvrs; auto genPGparameters(int i) { import std.typecons; import std.exception; // Showing both cast and assumeUnique: return tuple(1u, 2u, cast(immutable)[3u], [4u].assumeUnique); } unittest { import std.meta : AliasSeq; int pg; // No need for temporaries: AliasSeq!(modpg, res_0, restwins, resinvrs) = genPGparameters(pg); } The compiler only has a problem, it seems, with the arrays and not the single values. Correct - since there are no indirections in a uint, you can assign directly to a shared(uint) - nobody else will have a non-shared pointer to that uint, so it's somewhat safe. If you do the same with uint[], you'll still have a pointer to the same values, and changing a value that says it's thread-local (no shared) will change values that is shared with the rest of the program. There are some issues with the current shared design, but this is what it's intended to do. -- Simen
Re: Input/Output multiple values from function
On Thursday, 29 August 2019 at 10:58:47 UTC, Simen Kjærås wrote: On Thursday, 29 August 2019 at 10:39:44 UTC, Jabari Zakiya wrote: [...] Great - then you can use shared(immutable(uint)[]). You should be able to convert from immutable(uint[]) to that without issue. There's a utility function in std.exception called assumeUnique that can be used for conversion to immutable that may be more informative than cast(immutable): shared(uint) modpg; shared(uint) res_0; shared(immutable(uint)[]) restwins; shared(immutable(uint)[]) resinvrs; auto genPGparameters(int i) { import std.typecons; import std.exception; // Showing both cast and assumeUnique: return tuple(1u, 2u, cast(immutable)[3u], [4u].assumeUnique); } unittest { import std.meta : AliasSeq; int pg; // No need for temporaries: AliasSeq!(modpg, res_0, restwins, resinvrs) = genPGparameters(pg); } [...] Correct - since there are no indirections in a uint, you can assign directly to a shared(uint) - nobody else will have a non-shared pointer to that uint, so it's somewhat safe. If you do the same with uint[], you'll still have a pointer to the same values, and changing a value that says it's thread-local (no shared) will change values that is shared with the rest of the program. There are some issues with the current shared design, but this is what it's intended to do. -- Simen It still won't compile, with this error. Error: AliasSeq!(modpg, res_0, restwins, resinvrs) is not an lvalue and cannot be modified Here's a gist of the code. Top functions in code with issues are genPgParameters and selectPG https://gist.github.com/jzakiya/9227e4810e1bd5b4b31e949d1cbd5c5d
Re: Input/Output multiple values from function
On Sunday, 1 September 2019 at 20:42:28 UTC, Jabari Zakiya wrote: It still won't compile, with this error. Error: AliasSeq!(modpg, res_0, restwins, resinvrs) is not an lvalue and cannot be modified Here's a gist of the code. Top functions in code with issues are genPgParameters and selectPG https://gist.github.com/jzakiya/9227e4810e1bd5b4b31e949d1cbd5c5d You can't do multiple assignments at once using AliasSeq; you have to assign each variable individually: auto parameters = genPgParameters(pg); modpg = parameters[0]; res_0 = parameters[1]; restwins = parameters[2]; resinvrs = parameters[3];
Re: Input/Output multiple values from function
On Sunday, 1 September 2019 at 20:50:42 UTC, Paul Backus wrote: On Sunday, 1 September 2019 at 20:42:28 UTC, Jabari Zakiya wrote: It still won't compile, with this error. Error: AliasSeq!(modpg, res_0, restwins, resinvrs) is not an lvalue and cannot be modified Here's a gist of the code. Top functions in code with issues are genPgParameters and selectPG https://gist.github.com/jzakiya/9227e4810e1bd5b4b31e949d1cbd5c5d You can't do multiple assignments at once using AliasSeq; you have to assign each variable individually: auto parameters = genPgParameters(pg); modpg = parameters[0]; res_0 = parameters[1]; restwins = parameters[2]; resinvrs = parameters[3]; There's still are problem When I do this. auto parameters = genPgParameters(pg); modpg = parameters[0]; res_0 = parameters[1]; restwins = parameters[2]; resinvrs = parameters[3]; The compiler says this: Error: cannot implicitly convert expression parameters.__expand_field_3 of type uint[] to shared(uint[]) If I comment out the last line the programs compiles (and crashes when run since all the parameters aren't available). So why does it accept parameter[2] but not [3] as they are both defined the same? Is this a compiler bug? I have to say, getting this simple parameter passing to work in D has been an infuriating experience. :-(
Re: Input/Output multiple values from function
On 08/27/2019 10:17 PM, Jabari Zakiya wrote: > I can't do (a, b, c,d) = func1(i) directly. > What do I do to assign the output of func1 to the individual variables? I had some time to play with the following syntax, similar usage of which has been proposed a number of times as a replacement for tuple expansion. Assuming that foo() returns a struct of int, double, string; the following expression will set the variables to those members: int i; double d; string s; foo(42).into!(i, d, s); Here is the complete program: import std.stdio; import std.string; struct S { int i; double d; string s; } S foo(int i) { return S(i, 1.5, "hi"); } template into(args...) { auto into(From)(From from) if (is (From == struct)) { static foreach (i, m; __traits(allMembers, From)) {{ alias argT = typeof(args[i]); alias memT = typeof(__traits(getMember, from, m)); static assert (is (argT == memT), format!"Cannot expand '%s %s.%s' into '%s %s' argument." (memT.stringof, From.stringof, m, argT.stringof, args[i].stringof)); mixin (format!"args[%s] = from.%s;"(i, m)); }} } } void main() { int i; double d; string s; foo(42).into!(i, d, s); writeln(i); writeln(d); writeln(s); } I know that it does not address attributes like shared, etc. but it shows how expressive nested templates can be. Ali