1 /++ 2 A simple I/O routines around `<stdio.h>`. 3 4 The implementation is CTFE-friendly. 5 +/ 6 module mir.stdio; 7 8 static import core.stdc.stdio; 9 10 import mir.exception: toMutable; 11 12 /// Writes values in a text form 13 void writeln(string separator = "", Args...)(auto ref const Args args) 14 if (Args.length > 0) 15 { 16 dout.write!separator(args); 17 dout << endl; 18 } 19 20 /// ditto 21 void write(string separator = "", Args...)(auto ref const Args args) 22 if (Args.length > 0) 23 { 24 dout.write!separator(args); 25 } 26 27 /// Writes values in a text form using nothrow $(LREF tout) 28 void dump(string separator = " ", Args...)(auto ref const Args args) 29 if (Args.length > 0) 30 { 31 tout.write!separator(args); 32 tout << endl; 33 } 34 35 /// Standart output 36 File dout()() @trusted nothrow @nogc @property 37 { 38 version(LDC) 39 pragma(inline, true); 40 return File(__ctfe ? null : core.stdc.stdio.stdout); 41 } 42 43 /// ditto 44 File derr()() @trusted nothrow @nogc @property 45 { 46 version(LDC) 47 pragma(inline, true); 48 return File(__ctfe ? null : core.stdc.stdio.stderr); 49 } 50 51 /// 52 version(mir_test) 53 @safe @nogc 54 unittest 55 { 56 dout << "mir.stdio.dout test! - @nogc I/O" << endl; 57 derr << "mir.stdio.derr test! - @nogc I/O" << endl; 58 } 59 60 /++ 61 Nothrow standart output to use in pair with `debug` expression in nothrow 62 and pure code for testing purpose. 63 64 See_also: $(LREF AssumeNothrowFile) 65 +/ 66 AssumeNothrowFile tout()() @trusted nothrow @nogc @property 67 { 68 version(LDC) 69 pragma(inline, true); 70 return AssumeNothrowFile(__ctfe ? null : core.stdc.stdio.stdout); 71 } 72 73 /// ditto 74 AssumeNothrowFile terr()() @trusted nothrow @nogc @property 75 { 76 version(LDC) 77 pragma(inline, true); 78 return AssumeNothrowFile(__ctfe ? null : core.stdc.stdio.stderr); 79 } 80 81 /// 82 version(mir_test) 83 pure @safe @nogc nothrow 84 unittest 85 { 86 debug tout << "mir.stdio.tout test! - @nogc nothrow I/O" << endl; 87 debug terr << "mir.stdio.terr test! - @nogc nothrow I/O" << endl; 88 } 89 90 /++ 91 When used as `file << endl` it adds new line flushes the stream. 92 +/ 93 enum NewLine 94 { 95 lf = "\n", 96 lf_cf = "\r\n", 97 } 98 99 /// ditto 100 enum endl = NewLine.lf; 101 102 /++ 103 +/ 104 struct File 105 { 106 /// 107 core.stdc.stdio.FILE* fp; 108 109 mixin FileMembers; 110 111 @trusted @nogc: 112 113 /++ 114 Throws: $(LREF FileException) 115 +/ 116 void rawWrite(scope const(void)[] data) scope 117 in (__ctfe || fp !is null) 118 { 119 if (__ctfe) 120 return; 121 core.stdc.stdio.fwrite(data.ptr, 1, data.length, fp); 122 if (core.stdc.stdio.ferror(fp)) 123 throw writeException.toMutable; 124 } 125 126 /++ 127 Throws: $(LREF FileException) 128 +/ 129 void flush() scope 130 in (__ctfe || fp !is null) 131 { 132 if (__ctfe) 133 return; 134 core.stdc.stdio.fflush(fp); 135 if (core.stdc.stdio.ferror(fp)) 136 throw writeException.toMutable; 137 } 138 } 139 140 /++ 141 Nothrow File implementation for testing purposes. 142 See_also: $(LREF tout), $(LREF terr) 143 +/ 144 struct AssumeNothrowFile 145 { 146 /// 147 core.stdc.stdio.FILE* fp; 148 149 mixin FileMembers; 150 151 @trusted @nogc nothrow: 152 153 /++ 154 Throws: $(LREF FileError) 155 +/ 156 void rawWrite(scope const(void)[] data) scope 157 in (__ctfe || fp !is null) 158 { 159 if (__ctfe) 160 return; 161 core.stdc.stdio.fwrite(data.ptr, 1, data.length, fp); 162 if (core.stdc.stdio.ferror(fp)) 163 throw writeError.toMutable; 164 } 165 166 /++ 167 Throws: $(LREF FileError) 168 +/ 169 void flush() scope 170 in (__ctfe || fp !is null) 171 { 172 if (__ctfe) 173 return; 174 core.stdc.stdio.fflush(fp); 175 if (core.stdc.stdio.ferror(fp)) 176 throw writeError.toMutable; 177 } 178 } 179 180 /// 181 mixin template FileMembers() 182 { 183 /// 184 void put(C)(const C data) scope 185 if (is(C == char) || is(C == wchar) | is(C == dchar)) 186 { 187 C[1] array = [data]; 188 this.rawWrite(array); 189 } 190 191 /// 192 void put(C)(scope const(C)[] data) scope 193 if (is(C == char) || is(C == wchar) | is(C == dchar)) 194 { 195 this.rawWrite(data); 196 } 197 198 /// 199 template opBinary(string op : "<<") 200 { 201 /// 202 ref opBinary(T)(auto ref const T value) scope return 203 { 204 if (__ctfe) 205 return this; 206 import mir.format: print; 207 print!char(this, value); 208 return this; 209 } 210 211 /// Prints new line and flushes the stream 212 ref opBinary(NewLine endl) scope return 213 { 214 if (__ctfe) 215 return this; 216 this.put(endl); 217 this.flush; 218 return this; 219 } 220 } 221 222 /// Writes values in a text form 223 void writeln(string separator = "", Args...)(auto ref const Args args) scope 224 if (Args.length > 0) 225 { 226 write(args); 227 this << endl; 228 } 229 230 /// ditto 231 void write(string separator = "", Args...)(auto ref const Args args) scope 232 if (Args.length > 0) 233 { 234 pragma(inline, false); 235 if (__ctfe) 236 return; 237 import mir.format: print, printStaticString; 238 foreach (i, ref arg; args) 239 { 240 print!char(this, arg); 241 static if (separator.length && i + 1 < args.length) 242 { 243 printStaticString!char(this, separator); 244 } 245 } 246 } 247 } 248 249 /++ 250 File Exception 251 +/ 252 class FileException : Exception 253 { 254 /// 255 this( 256 string msg, 257 string file = __FILE__, 258 size_t line = __LINE__, 259 Throwable next = null) pure nothrow @nogc @safe 260 { 261 super(msg, file, line, next); 262 } 263 264 /// 265 this( 266 string msg, 267 Throwable next, 268 string file = __FILE__, 269 size_t line = __LINE__, 270 ) pure nothrow @nogc @safe 271 { 272 this(msg, file, line, next); 273 } 274 275 FileException toMutable() @trusted pure nothrow @nogc const 276 { 277 return cast() this; 278 } 279 280 alias toMutable this; 281 } 282 283 /++ 284 File Error 285 +/ 286 class FileError : Error 287 { 288 /// 289 this( 290 string msg, 291 string file = __FILE__, 292 size_t line = __LINE__, 293 Throwable next = null) pure nothrow @nogc @safe 294 { 295 super(msg, file, line, next); 296 } 297 298 /// 299 this( 300 string msg, 301 Throwable next, 302 string file = __FILE__, 303 size_t line = __LINE__, 304 ) pure nothrow @nogc @safe 305 { 306 this(msg, file, line, next); 307 } 308 309 FileError toMutable() @trusted pure nothrow @nogc const 310 { 311 return cast() this; 312 } 313 314 alias toMutable this; 315 } 316 317 private static immutable writeException = new FileException("Error on file write"); 318 private static immutable flushException = new FileException("Error on file flush"); 319 private static immutable writeError = new FileError("Error on file write"); 320 private static immutable flushError = new FileError("Error on file flush"); 321 322 version(mir_test) 323 @safe unittest 324 { 325 static struct ParameterSpec 326 { 327 string name; 328 string type; 329 string default_; 330 } 331 332 static struct FunctionOverloadSpec 333 { 334 ParameterSpec[] parameters; 335 string doc; 336 string test; 337 size_t minArgs; 338 } 339 340 import mir.algebraic; 341 import mir.stdio; 342 if (false) 343 Algebraic!(FunctionOverloadSpec, ParameterSpec).init.writeln; // error 344 }