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.
Related
I am attempting to create a C++ DLL to export, and I need to export all functions of a class. So I came up with this idea to cut out the boilerplate:
#define CONCATENATE_WRAPPED(arg1, arg2) CONCATENATE_WRAPPED_1(arg1, arg2)
#define CONCATENATE_WRAPPED_1(arg1, arg2) CONCATENATE_WRAPPED_2(arg1, arg2)
#define CONCATENATE_WRAPPED_2(arg1, arg2) CONCATENATE_WRAPPED_3(arg1, arg2)
#define CONCATENATE_WRAPPED_3(arg1, arg2) CONCATENATE_WRAPPED_4(arg1, arg2)
#define CONCATENATE_WRAPPED_4(arg1, arg2) arg1##arg2
// Counts the number of pairs:
#define PAIR_SEQUENCER(_1, _1x, _2, _2x, _3, _3x, _4, _4x, N, ...) N
#define COUNT_PAIRS(...) PAIR_SEQUENCER(__VA_ARGS__, 4, x, 3, x, 2, x, 1, 0, 0, x)
// Internal for declaring arguments:
#define DECLARE_ARGUMENTS_0(...)
#define DECLARE_ARGUMENTS_1(typeName, varName, ...)\
typeName varName
#define DECLARE_ARGUMENTS_2(typeName, varName, ...)\
typeName varName,\
DECLARE_ARGUMENTS_1(__VA_ARGS__)
#define DECLARE_ARGUMENTS_3(typeName, varName, ...)\
typeName varName,\
DECLARE_ARGUMENTS_2(__VA_ARGS__)
// Internal for passing arguments:
#define PASS_ARGUMENTS_0(...)
#define PASS_ARGUMENTS_1(typeName, varName, ...)\
varName\
PASS_ARGUMENTS_0(__VA_ARGS__)
#define PASS_ARGUMENTS_2(typeName, varName, ...)\
varName, \
PASS_ARGUMENTS_1(__VA_ARGS__)
#define PASS_ARGUMENTS_3(typeName, varName, ...)\
varName, \
PASS_ARGUMENTS_2(__VA_ARGS__)
// Macro to call when declaring parameters and will adjust depending on the number of params, so that
// DECLARE_ARGUMENTS(int, a, float, b) expands to (int a, float b) for up to three pairs.
#define DECLARE_ARGUMENTS(...) ( CONCATENATE_WRAPPED(DECLARE_ARGUMENTS_, COUNT_PAIRS(__VA_ARGS__))(__VA_ARGS__) ))
// Macro to call when passing parameters and will adjust depending on the number of params, so that
// PASS_ARGUMENTS(int, a, float, b) expands to (a, b) for up to three pairs.
#define PASS_ARGUMENTS(...) ( CONCATENATE_WRAPPED(PASS_ARGUMENTS_, COUNT_PAIRS(__VA_ARGS__))(__VA_ARGS__) )
With all this context out of the way, this is what my main macro looks like:
#define IMPLEMENT_CONSTRUCTOR(typeName, functionName, ...) \
extern "C" __declspec(dllexport) typeName* __stdcall functionName DECLARE_ARGUMENTS(__VA_ARGS__) \
{ \
return new typeName PASS_ARGUMENTS(__VA_ARGS__); \
} \
typeName::typeName DECLARE_ARGUMENTS(__VA_ARGS__)
// the intention is so that
IMPLEMENT_CONSTRUCTOR(MacroTestObject, CreateMacroTestObjectWithTwoParams, int, TwoParamFirst, float, TwoParamSecond)
{ }
//expands to
extern "C" __declspec(dllexport) MacroTestObject* __stdcall CreateMacroTestObjectWithTwoParams(int TwoParamFirst, float TwoParamSecond)
{
return new MacroTestObject(TwoParamFirst, TwoParamSecond);
}
MacroTestObject::MacroTestObject(int TwoParamFirst, float TwoParamSecond)
{ }
But the problem seems to be that the C++ compiler starts compiling even before all the macros have expanded, which throws out compile-time errors like crazy, showing something like:
0><Project>\MacroTestObject.cpp(4,1): Error C2512 : 'MacroTestObject::MacroTestObject': no appropriate default constructor available
0><Project>\MacroTestObject.cpp(5,1): Error C2511 : 'MacroTestObject::MacroTestObject(void)': overloaded member function not found in 'MacroTestObject'
But when I use (Rider for Unreal Engine)'s "Substitute macro calls and all nested calls" function, the macro expands out just fine.
Can someone help me out with this, I don't know why this is happening and I can't seem to find any particular assistance online.
This DLL is meant to be used in Unity3D as a native plug-in and so as far as I know using C++/CLI is out of the question. I don't think COM interop is an option either because all the info I read about it implies that I need to create TLB files, and since Unity handles C# compilation, that seems out of the question too.
EDIT:
I think I kind of figured out why this is causing issues.
// Something like
COUNT_PAIRS(a, b, c, d, e, f)
// always expands to
0
// because __VA_ARGS__ is considered as a single argument
// and a recount is not triggered. I tried using CONCATENATE_WRAPPED
// in different places but unfortunately it has been no help.
Could someone help me out with this?
Edit 2:
Solved the problem, switched to Clang. Also, there's an extra bracket in DECLARE_ARGUMENTS macro.
So I think this version should now work as you would want.
I've found two problems:
The already mentioned additional ")" in DECLARE_ARGUMENT, that was an easy fix
The fact that MSVC seems to trat __VA_ARGS__ differently than gcc and clang, it doesn't expand the arguments into themselves but treats it one. That's why e.g. COUNT_PAIRS didn't work correctly. The fix is taken from this SO Question
I suggest playing around with it a bit in godbolt to see how it works ;)
Hope this solves your problem
EDIT: I also just saw here that Visual Studio has apparently updated their preprocessor, so depending on which version one is using it could be possible to avoid all the ugly EXPAND calls by using the compiler switch /Zc:preprocessor
I'm used to developing with C++ in Qt Creator, much more so than Visual Studio. In the former, you can do something like this
qDebug()<< my_trace_string;
to output my_trace_string to a "console" which can be handy so that you can see what is going on.
I wondered if there is a Visual Studio window to which an application can easily write, independently of the dialogs and controls. This is complementary to the use of the debugger. It leaves a visual trace for example.
OutputDebugString() from debuapi.h (just #include <windows.h>) is your friend. I usually wrap it up into a function like this (use A suffix for ASCII or W suffix for for UNICODE):
void dbgMsg(PCWSTR _format, ...)
{
va_list args;
va_start(args, _format);
WCHAR msg[MAX_PATH];
if(SUCCEEDED(StringCbVPrintfW(msg, sizeof(msg), _format, args))){
OutputDebugStringW(msg);
}
}
That will output into Output window in IDE (next to Call stack, Error List and others)
In Visual Studio when working on GUI programs the usual method for such is to use the _RPTX (where X is the number of varadic parameters) macros which when report type is _CRT_WARN print to the Output window within VS itself rather than use an external console window.
For example:
_RPT1(_CRT_WARN, __FUNCTION__ ": hWnd: %#x\r\n", m_hWnd);
Personally I prefer using the following (custom) macros which do not take the report type (I simple use _CRT_WARN) and also do away with the need to know the number of vararg parameters and also the ability to take more than 4 vararg parameters (the limit with the default macros). The _RPT macros have simply never been updated to take advantage of the variadic macro capable preprocessor:
#if !defined(_RPTW)
#if defined(_DEBUG)
#define _RPTW(pszFmt, ...) _CrtDbgReportW(_CRT_WARN, NULL, __LINE__, NULL, (pszFmt), __VA_ARGS__)
#define _RPTWF(dest, fmt, ...) _CrtDbgReportW((dest), _T(__FILE__), __LINE__, NULL, (pszFmt), __VA_ARGS__)
#else
#define _RPTW(pszFmt, ...)
#define _RPTWF(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 _RPTAF(dest, fmt, ...) _CrtDbgReport((dest), __FILE__, __LINE__, NULL, (pszFmt), __VA_ARGS__)
#else
#define _RPTA(pszFmt, ...)
#define _RPTAF(dest, pszFmt)
#endif
#endif // #if !defined(_RPTA)
#if !defined(_RPTT)
#if defined(_UNICODE)
#define _RPTT _RPTW
#define _RPTTF _RPTWF
#else
#define _RPTT _RPTA
#define _RPTTF _RPTAF
#endif
#endif // #if !defined(_RPTT)
Using the custom macros the original example becomes:
_RPTA(__FUNCTION__ ": hWnd: %#x\r\n", m_hWnd);
The original way does have the benefit of taking the report type as a parameter, using that output can be redirected to a window (as _ASSERT does), the output window (the default with _CRT_WARN) or to a trace file (requires calling _CrtSetReportMode and _CrtSetReportFile).
First of all you could try to call the AllocConsole function to create a Console for your process.
Send your output to the console via _cprintf()And don't forget to #include wherever you use _cprintf()
If you need to close the console output, call FreeConsole();
For more details you could refer to this link
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.
Implementing an assertion macro I stumbled upon a problem on Xcode clang.
I use the following code:
void Log(LogLevel logLevel, const std::string& message);
std::string FormatString(const char* format, ...);
#define LOG(Level, Format, ...) \
{ \
std::string _ac_err_str_ = FormatString(Format, ##__VA_ARGS__); \
Log(Level, _ac_err_str_); \
}
#define checkf(expr, Format, ...) { if(!(expr)) { LOG(LL_ErrorMessage, TXT("Log in file ") __FILE__ TXT(" at line %d: ") TXT(#expr) TXT(". ") Format, __LINE__, __VA_ARGS__); } }
Everything works fine with MSVC but on Clang I get the following error: Expected expression
I've tracked down the problem on this line:
std::string _ac_err_str_ = FormatString(Format, ##__VA_ARGS__);
More specifically the problem is on the ##__VA_ARGS__ part because if I remove it everything compiles.
Also, if I pass a third parameter to the macro it compiles, e.g.:
checkf(false, "Error message"); // Won't compile
checkf(false, "Error %s", "message"); // Will compile
It seems like there's an error on passing the macro's variadic arguments to FormatString, but I can't find out the reason or how to fix this.
Apparently on Clang I need to change my checkf macro like this
#define checkf(expr, Format, ...) { if(!(expr)) { LOG(LL_ErrorMessage, TXT("Log in file ") __FILE__ TXT(" at line %d: ") TXT(#expr) TXT(". ") Format, __LINE__, ##__VA_ARGS__); } }
Notice that I've used ##__VA_ARGS__ instead of just __VA_ARGS__.
It compiles with both MSVC and Clang as well.
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.