From 47d5edc7600403fce6ab3b9b0e2cd61b5f9c9056 Mon Sep 17 00:00:00 2001 From: Moritz Koenemann Date: Sun, 11 Aug 2019 19:15:30 +0200 Subject: [PATCH 1/2] Make use of variadic macro for logging call. The function clog_info (/_debug/..) is now replaced by a variadic macro that "knows" the linenumber where it is. This change is due, because the dependency on `va_copy` already kind of enforces the use of C99 or later. This very much simplifies the use of the logger: Instead of typing clog_info(CLOG(my_logger_id), "Hello, %s!", "world"); if suffices to pass the logger id: clog_info(my_logger_id, "Hello, %s!", "world"); The replacement depends on the C/C++ version used (variadic macros are part of the standard since C99/C++11). The preprocessor checks for the version and defines the macro if applicable. This is backwards compatible, because if the macros are defined, then CLOG(id) is defined as the identity function doing nothing. --- clog.h | 78 ++++++++++++++++++++++++++++++++++++++++++++------- test/Makefile | 2 +- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/clog.h b/clog.h index 525de1e..f2fb19a 100644 --- a/clog.h +++ b/clog.h @@ -73,6 +73,20 @@ #include #include +/* check whether C/C++ standard supports variadic macros + * (they are supported as of C99 and C++11). */ +#ifndef USE_VARIADIC +# ifdef __cplusplus +# define USE_VARIADIC (__cplusplus >= 201103L) +# else +# ifdef __STDC_VERSION__ +# define USE_VARIADIC (__STDC_VERSION__ >= 199901L) +# else +# define USE_VARIADIC 0 +# endif +# endif +#endif + /* Number of loggers that can be defined. */ #define CLOG_MAX_LOGGERS 16 @@ -139,8 +153,6 @@ int clog_init_fd(int id, int fd); */ void clog_free(int id); -#define CLOG(id) __FILE__, __LINE__, id - /** * Log functions (one per level). Call these to write messages to the log * file. The first three arguments can be replaced with a call to the CLOG @@ -163,10 +175,56 @@ void clog_free(int id); * @param ... * Any additional format arguments. */ -void clog_debug(const char *sfile, int sline, int id, const char *fmt, ...); -void clog_info(const char *sfile, int sline, int id, const char *fmt, ...); -void clog_warn(const char *sfile, int sline, int id, const char *fmt, ...); -void clog_error(const char *sfile, int sline, int id, const char *fmt, ...); +void _clog_debug(const char *sfile, int sline, int id, const char *fmt, ...); +void _clog_info(const char *sfile, int sline, int id, const char *fmt, ...); +void _clog_warn(const char *sfile, int sline, int id, const char *fmt, ...); +void _clog_error(const char *sfile, int sline, int id, const char *fmt, ...); + +/** + * Variadic macro log functions (one per level). Insert at any point in the + * code to write messages to the log file. The first argument is the logger id, + * while the following arguments are the same as for the printf function: + * + * clog_debug(MY_LOGGER_ID, "%s is %d log message.", "This", 1); + * + * @param id + * The id of the logger to write to. + * + * @param fmt + * The format string for the message (printf formatting). + * + * @param ... + * Any additional format arguments. + * + * Note, that these are only available with the standard C99/C++11 or later. + * For earlier versions the behaviour is the same as the clog_xxx log functions. + */ +#if USE_VARIADIC +/* for backwards compatibility: */ +#define CLOG(id) id + +#define clog_debug(id, ...) _clog_debug(__FILE__, __LINE__, id, __VA_ARGS__) +#define clog_info(id, ...) _clog_info(__FILE__, __LINE__, id, __VA_ARGS__) +#define clog_warn(id, ...) _clog_warn(__FILE__, __LINE__, id, __VA_ARGS__) +#define clog_error(id, ...) _clog_error(__FILE__, __LINE__, id, __VA_ARGS__) + +#else +/* formerly, the macro supplying the linenumber and filename had to be + * included in the function call: + * + * clog_debug(CLOG(MY_LOGGER_ID), "This is a log message."); + * + * if the C-standard does not support variadic macros or USE_VARIADIC is + * defined as `false`, keep it this way: + */ +#define CLOG(id) __FILE__, __LINE__, id + +/* use dummy macros pointing to the function call */ +#define clog_debug _clog_debug +#define clog_info _clog_info +#define clog_warn _clog_warn +#define clog_error _clog_error +#endif /** * Set the minimum level of messages that should be written to the log. @@ -591,7 +649,7 @@ _clog_log(const char *sfile, int sline, enum clog_level level, } void -clog_debug(const char *sfile, int sline, int id, const char *fmt, ...) +_clog_debug(const char *sfile, int sline, int id, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -600,7 +658,7 @@ clog_debug(const char *sfile, int sline, int id, const char *fmt, ...) } void -clog_info(const char *sfile, int sline, int id, const char *fmt, ...) +_clog_info(const char *sfile, int sline, int id, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -609,7 +667,7 @@ clog_info(const char *sfile, int sline, int id, const char *fmt, ...) } void -clog_warn(const char *sfile, int sline, int id, const char *fmt, ...) +_clog_warn(const char *sfile, int sline, int id, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -618,7 +676,7 @@ clog_warn(const char *sfile, int sline, int id, const char *fmt, ...) } void -clog_error(const char *sfile, int sline, int id, const char *fmt, ...) +_clog_error(const char *sfile, int sline, int id, const char *fmt, ...) { va_list ap; va_start(ap, fmt); diff --git a/test/Makefile b/test/Makefile index 115b997..a667478 100644 --- a/test/Makefile +++ b/test/Makefile @@ -20,6 +20,6 @@ check: clog_test @./clog_test clean: - rm -f clog_test clog_test.out *.o + rm -f clog_test *.out *.o .PHONY: clean check From d2aec29f9b4986bb5cb08a73c3a1659af6ff3453 Mon Sep 17 00:00:00 2001 From: Moritz Koenemann Date: Mon, 12 Aug 2019 20:59:38 +0200 Subject: [PATCH 2/2] Replace Unix write with stdlib fwrite. This should make it platform independent and just as well usable on windows. TODO: This broke the test cases! --- clog.h | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/clog.h b/clog.h index f2fb19a..48266bc 100644 --- a/clog.h +++ b/clog.h @@ -62,16 +62,12 @@ #ifndef __CLOG_H__ #define __CLOG_H__ -#include -#include -#include -#include #include -#include +#include // malloc, realloc, free #include -#include +#include // strcpy, ... #include -#include +#include // errno /* check whether C/C++ standard supports variadic macros * (they are supported as of C99 and C++11). */ @@ -88,7 +84,7 @@ #endif /* Number of loggers that can be defined. */ -#define CLOG_MAX_LOGGERS 16 +#define CLOG_MAX_LOGGERS FOPEN_MAX /* (typically 16) */ /* Format strings cannot be longer than this. */ #define CLOG_FORMAT_LENGTH 256 @@ -142,7 +138,7 @@ int clog_init_path(int id, const char *const path); * @return * Zero on success, non-zero on failure. */ -int clog_init_fd(int id, int fd); +int clog_init_fd(int id, FILE* fd); /** * Destroy (clean up) a logger. You should do this at the end of execution, @@ -314,7 +310,7 @@ struct clog { enum clog_level level; /* The file being written. */ - int fd; + FILE* fd; /* The format specifier. */ char fmt[CLOG_FORMAT_LENGTH]; @@ -349,13 +345,13 @@ const char *const CLOG_LEVEL_NAMES[] = { int clog_init_path(int id, const char *const path) { - int fd = open(path, O_CREAT | O_WRONLY | O_APPEND, 0666); - if (fd == -1) { + FILE* fd = fopen(path, "a"); + if (! fd) { _clog_err("Unable to open %s: %s\n", path, strerror(errno)); return 1; } if (clog_init_fd(id, fd)) { - close(fd); + fclose(fd); return 1; } _clog_loggers[id]->opened = 1; @@ -363,7 +359,7 @@ clog_init_path(int id, const char *const path) } int -clog_init_fd(int id, int fd) +clog_init_fd(int id, FILE* fd) { struct clog *logger; @@ -394,7 +390,7 @@ clog_free(int id) { if (_clog_loggers[id]) { if (_clog_loggers[id]->opened) { - close(_clog_loggers[id]->fd); + fclose(_clog_loggers[id]->fd); } free(_clog_loggers[id]); } @@ -594,7 +590,7 @@ _clog_log(const char *sfile, int sline, enum clog_level level, char *dynbuf = buf; char *message; va_list ap_copy; - int result; + size_t result; struct clog *logger = _clog_loggers[id]; if (!logger) { @@ -613,7 +609,7 @@ _clog_log(const char *sfile, int sline, enum clog_level level, buf_size = result + 1; dynbuf = (char *) malloc(buf_size); result = vsnprintf(dynbuf, buf_size, fmt, ap_copy); - if ((size_t) result >= buf_size) { + if (result >= buf_size) { /* Formatting failed -- too large */ _clog_err("Formatting failed (1).\n"); va_end(ap_copy); @@ -635,8 +631,9 @@ _clog_log(const char *sfile, int sline, enum clog_level level, } return; } - result = write(logger->fd, message, strlen(message)); - if (result == -1) { + size_t count = strlen(message); + result = fwrite(message, sizeof(char), count, logger->fd); + if (result < count) { _clog_err("Unable to write to log file: %s\n", strerror(errno)); } if (message != message_buf) {