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.

280 lines
9.6 KiB

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. TYPES_H_FTAG;
  10. vfprintf(stdout, format, args);
  11. }
  12. void vprintln(const char* format, va_list args) {
  13. 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. 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. 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. /**
  38. * Prints a stack trace.
  39. * Implementation varies for Win32 vs. *nix
  40. */
  41. #define BACKTRACE_MAX_FRAMES 63
  42. #ifdef _WIN32
  43. #include <windows.h>
  44. #include <dbghelp.h>
  45. // if |string| is non-null, then the stack trace will be concatenated to it instead of being printed to stdout.
  46. void trace(String* string) {
  47. TYPES_H_FTAG;
  48. #define BACKTRACE_MAX_FUNCTION_NAME_LENGTH 1024
  49. HANDLE processHandle = GetCurrentProcess();
  50. SymInitialize(processHandle, null, true);
  51. void* stack[BACKTRACE_MAX_FRAMES];
  52. unsigned short numFrames = CaptureStackBackTrace(0, BACKTRACE_MAX_FRAMES, stack, null);
  53. char buffer[sizeof(SYMBOL_INFO) + (BACKTRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR)];
  54. SYMBOL_INFO* symbol = (SYMBOL_INFO*) buffer;
  55. symbol->MaxNameLen = BACKTRACE_MAX_FUNCTION_NAME_LENGTH;
  56. symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  57. DWORD displacement;
  58. IMAGEHLP_LINE64 line;
  59. line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
  60. for (u32 i = 0; i < numFrames; i++) {
  61. DWORD64 address = (DWORD64) stack[i];
  62. SymFromAddr(processHandle, address, null, symbol);
  63. if (SymGetLineFromAddr64(processHandle, address, &displacement, &line)) {
  64. if (string == null) {
  65. print("\tat %s in %s: line: %lu: address %0x%0X\n", symbol->Name, line.FileName, line.LineNumber, symbol->Address);
  66. } else {
  67. string->appendf("\tat %s in %s: line: %lu: address %0x%0X\n", symbol->Name, line.FileName, line.LineNumber, symbol->Address);
  68. }
  69. } else {
  70. if (string == null) {
  71. print("\tSymGetLineFromAddr64 returned error code %lu.\n", GetLastError());
  72. print("\tat %s, address 0x%0X.\n", symbol->Name, symbol->Address);
  73. } else {
  74. string->appendf("\tSymGetLineFromAddr64 returned error code %lu.\n", GetLastError());
  75. string->appendf("\tat %s, address 0x%0X.\n", symbol->Name, symbol->Address);
  76. }
  77. }
  78. }
  79. #undef BACKTRACE_MAX_FUNCTION_NAME_LENGTH
  80. }
  81. #else
  82. // OSX and Linux stacktrace stuff.
  83. #include <execinfo.h> // backtrace, backtrace_symbols
  84. #include <cxxabi.h> // abi::__cxa_demangle
  85. // if |string| is non-null, then the stack trace will be concatenated to it instead of being printed to stdout.
  86. void trace(String* string) {
  87. TYPES_H_FTAG;
  88. void* stack[BACKTRACE_MAX_FRAMES];
  89. u32 stackSize = backtrace(stack, BACKTRACE_MAX_FRAMES);
  90. // resolve addresses into strings containing "filename(function+address)"
  91. // this array must be free()-ed
  92. char** traces = backtrace_symbols(stack, stackSize);
  93. // iterate over the returned symbol lines. skip the first, it is the address of this function
  94. for (u32 i = 1; i < stackSize; i++) {
  95. // the names as provided by 'backtrace_symbols' are mangled for some reason.
  96. // we have to demangle them, using this weird api
  97. char buffer[1024];
  98. const char* mangledNameBegin = String::firstCharOccurence(traces[i], '_');
  99. const char* mangledNameEnd = String::lastCharOccurence(traces[i], '+');
  100. if (mangledNameBegin == null || mangledNameEnd == null) {
  101. // we can't demangle this name for some reason, just copy the mangled name to the buffer
  102. size_t length = String::len(traces[i]);
  103. String::memcpy(buffer, (void*)traces[i], length);
  104. buffer[length] = '\0';
  105. } else {
  106. size_t length = mangledNameEnd - 1 - mangledNameBegin;
  107. String::memcpy(buffer, (void*)mangledNameBegin, length);
  108. buffer[length] = '\0';
  109. }
  110. s32 status = -1;
  111. char* trace = abi::__cxa_demangle(buffer, null, null, &status);
  112. if (trace == null) {
  113. println("warning: failed to demangle name: %s", traces[i]);
  114. continue;
  115. }
  116. if (string == null) {
  117. print("%s\n", trace);
  118. } else {
  119. string->appendf("%s\n", trace);
  120. }
  121. }
  122. pFree(traces);
  123. }
  124. #undef BACKTRACE_MAX_FRAMES
  125. #endif
  126. void _debug(const char* format, ...) {
  127. TYPES_H_FTAG;
  128. if (format == null) {
  129. print("%sdebug:%s null\n", ANSI_BLUE, ANSI_RESET);
  130. return;
  131. }
  132. va_list args;
  133. va_start(args, format);
  134. print("%sdebug:%s ", ANSI_BLUE, ANSI_RESET);
  135. vprintln(format, args);
  136. va_end(args);
  137. }
  138. void _warn(const char* format, ...) {
  139. TYPES_H_FTAG;
  140. if (format == null) {
  141. print("%swarning:%s null\n", ANSI_YELLOW, ANSI_RESET);
  142. return;
  143. }
  144. va_list args;
  145. va_start(args, format);
  146. print("%swarning:%s ", ANSI_YELLOW, ANSI_RESET);
  147. vprintln(format, args);
  148. va_end(args);
  149. }
  150. static void (*customDie)(const char* string) = null;
  151. // if you want to override what happens by default when your program calls 'die', you can do so here.
  152. // just keep in mind the intention is for 'die' to be for when your program has encountered a fatal, unrecoverable error.
  153. void setCustomDieBehavior(void (*dieBehavior)(const char* string)) {
  154. customDie = dieBehavior;
  155. }
  156. // for fatal errors which may occur at runtime, even on a release binary.
  157. // if a fatal error should not occur at runtime on a release binary, consider preferring 'massert'
  158. // it's unclear when you should use asserts vs. die actually. idk man
  159. void die(const char* format, ...) {
  160. TYPES_H_FTAG;
  161. if (format == null) {
  162. if (customDie == null) {
  163. print("%serror:%s (unspecified error)\n", ANSI_RED, ANSI_RESET);
  164. trace();
  165. exit(1);
  166. return;
  167. } else {
  168. String string = String128f("error: (unspecified error)\n");
  169. trace(&string);
  170. customDie(string.c_str());
  171. return;
  172. }
  173. }
  174. va_list args;
  175. va_start(args, format);
  176. if (customDie == null) {
  177. println("%serror:%s", ANSI_RED, ANSI_RESET);
  178. vprintln(format, args);
  179. println();
  180. va_end(args);
  181. trace();
  182. exit(1);
  183. } else {
  184. String string = String128f("");
  185. string.setfv(format, args);
  186. string.append("\n");
  187. trace(&string);
  188. va_end(args);
  189. customDie(string.c_str());
  190. }
  191. }
  192. void print(bool b) { TYPES_H_FTAG; print("%s", b ? "true" : "false"); }
  193. void print(char c) { TYPES_H_FTAG; print("%c", c); }
  194. void print(signed int i) { TYPES_H_FTAG; print("%d", i); }
  195. void print(unsigned int i) { TYPES_H_FTAG; print("%u", i); }
  196. void print(float f) { TYPES_H_FTAG; print("%.14g", f); }
  197. void print(double d) { TYPES_H_FTAG; print("%.14g", d); }
  198. void print(void* p) { TYPES_H_FTAG; print("%p", p); }
  199. void print(char* s) { TYPES_H_FTAG; print("%s", s); }
  200. #ifndef _WIN32
  201. void print(size_t i) { TYPES_H_FTAG; print("%u", i); }
  202. void println(size_t i) { TYPES_H_FTAG; print(i); print("\n"); }
  203. #endif
  204. void println(bool b) { TYPES_H_FTAG; print(b); print("\n"); }
  205. void println(char c) { TYPES_H_FTAG; print(c); print("\n"); }
  206. void println(signed int i) { TYPES_H_FTAG; print(i); print("\n"); }
  207. void println(unsigned int i) { TYPES_H_FTAG; print(i); print("\n"); }
  208. void println(float f) { TYPES_H_FTAG; print(f); print("\n"); }
  209. void println(double d) { TYPES_H_FTAG; print(d); print("\n"); }
  210. void println(void* p) { TYPES_H_FTAG; print(p); print("\n"); }
  211. void println(char* s) { TYPES_H_FTAG; print(s); print("\n"); }
  212. void println() { TYPES_H_FTAG; print("\n"); }
  213. #ifdef _USING_GLM_TYPES__
  214. void print(glm::vec<2, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print("vec2: %.14g,%.14g", v.x, v.y); }
  215. void print(glm::vec<3, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print("vec3: %.14g,%.14g,%.14g", v.x, v.y, v.z); }
  216. void print(glm::vec<4, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print("vec4: %.14g,%.14g,%.14g,%.14g", v.x, v.y, v.z, v.w); }
  217. void print(glm::mat<2, 2, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print("mat2: "); print(m[0]); print(m[1]); }
  218. void print(glm::mat<3, 3, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print("mat3: "); print(m[0]); print(m[1]); print(m[2]); }
  219. void print(glm::mat<4, 4, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print("mat4: "); print(m[0]); print(m[1]); print(m[2]); print(m[3]); }
  220. void println(glm::vec<2, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print(v); print("\n"); }
  221. void println(glm::vec<3, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print(v); print("\n"); }
  222. void println(glm::vec<4, float, (glm::qualifier) 3> v) { TYPES_H_FTAG; print(v); print("\n"); }
  223. void println(glm::mat<2, 2, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print(m); print("\n"); }
  224. void println(glm::mat<3, 3, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print(m); print("\n"); }
  225. void println(glm::mat<4, 4, float, (glm::qualifier) 3> m) { TYPES_H_FTAG; print(m); print("\n"); }
  226. #endif