Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import std.traits : isIntegral;
- version(unittest) import std.conv : to;
- struct MyInt
- {
- long value;
- version (property) {
- static MyInt min() @property
- {
- enum min = MyInt(ulong.min);
- return min;
- }
- static MyInt max() @property
- {
- enum max = MyInt(ulong.max);
- return max;
- }
- }
- else enum {
- min = MyInt(long.min),
- max = MyInt(long.max)
- }
- version (invariant_) invariant { }
- this(long n)
- {
- this.value = n;
- }
- unittest {
- const n = MyInt();
- assert(n.value == 0, n.value.to!string);
- }
- unittest {
- const x = 42;
- const n = MyInt(x);
- assert(n.value == x, n.value.to!string);
- }
- // Зачем писть T : this, когда проще написать this()(auto ref const T) ?
- // Нам нужно, чтобы MyInt можно было использовать как базовый тип перечеслений, и чтобы экземпляры этих перечеслений
- // можно было использовать там, где ожидается MyInt, но auto ref означает что переменные будут передаваться по ссылке,
- // и значит, что если такая переменная передается по ссылке, то она неможет быть неявно преведена к другому типу. И
- // как следствие этого, наш конструктор не смог бы работать с такими переменными.
- this(T : this)(auto ref const T that)
- {
- this.value = that.value;
- }
- unittest {
- const x = 42;
- const n = MyInt(MyInt(x));
- assert(n.value == x, n.value.to!string);
- }
- // opEquals
- unittest {
- enum E {
- nil = MyInt.init
- }
- assert(E.nil == MyInt.init);
- assert(MyInt.init == E.nil);
- }
- long opCmp(Number)(auto ref const Number rhs) const if (isValidType!Number)
- {
- return mixin("this.value - rhs" ~ (isIntegral!Number ? "" : ".value"));
- }
- unittest {
- enum {
- zero = MyInt(),
- one = MyInt(1),
- two = MyInt(2)
- }
- assert(zero < one);
- assert(one < two);
- assert(two > zero);
- assert(zero.opCmp(zero) == 0);
- assert(zero.opCmp(0) == 0);
- assert(one.opCmp(one) == 0);
- assert(one.opCmp(1) == 0);
- assert(two > 1);
- assert(two < MyInt.max);
- assert(zero > -1, "-1");
- assert(zero < long.max);
- }
- ref MyInt opOpAssign(string op, Number)(auto ref const Number rhs) if (isValidType!Number && isValidOp!op)
- {
- import std.string : format;
- mixin("this.value %s= rhs%s;".format(op, isIntegral!Number ? "" : ".value"));
- return this;
- }
- MyInt opBinary(string op, Number)(auto ref const Number rhs) const if (isValidType!Number && isValidOp!op)
- {
- return MyInt(this).opOpAssign!op(rhs);
- }
- unittest {
- const zero = MyInt();
- const one = MyInt(1);
- auto n = zero + one;
- assert(n.value == 1, n.value.to!string);
- n += n;
- assert(n.value == 2, n.value.to!string);
- auto m = n + 3;
- assert(m.value == 5, m.value.to!string);
- m += 5;
- assert(m.value == 10, m.value.to!string);
- }
- // 'Number : MyInt' а не 'Number == Int', т.к. нам нужжно чтобы если MyInt будет использоваться в качестве
- // базового типа в перечеслении, то чтобы экземпляры перечесления поддерживали все операции поддерживаемые MyInt.
- // Обычно нет необъодимости так писать, ведь перечесления неявно преобразуются к EnumBaseType когда нужно.
- // Но они не пройдут проверку если написать is(Type == MyType), т.к. тогда ни о каком приведении не может быть и речи.
- enum isValidType(Number) = is(Number : this) || is(Number : long);
- enum isValidOp(string op) = op == "+" || op == "-" || op == "*" || op == "/";
- }
- void main()
- {
- MyInt n;
- n + 0; // MyInt + int
- n += 0; // MyInt += int
- n + n; // MyInt + MyInt
- n += n; // MyInt += MyInt
- enum Enum {
- nil = MyInt.init,
- one = nil + 1,
- max_ = MyInt.max
- }
- Enum m;
- n + m; // MyInt + Enum
- n += m; // MyInt += Enum
- enum Enum2 {
- nil = MyInt.init
- }
- enum E : MyInt;
- /*
- * Тот код что выше, вполне себе компилируется. Тот же код, что ниже, вроде как не содержит ошибок (кроме, наверное, версии error1), но некомпилируется.
- */
- version(error1) {
- E e;
- e + e;
- }
- version(error2) enum Error2 : MyInt {
- nil
- }
- version(error3) enum Error3 {
- nil = MyInt.init,
- one
- }
- }
- /+
- Компилируем файл:
- > dmd test
- все работает.
- Пробуем так:
- > dmd test -version=error1
- test.d(168): Error: enum test.main.E forward reference of E.init
- test.d(161): Error: enum test.main.E is forward referenced when looking for 'value'
- test.d(161): Error: enum test.main.E is forward referenced when looking for 'Value'
- test.d(161): Error: enum test.main.E is forward referenced when looking for '_value'
- ...
- test.d(161): Error: enum test.main.E is forward referenced when looking for 'Blue'
- test.d(161): Error: enum test.main.E is forward referenced when looking for 'val'
- test.d(161): Error: enum test.main.E is forward referenced when looking for 'valp'
- ...
- Первое сообщение об ошибке `... forward reference of E.init` не очень интересно, но вот остальные, наверное указывают на какуето ошибку в компиляторе связанную со spellchecker'ом встроенным в него.
- > dmd test -version=error2
- test.d(173): Error: cannot implicitly convert expression (0) of type int to MyInt
- И в действительности `MyInt` неявно не приводится к `int`, но в спецификации сказано что подставляется значение `MyInt.init` а не `0`.
- > dmd test -version=error3 // можно и так '> dmd test -version=error3 -version=property' - эффект будет тот-же самый.
- test.d(176): Error: no property 'max' for type 'MyInt', did you mean 'max'?
- Когда пользовательский тип используется в качестве основного для перечисления, вероятно, компилятор даже и не пытается посмотреть, а есть ли в действительности поле max, и просто для всех пользовательских типов пишет что нету такого поля.
- +/
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement