Advertisement
Guest User

enum

a guest
Jun 19th, 2017
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
D 6.95 KB | None | 0 0
  1. import std.traits : isIntegral;
  2.  
  3. version(unittest) import std.conv : to;
  4.  
  5. struct MyInt
  6. {
  7.   long value;
  8.  
  9.   version (property) {
  10.     static MyInt min() @property
  11.     {
  12.       enum min = MyInt(ulong.min);
  13.       return min;
  14.     }
  15.  
  16.     static MyInt max() @property
  17.     {
  18.       enum max = MyInt(ulong.max);
  19.       return max;
  20.     }
  21.   }
  22.   else enum {
  23.     min = MyInt(long.min),
  24.     max = MyInt(long.max)
  25.   }
  26.  
  27.   version (invariant_) invariant { }
  28.  
  29.   this(long n)
  30.   {
  31.     this.value = n;
  32.   }
  33.  
  34.   unittest {
  35.     const n = MyInt();
  36.     assert(n.value == 0, n.value.to!string);
  37.   }
  38.  
  39.   unittest {
  40.     const x = 42;
  41.     const n = MyInt(x);
  42.     assert(n.value == x, n.value.to!string);
  43.   }
  44.  
  45.   // Зачем писть T : this, когда проще написать this()(auto ref const T) ?
  46.   // Нам нужно, чтобы MyInt можно было использовать как базовый тип перечеслений, и чтобы экземпляры этих перечеслений
  47.   // можно было использовать там, где ожидается MyInt, но auto ref означает что переменные будут передаваться по ссылке,
  48.   // и значит, что если такая переменная передается по ссылке, то она неможет быть неявно преведена к другому типу. И
  49.   // как следствие этого, наш конструктор не смог бы работать с такими переменными.
  50.   this(T : this)(auto ref const T that)
  51.   {
  52.     this.value = that.value;
  53.   }
  54.  
  55.   unittest {
  56.     const x = 42;
  57.     const n = MyInt(MyInt(x));
  58.     assert(n.value == x, n.value.to!string);
  59.   }
  60.  
  61.   // opEquals
  62.   unittest {
  63.     enum E {
  64.       nil = MyInt.init
  65.     }
  66.  
  67.     assert(E.nil == MyInt.init);
  68.     assert(MyInt.init == E.nil);
  69.   }
  70.  
  71.   long opCmp(Number)(auto ref const Number rhs) const if (isValidType!Number)
  72.   {
  73.     return mixin("this.value - rhs" ~ (isIntegral!Number ? "" : ".value"));
  74.   }
  75.  
  76.   unittest {
  77.     enum {
  78.       zero = MyInt(),
  79.       one  = MyInt(1),
  80.       two  = MyInt(2)
  81.     }
  82.  
  83.     assert(zero < one);
  84.     assert(one < two);
  85.     assert(two > zero);
  86.  
  87.     assert(zero.opCmp(zero) == 0);
  88.     assert(zero.opCmp(0)    == 0);
  89.  
  90.     assert(one.opCmp(one) == 0);
  91.     assert(one.opCmp(1)   == 0);
  92.  
  93.     assert(two > 1);
  94.     assert(two < MyInt.max);
  95.  
  96.     assert(zero > -1, "-1");
  97.  
  98.     assert(zero <  long.max);
  99.   }
  100.  
  101.   ref MyInt opOpAssign(string op, Number)(auto ref const Number rhs) if (isValidType!Number && isValidOp!op)
  102.   {
  103.     import std.string : format;
  104.     mixin("this.value %s= rhs%s;".format(op, isIntegral!Number ? "" : ".value"));
  105.  
  106.     return this;
  107.   }
  108.  
  109.   MyInt opBinary(string op, Number)(auto ref const Number rhs) const if (isValidType!Number && isValidOp!op)
  110.   {
  111.     return MyInt(this).opOpAssign!op(rhs);
  112.   }
  113.  
  114.   unittest {
  115.     const zero = MyInt();
  116.     const one = MyInt(1);
  117.  
  118.     auto n = zero + one;
  119.     assert(n.value == 1, n.value.to!string);
  120.  
  121.     n += n;
  122.     assert(n.value == 2, n.value.to!string);
  123.  
  124.     auto m = n + 3;
  125.     assert(m.value == 5, m.value.to!string);
  126.  
  127.     m += 5;
  128.     assert(m.value == 10, m.value.to!string);
  129.   }
  130.  
  131.   // 'Number : MyInt' а не 'Number == Int', т.к. нам нужжно чтобы если MyInt будет использоваться в качестве
  132.   // базового типа в перечеслении, то чтобы экземпляры перечесления поддерживали все операции поддерживаемые MyInt.
  133.   // Обычно нет необъодимости так писать, ведь перечесления неявно преобразуются к EnumBaseType когда нужно.
  134.   // Но они не пройдут проверку если написать is(Type == MyType), т.к. тогда ни о каком приведении не может быть и речи.
  135.   enum isValidType(Number) = is(Number : this) || is(Number : long);
  136.   enum isValidOp(string op) = op == "+" || op == "-" || op == "*" || op ==  "/";
  137. }
  138.  
  139. void main()
  140. {
  141.   MyInt n;
  142.   n +  0; // MyInt +  int
  143.   n += 0; // MyInt += int
  144.   n +  n; // MyInt +  MyInt
  145.   n += n; // MyInt += MyInt
  146.  
  147.   enum Enum {
  148.     nil = MyInt.init,
  149.     one = nil + 1,
  150.     max_ = MyInt.max
  151.   }
  152.  
  153.   Enum m;
  154.   n +  m; // MyInt +  Enum
  155.   n += m; // MyInt += Enum
  156.  
  157.   enum Enum2 {
  158.     nil = MyInt.init
  159.   }
  160.  
  161.   enum E : MyInt;
  162.  
  163. /*
  164.  * Тот код что выше, вполне себе компилируется. Тот же код, что ниже, вроде как не содержит ошибок (кроме, наверное, версии error1), но некомпилируется.
  165. */
  166.  
  167.   version(error1) {
  168.     E e;
  169.     e + e;
  170.   }
  171.  
  172.   version(error2) enum Error2 : MyInt {
  173.     nil
  174.   }
  175.  
  176.   version(error3) enum Error3 {
  177.     nil = MyInt.init,
  178.     one
  179.   }
  180. }
  181.  
  182. /+
  183. Компилируем файл:
  184.  
  185. > dmd test
  186.  
  187. все работает.
  188.  
  189. Пробуем так:
  190. > dmd test -version=error1
  191. test.d(168): Error: enum test.main.E forward reference of E.init
  192. test.d(161): Error: enum test.main.E is forward referenced when looking for 'value'
  193. test.d(161): Error: enum test.main.E is forward referenced when looking for 'Value'
  194. test.d(161): Error: enum test.main.E is forward referenced when looking for '_value'
  195. ...
  196. test.d(161): Error: enum test.main.E is forward referenced when looking for 'Blue'
  197. test.d(161): Error: enum test.main.E is forward referenced when looking for 'val'
  198. test.d(161): Error: enum test.main.E is forward referenced when looking for 'valp'
  199. ...
  200.  
  201. Первое сообщение об ошибке `... forward reference of E.init` не очень интересно, но вот остальные, наверное указывают на какуето ошибку в компиляторе связанную со spellchecker'ом встроенным в него.
  202.  
  203. > dmd test -version=error2
  204. test.d(173): Error: cannot implicitly convert expression (0) of type int to MyInt
  205.  
  206. И в действительности `MyInt` неявно не приводится к `int`, но в спецификации сказано что подставляется значение `MyInt.init` а не `0`.
  207.  
  208. > dmd test -version=error3 // можно и так '> dmd test -version=error3 -version=property' - эффект будет тот-же самый.
  209. test.d(176): Error: no property 'max' for type 'MyInt', did you mean 'max'?
  210.  
  211. Когда пользовательский тип используется в качестве основного для перечисления, вероятно, компилятор даже и не пытается посмотреть, а есть ли в действительности поле max, и просто для всех пользовательских типов пишет что нету такого поля.
  212. +/
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement