• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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