1 // Copyright (c) 2019 nyorain 2 // Distributed under the Boost Software License, Version 1.0. 3 // See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt 4 5 #ifndef INC_DLG_DLG_H_ 6 #define INC_DLG_DLG_H_ 7 8 #include <stdbool.h> 9 #include <stddef.h> 10 #include <stdlib.h> 11 #include <stdarg.h> 12 #include <stdio.h> 13 14 // Hosted at https://github.com/nyorain/dlg. 15 // There are examples and documentation. 16 // Issue reports and contributions appreciated. 17 18 // - CONFIG - 19 // Define this macro to make all dlg macros have no effect at all 20 // #define DLG_DISABLE 21 22 // the log/assertion levels below which logs/assertions are ignored 23 // defaulted depending on the NDEBUG macro 24 #ifndef DLG_LOG_LEVEL 25 #ifdef NDEBUG 26 #define DLG_LOG_LEVEL dlg_level_warn 27 #else 28 #define DLG_LOG_LEVEL dlg_level_trace 29 #endif 30 #endif 31 32 #ifndef DLG_ASSERT_LEVEL 33 #ifdef NDEBUG 34 #define DLG_ASSERT_LEVEL dlg_level_warn 35 #else 36 #define DLG_ASSERT_LEVEL dlg_level_trace 37 #endif 38 #endif 39 40 // the assert level of dlg_assert 41 #ifndef DLG_DEFAULT_ASSERT 42 #define DLG_DEFAULT_ASSERT dlg_level_error 43 #endif 44 45 // evaluated to the 'file' member in dlg_origin 46 #ifndef DLG_FILE 47 #define DLG_FILE dlg__strip_root_path(__FILE__, DLG_BASE_PATH) 48 49 // the base path stripped from __FILE__. If you don't override DLG_FILE set this to 50 // the project root to make 'main.c' from '/some/bullshit/main.c' 51 #ifndef DLG_BASE_PATH 52 #define DLG_BASE_PATH "" 53 #endif 54 #endif 55 56 // Default tags applied to all logs/assertions (in the defining file). 57 // Must be in format ```#define DLG_DEFAULT_TAGS "tag1", "tag2"``` 58 // or just nothing (as defaulted here) 59 #ifndef DLG_DEFAULT_TAGS 60 #define DLG_DEFAULT_TAGS_TERM NULL 61 #else 62 #define DLG_DEFAULT_TAGS_TERM DLG_DEFAULT_TAGS, NULL 63 #endif 64 65 // The function used for formatting. Can have any signature, but must be callable with 66 // the arguments the log/assertions macros are called with. Must return a const char* 67 // that will not be freed by dlg, the formatting function must keep track of it. 68 // The formatting function might use dlg_thread_buffer or a custom owned buffer. 69 // The returned const char* has to be valid until the dlg log/assertion ends. 70 // Usually a c function with ... (i.e. using va_list) or a variadic c++ template do 71 // allow formatting. 72 #ifndef DLG_FMT_FUNC 73 #define DLG_FMT_FUNC dlg__printf_format 74 #endif 75 76 // Only overwrite (i.e. predefine) this if you know what you are doing. 77 // On windows this is used to add the dllimport specified. 78 // If you are using the static version of dlg (on windows) define 79 // DLG_STATIC before including dlg.h 80 #ifndef DLG_API 81 #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(DLG_STATIC) 82 #define DLG_API __declspec(dllimport) 83 #else 84 #define DLG_API 85 #endif 86 #endif 87 88 // - utility - 89 // two methods needed since cplusplus does not support compound literals 90 // and c does not support uniform initialization/initializer lists 91 #ifdef __cplusplus 92 #include <initializer_list> 93 #define DLG_CREATE_TAGS(...) std::initializer_list<const char*> \ 94 {DLG_DEFAULT_TAGS_TERM, __VA_ARGS__, NULL}.begin() 95 #else 96 #define DLG_CREATE_TAGS(...) (const char* const[]) {DLG_DEFAULT_TAGS_TERM, __VA_ARGS__, NULL} 97 #endif 98 99 #ifdef __GNUC__ 100 #define DLG_PRINTF_ATTRIB(a, b) __attribute__ ((format (printf, a, b))) 101 #else 102 #define DLG_PRINTF_ATTRIB(a, b) 103 #endif 104 105 #ifdef __cplusplus 106 extern "C" { 107 #endif 108 109 110 // Represents the importance of a log/assertion call. 111 enum dlg_level { 112 dlg_level_trace = 0, // temporary used debug, e.g. to check if control reaches function 113 dlg_level_debug, // general debugging, prints e.g. all major events 114 dlg_level_info, // general useful information 115 dlg_level_warn, // warning, something went wrong but might have no (really bad) side effect 116 dlg_level_error, // something really went wrong; expect serious issues 117 dlg_level_fatal // critical error; application is likely to crash/exit 118 }; 119 120 // Holds various information associated with a log/assertion call. 121 // Forwarded to the output handler. 122 struct dlg_origin { 123 const char* file; 124 unsigned int line; 125 const char* func; 126 enum dlg_level level; 127 const char** tags; // null-terminated 128 const char* expr; // assertion expression, otherwise null 129 }; 130 131 // Type of the output handler, see dlg_set_handler. 132 typedef void(*dlg_handler)(const struct dlg_origin* origin, const char* string, void* data); 133 134 #ifdef DLG_DISABLE 135 // Tagged/Untagged logging with variable level 136 // Tags must always be in the format `("tag1", "tag2")` (including brackets) 137 #define dlg_log(level, ...) 138 #define dlg_logt(level, tags, ...) 139 140 // Dynamic level assert macros in various versions for additional arguments 141 #define dlg_assertl(level, expr) // assert without tags/message 142 #define dlg_assertlt(level, tags, expr) // assert with tags 143 #define dlg_assertlm(level, expr, ...) // assert with message 144 #define dlg_assertltm(level, tags, expr, ...) // assert with tags & message 145 146 // Sets the handler that is responsible for formatting and outputting log calls. 147 // This function is not thread safe and the handler is set globally. 148 // The handler itself must not change dlg tags or call a dlg macro (if it 149 // does so, the provided string or tags array in 'origin' might get invalid). 150 // The handler can also be used for various other things such as dealing 151 // with failed assertions or filtering calls based on the passed tags. 152 // The default handler is dlg_default_output (see its doc for more info). 153 // If using c++ make sure the registered handler cannot throw e.g. by 154 // wrapping everything into a try-catch blog. dlg_set_handler(dlg_handler handler,void * data)155 inline void dlg_set_handler(dlg_handler handler, void* data) { 156 (void) handler; 157 (void) data; 158 } 159 160 // Returns the currently active dlg handler and sets `data` to 161 // its user data pointer. `data` must not be NULL. 162 // Useful to create handler chains. 163 // This function is not threadsafe, i.e. retrieving the handler while 164 // changing it from another thread is unsafe. 165 // See `dlg_set_handler`. dlg_get_handler(void ** data)166 inline dlg_handler dlg_get_handler(void** data) { 167 *data = NULL; 168 return NULL; 169 } 170 171 // The default output handler. 172 // Only use this to reset the output handler, prefer to use 173 // dlg_generic_output (from output.h) which this function simply calls. 174 // It also flushes the stream used and correctly outputs even from multiple threads. dlg_default_output(const struct dlg_origin * o,const char * str,void * data)175 inline void dlg_default_output(const struct dlg_origin* o, const char* str, void* data) { 176 (void) o; 177 (void) str; 178 (void) data; 179 } 180 181 // Adds the given tag associated with the given function to the thread specific list. 182 // If func is not NULL the tag will only applied to calls from the same function. 183 // Remove the tag again calling dlg_remove_tag (with exactly the same pointers!). 184 // Does not check if the tag is already present. dlg_add_tag(const char * tag,const char * func)185 inline void dlg_add_tag(const char* tag, const char* func) { 186 (void) tag; 187 (void) func; 188 } 189 190 // Removes a tag added with dlg_add_tag (has no effect for tags no present). 191 // The pointers must be exactly the same pointers that were supplied to dlg_add_tag, 192 // this function will not check using strcmp. When the same tag/func combination 193 // is added multiple times, this function remove exactly one candidate, it is 194 // undefined which. Returns whether a tag was found (and removed). dlg_remove_tag(const char * tag,const char * func)195 inline bool dlg_remove_tag(const char* tag, const char* func) { 196 (void) tag; 197 (void) func; 198 return true; 199 } 200 201 // Returns the thread-specific buffer and its size for dlg. 202 // The buffer should only be used by formatting functions. 203 // The buffer can be reallocated and the size changed, just make sure 204 // to update both values correctly. dlg_thread_buffer(size_t ** size)205 inline char** dlg_thread_buffer(size_t** size) { 206 (void) size; 207 return NULL; 208 } 209 210 #else // DLG_DISABLE 211 #define dlg_log(level, ...) if(level >= DLG_LOG_LEVEL) \ 212 dlg__do_log(level, DLG_CREATE_TAGS(NULL), DLG_FILE, __LINE__, __func__, \ 213 DLG_FMT_FUNC(__VA_ARGS__), NULL) 214 #define dlg_logt(level, tags, ...) if(level >= DLG_LOG_LEVEL) \ 215 dlg__do_log(level, DLG_CREATE_TAGS tags, DLG_FILE, __LINE__, __func__, \ 216 DLG_FMT_FUNC(__VA_ARGS__), NULL) 217 218 #define dlg_assertl(level, expr) if(level >= DLG_ASSERT_LEVEL && !(expr)) \ 219 dlg__do_log(level, DLG_CREATE_TAGS(NULL), DLG_FILE, __LINE__, __func__, NULL, #expr) 220 #define dlg_assertlt(level, tags, expr) if(level >= DLG_ASSERT_LEVEL && !(expr)) \ 221 dlg__do_log(level, DLG_CREATE_TAGS tags, DLG_FILE, __LINE__, __func__, NULL, #expr) 222 #define dlg_assertlm(level, expr, ...) if(level >= DLG_ASSERT_LEVEL && !(expr)) \ 223 dlg__do_log(level, DLG_CREATE_TAGS(NULL), DLG_FILE, __LINE__, __func__, \ 224 DLG_FMT_FUNC(__VA_ARGS__), #expr) 225 #define dlg_assertltm(level, tags, expr, ...) if(level >= DLG_ASSERT_LEVEL && !(expr)) \ 226 dlg__do_log(level, DLG_CREATE_TAGS tags, DLG_FILE, __LINE__, \ 227 __func__, DLG_FMT_FUNC(__VA_ARGS__), #expr) 228 229 DLG_API void dlg_set_handler(dlg_handler handler, void* data); 230 DLG_API dlg_handler dlg_get_handler(void** data); 231 DLG_API void dlg_default_output(const struct dlg_origin*, const char* string, void*); 232 DLG_API void dlg_add_tag(const char* tag, const char* func); 233 DLG_API bool dlg_remove_tag(const char* tag, const char* func); 234 DLG_API char** dlg_thread_buffer(size_t** size); 235 236 // - Private interface: not part of the abi/api but needed in macros - 237 // Formats the given format string and arguments as printf would, uses the thread buffer. 238 DLG_API const char* dlg__printf_format(const char* format, ...) DLG_PRINTF_ATTRIB(1, 2); 239 DLG_API void dlg__do_log(enum dlg_level lvl, const char* const*, const char*, int, 240 const char*, const char*, const char*); 241 DLG_API const char* dlg__strip_root_path(const char* file, const char* base); 242 #endif // DLG_DISABLE 243 244 // Untagged leveled logging 245 #define dlg_trace(...) dlg_log(dlg_level_trace, __VA_ARGS__) 246 #define dlg_debug(...) dlg_log(dlg_level_debug, __VA_ARGS__) 247 #define dlg_info(...) dlg_log(dlg_level_info, __VA_ARGS__) 248 #define dlg_warn(...) dlg_log(dlg_level_warn, __VA_ARGS__) 249 #define dlg_error(...) dlg_log(dlg_level_error, __VA_ARGS__) 250 #define dlg_fatal(...) dlg_log(dlg_level_fatal, __VA_ARGS__) 251 252 // Tagged leveled logging 253 #define dlg_tracet(tags, ...) dlg_logt(dlg_level_trace, tags, __VA_ARGS__) 254 #define dlg_debugt(tags, ...) dlg_logt(dlg_level_debug, tags, __VA_ARGS__) 255 #define dlg_infot(tags, ...) dlg_logt(dlg_level_info, tags, __VA_ARGS__) 256 #define dlg_warnt(tags, ...) dlg_logt(dlg_level_warn, tags, __VA_ARGS__) 257 #define dlg_errort(tags, ...) dlg_logt(dlg_level_error, tags, __VA_ARGS__) 258 #define dlg_fatalt(tags, ...) dlg_logt(dlg_level_fatal, tags, __VA_ARGS__) 259 260 // Assert macros useing DLG_DEFAULT_ASSERT as level 261 #define dlg_assert(expr) dlg_assertl(DLG_DEFAULT_ASSERT, expr) 262 #define dlg_assertt(tags, expr) dlg_assertlt(DLG_DEFAULT_ASSERT, tags, expr) 263 #define dlg_assertm(expr, ...) dlg_assertlm(DLG_DEFAULT_ASSERT, expr, __VA_ARGS__) 264 #define dlg_asserttm(tags, expr, ...) dlg_assertltm(DLG_DEFAULT_ASSERT, tags, expr, __VA_ARGS__) 265 266 #ifdef __cplusplus 267 } 268 #endif 269 270 #endif // header guard 271