A collection of basic/generally desirable code I use across multiple C++ projects.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

382 lines
14 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. #include <stdarg.h> // va_list, va_start, va_end
  2. #include <stdio.h> // FILE, stderr, stdout | vfprintf
  3. #include <stdlib.h> // exit
  4. #include "alloc.h"
  5. #include "string.h"
  6. #include "print.h"
  7. #include "types.h"
  8. void vprint(const char* format, va_list args) {
  9. ULE_TYPES_H_FTAG;
  10. vfprintf(stdout, format, args);
  11. }
  12. void vprintln(const char* format, va_list args) {
  13. ULE_TYPES_H_FTAG;
  14. vprint(format, args);
  15. print("\n");
  16. }
  17. /**
  18. * The entire purpose of this is so we don't have to #import <stdio.h> everywhere
  19. * +we intend to replace printf at some point with this
  20. */
  21. void print(const char* format, ...) {
  22. ULE_TYPES_H_FTAG;
  23. if (format == null) { print("null"); return; }
  24. va_list args;
  25. va_start(args, format);
  26. vprint(format, args);
  27. va_end(args);
  28. }
  29. void println(const char* format, ...) {
  30. ULE_TYPES_H_FTAG;
  31. if (format == null) { print("null\n"); return; }
  32. va_list args;
  33. va_start(args, format);
  34. vprintln(format, args);
  35. va_end(args);
  36. }
  37. void printBitsLE(const size_t count, void* ptr) {
  38. u8* b = (u8*) ptr;
  39. u8 byte;
  40. for (s32 i = count - 1; i >= 0; i--) {
  41. for (s32 j = 7; j >= 0; j--) {
  42. byte = (b[i] >> j) & 1;
  43. print("%u", byte);
  44. }
  45. }
  46. println();
  47. }
  48. /**
  49. * Prints a stack trace.
  50. * Implementation varies for Win32 vs. *nix
  51. */
  52. #define BACKTRACE_MAX_FRAMES 63
  53. #ifdef _WIN32
  54. #include <windows.h>
  55. #include <dbghelp.h>
  56. // if |string| is non-null, then the stack trace will be concatenated to it instead of being printed to stdout.
  57. void trace(String* string) {
  58. ULE_TYPES_H_FTAG;
  59. #define BACKTRACE_MAX_FUNCTION_NAME_LENGTH 1024
  60. HANDLE processHandle = GetCurrentProcess();
  61. SymInitialize(processHandle, null, true);
  62. SymSetOptions(SYMOPT_LOAD_LINES);
  63. void* stack[BACKTRACE_MAX_FRAMES];
  64. unsigned short numFrames = CaptureStackBackTrace(0, BACKTRACE_MAX_FRAMES, stack, null);
  65. char buffer[sizeof(SYMBOL_INFO) + BACKTRACE_MAX_FUNCTION_NAME_LENGTH*sizeof(TCHAR)];
  66. SYMBOL_INFO* symbol = (SYMBOL_INFO*) buffer;
  67. symbol->MaxNameLen = BACKTRACE_MAX_FUNCTION_NAME_LENGTH;
  68. symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  69. // @TODO I believe that 'displacement' and line.LineNumber are supposed to be source code column numbers
  70. // and line numbers respectively, but displacement doesn't work at all (seems like) and line.LineNumber
  71. // is consistently off by a few lines. Perhaps it's post-processed source? I don't know. For now,
  72. // filename + line.LineNUmber are printed and we hope that's enough, and understand the line #s are only
  73. // approximate.
  74. DWORD displacement;
  75. IMAGEHLP_LINE64 line;
  76. line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
  77. for (u32 i = 0; i < numFrames; i++) {
  78. DWORD64 address = (DWORD64) stack[i];
  79. SymFromAddr(processHandle, address, null, symbol);
  80. if (SymGetLineFromAddr64(processHandle, address, &displacement, &line)) {
  81. if (string == null) {
  82. print(" %-30s %s:%u\n", symbol->Name, line.FileName, line.LineNumber);
  83. } else {
  84. string->appendf(" %-30s %s:%u\n", symbol->Name, line.FileName, line.LineNumber);
  85. }
  86. } else {
  87. if (string == null) {
  88. warn("SymGetLineFromAddr64 returned error code %lu.\n", GetLastError());
  89. print(" %-30s unknown file\n", symbol->Name);
  90. } else {
  91. warn("SymGetLineFromAddr64 returned error code %lu.\n", GetLastError());
  92. string->appendf(" %-30s unknown file\n", symbol->Name);
  93. }
  94. }
  95. }
  96. #undef BACKTRACE_MAX_FUNCTION_NAME_LENGTH
  97. }
  98. #include <Minidumpapiset.h>
  99. #include <tchar.h>
  100. void writeMinidump(void* exceptionPointers) { // 'EXCEPTION_POINTERS*' actually
  101. // create a file
  102. EXCEPTION_POINTERS* ep = (EXCEPTION_POINTERS*) exceptionPointers;
  103. HANDLE hFile = CreateFile(_T("MiniDump.dmp"), GENERIC_READ | GENERIC_WRITE, 0, null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, null);
  104. if ((hFile != null) && (hFile != INVALID_HANDLE_VALUE)) {
  105. // carry on with creating the minidump
  106. MINIDUMP_EXCEPTION_INFORMATION mdei;
  107. mdei.ThreadId = GetCurrentThreadId();
  108. mdei.ExceptionPointers = ep;
  109. mdei.ClientPointers = FALSE;
  110. MINIDUMP_TYPE mdt = MiniDumpNormal;
  111. BOOL rv = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, mdt, (ep != 0) ? &mdei : 0, 0, 0);
  112. if (!rv) {
  113. println(_T("MiniDumpWriteDump failed. Error: %u"), GetLastError());
  114. } else {
  115. println(_T("MiniDump created."));
  116. }
  117. CloseHandle(hFile);
  118. } else {
  119. println(_T("Failed to CreateFile for MiniDump. Error: %u"), GetLastError());
  120. }
  121. }
  122. #else
  123. void writeMinidump(void* exceptionPointers) {} // stub... does nothing on Unix
  124. // OSX and Linux stacktrace stuff.
  125. #include <execinfo.h> // backtrace, backtrace_symbols
  126. #include <cxxabi.h> // abi::__cxa_demangle
  127. // if |string| is non-null, then the stack trace will be concatenated to it instead of being printed to stdout.
  128. void trace(String* string) {
  129. ULE_TYPES_H_FTAG;
  130. void* stack[BACKTRACE_MAX_FRAMES];
  131. u32 stackSize = backtrace(stack, BACKTRACE_MAX_FRAMES);
  132. // resolve addresses into strings containing "filename(function+address)"
  133. // this array must be free()-ed
  134. char** traces = backtrace_symbols(stack, stackSize);
  135. // iterate over the returned symbol lines. skip the first, it is the address of this function
  136. for (u32 i = 1; i < stackSize; i++) {
  137. // the names as provided by 'backtrace_symbols' are mangled for some reason.
  138. // we have to demangle them, using this weird api
  139. // example mangled names (wrapped in double quotes):
  140. // "2 shard_tracy 0x00000001032d5618 _ZL17drawSettingsPanelv + 904"
  141. // "2 shard 0x000000010ed8be34 _ZN6ShaderC2EPKcS1_P5ArrayIS1_ES1_ + 1108"
  142. // "12 shard 0x000000010ed5edeb main + 43"
  143. //
  144. // the rule for finding the 'first' character is annoying, because it's usually but not always starting with an underscore,
  145. // and when it is an underscore it's usually but not always the first underscore in the string.
  146. char buffer[1024];
  147. const char* mangledNameEnd = String::lastCharOccurence(traces[i], '+'); // it actually ends one char before.
  148. const char* mangledNameBegin = null;
  149. if (mangledNameEnd != null && ((mangledNameEnd - traces[i]) > 2)) {
  150. const char* cursor = mangledNameEnd - 2;
  151. while (cursor != traces[i]) {
  152. if (*cursor == '_') {
  153. mangledNameBegin = cursor;
  154. } else if (*cursor == ' ') {
  155. mangledNameBegin = cursor + 1;
  156. break;
  157. }
  158. cursor--;
  159. }
  160. }
  161. if (mangledNameBegin == null || mangledNameEnd == null) {
  162. // we can't demangle this name for some reason, just copy the mangled name to the buffer
  163. size_t length = String::len(traces[i]);
  164. String::memcpy(buffer, (void*)traces[i], length);
  165. buffer[length] = '\0';
  166. } else {
  167. size_t length = mangledNameEnd - mangledNameBegin - 1;
  168. String::memcpy(buffer, (void*)mangledNameBegin, length);
  169. buffer[length] = '\0';
  170. }
  171. s32 status = -1;
  172. char* trace = abi::__cxa_demangle(buffer, null, null, &status);
  173. if (trace == null) {
  174. // @HACK, both 'main' and 'start' symbols will fail to be demangled, and we don't really care about printing them
  175. // in most cases. your application (certainly true for us) will have its own endpoint which itself is of questionable
  176. // usefulness to print, but 'main' and 'start' are even less useful.
  177. if (String::memeq((const unsigned char*)(mangledNameBegin), (const unsigned char*)"main", sizeof("main") - 1)) {
  178. continue;
  179. } else if (String::memeq((const unsigned char*)(mangledNameBegin), (const unsigned char*)"start", sizeof("start") - 1)) {
  180. continue;
  181. } else {
  182. warn("warning: failed to demangle name: %s, exit status of attempt: %d", traces[i], status);
  183. // just write back the original trace, un-demangled.
  184. trace = traces[i];
  185. }
  186. }
  187. if (string == null) {
  188. print(" %s\n", trace);
  189. } else {
  190. string->appendf(" %s\n", trace);
  191. }
  192. }
  193. pFree(traces);
  194. }
  195. #undef BACKTRACE_MAX_FRAMES
  196. #endif
  197. void _debug(
  198. const char* date,
  199. const char* time,
  200. const char* filename,
  201. const int line,
  202. const char* format,
  203. ...
  204. ) {
  205. ULE_TYPES_H_FTAG;
  206. if (format == null) {
  207. print("[%s, %s] [%s:%d] null\n", date, time, filename, line);
  208. return;
  209. }
  210. va_list args;
  211. va_start(args, format);
  212. print("[%s, %s] [%s:%d] ", date, time, filename, line);
  213. vprintln(format, args);
  214. va_end(args);
  215. }
  216. void _warn(
  217. const char* date,
  218. const char* time,
  219. const char* filename,
  220. const int line,
  221. const char* format,
  222. ...
  223. ) {
  224. ULE_TYPES_H_FTAG;
  225. if (format == null) {
  226. print("[%s, %s] [%s:%d] %swarning:%s null\n", date, time, filename, line, ANSI_YELLOW, ANSI_RESET);
  227. return;
  228. }
  229. va_list args;
  230. va_start(args, format);
  231. print("[%s, %s] [%s:%d] %swarning:%s ", date, time, filename, line, ANSI_YELLOW, ANSI_RESET);
  232. vprintln(format, args);
  233. va_end(args);
  234. }
  235. static void (*customDie)(const char* string) = null;
  236. // if you want to override what happens by default when your program calls 'die', you can do so here.
  237. // just keep in mind the intention is for 'die' to be for when your program has encountered a fatal, unrecoverable error.
  238. void setCustomDieBehavior(void (*dieBehavior)(const char* string)) {
  239. customDie = dieBehavior;
  240. }
  241. // for fatal errors which may occur at runtime, even on a release binary.
  242. // if a fatal error should not occur at runtime on a release binary, consider preferring 'massert'
  243. // it's unclear when you should use asserts vs. die actually. idk man, they kinda do the same thing right now
  244. void _die(
  245. const char* date,
  246. const char* time,
  247. const char* filename,
  248. const int line,
  249. const char* format,
  250. ...
  251. ) {
  252. ULE_TYPES_H_FTAG;
  253. if (format == null) {
  254. if (customDie == null) {
  255. print("[%s, %s] [%s:%d] %serror:%s (unspecified error)", date, time, filename, line, ANSI_RED, ANSI_RESET);
  256. trace();
  257. exit(1);
  258. return;
  259. } else {
  260. String string = String128f("error: (unspecified error)\n");
  261. trace(&string);
  262. customDie(string.c_str());
  263. return;
  264. }
  265. }
  266. va_list args;
  267. va_start(args, format);
  268. if (customDie == null) {
  269. println("[%s, %s] [%s:%d] %serror:%s ", date, time, filename, line, ANSI_RED, ANSI_RESET);
  270. vprintln(format, args);
  271. println();
  272. va_end(args);
  273. trace();
  274. exit(1);
  275. } else {
  276. String string = String128f("");
  277. string.appendfv(format, args);
  278. string.append("\n");
  279. trace(&string);
  280. va_end(args);
  281. customDie(string.c_str());
  282. }
  283. }
  284. void print(bool b) { ULE_TYPES_H_FTAG; print("%s", b ? "true" : "false"); }
  285. void print(char c) { ULE_TYPES_H_FTAG; print("%c", c); }
  286. void print(signed int i) { ULE_TYPES_H_FTAG; print("%d", i); }
  287. void print(unsigned int i) { ULE_TYPES_H_FTAG; print("%u", i); }
  288. void print(float f) { ULE_TYPES_H_FTAG; print("%.14g", f); }
  289. void print(double d) { ULE_TYPES_H_FTAG; print("%.14g", d); }
  290. void print(void* p) { ULE_TYPES_H_FTAG; print("%p", p); }
  291. void print(char* s) { ULE_TYPES_H_FTAG; print("%s", s); }
  292. #ifndef _WIN32
  293. void print(size_t i) { ULE_TYPES_H_FTAG; print("%u", i); }
  294. void println(size_t i) { ULE_TYPES_H_FTAG; print(i); print("\n"); }
  295. #endif
  296. void println(bool b) { ULE_TYPES_H_FTAG; print(b); print("\n"); }
  297. void println(char c) { ULE_TYPES_H_FTAG; print(c); print("\n"); }
  298. void println(signed int i) { ULE_TYPES_H_FTAG; print(i); print("\n"); }
  299. void println(unsigned int i) { ULE_TYPES_H_FTAG; print(i); print("\n"); }
  300. void println(float f) { ULE_TYPES_H_FTAG; print(f); print("\n"); }
  301. void println(double d) { ULE_TYPES_H_FTAG; print(d); print("\n"); }
  302. void println(void* p) { ULE_TYPES_H_FTAG; print(p); print("\n"); }
  303. void println(char* s) { ULE_TYPES_H_FTAG; print(s); print("\n"); }
  304. void println() { ULE_TYPES_H_FTAG; print("\n"); }
  305. #ifdef ULE_CONFIG_OPTION_USE_GLM
  306. void print(glm::vec<2, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; print("vec2: %.14g,%.14g", v.x, v.y); }
  307. void print(glm::vec<3, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; print("vec3: %.14g,%.14g,%.14g", v.x, v.y, v.z); }
  308. void print(glm::vec<4, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; print("vec4: %.14g,%.14g,%.14g,%.14g", v.x, v.y, v.z, v.w); }
  309. void print(glm::mat<2, 2, float, (glm::qualifier) 3> m) { ULE_TYPES_H_FTAG; print("mat2: "); print(m[0]); print(m[1]); }
  310. void print(glm::mat<3, 3, float, (glm::qualifier) 3> m) { ULE_TYPES_H_FTAG; print("mat3: "); print(m[0]); print(m[1]); print(m[2]); }
  311. void print(glm::mat<4, 4, float, (glm::qualifier) 3> m) { ULE_TYPES_H_FTAG; print("mat4: "); print(m[0]); print(m[1]); print(m[2]); print(m[3]); }
  312. void println(glm::vec<2, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; print(v); print("\n"); }
  313. void println(glm::vec<3, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; print(v); print("\n"); }
  314. void println(glm::vec<4, float, (glm::qualifier) 3> v) { ULE_TYPES_H_FTAG; print(v); print("\n"); }
  315. void println(glm::mat<2, 2, float, (glm::qualifier) 3> m) { ULE_TYPES_H_FTAG; print(m); print("\n"); }
  316. void println(glm::mat<3, 3, float, (glm::qualifier) 3> m) { ULE_TYPES_H_FTAG; print(m); print("\n"); }
  317. void println(glm::mat<4, 4, float, (glm::qualifier) 3> m) { ULE_TYPES_H_FTAG; print(m); print("\n"); }
  318. #endif // ULE_CONFIG_OPTION_USE_GLM