1 module dc; 2 3 import std.algorithm.comparison : predSwitch; 4 import std.conv : to; 5 import std.string : strip; 6 7 /// https://dlang.org/blog/2017/02/13/a-new-import-idiom/ 8 template from(alias name) { 9 mixin("import from = " ~ name ~ ";"); 10 } 11 12 unittest { 13 static assert(__traits(compiles, from!"std".writeln())); 14 static assert(__traits(compiles, from!"std.stdio".readln())); 15 } 16 17 /// Enum that contains compile date in YYYYMMDD format 18 enum compileDate = 19 __DATE__[7 .. $].to!int * 10000 20 + __DATE__[0 .. 3].predSwitch( 21 "Jan", 1, 22 "Feb", 2, 23 "Mar", 3, 24 "Apr", 4, 25 "May", 5, 26 "Jun", 6, 27 "Jul", 7, 28 "Aug", 8, 29 "Sep", 9, 30 "Oct", 10, 31 "Nov", 11, 32 "Dec", 12 33 ) * 100 34 + __DATE__[4 .. 6].strip.to!int; 35 36 unittest { 37 import std.math : log10; 38 static assert(cast(int) log10(compileDate) == 7, "Date \"" ~ compileDate.to!string ~ "\" isn't 8 numbers long"); 39 enum month = compileDate / 100 % 100; 40 static assert(month >= 1 && month <= 12, "Invalid month: " ~ month.to!string); 41 enum day = compileDate % 100; 42 static assert(day >= 1 && day <= 31, "Invalid day: " ~ day.to!string); 43 } 44 45 /// Template that calls `static assert` if compile date is past of what was passed as an argument. Go figure out what to do with that. 46 template bestBefore(int deprecatedStamp) { 47 static assert(compileDate < deprecatedStamp); 48 } 49 50 unittest { 51 static assert(__traits(compiles, { 52 @bestBefore!99991231 53 struct A { } 54 })); 55 static assert(!__traits(compiles, { 56 @bestBefore!19700101 57 struct A { } 58 })); 59 } 60 61 /++ 62 $(P Struct that provides following things:) 63 1. RAII wrapper above handle type 64 2. Mechanism that strips prefix from names of functions in given module. Those functions are available through `opDispatch`-based mechanism. 65 66 Params: 67 module_ = module with functions from which given prefix should be stripped 68 prefix = prefix that should be stripped from names of functions 69 constructor = a function that creates a handle. Note: constructor must take at least one non-optional argument 70 destructor = function that gets called when struct gets destroyed. Mustn't take any arguments 71 72 Examples: 73 --- 74 import etc.c.curl; 75 alias Curl = CWrap!(etc.c.curl, "curl_easy_", (int _) => curl_easy_init(), curl_easy_cleanup); 76 auto c = Curl(0); 77 c.setopt(CurlOption.url, "https://dlang.org"); 78 c.perform(); 79 --- 80 +/ 81 struct CWrap(alias module_, string prefix, alias constructor, alias destructor) { 82 import std.traits : Parameters, ReturnType; 83 84 ReturnType!constructor handle; 85 alias handle this; 86 87 @disable this(); 88 @disable this(ref typeof(this)); 89 this(Parameters!constructor args) { 90 handle = constructor(args); 91 } 92 93 ~this() { 94 destructor(handle); 95 } 96 97 template opDispatch(string f) { 98 alias fun = __traits(getMember, module_, prefix ~ f); 99 alias returnType = ReturnType!fun; 100 returnType opDispatch(Args...)(Args args) { 101 static if(is(returnType == void)) { 102 fun(handle, args); 103 } else { 104 return fun(handle, args); 105 } 106 } 107 } 108 } 109 110 unittest { 111 bool didDestroy = false; 112 struct Foo_ { 113 int a; 114 } 115 Foo_* foo_new(int a) { 116 return new Foo_(a); 117 } 118 void foo_del(Foo_* h) @trusted { 119 h.a = 0; 120 from!"core.memory".GC.free(h); 121 didDestroy = true; 122 } 123 int foo_get_a(Foo_* h) { 124 return h.a; 125 } 126 void foo_set_a(Foo_* h, int a) { 127 h.a = a; 128 } 129 struct mod { 130 alias foo__new = foo_new; 131 alias foo__del = foo_del; 132 alias foo__get_a = foo_get_a; 133 alias foo__set_a = foo_set_a; 134 } 135 alias Foo = CWrap!(mod, "foo__", foo_new, foo_del); 136 { 137 auto foo = Foo(3); 138 assert(foo.get_a == 3); 139 foo.set_a(4); 140 assert(foo.get_a == 4); 141 } 142 assert(didDestroy); 143 } 144 145 /++ 146 A copy of std::tie from C++. 147 TODO: write proper documentation for this 148 Examples: 149 --- 150 int a; 151 string b; 152 unwrap(a, b) = tuple(3, "hi"); 153 assert(a == 3 && b == hi); 154 --- 155 +/ 156 auto unwrap(T...)(out T args) { 157 struct UnwrapImpl { 158 void opAssign(U)(U tuple) if(U.length == T.length) { 159 static foreach(i; 0 .. U.length) { 160 args[i] = tuple[i]; 161 } 162 } 163 } 164 165 return UnwrapImpl(); 166 } 167 168 unittest { 169 int a; 170 string b; 171 double[] c; 172 unwrap(a, b, c) = from!"std.typecons".tuple(1, "b", [1.0, 4.3]); 173 assert(a == 1); 174 assert(b == "b"); 175 assert(c == [1.0, 4.3]); 176 } 177 178 /++ 179 Template that generates sort of infix functions. 180 To use generated function, surround it with any of overloadable binary operators. 181 182 adr thinks this is disgusting. 183 184 Params: 185 fun = function that takes two arguments, lhs and rhs respectively 186 187 Examples: 188 --- 189 enum w = Infix!((a, b) => a + b); 190 enum map = Infix!((a, b) => from!"std".map!(a => a.b)(a)); 191 assert(2 ~w~ 3 == 5); 192 assert([1, 2, 3] <<map>> ((int a) => a * a)); 193 --- 194 +/ 195 template Infix(alias fun) { 196 struct Impl { 197 auto opBinaryRight(string op, T)(T lhs) { 198 struct Other { 199 auto opBinary(string other, U)(U rhs) { 200 return fun(lhs, rhs); 201 } 202 } 203 return Other(); 204 } 205 } 206 enum Infix = Impl(); 207 } 208 209 unittest { 210 import std : array, stdMap = map; 211 enum w = Infix!((a, b) => a + b); 212 enum map = Infix!((a, b) => (a.stdMap!(a => b(a)).array)); 213 assert(2 ~w~ 3 == 5); 214 assert([1, 2, 3] <<map>> ((int a) => a * a) == [1, 4, 9]); 215 }