Thread overview
How to check if variable of some type can be of null value?
Jul 24
Alexey
Jul 24
Alexey
Jul 24
JG
Jul 24
JG
July 24

I've tried to use typeof(t) is cast(t)null, but compiler exits with error and so this can't be used for checking this issue.

The goal I with to achieve by this check - is to use template and to assign value to variable basing on it's ability to accept null as a value.

some testing code below.

import std.stdio;

interface I1
{
}

class C1 : I1
{
}

struct S1
{
}

struct S2
{
    int a=1;
}

void main()
{

    auto c1 = new C1;

    I1 i1 = c1;

    auto s1 = S1();
    auto s2 = S2();

    static assert(typeof(c1).init is cast(typeof(c1)) null);
    static assert(typeof(i1).init is cast(typeof(i1)) null);
    static assert(typeof(s1).init is cast(typeof(s1)) null);
    static assert(typeof(s2).init is cast(typeof(s2)) null);
    static assert(int.init is cast(int) null);

}

July 24

On Saturday, 24 July 2021 at 18:10:07 UTC, Alexey wrote:

>

I've tried to use typeof(t) is cast(t)null, but compiler exits with error and so this can't be used for checking this issue.

The goal I with to achieve by this check - is to use template and to assign value to variable basing on it's ability to accept null as a value.

currently I ended up using __traits(compiles, cast(T1)null) for this check. but don't know is this really semantically correct.

July 24

On Saturday, 24 July 2021 at 19:39:02 UTC, Alexey wrote:

>

On Saturday, 24 July 2021 at 18:10:07 UTC, Alexey wrote:

>

I've tried to use typeof(t) is cast(t)null, but compiler exits with error and so this can't be used for checking this issue.

The goal I with to achieve by this check - is to use template and to assign value to variable basing on it's ability to accept null as a value.

currently I ended up using __traits(compiles, cast(T1)null) for this check. but don't know is this really semantically correct.

There are probably better ways. However, this seems to work:

import std;
enum canBeSetToNull(T) = __traits(compiles,(T.init is null));

interface I1
{
}

class C1 : I1
{
}

struct S1
{
}

    struct S2
    {
        int a=1;
    }

    void main()
    {
        auto c1 = new C1;

        I1 i1 = c1;

        auto s1 = S1();
        auto s2 = S2();

        static assert(canBeSetToNull!(typeof(c1)));
        static assert(canBeSetToNull!(typeof(i1)));
        static assert(!canBeSetToNull!(typeof(s1)));
        static assert(!canBeSetToNull!(typeof(s2)));
        static assert(!canBeSetToNull!(int));
        static assert(canBeSetToNull!(int*));

}```
July 24

On Saturday, 24 July 2021 at 20:10:37 UTC, JG wrote:

>

On Saturday, 24 July 2021 at 19:39:02 UTC, Alexey wrote:

>

[...]

There are probably better ways. However, this seems to work:

import std;
enum canBeSetToNull(T) = __traits(compiles,(T.init is null));

interface I1
{
}

class C1 : I1
{
}

struct S1
{
}

    struct S2
    {
        int a=1;
    }

    void main()
    {
        auto c1 = new C1;

        I1 i1 = c1;

        auto s1 = S1();
        auto s2 = S2();

        static assert(canBeSetToNull!(typeof(c1)));
        static assert(canBeSetToNull!(typeof(i1)));
        static assert(!canBeSetToNull!(typeof(s1)));
        static assert(!canBeSetToNull!(typeof(s2)));
        static assert(!canBeSetToNull!(int));
        static assert(canBeSetToNull!(int*));

}```

Sorry, I see that is basically what you had.

July 25

On Saturday, 24 July 2021 at 18:10:07 UTC, Alexey wrote:

>

The goal I with to achieve by this check - is to use template and to assign value to variable basing on it's ability to accept null as a value.

The most direct representation of that is __traits(compiles, (T t) { t = null; });

Another cool trick to consider is:

is(typeof(null) : Whatever)

What that means is if the null literal will implicitly convert to Whatever type. This means you can pass null as an argument to a function accepting Whatever. Thus it includes pointers, classes, interfaces, arrays. But does NOT include structs, even if they have a null accepting constructor / opAssign since you still must explicitly construct them.

struct S {
void opAssign(typeof(null) n) {}
}

void main() {
S s;
s = null; // allowed due to opAssign
}

pragma(msg, is(typeof(null) : S)); // FALSE because this check only looks for implicit conversion, not user-defined assign overloads or constructors.

The traits compiles check will allow this, since it is looking at assign... but the traits compiles will say false if it gets a const type since obviously then assign is not allowed, even if implicit conversion would be.

Depending on your needs you might use one of these, or perhaps both.