Thread overview
How to make a function that accepts optional struct but can accept struct literal too
Oct 15, 2021
JN
Oct 15, 2021
SomeGuy
Oct 15, 2021
jfondren
Oct 16, 2021
Tejas
Oct 15, 2021
Paul Backus
Oct 16, 2021
russhy
Oct 16, 2021
Elronnd
Oct 16, 2021
Basile B.
October 15, 2021

Is there some nice way of achieving something like this C99 code in D?

#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.

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
}
October 15, 2021

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:

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
}
October 15, 2021

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:

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

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

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

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

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
}
October 15, 2021

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?

#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
}
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)));
}
October 16, 2021

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:

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

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

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

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

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 structs just fine:

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:

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.

October 16, 2021

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?

#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
}
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

October 16, 2021
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.
October 16, 2021

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 in the C version creates an alloca too but it's hidden.