I want to customize fprintf function for enable/disable Logging
My project use Makefile.
#define ENABLE_LOG
void log(fmt, ...){
#ifdef ENABLE_LOG
va_list vargs;
va_start(vargs, fmt);
fprintf(stderr, vargs);
va_end(vargs);
#endif
}
log("Test"); # build error
log("TEST %d", 1); # build error
How I can create custom log function that can enable/disable with ENABLE_LOG
You need to use vfprintf(). Also, I would make an empty macro that would avoid a call to an empty function.
#define ENABLE_LOG
#ifdef ENABLE_LOG
void log(const char *fmt, ...) {
va_list vargs;
va_start(vargs, fmt);
vfprintf(stderr, fmt, vargs);
va_end(vargs);
}
#else
#define log(fmt, ...)
#endif
Also, note that there is already a C++ function named log. You might consider a different name (especially if you use the dreaded using namespace std; - even though your code looks like pure C.)
Related
I faced a problem while implementing the logger.
First, I used to __LINE__ and __FILE__ with standard C Macro function like below
// global.h
..
namespace MyLogger {
class Logger
{
..
void _write(int _level, const char* _file, int _line, const char* _fmt, ...);
};
static Logger logger;
}; // namespace MyLogger
..
// avoid conflict..
#undef error
#undef trace
#define error(_MESSAGE_, ...) _write(LOG_LEVEL_ERROR, (const char*)__FILE__, (int)__LINE__, (const char*)_MESSAGE_, ##__VA_ARGS__)
#define info(_MESSAGE_, ...) _write(LOG_LEVEL_INFO, (const char*)__FILE__, (int)__LINE__, (const char*)_MESSAGE_, ##__VA_ARGS__)
#define trace(_MESSAGE_, ...) _write(LOG_LEVEL_TRACE, (const char*)__FILE__, (int)__LINE__, (const char*)_MESSAGE_, ##__VA_ARGS__)
..
// main.cpp
using namespace MyLogger;
// using like this
logger.error("- log error");
logger.info("- log info %s", "test 1");
..
In fact, it seems to work well.
However, the problem occurred while using openv.
error and trace seem to conflicted with error and trace in opencv.
error: expected primary-expression before ‘int’
[build] CV_EXPORTS CV_NORETURN void error(int _code, const String& _err, const char* _func, const char* _file, int _line);
So I'm thinking about other methods using inline functions, not macros.
// global.h
..
//void _write(int _level, const char* _file, int _line, const char* _fmt, ...);
static inline void error(/* Is it possible to toss __LINE__ or __FILE__ ? */);
static inline void info(/* .. */);
static inline void trace(/* .. */);
..
// main.cpp
logger::error("- log error");
logger::info("%d %c %f, 1, 'A', '0.1');
Can I know the line or file of the location where the log was output through a method other than a macro?
No, inline functions/methods won't work the same as macro in this context.
Macros are simply text replacements, that's why the __LINE__ and __FILE__ will give accurate results. Thus macros are your only choice; See if you can name them better to avoid conflicts.
However the inline functions are systematically compiled subunits. So the line number and the file names will always be same that of those functions where it resides.
Refer: Inline functions vs Preprocessor macros
C++20
As suggested in this useful comment, you may also consider using std::source_location which allows you to avoid the macro trickery via the help of standard library & language features.
There are 3 stages in which C++ becomes a running program.
Preprocessor
Compiler
Linker
__LINE__ and __FILE__ are processed by the preprocessor (stage 1). However, it's the compiler that decides about inlining (stage 2).
Other languages like C# have this in the compiler stage: [CallerMemberName], [CallerFilePath] and [CallerLineNumber] [MSDN] but I am don't think this exists in C++ at the moment.
How about this:
#define log_error(_MESSAGE_, ...) \
logger._write( \
LOG_LEVEL_ERROR, \
(const char*)__FILE__, \
(int)__LINE__, \
(const char*)_MESSAGE_, \
##__VA_ARGS__)
There is no much difference (in the sense of typed characters) between logger.error and log_error. The advantages are, that you use a macro (__FILE__, __LINE__ are utilized as desired) and have your logger class used only at one place (via the macro, in case you want to use a different logging mechanism), refactoring should be without any headaches.
I've been searching around on the web for a while on how to output a integer or optionally a float using OutputDebugString().
It would be easier if i could write my debug data straight to the console from my external executable using this command. However i only got it to work with a const char.
The solutions i found were outdated i tried copy and pasting the code straight from the web but didn't work. Even after modifying the code i couldn't get it to typecast correctly.
Is there anyone that could help me typecast something into the OutputDebugString as clean as possible, it's for debugging purposes only so i rather keep the code short and easily readable than having a more complex and clunky typecast IF that is possible. Many thanks!
Provided alternatively solution. The function below encapsulates OutputDebugString that can accept formatted arguments.
#include <vector>
#include <string>
void DbgMsg(const char * zcFormat, ...)
{
// initialize use of the variable argument array
va_list vaArgs;
va_start(vaArgs, zcFormat);
// reliably acquire the size
// from a copy of the variable argument array
// and a functionally reliable call to mock the formatting
va_list vaArgsCopy;
va_copy(vaArgsCopy, vaArgs);
const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy);
va_end(vaArgsCopy);
// return a formatted string without risking memory mismanagement
// and without assuming any compiler or platform specific behavior
std::vector<char> zc(iLen + 1);
std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
va_end(vaArgs);
std::string strText(zc.data(), iLen);
OutputDebugStringA(strText.c_str());
}
For example, the code below shows how to print an integer variable by OutputDebugString through DbgMsg().
int foo=12;
DbgMsg(" foo=%d", foo);
OutputDebugString can only take strings, if you want formatted output you will have to do that yourself before feeding it to OutputDebugString. If you are using MSVC I suggest that you use _CrtDbgReport or _CrtDbgReportW. With recent versions of MSVC that support variadic macros I use the following:
#if !defined(_RPTW)
#if defined(_DEBUG)
#define _RPTW(pszFmt, ...) _CrtDbgReportW(_CRT_WARN, NULL, __LINE__, NULL, (pszFmt), __VA_ARGS__)
#define _RPTW_(dest, fmt, ...) _CrtDbgReportW((dest), NULL, __LINE__, NULL, (pszFmt), __VA_ARGS__)
#else
#define _RPTW(pszFmt, ...)
#define _RPTW(dest, pszFmt)
#endif
#endif // #if !defined(_RPTW)
#if !defined(_RPTA)
#if defined(_DEBUG)
#define _RPTA(pszFmt, ...) _CrtDbgReport(_CRT_WARN, NULL, __LINE__, NULL, (pszFmt), __VA_ARGS__)
#define _RPTA_(dest, fmt, ...) _CrtDbgReport((dest), NULL, __LINE__, NULL, (pszFmt), __VA_ARGS__)
#else
#define _RPTA(pszFmt, ...)
#define _RPTA_(dest, pszFmt)
#endif
#endif // #if !defined(_RPTA)
#if !defined(_RPTT)
#if defined(_UNICODE)
#define _RPTT _RPTW
#define _RPTT_ _RPTW_
#else
#define _RPTT _RPTA
#define _RPTT_ _RPTA_
#endif
#endif // #if !defined(_RPTT)
The second forms allow providing a different level of report (_CRT_ASSERT or c_CRT_ERROR instead of _CRT_WARN)
I would recommend to use sprintf like that:
// issues with the int x, let's output and look at it in DebugView
char msg[255] = {0};
sprintf(msg, ">> Watch out x=%d\n", x);
OutputDebugString(msg);
Maybe I did not understand the question, but that's how I quickly dump integers. And to be honest I do not know these MACROS which SoronelHaetir listed but indeed you've got to format yourself. So I Hope this helps and is rather straight forward.
I've inherited some code that causes an amazing amount of compiler warnings and am not a strong enough C++ programmer to figure out how to address it properly. To wit:
log.h:
#include <stdarg.h>
#include <libgen.h>
#include <errno.h>
#include <string.h>
void log(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
#define error(fmt, ...) ow_log("ERROR[%s:%s()#%d]: " fmt, basename(__FILE__), __func__, __LINE__, ##__VA_ARGS__)
#define sys_error(fmt, ...) ow_log("ERROR[%s:%s()#%d]: System error: %s:" fmt, basename(__FILE__), __func__, __LINE__, strerror(errno), ##__VA_ARGS__)
log.cpp
#include <stdarg.h>
#include <stdio.h>
#include <pthread.h>
void log(const char* fmt, ...)
{
time_t t = time(NULL);
struct tm tm = *localtime(&t);
printf("%04d-%02d-%02d %02d:%02d:%02d ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
printf("\n");
fflush(stdout);
va_end(args);
}
Example use:
int args = 6;
log("Some string not explicitly typed with %i interpolated arguments", args);
error("Something went wrong here");
C++ programmers probably immediately see it, but I can't. What I get whenever this is used is:
log.h:11:79: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
#define error(fmt, ...) log("ERROR[%s:%s()#%d]: " fmt, basename(__FILE__), __func__, __LINE__, ##__VA_ARGS__)
^
It looks like the code has been this way for some time, and I don't see any terribly bad ill effects, but it makes reading the compiler output quite difficult (and seems actually wrong), so I tried to fix it. I tried some different casting strategies but nothing seems to work, I think I'm misunderstanding how macros work and all the builtins that it uses.
According to this page the prototype for basename() is
char *basename(char *path);
and the documentation says:
The basename() function may modify the string pointed to by path, and may return a pointer to static storage that may then be overwritten by a subsequent call to basename().
Therefore you should not pass a string literal to this function.
The function you should be using is
char * basename_r(const char *path, char *bname);
where bname is scratch storage for the function to use.
I've written simple logger for unified logging in my library.
What I have now:
enum LogLevel {
DEBUG,
INFO,
WARNING,
ERROR
};
//this calls vfprintf(...)
void Log( LogLevel level, const char * format, ...);
//compile time log string concatenation, if logs should be disabled define NO_LOGS during compilation
#ifndef NO_LOGS
#define LOG(LEVEL, MSG, ...) Log(LEVEL, "[" #LEVEL "] " MSG "\n", ##__VA_ARGS__)
#else
#define LOG(LEVEL, MSG, ...)
#endif
//defines for fast logging
#define LOGD(MSG, ...) LOG(DEBUG, MSG, ##__VA_ARGS__)
#define LOGI(MSG, ...) LOG(INFO, MSG, ##__VA_ARGS__)
#define LOGW(MSG, ...) LOG(WARNING, MSG, ##__VA_ARGS__)
#define LOGE(MSG, ...) LOG(ERROR, MSG, ##__VA_ARGS__)
This is awesome but I have one problem with it. While calling LOGI("log value %d", value) works as expects, it's not possible to call LOGI("simple log msg") instead I have to call LOGI("%s","simple log msg")which just looks awful. Is it possible to solve this problem in defines? I've tried a following code but it doesn't compile since define overloading based on arguments is not possible in C++.
#define LOGD(MSG, ...) LOG(DEBUG, MSG, ##__VA_ARGS__)
#define LOGD(MSG) LOG(DEBUG, "%s", MSG)
I know I can easily solve this by using normal functions overloading, but I really want to have logging system with minimal overhead hence usage of defines that allow me to concatenate strings in compile time.
Any ideas?
You can check the number of the input arguments inside the Log function. If there are none, you just use fputs instead of vfprintf.
I have a macro with varargs that auto injects some arguments, like the first below injecting the argument "__FNAME__":
#ifdef VERBOSE
#define logdbg(format, ...) debugff(__FNAME__, format, ##__VA_ARGS__)
#elif defined(NORMAL)
#define logdbg(format, ...) debugf(format, ##__VA_ARGS__)
#else
#define logdbg(format, ...) /* debud off */
#endif
But I need to keep this macro working with compilers without MACRO VARIADIC support (in SCO Unix and AIX 4.3 Copmiler v3).
In these environments I have now:
#ifdef VERBOSE
#define logdbg debugff(__FNAME__, format, ##__VA_ARGS__)
#elif defined(NORMAL)
#define logdbg debugf
#else
#define logdbg if(1);else debugf
#endif
These compilers didn't accepted the comment in the last macro definition, and I get the if(1);else blablabla that works fine from https://stackoverflow.com/a/687412/926064
But I need yet a solution to the first case, where an argument is "injected" by macro.
Some workaround to do that ?
EDIT:
As it isn't a software with multithread support, I'm thinking to change the debug 'framework' to inject the arguments using side functions to set values in 'context' variables (global, static, etc):
#define logdbg pass_args(__FNAME__); debugf
More possibles workarounds ?
Assuming it is impossible to use a different compiler (which seems a dubious requirement, but let's put that aside), for certain, you will need a different function for logdbg to expand into. Probably, that function would take the __FNAME__ argument from another source, like a global variable.
#define logdbg ((logdbg_fname__ = __FNAME__), debugff_broken)
void debugff_broken(const char *fmt, ...) {
extern const char *logdbg_fname__;
va_list ap;
va_start(ap, fmt);
vdebugff(logdbg_fname__, fmt, ap);
va_end(ap);
}
Where vdebugff is like debugff except it takes a va_list.
If thread safety is required, use thread specific storage instead of a common global.