Jump to page: 1 2
Thread overview
Input/Output multiple values from function
Aug 28, 2019
Jabari Zakiya
Aug 28, 2019
Mike Parker
Aug 28, 2019
Jabari Zakiya
Aug 28, 2019
a11e99z
Aug 28, 2019
Simen Kjærås
Aug 28, 2019
Jabari Zakiya
Aug 29, 2019
Simen Kjærås
Aug 29, 2019
Jabari Zakiya
Aug 29, 2019
Simen Kjærås
Sep 01, 2019
Jabari Zakiya
Sep 01, 2019
Paul Backus
Sep 02, 2019
Jabari Zakiya
Sep 03, 2019
Ali Çehreli
August 28, 2019
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.
August 28, 2019
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?
August 28, 2019
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?

August 28, 2019
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 ?

August 28, 2019
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
August 28, 2019
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.

August 29, 2019
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
August 29, 2019
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.
August 29, 2019
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
September 01, 2019
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
« First   ‹ Prev
1 2