1 /** Backwards-compatible extensions of `std.stdio.{f}write{ln}` to `p{f}write{ln}`. 2 * 3 * Test: dmd -version=show -preview=dip1000 -preview=in -vcolumns -d -I.. -i -debug -g -checkaction=context -allinst -unittest -main -run stdio.d 4 * 5 * See_Also: `core.internal.dassert` 6 * 7 * NOTE: Replacing calls to overloaded `fpwrite1` with non-overloaded versions 8 * such as `fpwrite1_char`, `fpwrite1_string` reduces compilation memory usage. 9 * 10 * TODO: Cast to `arg const` for `T` being `struct` and `class` with `const toString` to avoid template-bloat 11 * 12 * TODO: Use setlocale to enable correct printing {w|d}char([]) 13 * #include <wchar.h> 14 * #include <locale.h> 15 * #include <stdio.h> 16 * 17 * int main() { 18 * // Set the locale to the user default, which should include Unicode support 19 * setlocale(LC_ALL, ""); 20 * 21 * wchar_t letter1 = L'å'; 22 * wchar_t letter2 = L'ä'; 23 * wchar_t letter3 = L'ö'; 24 * 25 * wprintf(L"%lc %lc %lc\n", letter1, letter2, letter3); 26 * 27 * return 0; 28 * } 29 * 30 * In D you use: 31 * 32 * import core.stdc.locale : setlocale, LC_ALL; 33 * () @trusted { setlocale(LC_ALL, ""); }(); 34 * 35 * TODO: Perhaps make public (top) functions throw static exceptions keeping them `@nogc`. 36 */ 37 module nxt.stdio; 38 39 import core.stdc.stdio : stdout, fputc, fprintf, FILE, EOF, c_fwrite = fwrite; 40 import core.stdc.wchar_ : fputwc, fwprintf; 41 import nxt.visiting : Addrs = Addresses; 42 43 // version = show; 44 45 @safe: 46 47 /++ Writing/Printing format. +/ 48 @safe struct Format { 49 @safe pure nothrow @nogc: 50 static Format plain() { 51 typeof(return) result; 52 result.showClassValues = true; 53 result.showPointerValues = true; 54 result.showEnumeratorEnumType = true; 55 result.showEnumatorValues = true; 56 result.useFonts = true; 57 return result; 58 } 59 static Format pretty() { 60 typeof(return) result = Format.plain; 61 result.showFieldNames = true; 62 result.useFonts = true; 63 return result; 64 } 65 static Format fancy() { 66 typeof(return) result = Format.pretty; 67 result.multiLine = true; 68 return result; 69 } 70 static Format debugging() { 71 typeof(return) result = Format.fancy; 72 result.showVoidArrayValues = true; 73 result.dynamicArrayLengthMax = 8; 74 result.multiLine = true; 75 return result; 76 } 77 static Format everything() { 78 typeof(return) result = Format.fancy; 79 result.multiLine = true; 80 return result; 81 } 82 83 size_t level = 0; ///< Level of (aggregate) nesting starting at 0. 84 string indentation = "\t"; ///< Indentation. 85 char arrayPrefix = '['; ///< (Associative) Array prefix. 86 char arraySuffix = ']'; ///< (Associative) Array suffix. 87 char aggregatePrefix = '('; ///< Array fields prefix. 88 char aggregateSuffix = ')'; ///< Array fields suffix. 89 static immutable arrayElementSeparator = ", "; ///< Array element separator. 90 static immutable aggregateFieldSeparator = ", "; ///< Aggregate field separator. 91 char backReferencePrefix = '#'; ///< Backward reference prefix. 92 // TODO: Use bitfields 93 bool quoteChars; ///< Wrap characters {char|wchar|dchar} in ASCII single-quote character '\''. 94 bool quoteStrings; ///< Wrap strings {string|wstring|dstring} in ASCII single-quote character '"'. 95 bool showClassValues; ///< Show fields of non-null classes (instead of just pointer). 96 bool showPointerValues; ///< Show values of non-null pointers (instead of just pointer). 97 bool showEnumeratorEnumType; ///< Show enumerators as `EnumType.enumeratorValue` instead of `enumeratorValue`. 98 bool showEnumatorValues; ///< Show values of enumerators instead of their names. 99 bool showFieldNames = false; ///< Show names of fields. 100 bool showFieldTypes = false; ///< Show types of values. 101 bool showVoidArrayValues = false; ///< Show values of void arrays as ubytes instead of `[?]`. 102 bool multiLine = false; ///< Span multiple lines using `indent`. 103 bool useFonts; ///< Use different fonts for different types. TODO: Use nxt.ansi_escape 104 size_t dynamicArrayLengthMax = size_t.max; ///< Limit length of dynamic arrays to this value when printing. 105 } 106 107 /// 108 @safe pure unittest { 109 assert(Format.plain != Format.pretty); 110 assert(Format.pretty != Format.fancy); 111 } 112 113 /** Pretty-formatted `fwrite(sm, ...)`. */ 114 void fpwrite(Args...)(scope FILE* sm, in Format fmt, in Args args) { 115 // pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", Args); 116 scope Addrs addrs; 117 foreach (ref arg; args) { 118 static if (is(typeof(arg) == enum)) 119 sm.fpwrite1_enum(arg, fmt, addrs); 120 else 121 sm.fpwrite1(arg, fmt, addrs); 122 } 123 } 124 125 /** Pretty-formatted `fwrite(stderr, ...)`. */ 126 void epwrite(Args...)(in Format fmt, in Args args) 127 => fpwrite(stderr, fmt, args); 128 129 /** Alternative to `std.stdio.fwrite`. */ 130 void fwrite(Args...)(scope FILE* sm, in Args args) 131 => sm.fpwrite!(Args)(Format.init, args); 132 133 /** Alternative to `std.stdio.write(stdout)`. */ 134 void write(Args...)(in Args args) 135 => stdout.fwrite(args); 136 137 /** Alternative to `std.stdio.write(stderr)`. */ 138 void ewrite(Args...)(in Args args) 139 => stderr.fwrite(args); 140 141 /** Pretty-formatted `fwriteln`. */ 142 void fpwriteln(Args...)(scope FILE* sm, in Format fmt, in Args args) { 143 sm.fpwrite!(Args)(fmt, args); 144 const st = sm.fpwrite1_char('\n'); 145 // sm.fflush(); 146 } 147 148 /** Pretty-formatted `writeln`. */ 149 void pwriteln(Args...)(in Format fmt, in Args args) 150 => stdout.fpwriteln(fmt, args); 151 /** Pretty-formatted `stderr.writeln`. */ 152 void epwriteln(Args...)(in Format fmt, in Args args) 153 => stderr.fpwriteln(fmt, args); 154 155 /** Alternative to `std.stdio.fwriteln`. */ 156 void fwriteln(Args...)(scope FILE* sm, in Args args) { 157 sm.fwrite(args); 158 const st = sm.fpwrite1_char('\n'); 159 // sm.fflush(); 160 } 161 162 /** Alternative to `std.stdio.writeln`. */ 163 void writeln(Args...)(in Args args) 164 => stdout.fwriteln(args); 165 166 private: 167 168 /** Pretty-formatted write single argument `arg` to `sm`. */ 169 void fpwrite1(T)(scope FILE* sm, in T arg, in Format fmt, scope ref Addrs addrs) { 170 // pragma(msg, __FILE__, "(", __LINE__, ",1): Debug: ", T); 171 static if (is(T == enum)) { 172 sm.fpwrite1_enum(arg, fmt, addrs); 173 } else static if (__traits(hasMember, T, "toString") && is(typeof(arg.toString) : string)) { 174 string str; 175 () @trusted { 176 /+ avoid scope compilation errors such as: 177 scope variable `arg` calling non-scope member function `JSONValue.toString()` 178 +/ 179 str = arg.toString; 180 }(); 181 const int st = sm.fpwrite1_string(str, fmt.quoteStrings); 182 return; // TODO: forward `st` 183 } else static if (is(T == enum)) { 184 static assert(0, "TODO: Branch on `fmt.showEnumatorValues`"); 185 } else static if (is(T : __vector(U[N]), U, size_t N)) /+ must come before `__traits(isArithmetic, T)` below because `is(__traits(isArithmetic, float4)` holds: +/ { 186 const st = sm.fpwrite1ArrayValueExceptString(arg, fmt, addrs, false); 187 } else static if (is(immutable T == immutable bool)) { 188 const st = sm.fpwrite1_string(arg ? "true" : "false"); 189 } else static if (__traits(isArithmetic, T)) { 190 static if (is(immutable T == immutable char)) { 191 if (fmt.quoteChars) { const st1 = fputc('\'', sm); } 192 const st = fputc(arg, sm); 193 if (fmt.quoteChars) { const st2 = fputc('\'', sm); } 194 } else static if (is(immutable T == immutable wchar) || is(immutable T == immutable dchar)) { 195 if (fmt.quoteChars) { const st1 = fputc('\'', sm); } 196 const st = fputwc(arg, sm); 197 if (fmt.quoteChars) { const st2 = fputc('\'', sm); } 198 } else { 199 // See: `getPrintfFormat` in "core/internal/dassert.d" 200 static if (is(immutable T == immutable byte)) 201 immutable pff = "%hhd"; 202 else static if (is(immutable T == immutable ubyte)) 203 immutable pff = "%hhu"; 204 else static if (is(immutable T == immutable short)) 205 immutable pff = "%hd"; 206 else static if (is(immutable T == immutable ushort)) 207 immutable pff = "%hu"; 208 else static if (is(immutable T == immutable int)) 209 immutable pff = "%d"; 210 else static if (is(immutable T == immutable uint)) 211 immutable pff = "%u"; 212 else static if (is(immutable T == immutable long)) 213 immutable pff = "%lld"; 214 else static if (is(immutable T == immutable ulong)) 215 immutable pff = "%llu"; 216 else static if (is(immutable T == immutable float)) 217 immutable pff = "%g"; // or %e %g 218 else static if (is(immutable T == immutable double)) 219 immutable pff = "%lg"; // or %le %lg 220 else static if (is(immutable T == immutable real)) 221 immutable pff = "%Lg"; // or %Le %Lg 222 else 223 static assert(0, "TODO: Handle argument of type " ~ T.stringof); 224 () @trusted { const st = sm.fprintf(pff.ptr, arg);} (); 225 } 226 } else static if (is(T : U[N], U, size_t N)) { // isStaticArray 227 static if (is(U == char) || is(U == wchar) || is(U == dchar)) { // `isSomeChar` 228 if (fmt.quoteStrings) { const st1 = sm.fpwrite1_char('"'); } 229 const stm = sm.fpwrite1(arg, fmt); 230 if (fmt.quoteStrings) { const st2 = sm.fpwrite1_char('"'); } 231 } else { 232 const st = sm.fpwrite1ArrayValueExceptString(arg[]/+ `arg[]` avoids template bloat +/, fmt, addrs, false); 233 } 234 } else static if (is(T : const(U)[], U)) { // isDynamicArray 235 static if (is(U == char) || is(U == wchar) || is(U == dchar)) { // `isSomeChar` 236 if (fmt.quoteStrings) { const st1 = sm.fpwrite1_char('"'); } 237 const stm = sm.fpwrite1(arg, fmt); 238 if (fmt.quoteStrings) { const st2 = sm.fpwrite1_char('"'); } 239 } else { 240 import std.algorithm.comparison : min; 241 const truncated = arg.length > fmt.dynamicArrayLengthMax; 242 const st = sm.fpwrite1ArrayValueExceptString(arg[0 .. min(arg.length, fmt.dynamicArrayLengthMax)], fmt, addrs, truncated); 243 } 244 } else static if (is(typeof(T.init.byKeyValue))) { // isAssociativeArray || isMap 245 const st1 = sm.fpwrite1_char(fmt.arrayPrefix); 246 size_t i; 247 Format keyFmt = fmt; // key format 248 Format valFmt = fmt; // value format 249 alias Key = typeof(T.init.keys[0]); 250 alias Val = typeof(T.init.values[0]); 251 static if (!is(Key == struct) && !is(Key == class)) 252 keyFmt.indentation = null; 253 static if (!is(Val == struct) && !is(Val == class)) 254 valFmt.indentation = null; 255 foreach (ref kv; arg.byKeyValue) { 256 if (i) 257 const st1_ = sm.fpwrite1_string(fmt.arrayElementSeparator); 258 sm.fpwrite1(kv.key, keyFmt, addrs); 259 const st = sm.fpwrite1_string(": "); 260 sm.fpwrite1(kv.value, valFmt, addrs); 261 i += 1; 262 } 263 const st2 = sm.fpwrite1_char(fmt.arraySuffix); 264 } else static if (is(T == struct)) { 265 sm.fpwriteAggregate(arg, fmt, addrs); 266 } else static if (is(T == class) || is(T == const(U)*, U)) { // isAddress 267 const bool isNull = arg is null; 268 if (isNull) { 269 const st = sm.fpwrite1_string("null"); 270 return; 271 } 272 void* addr; 273 () @trusted { addr = cast(void*)arg; }(); 274 () @trusted { const st = sm.fprintf("%lX", cast(size_t)addr, arg);} (); 275 import nxt.algorithm.searching : indexOf; 276 const ix = addrs[].indexOf(addr); 277 if (ix != -1) { // `addr` already printed 278 const st1 = sm.fpwrite1_char(fmt.backReferencePrefix); 279 sm.fpwrite1(ix, fmt, addrs); 280 return; 281 } 282 () @trusted { addrs ~= addr; }(); // `addrs`.lifetime <= `arg`.lifetime 283 static if (is(T == class)) { 284 if (fmt.showClassValues) { 285 const st = sm.fpwrite1_string(" . "); 286 sm.fpwriteAggregate(arg, fmt, addrs); 287 } 288 } else { 289 if (fmt.showPointerValues) { 290 static if (is(typeof(*arg))) { 291 const st = sm.fpwrite1_string(" -> "); 292 () @trusted { 293 sm.fpwrite1(cast()*arg, fmt, addrs); 294 }(); 295 } else { 296 // TODO: Print something like this instead?: 297 // const st = sm.fpwrite1_string(" -> "); 298 // sm.fpwrite1_char('?'); 299 } 300 } 301 } 302 } else { 303 static assert(0, "TODO: Handle argument of type " ~ T.stringof); 304 } 305 } 306 307 int fpwrite1ArrayValueExceptString(T)(scope FILE* sm, in T arg, in Format fmt, scope ref Addrs addrs, bool truncated) { 308 const st1 = sm.fpwrite1_char(fmt.arrayPrefix); 309 static if (is(immutable T == immutable void[])) { 310 if (fmt.showVoidArrayValues) { 311 ubyte[] bytes; 312 () @trusted { 313 bytes = cast(ubyte[])arg/+to mutable to avoid template-bloat+/; 314 }(); 315 sm.fpwriteArrayValues(bytes, fmt, addrs, truncated); // TODO: perhaps show as hex instead 316 } 317 else if (arg.length != 0) // if any unprintable elements 318 const st = sm.fpwrite1_string("?"); // indicate that 319 } else { 320 sm.fpwriteArrayValues(arg[], fmt, addrs, truncated); 321 } 322 const st2 = sm.fpwrite1_char(fmt.arraySuffix); 323 return 0; 324 } 325 326 int fpwriteArrayValues(E)(scope FILE* sm, in E[] arg, in Format fmt, scope ref Addrs addrs, bool truncated) { 327 Format eltFmt = fmt; // element format 328 static if (!is(E == struct) && !is(E == class)) 329 eltFmt.indentation = null; 330 foreach (const i, ref elt; arg) { 331 if (i) 332 const st1_ = sm.fpwrite1_string(fmt.arrayElementSeparator); 333 sm.fpwrite1(elt, eltFmt, addrs); 334 } 335 if (truncated && arg.length != 0) 336 sm.fpwrite1_string(", …"); 337 return 0; 338 } 339 340 void fpwriteAggregate(T)(scope FILE* sm, in T arg, in Format fmt, scope ref Addrs addrs) 341 if (is(T == struct) || is(T == class)) { 342 const stT = sm.fpwrite1_string(T.stringof); 343 const stP = sm.fpwrite1_char(fmt.aggregatePrefix); 344 sm.fpwriteFieldsOf(arg, fmt, addrs); 345 const stS = sm.fpwrite1_char(fmt.aggregateSuffix); 346 } 347 348 void fpwriteFieldsOf(T)(scope FILE* sm, in T arg, in Format fmt, scope ref Addrs addrs) 349 if (is(T == struct) || is(T == class)) { 350 import std.traits : isCallable; 351 size_t i; 352 foreach (memberName; __traits(allMembers, T)) { 353 static if (memberName != "__ctor" && memberName != "__dtor" && memberName != "__postblit" && 354 memberName != "__xctor" && memberName != "__xdtor" && memberName != "__xpostblit" && 355 !is(__traits(getMember, arg, memberName))) { /* exclude type members */ 356 static if (__traits(compiles, { const _ = __traits(getMember, arg, memberName); } )) { /+ TODO: try to replace this with another __traits() +/ 357 alias Member = typeof(__traits(getMember, arg, memberName)); 358 static if (!isCallable!Member) { 359 if (i != 0) 360 const sta = sm.fpwrite1_string(fmt.aggregateFieldSeparator); 361 if (fmt.multiLine) 362 const st1 = sm.fpwrite1_char('\n'); 363 Format fieldFmt = fmt; // field format 364 fieldFmt.quoteChars = true; // mimics `std.stdio` 365 fieldFmt.quoteStrings = true; // mimics `std.stdio` 366 fieldFmt.level += 1; 367 sm.indent(fieldFmt); 368 if (fmt.showFieldTypes) { 369 const st1 = sm.fpwrite1_string(Member.stringof); 370 const st2 = sm.fpwrite1_char(' '); 371 } 372 if (fmt.showFieldNames) { 373 const st1 = sm.fpwrite1_string(memberName); 374 const st2 = sm.fpwrite1_string(": "); 375 } 376 () @trusted { 377 static if (is(Member == enum)) 378 sm.fpwrite1_enum(__traits(getMember, arg, memberName), fieldFmt, addrs); 379 else { 380 try { 381 sm.fpwrite1(cast()__traits(getMember, arg, memberName), fieldFmt, addrs); // TODO: remove cast() 382 } catch (Exception _) { 383 const st = sm.fpwrite1_string("TODO: Avoid this Exception that is maybe triggered by casting away shared above"); 384 } 385 } 386 }(); 387 i += 1; 388 } 389 } 390 } 391 } 392 } 393 394 int fpwrite1(scope FILE* sm, in char arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 395 return sm.fpwrite1_char(arg, fmt.quoteChars); 396 } 397 int fpwrite1(scope FILE* sm, in wchar arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 398 return sm.fpwrite1_wchar(arg, fmt.quoteChars); 399 } 400 int fpwrite1(scope FILE* sm, in dchar arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 401 return sm.fpwrite1_dchar(arg, fmt.quoteChars); 402 } 403 404 int fpwrite1(scope FILE* sm, in char[] arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 405 return sm.fpwrite1_string(arg, fmt.quoteStrings); 406 } 407 int fpwrite1(scope FILE* sm, in wchar[] arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 408 return sm.fpwrite1_wstring(arg, fmt.quoteStrings); 409 } 410 int fpwrite1(scope FILE* sm, in dchar[] arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 411 return sm.fpwrite1_dstring(arg, fmt.quoteStrings); 412 } 413 414 int fpwrite1(scope FILE* sm, in bool arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 415 return sm.fpwrite1_string(arg ? "true" : "false"); 416 } 417 int fpwrite1(scope FILE* sm, in byte arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 418 return sm.fprintf("%hhd".ptr, arg); 419 } 420 int fpwrite1(scope FILE* sm, in ubyte arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 421 return sm.fprintf("%hhu".ptr, arg); 422 } 423 424 int fpwrite1(scope FILE* sm, in short arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 425 return sm.fprintf("%hd".ptr, arg); 426 } 427 int fpwrite1(scope FILE* sm, in ushort arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 428 return sm.fprintf("%hu".ptr, arg); 429 } 430 431 int fpwrite1(scope FILE* sm, in int arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 432 return sm.fprintf("%d".ptr, arg); 433 } 434 int fpwrite1(scope FILE* sm, in uint arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 435 return sm.fprintf("%u".ptr, arg); 436 } 437 438 int fpwrite1(scope FILE* sm, in long arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 439 return sm.fprintf("%lld".ptr, arg); 440 } 441 int fpwrite1(scope FILE* sm, in ulong arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 442 return sm.fprintf("%llu".ptr, arg); 443 } 444 445 int fpwrite1(scope FILE* sm, in float arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 446 return sm.fprintf("%g".ptr, arg); 447 } 448 int fpwrite1(scope FILE* sm, in double arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 449 return sm.fprintf("%lg".ptr, arg); 450 } 451 int fpwrite1(scope FILE* sm, in real arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc { 452 return sm.fprintf("%Lg".ptr, arg); 453 } 454 455 int fpwrite1_enum(T)(scope FILE* sm, in T arg, in Format fmt, scope ref Addrs addrs) @trusted nothrow @nogc if (is(T == enum)) { 456 if (fmt.showEnumeratorEnumType) { 457 sm.fpwrite1_string(T.stringof, false); 458 sm.fpwrite1_char('.', false); 459 } 460 return sm.fpwrite1_string(arg.enumToString!T(fmt, "__unknown__"), false); 461 } 462 463 int fpwrite1_char(scope FILE* sm, in char arg, in bool quote = false) @trusted nothrow @nogc { 464 if (quote) { const st1 = fputc('\'', sm); } 465 const st = fputc(arg, sm); 466 if (st == EOF) {} /+ TODO: throw or return error status +/ 467 if (quote) { const st1 = fputc('\'', sm); } 468 return 0; 469 } 470 471 int fpwrite1_wchar(scope FILE* sm, in wchar arg, in bool quote = false) @trusted nothrow @nogc { 472 if (quote) { const st1 = fputc('\'', sm); } 473 const st = fputwc(arg, sm); 474 if (st == EOF) {} /+ TODO: throw or return error status +/ 475 if (quote) { const st1 = fputc('\'', sm); } 476 return 0; 477 } 478 479 int fpwrite1_dchar(scope FILE* sm, in dchar arg, in bool quote = false) @trusted nothrow @nogc { 480 if (quote) { const st1 = fputc('\'', sm); } 481 const st = fputwc(arg, sm); 482 if (st == EOF) {} /+ TODO: throw or return error status +/ 483 if (quote) { const st1 = fputc('\'', sm); } 484 return 0; 485 } 486 487 int fpwrite1_string(scope FILE* sm, in char[] arg, in bool quote = false) @trusted nothrow @nogc { 488 if (quote) { const st1 = fputc('"', sm); } 489 typeof(return) st; 490 if (arg.length) 491 st = cast(typeof(return))c_fwrite(&arg[0], 1, arg.length, sm); 492 if (quote) { const st2 = fputc('"', sm); } 493 return st; 494 } 495 496 int fpwrite1_wstring(scope FILE* sm, in wchar[] arg, in bool quote = false) @trusted nothrow @nogc { 497 if (quote) { const st1 = fputc('"', sm); } 498 typeof(return) st; 499 if (arg.length) 500 st = cast(typeof(return))c_fwrite(&arg[0], 1, arg.length, sm); 501 if (quote) { const st2 = fputc('"', sm); } 502 return st; 503 } 504 505 int fpwrite1_dstring(scope FILE* sm, in dchar[] arg, in bool quote = false) @trusted nothrow @nogc { 506 if (quote) { const st1 = fputc('"', sm); } 507 typeof(return) st; 508 if (arg.length) 509 st = cast(typeof(return))c_fwrite(&arg[0], 1, arg.length, sm); 510 if (quote) { const st2 = fputc('"', sm); } 511 return st; 512 } 513 514 string enumToString(T)(in T arg, in Format fmt, string defaultValue) if (is(T == enum)) { 515 switch (arg) { // instead of slower `std.conv.to`: 516 static foreach (member; __traits(allMembers, T)) { // instead of slower `EnumMembers` 517 case __traits(getMember, T, member): 518 return member; 519 } 520 default: 521 return defaultValue; 522 } 523 } 524 525 void indent(scope FILE* sm, in Format fmt) nothrow @nogc { 526 if (fmt.multiLine) 527 foreach (_; 0 .. fmt.level) 528 const int st = sm.fpwrite1_string(fmt.indentation, false); 529 } 530 531 /// 532 version (show) 533 @safe nothrow unittest { 534 import core.simd; 535 static struct Uncopyable { this(this) @disable; int _x; } 536 scope const int* x = new int(42); 537 enum NormalEnum { 538 first = 0, 539 second = 1, 540 } 541 writeln(NormalEnum.first); 542 writeln(NormalEnum.second); 543 version (none) enum StringEnum { // TODO: use 544 OPENED = "opened", 545 MERGED = "merged", 546 } 547 class Base { 548 int baseField1; 549 int baseField2; 550 } 551 class Derived : Base { 552 this(int derivedField1, int derivedField2) { 553 this.derivedField1 = derivedField1; 554 this.derivedField2 = derivedField2; 555 } 556 int derivedField1; 557 int derivedField2; 558 } 559 struct T { 560 int x = 111, y = 222; 561 } 562 struct U { 563 const bool b_f = false; 564 const bool b_t = true; 565 } 566 struct V { 567 const char ch_a = 'a'; 568 const wchar wch = 'ä'; 569 const dchar dch = 'ö'; 570 const c_ch = 'b'; 571 const i_ch = 'c'; 572 } 573 struct W { 574 const str = "åäö"; 575 const wstring wstr = "åäö"; 576 const dstring dstr = "åäö"; 577 const ubyte[] ua = [1,2,3]; 578 const void[] va = cast(void[])[1,2,3]; 579 } 580 struct X { 581 const byte[3] ba = [byte.min, 0, byte.max]; 582 const short[3] sa = [short.min, 0, short.max]; 583 const int[3] ia = [int.min, 0, int.max]; 584 const long[3] la = [long.min, 0, long.max]; 585 } 586 struct Y { 587 const float[3] fa = [-float.infinity, 3.14, +float.infinity]; 588 const double[3] da = [-double.infinity, 3.333333333, +double.infinity]; 589 const real[3] ra = [-real.infinity, 0, +real.infinity]; 590 } 591 struct S { 592 T t; 593 U u; 594 V v; 595 W w; 596 X x; 597 Y y; 598 int ix = 32; 599 const int iy = 42; 600 immutable int iz = 52; 601 const(int)* null_p; 602 const(int)* xp; 603 NormalEnum normalEnum; 604 version (none) StringEnum stringEnum; // TODO: use 605 const aa = [1:1, 2:2]; 606 float4 f4; 607 void* voidPtr; 608 Derived derived; 609 Base baseBeingDerived; 610 } 611 612 S s; 613 s.xp = x; 614 s.voidPtr = new int(42); 615 s.derived = new Derived(33, 44); 616 s.baseBeingDerived = new Derived(55, 66); 617 618 writeln(Uncopyable.init); 619 620 Format fmt; 621 622 fmt = Format.init; 623 fmt.showFieldNames = true; 624 pwriteln(fmt, s); 625 626 fmt = Format.init; 627 fmt.showFieldTypes = true; 628 pwriteln(fmt, s); 629 630 fmt = Format.init; 631 fmt.showPointerValues = true; 632 pwriteln(fmt, s); 633 634 fmt = Format.init; 635 fmt.showEnumeratorEnumType = true; 636 pwriteln(fmt, s); 637 638 fmt = Format.fancy; 639 fmt.showFieldTypes = false; 640 pwriteln(fmt, s); 641 642 fmt = Format.init; 643 pwriteln(fmt, s); 644 645 // import std.stdio : writeln; 646 // debug writeln(s); 647 }