www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to make a function that accepts optional struct but can accept

reply JN <666total wp.pl> writes:
Is there some nice way of achieving something like this C99 code 
in D?

```c
#include <stdio.h>

typedef struct {
     int x, y;
} inputs_t;

void foo(inputs_t* optional_inputs)
{
     if (!optional_inputs) {
         printf("0 0\n");
     } else {
         printf("%d %d \n", optional_inputs->x, 
optional_inputs->y);
     }
}

int main(void) {
     foo(NULL); // prints 0 0
     foo(&(inputs_t){.x = 5, .y = 6}); // prints 5 6
}
```

below code won't work. Yes, I know I can just use a local 
variable in this case and pass a pointer, but I'd like to get it 
to work with literal structs too.

```d
import std.stdio;

struct inputs_t {
     int x, y;
};

void foo(inputs_t* optional_inputs)
{
     if (!optional_inputs) {
         writeln("0 0");
     } else {
         writeln(optional_inputs.x, optional_inputs.y);
     }
}

void main() {
     foo(null); // prints 0 0
     foo(&(inputs_t(5, 6))); // error: inputs_t(5,6) is not an 
lvalue and cannot be modified
}
```
Oct 15 2021
next sibling parent SomeGuy <someguy mailinator.com> writes:
You could use `Nullable` from the standard library to achieve 
something similar, but it isn't as simple/nice as your C99 
compound literal example:

```D
import std.stdio;
import std.typecons; // 
https://dlang.org/phobos/std_typecons.html#Nullable

struct inputs_t { int x, y; };

void foo(Nullable!inputs_t optional_inputs)
{
     if (optional_inputs.isNull) {
         writeln("0 0");
     } else {
         auto non_null = optional_inputs.get;
         writeln(non_null.x, " ", non_null.y);
     }
}

void main() {
     foo(Nullable!(inputs_t)()); // prints 0 0
     foo(inputs_t(5, 6).nullable); // prints 5 6
}
```
Oct 15 2021
prev sibling next sibling parent reply jfondren <julian.fondren gmail.com> writes:
On Friday, 15 October 2021 at 20:33:33 UTC, JN wrote:
 Is there some nice way of achieving something like this C99 
 code in D?
option 1: use an intermediate lambda: ```d import std.stdio; struct inputs_t { int x, y; } // no ; needed here void foo(inputs_t* optional_inputs) { if (!optional_inputs) { writeln("0 0"); } else { writeln(optional_inputs.x, " ", optional_inputs.y); } } void main() { import std.functional : pipe; foo(null); // prints 0 0 inputs_t(5, 6).pipe!(s => foo(&s)); // prints 5 6 } ``` option 2: use a class ```d class inputs_t { int x, y; this(int x, int y) { this.x = x; this.y = y; } } void foo(inputs_t optional_inputs) { import std.stdio : writeln; if (!optional_inputs) { writeln("0 0"); } else { writeln(optional_inputs.x, " ", optional_inputs.y); } } void main() { foo(null); foo(new inputs_t(5, 6)); } ``` option 3: use std.sumtype ```d import std.sumtype; struct Point { int x, y; } alias Input = SumType!(Point, typeof(null)); void foo(Input inputs) { import std.stdio : writeln; inputs.match!( (typeof(null) _) => writeln("0 0"), (Point p) => writeln(p.x, " ", p.y), ); } void main() { foo(null.Input); foo(Point(5, 6).Input); } ``` option 4: use overloading ```d import std.stdio : writeln; struct Point { int x, y; } void foo() { writeln("0 0"); } void foo(Point p) { writeln(p.x, " ", p.y); } void main() { foo(); foo(Point(5, 6)); } ``` option 5: use S.init, when your exceptional value is handled the same ```d struct Point { int x, y; } void foo(Point p = Point.init) { import std.stdio : writeln; writeln(p.x, " ", p.y); } void main() { foo(); // 0 0 foo(Point.init); // 0 0 foo(Point(5, 6)); // 5 6 } ```
Oct 15 2021
parent Tejas <notrealemail gmail.com> writes:
On Friday, 15 October 2021 at 21:19:35 UTC, jfondren wrote:
 On Friday, 15 October 2021 at 20:33:33 UTC, JN wrote:
 Is there some nice way of achieving something like this C99 
 code in D?
