I am trying to define a global logger one time in a header file doing the following:
// log.c
#include <stdio.h>
#include "log.h"
void log_add_file(const char* filename)
{
printf("Adding file %s", filename);
}
void log_log(const char* filename, const char* function, int line, int level, ...)
{
printf("LOG");
}
// logmain.c
#include "log.h"
int main(void)
{
log_add_file("log.txt");
log_info("Hello");
}
#ifndef LOG_H
#define LOG_H
#include <stdio.h>
#define DEFAULT_FORMAT "[%(levelname)] %(time) %(name) - %(filename):%(funcName):%(lineno) - %(msg)"
enum LogLevel {
DEBUG=10,
INFO=20,
WARN=30,
ERROR=40,
};
struct logger {
int level;
const char* format;
FILE** handlers;
int num_handlers;
};
struct logger Logger = {INFO, DEFAULT_FORMAT, NULL, 1};
#define log_debug(...) log_log(__FILE__, __func__, __LINE__, DEBUG, __VA_ARGS__)
#define log_info(...) log_log(__FILE__, __func__, __LINE__, INFO , __VA_ARGS__)
#define log_warn(...) log_log(__FILE__, __func__, __LINE__, WARN , __VA_ARGS__)
#define log_error(...) log_log(__FILE__, __func__, __LINE__, ERROR , __VA_ARGS__)
void log_add_file(const char* filename);
void log_log(const char* filename, const char* function, int line, int level, ...);
#endif
The offending line is:
struct logger Logger = {INFO, DEFAULT_FORMAT, NULL, 1};
And the compiler says:
duplicate symbol _Logger____ in:
/var/folders/b1/mmc6r_jj00ldzr2prvqzy56m0000gp/T/log-9f31fa.o
/var/folders/b1/mmc6r_jj00ldzr2prvqzy56m0000gp/T/logmain-220d4d.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
What is the compiler saying in this case? First, why would a duplicate symbol matter here if logmain.c doesn't call log.c and vice versa? And secondly, what would be a way to resolve this?
Do I just add in the word static and call it a day? Or can that lead to other unintended consequences? Basically, I want all files to have the exact same Logger to use that includes it (a singleton).
Related
I am trying to create a log function which will print file name, function name, line number and error msg.
Is there a way to create macro for a small function which only takes the log type ,msg and macro value will add FILE, func, LINE and call the actual function
it might be something like this:
#define func(int type,const char *msg, ...) \
func(int type,char *__FILE__,char *__func__,char *__LINE__,const *msg,...)
If you want create a macro that during invocation replaces or adds some parameters, you don't have to write it as a prototype, but, because C preprocessor is a simple text replacement processor, you can write it as the invocation itself.
So your macro definition becomes:
#define func(type, msg, ...) \
func(type, __FILE__, __func__, __LINE__, msg, __VA_ARGS__)
The C preprocessor assign to the symbol __VA_ARGS__ the sequence of parameters, included the colon, that starts from elipsis (...) on.
Now using the macro as in the example below:
func(MYTYPE, "Info function called with stack: '%s' size %ld", bIsPrivilegedStack(stack) ? "Privileged" : "User", StackSize);
Will translate in:
func(MYTYPE, __FILE__, __func__, __LINE__, "Infofunction called with stack: '%s' size %ld", bIsPrivilegedStack(stack) ? "Privileged" : "User", StackSize);
The function prototype isn't in the macro and should be write once only, preferably contained in an header file, and appear in the compiling process before any invocation. It will contain all parameters types as in:
void func(int type, char *__FILE__, char *__func__, int _LINE__, const *msg, ...);
Note: the preprocessor symbol __LINE__ is defined as an int, not a char *.
Your file layout will be more or less:
//Prototype
void func(int type, char * file, char *fnc, int line, const *msg, ...);
//Macro definition
#define func(type, msg, ...) \
func(type, __FILE__, __func__, __LINE__, msg, __VA_ARGS__)
//Usage
void foo(void)
{
.....
func(MYTYPE, "Info function called with stack: '%s' size %ld", bIsPrivilegedStack(stack) ? "Privileged" : "User", StackSize);
.....
}
I want to enable "printf("macro MESSAGE is %d\n",MESSAGE);" during run time. For example , if i give argument 10 in run time, it should print the message. if it is not given, it should not print this message.Is it possible?
#include <stdio.h>
#define MESSAGE 10
int foo;
void main(int argc, char *argv[])
{
foo = atoi(argv[1]);
printf("foo is %d\n", foo);
#if MESSAGE==foo
printf("macro MESSAGE is %d\n",MESSAGE);
#endif
}
We can define a macro conditionally based on a preprocessor macro to control in compile time what the definition of the macro is:
#if DEBUGGING
#define debug(format, ...) fprintf(stderr, format, __VA_ARGS__)
#else
#define debug(format, ...) ()
#endif
The debug macro itself is actually an example in GCC's manual.
Or, we could make a similar function that checks in run time the value of some variable:
#include <stdarg.h>
#include <stdio.h>
int debugging = 10;
void debug(int msglevel, const char *fmt, ...)
{
if (debugging < msglevel) return;
va_list va;
va_start(va, fmt);
vfprintf(stderr, fmt, va);
va_end(va);
}
...
debug(10, "Error: %s\n", "some explanation");
A full function makes it easier to do a greater than comparison for the verbosity level. Of course we could still have an alternate definition of r the function on compile time to fully disable it. For the varargs, see the va_arg(3) man page.
I create a simple logger library in C.I have three files
My example file:
example.c
Library files:
loglib.h
loglib.c
My main logger function is called logme. I defined different macros as wrappers to indicate different log levels:
eg
WARN
ERROR
etc..
In example.c I call the macros:
int int_arg = 55;
WARN(1, "Warn message with level 1");
WARN(1, "Warn message with level %d", int_arg);
The WARN macro is defined in loglib.h:
#define WARN(LEVEL, ...) \
logme(LEVEL, 8, " <%s:%d> inside %s() -- "__VA_ARGS__, __FILE__, __LINE__, __func__);
And lastly here is the logme function:
void slog(int level, int flag, const char *msg, ...)
{
char string[10000];
bzero(string, sizeof(string));
va_list args;
va_start(args, msg);
vsprintf(string, msg, args);
va_end(args);
// .. do other things
}
When I run the example file this is what I get:
inside main() -- Warn message with level 1
Segmentation fault (core dumped)
I get segmentation fault when I call WARN with formated strings.
The segmentation fault appears in vsprintf(string, msg, args);
Is it something wrong with my macro?
Here is the make file of my lib:
CFLAGS = -g -O2 -Wall -lpthread
LIB = -lrt
OBJS = loglib.o
LIBINSTALL = /usr/local/lib
HEADINSTALL = /usr/local/include
.c.o:
$(CC) $(CFLAGS) -c $< $(LIB)
libslog.a: $(OBJS)
$(AR) rcs liblog.a $(OBJS)
#echo [-] Syncing static library
sync
install:
#test -d $(LIBINSTALL) || mkdir $(LIBINSTALL)
#test -d $(HEADINSTALL) || mkdir $(HEADINSTALL)
#install -m 0664 liblog.a $(LIBINSTALL)/
#install -m 0664 loglib.h $(HEADINSTALL)/
#echo [-] Done
loglib.o: loglib.h
.PHONY: clean
clean:
$(RM) liblog.a $(OBJS)
This can't work because you attempt string concatenation of __VA_ARGS__ here:
#define WARN(LEVEL, ...) \
logme(LEVEL, 8, " <%s:%d> inside %s() -- "__VA_ARGS__, __FILE__, __LINE__, __func__);
This messes up your sequence of parameters: __FILE__ will not go with the <%s, etc. You must redefine your WARN() macro so that __VA_ARGS__ is last. You'll have to write a proper variadic function if you want to prepend all that information. Note that __func__ is not a string literal, it references the appropriate string.
The ordering is incorrect when it hits the logme function.
You can do something like the following.
#include <strings.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
void logme(int level, int flag, const char *msg_preamble, const char* file, int line, const char* function, const char* msg, ... )
{
char buffer1[10000];
bzero(buffer1, sizeof(buffer1));
sprintf(buffer1, msg_preamble, file, line, function);
char buffer2[10000];
bzero(buffer2, sizeof(buffer2));
va_list args;
va_start(args, msg);
vsprintf(buffer2, msg, args);
va_end(args);
strcat(buffer1, buffer2);
printf("%s\n", buffer1);
}
#define WARN(LEVEL, ...) \
logme(LEVEL, 8, " <%s:%d> inside %s() -- ", __FILE__, __LINE__, __func__, __VA_ARGS__);
int main(int argc, char** argv)
{
int int_arg = 55;
WARN(1, "Warn message with level 1");
WARN(1, "Warn message with level %d", int_arg);
}
Here is the working solution:
/*
* SOURCE_THROW_LOCATION macro returns string which
* points to the file, as well as, the corresponding line
* of the caller function.
*/
#define LVL1(x) #x
#define LVL2(x) LVL1(x)
#define SOURCE_THROW_LOCATION "<"__FILE__":"LVL2(__LINE__)"> -- "
#define WARN(LEVEL, ...) \
logme(LEVEL, FWARN, SOURCE_THROW_LOCATION __VA_ARGS__);
The argument order is incorrect, hence the %s format does not correspond to the actual argument provided and the program invokes undefined behavior.
Here is a corrected version of the WARN macro:
#define WARN(LEVEL, ...) \
logme(LEVEL, 8, __FILE__, __LINE__, __func__, __VA_ARGS__);
And the corresponding logme function, using vsnprintf() instead of vsprintf() to avoid potential buffer overflows:
void slog(int level, int flag, const char *filename, in lineno,
const char *funcname, const char *msg, ...) {
char string[10000];
int prefix;
va_list args;
// check level, flag..
...
// format the message if needed
prefix = snprintf(string, sizeof string, " <%s:%d> inside %s() -- ",
filename, lineno, funcname);
va_start(args, msg);
vsnprintf(string + prefix, sizeof(string) - prefix, msg, args);
va_end(args);
// do other things
...
}
I have a simple log macro, this is the .h file :
#ifndef __LOGGER_H
#define __LOGGER_H
#define LOG_ERROR(errorMsg) logError(__FILE__, __LINE__, errorMsg)
#endif
...and this is the .c file :
#include <stdio.h>
#include "Logger.h"
void logError(const char* filename, int line, const char* errorMsg)
{
printf("[File : %s] - [Line: %d} - [Error Message : %s]\n", filename, line, errorMsg);
}
When I use the macro in a .c file :
#include <Common/Logger.h>
void func(int index, int value)
{
if (IsIndexOutOfRange(index, NUMBER_OF_PARAMS))
{
LOG_ERROR("Index out of range!");
return;
}
.
.
.
}
...and build (vs2013) I get :
warning C4013: 'logError' undefined; assuming extern returning int
Warning C4013 usually means that you forgot to include the correct library or need to use extern. I suspect I need extern somehow, but do not know how to fix this with a macro.
How do I solve this?
I'm having the following problem with a macro for a function. This was working until I added the print_heavyhitters function. I have this in m61.h:
#if !M61_DISABLE
#define malloc(sz) m61_malloc((sz), __FILE__, __LINE__)
#define free(ptr) m61_free((ptr), __FILE__, __LINE__)
#define realloc(ptr, sz) m61_realloc((ptr), (sz), __FILE__, __LINE__)
#define calloc(nmemb, sz) m61_calloc((nmemb), (sz), __FILE__, __LINE__)
#define print_heavyhitters(sz) print_heavyhitters((sz), __FILE__, __LINE__)
#endif
in m61.c, all of these functions are fine except print_heavyhitters(sz). I get " - Macro usage error on the function print_heavyhitters:
- Macro usage error for macro:
print_heavyhitters
- Syntax error
m61.c:
#include "m61.h"
...
void *m61_malloc(size_t sz, const char *file, int line) {...}
void print_heavyhitters(size_t sz, const char *file, int line) {...}
You use the same name for the macro and the function it was intended to expand to.
Using the same name for the macro and for the function name is OK as far as the preprocessor is concerned, since it won't expand it recursively, but it can easily lead to confusing errors such as this. You can do this as long as you're careful to #undef it in the right places, but I'd recommend using a different symbol name to avoid confusion.
I'd do something like this:
// Header file
#if !M61_DISABLE
...
#define print_heavyhitters(sz) m61_print_heavyhitters((sz), __FILE__, __LINE__)
#endif
// Source file
#include "m61.h"
#if !M61_DISABLE
#undef print_heavyhitters
#endif
void print_heavyhitters(size_t sz)
{
// Normal implementation
}
void m61_print_heavyhitters(size_t sz, const char *file, int line)
{
// Debug implementation
}