aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Angelilli <code@fedang.net>2024-06-06 21:40:59 +0200
committerFederico Angelilli <code@fedang.net>2024-06-06 21:40:59 +0200
commit180c90e48f1d24352d25965a6d880b762c0c4d84 (patch)
tree19f04b6e441f11e59b8240da08c8329a371dfa81
parentcedba71598af986e74fd04f3bee335c7037ea03f (diff)
Add custom type format to log_value
-rw-r--r--any_log.h107
-rw-r--r--any_sexp.h2
-rw-r--r--test/log.c29
3 files changed, 111 insertions, 27 deletions
diff --git a/any_log.h b/any_log.h
index 51ab03e..c760e69 100644
--- a/any_log.h
+++ b/any_log.h
@@ -132,20 +132,27 @@ typedef enum {
//
// By default ANY_LOG_VALUE_TYPE_SEP is the character ':'.
//
-// type_specifier | type | default format
-// | |
-// b | bool (promoted to int) | "%s", b ? true : false
-// d, i | int | "%d"
-// x, u | unsigned int | "%#x"
-// l | long int | "%ld"
-// p | void * | "%p"
-// f | double | "%lf"
-// s | char * (0-terminated) | "%s"
+// type_specifier | type | default format
+// | |
+// b | bool (promoted to int) | "%s", b ? "true" : "false"
+// d, i | int | "%d"
+// x, u | unsigned int | "%#x"
+// l | long int | "%ld"
+// p | void * | "%p"
+// f | double | "%lf"
+// s | char * (0-terminated) | "%s"
+//
+// c | any_log_formatter_t (function), void *
//
// If no type specifier is given the function will assume the type given
// by ANY_LOG_VALUE_DEFAULT_TYPE (by default string).
//
-// For example
+// You can log custom types with the 'c' specifier. Then you will have to
+// pass two parameters: a format function of type any_log_formatter_t and
+// a value parameter of type void *.
+// By defining ANY_LOG_NO_CUSTOM you can disable the custom type specifier.
+//
+// Example usage of value logging
//
// log_value_info("Created graphical context",
// "d:width", width,
@@ -153,6 +160,7 @@ typedef enum {
// "p:window", window_handle,
// "f:scale", scale_factor_dpi,
// "b:hidden", visibility == HIDDEN,
+// "c:widgets", ANY_LOG_FORMATTER(widget_format), widgets,
// "appname", "nice app");
//
// In the implementation you can customize the format of every key-value pair
@@ -194,6 +202,16 @@ typedef enum {
#define log_value_trace(...) any_log_value(ANY_LOG_TRACE, ANY_LOG_MODULE, ANY_LOG_FUNC, __VA_ARGS__, (char *)NULL)
#endif
+#ifndef ANY_LOG_NO_CUSTOM
+
+// The type of the format functions for custom types
+//
+typedef void (*any_log_formatter_t)(FILE *stream, void *value);
+
+#define ANY_LOG_FORMATTER(f) ((any_log_formatter_t)(f))
+
+#endif
+
#ifdef __GNUC__
#define ANY_LOG_ATTRIBUTE(...) __attribute__((__VA_ARGS__))
#else
@@ -203,7 +221,7 @@ typedef enum {
// All log functions will output to the file stream specified by any_log_stream.
//
// You should always set this global to a valid stream (eg in main) before
-// invoking any log macro or function!
+// invoking any_log macros or functions!
//
extern FILE *any_log_stream;
@@ -294,6 +312,7 @@ void any_log_panic(const char *file, int line, const char *module,
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
// For the C standard we can't assign stdout or any other streams here,
// since they are not constant.
@@ -503,6 +522,19 @@ void any_log_format(any_log_level_t level, const char *module,
#define ANY_LOG_VALUE_STRING(key, value) "%s=\"%s\"", key, value
#endif
+#ifndef ANY_LOG_NO_CUSTOM
+
+// Format custom types with the given formatter function
+#ifndef ANY_LOG_VALUE_CUSTOM
+#define ANY_LOG_VALUE_CUSTOM(key, stream, formatter, value) \
+ do { \
+ fprintf(stream, "%s=", key); \
+ formatter(stream, value); \
+ } while (false)
+#endif
+
+#endif
+
// The default is to use string
#ifndef ANY_LOG_VALUE_DEFAULT
#define ANY_LOG_VALUE_DEFAULT(key, value) ANY_LOG_VALUE_STRING(key, value)
@@ -531,43 +563,66 @@ void any_log_value(any_log_level_t level, const char *module,
while (key != NULL) {
if (key[0] != '\0' && key[1] == ANY_LOG_VALUE_TYPE_SEP) {
key += 2;
- switch (key[-2]) {
- case 'b':
- fprintf(any_log_stream, ANY_LOG_VALUE_BOOL(key, va_arg(args, int)));
+ switch (tolower(key[-2])) {
+ case 'b': {
+ int value = va_arg(args, int);
+ fprintf(any_log_stream, ANY_LOG_VALUE_BOOL(key, value));
break;
+ }
case 'd':
- case 'i':
- fprintf(any_log_stream, ANY_LOG_VALUE_INT(key, va_arg(args, int)));
+ case 'i': {
+ int value = va_arg(args, int);
+ fprintf(any_log_stream, ANY_LOG_VALUE_INT(key, value));
break;
+ }
case 'x':
- case 'u':
+ case 'u': {
+ unsigned int value = va_arg(args, unsigned int);
fprintf(any_log_stream, ANY_LOG_VALUE_HEX(key, va_arg(args, unsigned int)));
break;
+ }
- case 'l':
- fprintf(any_log_stream, ANY_LOG_VALUE_LONG(key, va_arg(args, long int)));
+ case 'l': {
+ long int value = va_arg(args, long int);
+ fprintf(any_log_stream, ANY_LOG_VALUE_LONG(key, value));
break;
+ }
- case 'p':
- fprintf(any_log_stream, ANY_LOG_VALUE_PTR(key, va_arg(args, void *)));
+ case 'p': {
+ void *value = va_arg(args, void *);
+ fprintf(any_log_stream, ANY_LOG_VALUE_PTR(key, value));
break;
+ }
- case 'f':
- fprintf(any_log_stream, ANY_LOG_VALUE_DOUBLE(key, va_arg(args, double)));
+ case 'f': {
+ double value = va_arg(args, double);
+ fprintf(any_log_stream, ANY_LOG_VALUE_DOUBLE(key, value));
break;
+ }
- case 's':
- fprintf(any_log_stream, ANY_LOG_VALUE_STRING(key, va_arg(args, char *)));
+ case 's': {
+ char *value = va_arg(args, char *);
+ fprintf(any_log_stream, ANY_LOG_VALUE_STRING(key, value));
break;
+ }
+#ifndef ANY_LOG_NO_CUSTOM
+ case 'c': {
+ any_log_formatter_t formatter = va_arg(args, any_log_formatter_t);
+ void *value = va_arg(args, void *);
+ ANY_LOG_VALUE_CUSTOM(key, any_log_stream, formatter, value);
+ break;
+ }
+#endif
default:
goto tdefault;
}
} else {
tdefault:
- fprintf(any_log_stream, ANY_LOG_VALUE_DEFAULT(key, va_arg(args, ANY_LOG_VALUE_DEFAULT_TYPE)));
+ ANY_LOG_VALUE_DEFAULT_TYPE value = va_arg(args, ANY_LOG_VALUE_DEFAULT_TYPE);
+ fprintf(any_log_stream, ANY_LOG_VALUE_DEFAULT(key, value));
}
key = va_arg(args, char *);
diff --git a/any_sexp.h b/any_sexp.h
index 534c455..31aa2c0 100644
--- a/any_sexp.h
+++ b/any_sexp.h
@@ -377,7 +377,7 @@ any_sexp_t any_sexp_read(any_sexp_reader_t *reader)
static int any_sexp_writer_puts(any_sexp_writer_t *writer, const char *string)
{
int i;
- for (i = 0; string[i] != '\0', i++) {
+ for (i = 0; string[i] != '\0'; i++) {
if (writer->putc(string[i], writer->stream) == EOF)
return EOF;
}
diff --git a/test/log.c b/test/log.c
index d5d7d34..5b6293d 100644
--- a/test/log.c
+++ b/test/log.c
@@ -14,9 +14,30 @@
#define ANY_LOG_VALUE_PTR(key, value) "\"%s\": \"%p\"", key, value
#define ANY_LOG_VALUE_DOUBLE(key, value) "\"%s\": %lf", key, value
#define ANY_LOG_VALUE_STRING(key, value) "\"%s \": \"%s\"", key, value
+#define ANY_LOG_VALUE_CUSTOM(key, stream, formatter, value) \
+ do { \
+ fprintf(stream, "\"%s\": ", key); \
+ formatter(stream, value); \
+ } while (false)
#define ANY_LOG_VALUE_AFTER(level, module, func, message) "}\n"
#include "any_log.h"
+struct pair {
+ const char *s1, *s2;
+};
+
+void pairs_format(FILE *stream, struct pair *pairs)
+{
+ fprintf(stream, "[");
+ while (pairs->s1 && pairs->s2) {
+ fprintf(stream, "%s -> %s", pairs->s1, pairs->s2);
+ pairs++;
+ if (pairs->s1 && pairs->s2)
+ fprintf(stream, ", ");
+ }
+ fprintf(stream, "]");
+}
+
int main()
{
any_log_init(stdout, ANY_LOG_DEBUG);
@@ -59,12 +80,20 @@ int main()
"f:dbl", 20.3333,
"p:a", NULL);
+ struct pair pairs[] = {
+ { "v", "v2" },
+ { "p", "xp" },
+ { "23", "42" },
+ { NULL, NULL },
+ };
+
log_value_info("Created graphical context",
"d:width", 100,
"d:height", 200,
"p:window", NULL,
"f:scale", 1.23,
"b:hidden", true,
+ "c:pairs", ANY_LOG_FORMATTER(pairs_format), pairs,
"appname", "nice app");
// Test any_log_format