値オブジェクトのオーバーライド

え〜、まず CopyTuple の修正。

template CopyTuple(Values...) {
    void to(Types...)(inout Types lhs)
        in { static assert(Values.length <= lhs.length); }
        body { foreach (i, v; Values) lhs[i] = v; }
}

で、次に Object のオーバーライドについて。

Object のオーバーライド可能なメソッドは

    void print();
    char[] toString();
    hash_t toHash();
    int opCmp(Object o);
    int opEquals(Object o);

があるが、このうち下の3つはオブジェクト全体の値に関するものである。
で、構造体と class にせっかく tupleof プロパティがあるので、これを汎用的にオーバーライドできないかと考えた。

opEquals と opCmp はともかく toHash は、型汎用の関数テンプレートを作らないといかんかなぁと思っていたが、id:kurimura さんとこで、TypeInfo を使う方法があったので、割と簡単にできた。

自分自身との比較をしてないとか、連想配列メンバがあると期待した動作にならないとかいろいろあるでしょうが、とりあえず暫定版。

但しこれ、オブジェクトの値に関することだけにやたらと制約が多い。

まず、

オブジェクト全体を値として評価できるクラスでないとオーバーライドしても無意味

つまり、データメンバを持たないクラスをオーバーライドしてもしょうがないし、あっても static や const しかなかったらこれまた無意味。(final はどうか知らん?)
また Thread のように、インスタンスが違ったら別物と考えなければいけない class もーバーライドしてはいけない。
まさか Singleton にした class をオーバーライドする人もいないでしょうが……。

クラス継承階層内で複数回オーバーライドしてはいけない。

これは、equals の対称性が壊れるから。

親と子で比較するメンバの個数が違うと、等号の左辺に親と子のどちらかがくるかで結果が違ってしまう。a == b のとき、b == a が成り立たなくなるのだ。

連想配列メンバがあると期待した動作にならない。

連想配列はアドレスによる比較しかできないため。
もっとも連想配列は辞書として使うだろうから、内容の同じ連想配列を複数持つ意味は殆どない。(前に連想配列dup とか作ってしまったが)

だけど、連想配列は参照型なので、複数オブジェクトが同一の連想配列を共有するということはありえる。その場合はアドレスによる比較が行われ、期待した動作になる。

まぁ、ユーザーがメソッドを追加しない限り、データメンバがどーなろうとやることは一緒なのでラクになったっちうことで。

import house.string;

template OverrideEquals(T) {
    static assert(is (T == class));
    int opEquals(Object o) {
        if (auto rhs = cast(typeof(this))o) {
            foreach (i, v; this.tupleof) {
                if (v != rhs.tupleof[i]) {
                    return 0;
                }
            }
            return 1;
        }
        return 0;
    }
}

template OverrideToHash(T) {
    static assert(is (T == struct) || is (T == class));
    hash_t toHash() {
        hash_t hash = 0;
        foreach (i, v; this.tupleof) {
            static if (is (v.toHash))
                hash = hash * 9 + v.toHash();
            else
                hash = hash * 9 + typeid(typeof(v)).getHash(&v);
        }
        return hash;
    }
}

template OverrideCompare(T) {
    static assert(is (T == class));
    int opCmp(Object o) {
        if (auto rhs = cast(typeof(this))o) {
            foreach (i, v; this.tupleof) {
                if (v != rhs.tupleof[i]) {
                    return (v < rhs.tupleof[i]) ? -1 : 1;
                }
            }
            return 0;
        }
        throw new Exception("Bad cast");
    }
}