option 1: use an intermediate lambda: ```d import std.stdio; struct inputs_t { int x, y; } // no ; needed here void foo(inputs_t* optional_inputs) { if (!optional_inputs) { writeln("0 0"); } else { writeln(optional_inputs.x, " ", optional_inputs.y); } } void main() { import std.functional : pipe; foo(null); // prints 0 0 inputs_t(5, 6).pipe!(s => foo(&s)); // prints 5 6 } ``` option 2: use a class ```d class inputs_t { int x, y; this(int x, int y) { this.x = x; this.y = y; } } void foo(inputs_t optional_inputs) { import std.stdio : writeln; if (!optional_inputs) { writeln("0 0"); } else { writeln(optional_inputs.x, " ", optional_inputs.y); } } void main() { foo(null); foo(new inputs_t(5, 6)); } ``` option 3: use std.sumtype ```d import std.sumtype; struct Point { int x, y; } alias Input = SumType!(Point, typeof(null)); void foo(Input inputs) { import std.stdio : writeln; inputs.match!( (typeof(null) _) => writeln("0 0"), (Point p) => writeln(p.x, " ", p.y), ); } void main() { foo(null.Input); foo(Point(5, 6).Input); } ``` option 4: use overloading ```d import std.stdio : writeln; struct Point { int x, y; } void foo() { writeln("0 0"); } void foo(Point p) { writeln(p.x, " ", p.y); } void main() { foo(); foo(Point(5, 6)); } ``` option 5: use S.init, when your exceptional value is handled the same ```d struct Point { int x, y; } void foo(Point p = Point.init) { import std.stdio : writeln; writeln(p.x, " ", p.y); } void main() { foo(); // 0 0 foo(Point.init); // 0 0 foo(Point(5, 6)); // 5 6 } ```
No need to use `class` to get a reference type out of `new`, it works on `struct`s just fine: ```d import std.stdio; struct inputs_t { int x, y; } void foo(inputs_t* optional_inputs) { if (!optional_inputs) { writeln("0 0"); } else { writeln(optional_inputs.x, " ", optional_inputs.y); } } void main() { foo(null); // prints 0 0 foo(new inputs_t(5,6)); } ``` If you dislike using new for some reason: ```d import std.stdio; struct inputs_t { int x, y; } T* byRef(T)(auto ref T a) { //Use only if you don't want to write new for some reason auto __internal_var__ = new T(a.tupleof); return __internal_var__; } void foo(inputs_t* optional_inputs) { if (!optional_inputs) { writeln("0 0"); } else { writeln(optional_inputs.x, " ", optional_inputs.y); } } void main() { foo(null); // prints 0 0 foo(inputs_t(5, 6).byRef); } ``` Thirdly, we can use the `rvalue reference` trick in d-idioms: https://p0nce.github.io/d-idioms/#Rvalue-references:-Understanding-auto-ref-and-then-not-using-it It uses template mixins and requires that you inject it in every single `struct` declaration, but is also more efficient since it avoids an unnecessary copy.
Oct 15 2021
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 15 October 2021 at 20:33:33 UTC, JN wrote:
 Is there some nice way of achieving something like this C99 
 code in D?

 ```c
 #include <stdio.h>

 typedef struct {
     int x, y;
 } inputs_t;

 void foo(inputs_t* optional_inputs)
 {
     if (!optional_inputs) {
         printf("0 0\n");
     } else {
         printf("%d %d \n", optional_inputs->x, 
 optional_inputs->y);
     }
 }

 int main(void) {
     foo(NULL); // prints 0 0
     foo(&(inputs_t){.x = 5, .y = 6}); // prints 5 6
 }
 ```
```d static global(alias value) = value; struct Inputs { int x, y; } void foo(Inputs* inputs) { import std.stdio; if (inputs is null) writeln("0 0"); else writeln(inputs.x, " ", inputs.y); } void main() { foo(null); foo(&global!(Inputs(5, 6))); } ```
Oct 15 2021
next sibling parent russhy <russhy gmail.com> writes:
On Friday, 15 October 2021 at 21:47:21 UTC, Paul Backus wrote:
 On Friday, 15 October 2021 at 20:33:33 UTC, JN wrote:
 Is there some nice way of achieving something like this C99 
 code in D?

 ```c
 #include <stdio.h>

 typedef struct {
     int x, y;
 } inputs_t;

 void foo(inputs_t* optional_inputs)
 {
     if (!optional_inputs) {
         printf("0 0\n");
     } else {
         printf("%d %d \n", optional_inputs->x, 
 optional_inputs->y);
     }
 }

 int main(void) {
     foo(NULL); // prints 0 0
     foo(&(inputs_t){.x = 5, .y = 6}); // prints 5 6
 }
 ```
```d static global(alias value) = value; struct Inputs { int x, y; } void foo(Inputs* inputs) { import std.stdio; if (inputs is null) writeln("0 0"); else writeln(inputs.x, " ", inputs.y); } void main() { foo(null); foo(&global!(Inputs(5, 6))); } ```
Nice trick, so far Paul's answer is the cleanest, 0 imports, doesn't change the signature of the method, and he doesn't create overloading I remember i was once trying to achieve the same as OP, i ended up just using a local variable
Oct 15 2021
prev sibling parent Elronnd <elronnd elronnd.net> writes:
On Friday, 15 October 2021 at 21:47:21 UTC, Paul Backus wrote:
 static global(alias value) = value;
I fear there will be issues with reentrancy.
Oct 15 2021
prev sibling parent Basile B. <b2.temp gmx.com> writes:
On Friday, 15 October 2021 at 20:33:33 UTC, JN wrote:
 Is there some nice way of achieving something like this C99 
 code in D?

 [...]
The [literal](https://www.ibm.com/docs/sr/xl-c-and-cpp-aix/13.1.0?topic=operators-compound- iteral-expressions) in the C version creates an alloca too but it's hidden.
Oct 16 2021