null references redux + Looney Tunes

bearophile bearophileHUGS at lycos.com
Sun Oct 4 15:49:38 PDT 2009


To test my level of ignorance of D2, and to test how structs can be used to implement complex numbers in the std lib I have done few experiments with something simile, subsets of integers.

I have found some problems, some of them may come from my ignorance. This is a natural number:

import std.stdio: writeln;

struct Natural {
    int x = 1;
    alias x this;

    invariant() {
        assert(x >= 1);
    }
}

void main() {
    Natural x, y;
    x = 10;
    y = -20; // OK, invariant() isn't called because it's a direct access

    // From the D2 docs: http://www.digitalmars.com/d/2.0/class.html#Invariant
    // The invariant can be checked when a class object is the argument
    // to an assert() expression, as:
    assert(x); // test.d(20): Error: expression x of type Natural does not have a boolean value
}

It seems invariant() can be used in structs too (even if in D2 docs they are named class invariants), but I think the assert(x) doesn't work.


To solve the problem of the invariant not being called in the assignment I have written this second version:

import std.stdio: writeln;
import std.conv: to;

struct Natural {
    int x_ = 1;

    int x() { return this.x_; }
    int x(int xx) { this.x_ = xx; return xx; }
    alias x this;

    invariant() { assert(this.x_ >= 1, "not a natural"); }

    string toString() { return to!string(this.x_); }
}

void main() {
    Natural x, y;
    x = 10;
    writeln(x, " ", x + x * 3); // OK

    // a problem: the error message gives the line number of the
    // assert instead of the assignment
    y = -20; // core.exception.AssertError at test2.d(11): not a natural
}

Now it works better, but the assert gives an unhelpful line number.



This is a variant, a ranged value:

import std.stdio: writeln;
import std.conv: to;

struct Ranged(int RANGED_MIN, int RANGED_MAX) {
    int x_ = RANGED_MIN;

    int x() { return this.x_; }
    int x(int xx) { this.x_ = xx; return xx; }
    alias x this;

    invariant() {
        //assert(this.x_ >= RANGED_MIN, "Ranged value too much small");
        assert(this.x_ < RANGED_MAX, "Ranged value too much big");
    }

    string toString() { return to!string(this.x_); }
}

void main() {
    typedef Ranged!(10, 20) ranged;
    ranged x;
    writeln(x);
    ranged y = 1000; // Uh, bypasses the setter, no errors
    writeln(y); // 0?
}


I have commented out the first assert to understand better what's happening.
In the line:
ranged y = 1000;
The invariant isn't called, the value 1000 goes nowhere, and even the int x_ = RANGED_MIN; is being bypassed, so in this.x_ goes a zero.
This will be another problem for library-defined complex numbers. So in the end I may like to keep complex numbers in the language for now.

Bye,
bearophile



More information about the Digitalmars-d mailing list