www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Input/Output multiple values from function

reply Jabari Zakiya <jzakiya gmail.com> writes:
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.
Aug 27 2019
parent reply Mike Parker <aldacron gmail.com> writes:
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?
Aug 27 2019
parent reply Jabari Zakiya <jzakiya gmail.com> writes:
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?
Aug 27 2019
next sibling parent a11e99z <black80 bk.ru> writes:
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 ?
Aug 28 2019
prev sibling next sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
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
Aug 28 2019
parent reply Jabari Zakiya <jzakiya gmail.com> writes:
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.
Aug 28 2019
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
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
Aug 29 2019
parent reply Jabari Zakiya <jzakiya gmail.com> writes:
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.
Aug 29 2019
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
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
Aug 29 2019
parent reply Jabari Zakiya <jzakiya gmail.com> writes:
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
Sep 01 2019
parent reply Paul Backus <snarwin gmail.com> writes:
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];
Sep 01 2019
parent Jabari Zakiya <jzakiya gmail.com> writes:
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. :-(
Sep 02 2019
prev sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Sep 02 2019