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