(1) log("SYSTEM-A", 1, "Example %s", "...message");
(2) log(1, "Example %s", "...message");
(3) log("Example %s", "...message");
From what I understand:
- (1) does not has
% in it's first argument.
- (2) first argument is int
- (3) has
% in it's argument.
You can:
- overload
log macro on number of arguments
- if one argument
- else
- _Generic on first argument
- If first argument is an int
- Else
- call some
_log_wrapper(const char *arg, ...)
- inspect if
strchr(arg, '%')
- if it does, call va_list version of (3)
- if it does not, call va_list version of (1)
A possible implementation looks like this:
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
void vlog_domain(const char *domain, int log_level, const char *fmt, va_list va) {
printf("domain\n");
}
void vlog_level(int log_level, const char *fmt, va_list va) {
printf("level\n");
}
void vlog_normal(const char *fmt, va_list va) {
printf("normal\n");
}
void _log_wrapper(int type, ...) {
va_list va;
va_start(va, type);
if (type == 1) {
int log_level = va_arg(va, int);
const char *fmt = va_arg(va, const char *);
vlog_level(log_level, fmt, va);
} else {
const char *arg = va_arg(va, const char*);
if (!strchr(arg, '%')) {
const char *domain = arg;
int log_level = va_arg(va, int);
const char *fmt = va_arg(va, const char*);
vlog_domain(domain, log_level, fmt, va);
} else {
const char *fmt = arg;
vlog_normal(fmt, va);
}
}
va_end(va);
}
#define _log_1(_1) vlog_normal(_1) // TODO
#define _log_2(_1, ...) _log_wrapper( \
_Generic((_1), int: 1, char *: 2), _1, ##__VA_ARGS__)
// this implementation supports max ca. 10 arguments
#define _log_N(_9,_8,_7,_6,_5,_4,_3,_2,_1,_0,N,...) _log_##N
#define log(...) _log_N(__VA_ARGS__,2,2,2,2,2,2,2,2,2,2,1)(__VA_ARGS__)
int main() {
log("SYSTEM-A", 1, "Example %s", "...message"); // domain
log(1, "Example %s", "...message"); // level
log("Example %s", "...message"); // normal
}
These are some time spent on writing the interface, that the next developer will most probably anyway not understand and will have to rewrite and refactor the whole code. I suggest instead to be as possible clear and write as possibly easy code to understand and just name your functions:
logd("SYSTEM-A", 1, "Example %s", "...message");
logl(1, "Example %s", "...message");
log("Example %s", "...message");
and be done with it.
Inspect other projects how they solved logging with "domain+loglevel" (which sounds like syslog() severity and facility....) have a look how other projects solved logging interface. From my mind I enjoyed zephyr project solved logging, and it's open source so see inspect it's sources.
LOG_NO_DOMAIN(level, fmt, ...) log(NULL, level, fmt, __VA_ARGS__)?logas a function name as it is a standard C function for taking natural logarithm.const char *then anintor maybe anint. Consider howlog("Ex %d %s", 42, "message");looks like thelog(const char *domain, int log_level, const char *fmt). Overall I see OP's goal as possible, but a lot of work as code needs to parse the first parameter for%specifiers. Recommend alternative goals.log("SYSTEM-A", 1, "Example")fromlog("Example %d%s", 1, "...message")?