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