Related
After upgrading to VS2013 I started receiving all my ATLTRACE2 messages in a "() : atlTraceGeneral - My output" format.
e.g.
ATLTRACE(_T("This is my data: %d\n"), 124);
... shown as
dllmain.cpp(1121) : atlTraceGeneral - This is my data: 124
I don't need any additional info. Is here some way to get back to the previous format so that the output would be just
This is my data: 124
The only working fix is to undef ATLTRACE under _DEBUG macro and implement trace by yourself. Guys at Microsoft recommended the same.
The solution looks like this:
#ifdef _DEBUG
#ifdef ATLTRACE
#undef ATLTRACE
#undef ATLTRACE2
#define ATLTRACE CustomTrace
#define ATLTRACE2 ATLTRACE
#endif // ATLTRACE
#endif // _DEBUG
with the following CustomTraces:
void CustomTrace(const wchar_t* format, ...)
{
const int TraceBufferSize = 1024;
wchar_t buffer[TraceBufferSize];
va_list argptr; va_start(argptr, format);
vswprintf_s(buffer, format, argptr);
va_end(argptr);
::OutputDebugString(buffer);
}
void CustomTrace(int dwCategory, int line, const wchar_t* format, ...)
{
va_list argptr; va_start(argptr, format);
CustomTrace(format, argptr);
va_end(argptr);
}
I went a different route -- I chose to edit the output like this (the message only gets shorter, so no allocation required):
#ifdef _DEBUG
static int __cdecl crtReportHookW(int nReportType, wchar_t* wszMsg, int* pnRet)
{
const wchar_t wszTrace[] = L"atlTraceGeneral - ";
const int ccTrace = _countof(wszTrace) - 1; // exclude L'\0'
if (nReportType == _CRT_WARN)
{
wchar_t* pwsz = wcsstr(wszMsg, wszTrace);
if (pwsz != nullptr)
{
int ccBuf = wcslen(pwsz) + 1; // remaining buffer size (include L'\0')
wmemmove_s(pwsz, ccBuf, &pwsz[ccTrace], ccBuf - ccTrace);
}
}
return FALSE; // always keep processing
}
#endif
And in the CWinApp-derived constructor:
#ifdef _DEBUG
_CrtSetReportHookW2(_CRT_RPTHOOK_INSTALL, crtReportHookW);
#endif
and CWinApp-derived destructor:
#ifdef _DEBUG
_CrtSetReportHookW2(_CRT_RPTHOOK_REMOVE, crtReportHookW);
#endif
For some reason, both the MCBS and wide-character versions of the hook are called with the same message, so only the wide-character hook is necessary even in an MBCS app.
I'm working on an application that uses an custom, platform-dependent logger. The application defines some printf-style macros:
#define LOG_DEBUG(format, ...) \
logger.log(DEBUG, __FUNCTION__, format, ##__VA_ARGS__)
...
The past few days I've been working on moving the application to use boost.log. The biggest problem I'm having is trying to retain this macro format so that only the logger internals need to be changed, since boost's logging API is implemented in iostream-style, i.e.
BOOST_LOG(logger) << "something";
Is there an easy way to provide a macro that takes printf-style args and prints them to the boost logger without having to use string buffers? (I would think that having to format to a string would be a performance impact)
If not, is there a way to format a string using va_args without having to #ifdef for different platform implementations of formatting functions? (this was the whole point of moving to boost.log in the first place)
Or just use Boost Format to deal with the printf format string. For example,
template<typename Level, typename T1>
inline void log_format(
const Level level,
const char* const fmt,
const T1& t1)
{
BOOST_LOG_SEV(logger::get(), level) << boost::format(fmt) % t1;
}
Creating logger and extending this to handle multiple arguments (most probably with variadic template arguments) is left as an exercise for the reader.
Unfortunately, there are no clean implementations without the #ifdef statement. I know you want to port your existing logging to boost log as-is. That would be an incorrect way of doing things. printf was designed to be used for C, while boost log was designed for C++. So, my suggestion would be to use it the right way. You can make your logging macros painless than what you've shown in your code sample.
Here's my implementation of boost log where instead of having BOOST_LOG(logger) << "something";, I have it as roLOG_INFO << "something";. I believe this example comes from one of boost log's samples.
RoLog.h
#include <boost/logging/format_fwd.hpp>
#include <boost/logging/writer/on_dedicated_thread.hpp>
// Optimize : use a cache string, to make formatting the message faster
BOOST_LOG_FORMAT_MSG( optimize::cache_string_one_str<> )
#ifndef BOOST_LOG_COMPILE_FAST
#include <boost/logging/format.hpp>
#include <boost/logging/writer/ts_write.hpp>
#endif
// Specify your logging class(es)
typedef boost::logging::logger_format_write< > log_type;
// Declare which filters and loggers you'll use
BOOST_DECLARE_LOG_FILTER(roLogFilter, boost::logging::level::holder)
BOOST_DECLARE_LOG(roLog, log_type)
#define roLOG_WHERE_EACH_LINE 0
#if defined(roLOG_WHERE_EACH_LINE) && roLOG_WHERE_EACH_LINE
# define _roLOG_WHERE << roWHERE
#else
# define _roLOG_WHERE
#endif
// Define the macros through which you'll log
#define roLOG_DBG BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), debug ) << "[ DEBUG ]:" _roLOG_WHERE
#define roLOG_WARN BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), warning) << "[ WARNING ]:" _roLOG_WHERE
#define roLOG_ERR BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), error ) << "[ ERROR ]:" _roLOG_WHERE
#define roLOG_CRIT BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), fatal ) << "[ CRITICAL]:" _roLOG_WHERE
#define roLOG_INFO BOOST_LOG_USE_LOG_IF_LEVEL(roLog(), roLogFilter(), info )
struct RoLogOptions;
void roInitLogs(const RoLogOptions& options);
RoLog.cpp
#include <Core/RoLog.h>
#include <Core/RoLogOptions.h>
#include <boost/logging/format.hpp>
#include <boost/logging/writer/ts_write.hpp>
using namespace boost::logging;
BOOST_DEFINE_LOG(roLog, log_type)
BOOST_DEFINE_LOG_FILTER(roLogFilter, level::holder)
#define BLOCK(stmts) do{stmts}while(false)
#define CHECK_AND_DO(var,stmt) if (var) stmt
//------------------------------------------------------------------------------
void roInitLogs(const RoLogOptions& options)
{
static bool initialize = true;
if (initialize)
{
// Add formatters and destinations
// That is, how the message is to be formatted...
CHECK_AND_DO(options.printIndex, roLog()->writer().add_formatter(formatter::idx()));
CHECK_AND_DO(options.printTimestamp, roLog()->writer().add_formatter(formatter::time(options.timestampFormat)));
CHECK_AND_DO(options.autoAppendLine, roLog()->writer().add_formatter(formatter::append_newline()));
// ... and where should it be written to
roLog()->writer().add_destination(destination::dbg_window());
CHECK_AND_DO(options.printToStdOut, roLog()->writer().add_destination(destination::cout()));
if (!options.logFile.empty())
{
destination::file_settings settings;
settings.do_append(options.logFileAppendExisting);
roLog()->writer().add_destination(destination::file(options.logFile, settings));
}
CHECK_AND_DO(options.turnOffCache, roLog()->turn_cache_off());
// ... and set the log-level
level::type boost_log_level;
switch(options.logLevel)
{
case eLogLevel_None:
boost_log_level = level::disable_all;
break;
case eLogLevel_All:
boost_log_level = level::enable_all;
break;
case eLogLevel_Debug:
boost_log_level = level::debug;
break;
case eLogLevel_Info:
boost_log_level = level::info;
break;
case eLogLevel_Warning:
boost_log_level = level::warning;
case eLogLevel_Error:
boost_log_level = level::error;
break;
case eLogLevel_Critical:
boost_log_level = level::fatal;
break;
case eLogLevel_Default:
default:
# ifdef _DEBUG
boost_log_level = level::debug;
# else
boost_log_level = level::info;
# endif // _DEBUG
break;
};
roLogFilter()->set_enabled(boost_log_level);
initialize = false;
}
}
DISCLAIMER: I'm not claiming this to be a perfect piece of code. It works for me and I'm happy with it.
Let's say you would still like the option of logging the printf style, then consider the following code:
class LogUtil
{
static void VLogError(const char* format, va_list argList)
{
#ifdef _WIN32
int32 size = _vscprintf(format, argList) + 1;
#else
int32 size = vsnprintf(0, 0, format, argList) + 1;
#endif
if (size > 0)
{
boost::scoped_array<char> formattedString(new char[size]);
vsnprintf(formattedString.get(), size, format, argList);
roLOG_ERR << formattedString.get();
}
}
}
#define roLOGF_ERR(format, ...) LogUtil::VLogError(format, ##__VA_ARGS)
DISCLAIMER: I haven't tested the above code. You might need to tweak it to make it work for you.
gcc/g++ 4.7.2
CXXFLAGS -Wall -Wextra -g -O2
Hello,
I have this header file (mu_test.h) that is written using C style. It contain the following marcos
#define GET_ERROR() ((errno == 0) ? "None" : strerror(errno))
#define LOG_ERR(fmt, ...) fprintf(stderr, "[ERROR] %s:%d: errno: %s " fmt "\n", __func__, __LINE__, GET_ERROR(), ##__VA_ARGS__)
#define MU_ASSERT(test, msg) do { \
if(!(test)) { \
LOG_ERR(msg); \
return msg; \
} \
} while(0)
I have a cpp file (floor_plan_src.cpp) compiled using g++ that includes the mu_test.h
#include "mu_test.h"
char* test_memory_allocation()
{
plan = new floor_plan();
MU_ASSERT(plan != NULL, "Failed to allocate memory for floor_plan");
return NULL;
}
I get this warning:
deprecated conversion from string constant to ‘char*’
So my string constant that I am passing to that function-like marco doesn't like it (C strings) as I have compiled my source using g++.
I thought this problem was to do with mixing c/c++.
solution 1: wrap all the macros in the mu_test.h with extern "C"
#ifdef __cplusplus
extern "C"
{
#endif /* _cplusplus */
#define GET_ERROR() ((errno == 0) ? "None" : strerror(errno))
#define LOG_ERR(fmt, ...) fprintf(stderr, "[ERROR] %s:%d: errno: %s " fmt "\n", __func__, __LINE__, GET_ERROR(), ##__VA_ARGS__)
#define MU_ASSERT(test, msg) do { \
if(!(test)) { \
LOG_ERR(msg); \
return msg; \
} \
} while(0)
#ifdef __cplusplus
}
#endif /* __cplusplus */
Solution 1 still gave me the same warning.
solution 2: wrap the header file in floor_plan_src.cpp
extern "C" {
#include "mu_test.h"
}
Solution 2 still gave me the same warning
Solution 3: wrap the function
extern "C" char* test_memory_allocation()
{
plan = new floor_plan();
MU_ASSERT(plan != NULL, "Failed to allocate memory for floor_plan");
return NULL;
}
Solution 3 same as above
Solution 4:
Try and convert the constant string to a non-const char*
MU_ASSERT(plan != NULL, (char*)"Failed to allocate memory for floor_plan");
gave the following error:
expected primary-expression before char
"[ERROR] %s:%d: errno: %s " cannot be used as a function
Many thanks for any suggestions,
The problem is that your test_memory_allocation might return a string literal, and you shouldn't make a string literal decay to non-const char*: it's allowed in C++, but still deprecated.
Your code gets expanded in:
char* test_memory_allocation()
{
plan = new floor_plan();
do {
if(!(plan != NULL)) {
LOG_ERR("Failed to allocate memory for floor_plan")
return "Failed to allocate memory for floor_plan";
}
} while(0);
return NULL;
}
To fix it you just need to make test_memory_allocation return a const char* or otherwise you could return a pointer to something that could decay to a non-const char* (for example a static char array or to a heap-allocated memory area).
extern "C" is only needed to avoid C++ name mangling, it only affects functions, not macros.
I have some C code I'm working with, and I'm finding errors when the code is running but have little info about how to do a proper try/catch (as in C# or C++).
For instance in C++ I'd just do:
try{
//some stuff
}
catch(...)
{
//handle error
}
but in ANSI C I'm a bit lost. I tried some online searches but I don't see enough info about how to make it happen / figured I'd ask here in case anyone can point me in the right direction.
Here's the code I'm working with (fairly simple, recursive method) and would like to wrap with try/catch (or equivalent error-handling structure).
However my main question is simply how to do a try / catch in ANSI C...the implementation / example doesn't have to be recursive.
void getInfo( int offset, myfile::MyItem * item )
{
ll::String myOtherInfo = item->getOtherInfo();
if( myOtherInfo.isNull() )
myOtherInfo = "";
ll::String getOne = "";
myfile::Abc * abc = item->getOrig();
if( abc != NULL )
{
getOne = abc->getOne();
}
for( int i = 0 ; i < offset ; i++ )
{
printf("found: %d", i);
}
if( abc != NULL )
abc->release();
int childCount = item->getChildCount();
offset++;
for( int i = 0 ; i < childCount ; i++ )
getInfo( offset, item->getChild(i) );
item->release();
}
Generally, you don't.
It's possible to use setjmp and longjmp to build something fairly similar to try/catch, although there's no such thing in C as destructors or stack unwinding, so RAII is out of the question. You could even approximate RAII with a so-called "cleanup stack" (see for example Symbian/C++), although it's not a very close approximation, and it's a lot of work.
The usual way to indicate errors or failure in C is to return a value indicating success status. Callers examine the return value and act accordingly. See for example the standard C functions: printf, read, open, for ideas how to specify your functions.
When mixing C and C++ code, you must ensure that a C++ exception never reaches C code. When writing C++ functions that will be called from C, catch everything.
C does not support exception handling.
There is info on one approach to this problem here. This shows the simple setjmp/longjmp approach but also provides a more sophisticated alternative, covered in depth.
There is the classic unwinding gotos pattern:
FILE *if = fopen(...);
FILE *of = NULL;
if (if == NULL) return;
of = fopen(...);
if (of == NULL) goto close_if;
/* ...code... */
if (something_is_wrong) goto close_of;
/* ... other code... */
close_of:
fclose(of);
close_if:
fclose(if);
return state;
Alternately you can fake it in a limited way by isolating the "try" code in another function
int try_code(type *var_we_must_write, othertype var_we_only_read /*, ... */){
/* ...code... */
if (!some_condition) return 1;
/* ...code... */
if (!another_condition) return 2;
/* ...code... */
if (last_way_to_fail) return 4;
return 0;
}
void calling_routine(){
/* ... */
if (try_code(&x,y/*, other state */) ) {
/* do your finally here */
}
/* ... */
}
but neither approach is fully equivalent. You have to manage all the resources yourself, you don't get automatic rollback until a handler is found, and so on...
One useful coding style I like to use is the following. I don't know if it has a particular name but I came across it when I was reverse engineering some assembly code into the equivalent C code. You do lose an indentation level but it's not such a big deal to me. Watch out for reviewer who will point out the infinite loop! :)
int SomeFunction() {
int err = SUCCESS;
do {
err = DoSomethingThatMayFail();
if (err != SUCCESS) {
printf("DoSomethingThatMayFail() failed with %d", err);
break;
}
err = DoSomethingElse();
if (err != SUCCESS) {
printf("DoSomethingElse() failed with %d", err);
break;
}
// ... call as many functions as needed.
// If execution gets there everything succeeded!
return SUCCESS;
while (false);
// Something went wrong!
// Close handles or free memory that may have been allocated successfully.
return err;
}
This is my implementation of an exception handling system in C: exceptions4c.
It's powered by macros, built on top of setjmp and longjmp and it is 100% portable ANSI C.
There you can also find a list of all the different implementations I know of.
You can find a possible implementation with longjmp in this book : C Interfaces and Implementations: Techniques for Creating Reusable Software - David Hanson
And you can find the code here and here as an example of the style used in the book :
except.h
/* $Id$ */
#ifndef EXCEPT_INCLUDED
#define EXCEPT_INCLUDED
#include <setjmp.h>
#define T Except_T
typedef struct T {
const char *reason;
} T;
typedef struct Except_Frame Except_Frame;
struct Except_Frame {
Except_Frame *prev;
jmp_buf env;
const char *file;
int line;
const T *exception;
};
enum { Except_entered=0, Except_raised,
Except_handled, Except_finalized };
extern Except_Frame *Except_stack;
extern const Except_T Assert_Failed;
void Except_raise(const T *e, const char *file,int line);
#ifdef WIN32
#include <windows.h>
extern int Except_index;
extern void Except_init(void);
extern void Except_push(Except_Frame *fp);
extern void Except_pop(void);
#endif
#ifdef WIN32
/* $Id$ */
#define RAISE(e) Except_raise(&(e), __FILE__, __LINE__)
#define RERAISE Except_raise(Except_frame.exception, \
Except_frame.file, Except_frame.line)
#define RETURN switch (Except_pop(),0) default: return
#define TRY do { \
volatile int Except_flag; \
Except_Frame Except_frame; \
if (Except_index == -1) \
Except_init(); \
Except_push(&Except_frame); \
Except_flag = setjmp(Except_frame.env); \
if (Except_flag == Except_entered) {
#define EXCEPT(e) \
if (Except_flag == Except_entered) Except_pop(); \
} else if (Except_frame.exception == &(e)) { \
Except_flag = Except_handled;
#define ELSE \
if (Except_flag == Except_entered) Except_pop(); \
} else { \
Except_flag = Except_handled;
#define FINALLY \
if (Except_flag == Except_entered) Except_pop(); \
} { \
if (Except_flag == Except_entered) \
Except_flag = Except_finalized;
#define END_TRY \
if (Except_flag == Except_entered) Except_pop(); \
} if (Except_flag == Except_raised) RERAISE; \
} while (0)
#else
#define RAISE(e) Except_raise(&(e), __FILE__, __LINE__)
#define RERAISE Except_raise(Except_frame.exception, \
Except_frame.file, Except_frame.line)
#define RETURN switch (Except_stack = Except_stack->prev,0) default: return
#define TRY do { \
volatile int Except_flag; \
Except_Frame Except_frame; \
Except_frame.prev = Except_stack; \
Except_stack = &Except_frame; \
Except_flag = setjmp(Except_frame.env); \
if (Except_flag == Except_entered) {
#define EXCEPT(e) \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} else if (Except_frame.exception == &(e)) { \
Except_flag = Except_handled;
#define ELSE \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} else { \
Except_flag = Except_handled;
#define FINALLY \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} { \
if (Except_flag == Except_entered) \
Except_flag = Except_finalized;
#define END_TRY \
if (Except_flag == Except_entered) Except_stack = Except_stack->prev; \
} if (Except_flag == Except_raised) RERAISE; \
} while (0)
#endif
#undef T
#endif
except.c
static char rcsid[] = "$Id$" "\n$Id$";
#include <stdlib.h>
#include <stdio.h>
#include "assert.h"
#include "except.h"
#define T Except_T
Except_Frame *Except_stack = NULL;
void Except_raise(const T *e, const char *file,
int line) {
#ifdef WIN32
Except_Frame *p;
if (Except_index == -1)
Except_init();
p = TlsGetValue(Except_index);
#else
Except_Frame *p = Except_stack;
#endif
assert(e);
if (p == NULL) {
fprintf(stderr, "Uncaught exception");
if (e->reason)
fprintf(stderr, " %s", e->reason);
else
fprintf(stderr, " at 0x%p", e);
if (file && line > 0)
fprintf(stderr, " raised at %s:%d\n", file, line);
fprintf(stderr, "aborting...\n");
fflush(stderr);
abort();
}
p->exception = e;
p->file = file;
p->line = line;
#ifdef WIN32
Except_pop();
#else
Except_stack = Except_stack->prev;
#endif
longjmp(p->env, Except_raised);
}
#ifdef WIN32
_CRTIMP void __cdecl _assert(void *, void *, unsigned);
#undef assert
#define assert(e) ((e) || (_assert(#e, __FILE__, __LINE__), 0))
int Except_index = -1;
void Except_init(void) {
BOOL cond;
Except_index = TlsAlloc();
assert(Except_index != TLS_OUT_OF_INDEXES);
cond = TlsSetValue(Except_index, NULL);
assert(cond == TRUE);
}
void Except_push(Except_Frame *fp) {
BOOL cond;
fp->prev = TlsGetValue(Except_index);
cond = TlsSetValue(Except_index, fp);
assert(cond == TRUE);
}
void Except_pop(void) {
BOOL cond;
Except_Frame *tos = TlsGetValue(Except_index);
cond = TlsSetValue(Except_index, tos->prev);
assert(cond == TRUE);
}
#endif
If you want to do a multiple level jump, look up setjmp() and longjmp(). They can be used as a primitive exception throw. The setjmp() function sets up a return-to place, and returns a status value. The longjmp() function goes to the return-to place, and provides the status value. You can create a catch function by having be called after setjmp() depending on the status value.
Do not, for whatever reason, use them in C++. They do not do stack unwinding or call destructors.
Since C++ was originally implemented as a C pre-processor and it had Try / Catch you could redo Bjarne Stroustrup's work and write a pre-processor to do it.
I'd like to make a debug logging function with the same parameters as printf. But one that can be removed by the pre-processor during optimized builds.
For example:
Debug_Print("Warning: value %d > 3!\n", value);
I've looked at variadic macros but those aren't available on all platforms. gcc supports them, msvc does not.
I still do it the old way, by defining a macro (XTRACE, below) which correlates to either a no-op or a function call with a variable argument list. Internally, call vsnprintf so you can keep the printf syntax:
#include <stdio.h>
void XTrace0(LPCTSTR lpszText)
{
::OutputDebugString(lpszText);
}
void XTrace(LPCTSTR lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);
int nBuf;
TCHAR szBuffer[512]; // get rid of this hard-coded buffer
nBuf = _vsnprintf(szBuffer, 511, lpszFormat, args);
::OutputDebugString(szBuffer);
va_end(args);
}
Then a typical #ifdef switch:
#ifdef _DEBUG
#define XTRACE XTrace
#else
#define XTRACE
#endif
Well that can be cleaned up quite a bit but it's the basic idea.
This is how I do debug print outs in C++. Define 'dout' (debug out) like this:
#ifdef DEBUG
#define dout cout
#else
#define dout 0 && cout
#endif
In the code I use 'dout' just like 'cout'.
dout << "in foobar with x= " << x << " and y= " << y << '\n';
If the preprocessor replaces 'dout' with '0 && cout' note that << has higher precedence than && and short-circuit evaluation of && makes the whole line evaluate to 0. Since the 0 is not used the compiler generates no code at all for that line.
Here's something that I do in C/C++. First off, you write a function that uses the varargs stuff (see the link in Stu's posting). Then do something like this:
int debug_printf( const char *fmt, ... );
#if defined( DEBUG )
#define DEBUG_PRINTF(x) debug_printf x
#else
#define DEBUG_PRINTF(x)
#endif
DEBUG_PRINTF(( "Format string that takes %s %s\n", "any number", "of args" ));
All you have to remember is to use double-parens when calling the debug function, and the whole line will get removed in non-DEBUG code.
Ah, vsprintf() was the thing I was missing. I can use this to pass the variable argument list directly to printf():
#include <stdarg.h>
#include <stdio.h>
void DBG_PrintImpl(char * format, ...)
{
char buffer[256];
va_list args;
va_start(args, format);
vsprintf(buffer, format, args);
printf("%s", buffer);
va_end(args);
}
Then wrap the whole thing in a macro.
Another fun way to stub out variadic functions is:
#define function sizeof
#CodingTheWheel:
There is one slight problem with your approach. Consider a call such as
XTRACE("x=%d", x);
This works fine in the debug build, but in the release build it will expand to:
("x=%d", x);
Which is perfectly legitimate C and will compile and usually run without side-effects but generates unnecessary code. The approach I usually use to eliminate that problem is:
Make the XTrace function return an int (just return 0, the return value doesn't matter)
Change the #define in the #else clause to:
0 && XTrace
Now the release version will expand to:
0 && XTrace("x=%d", x);
and any decent optimizer will throw away the whole thing since short-circuit evaluation would have prevented anything after the && from ever being executed.
Of course, just as I wrote that last sentence, I realized that perhaps the original form might be optimized away too and in the case of side effects, such as function calls passed as parameters to XTrace, it might be a better solution since it will make sure that debug and release versions will behave the same.
In C++ you can use the streaming operator to simplify things:
#if defined _DEBUG
class Trace
{
public:
static Trace &GetTrace () { static Trace trace; return trace; }
Trace &operator << (int value) { /* output int */ return *this; }
Trace &operator << (short value) { /* output short */ return *this; }
Trace &operator << (Trace &(*function)(Trace &trace)) { return function (*this); }
static Trace &Endl (Trace &trace) { /* write newline and flush output */ return trace; }
// and so on
};
#define TRACE(message) Trace::GetTrace () << message << Trace::Endl
#else
#define TRACE(message)
#endif
and use it like:
void Function (int param1, short param2)
{
TRACE ("param1 = " << param1 << ", param2 = " << param2);
}
You can then implement customised trace output for classes in much the same way you would do it for outputting to std::cout.
What platforms are they not available on? stdarg is part of the standard library:
http://www.opengroup.org/onlinepubs/009695399/basedefs/stdarg.h.html
Any platform not providing it is not a standard C implementation (or very, very old). For those, you will have to use varargs:
http://opengroup.org/onlinepubs/007908775/xsh/varargs.h.html
Part of the problem with this kind of functionality is that often it requires
variadic macros. These were standardized fairly recently(C99), and lots of
old C compilers do not support the standard, or have their own special work
around.
Below is a debug header I wrote that has several cool features:
Supports C99 and C89 syntax for debug macros
Enable/Disable output based on function argument
Output to file descriptor(file io)
Note: For some reason I had some slight code formatting problems.
#ifndef _DEBUG_H_
#define _DEBUG_H_
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "stdarg.h"
#include "stdio.h"
#define ENABLE 1
#define DISABLE 0
extern FILE* debug_fd;
int debug_file_init(char *file);
int debug_file_close(void);
#if HAVE_C99
#define PRINT(x, format, ...) \
if ( x ) { \
if ( debug_fd != NULL ) { \
fprintf(debug_fd, format, ##__VA_ARGS__); \
} \
else { \
fprintf(stdout, format, ##__VA_ARGS__); \
} \
}
#else
void PRINT(int enable, char *fmt, ...);
#endif
#if _DEBUG
#if HAVE_C99
#define DEBUG(x, format, ...) \
if ( x ) { \
if ( debug_fd != NULL ) { \
fprintf(debug_fd, "%s : %d " format, __FILE__, __LINE__, ##__VA_ARGS__); \
} \
else { \
fprintf(stderr, "%s : %d " format, __FILE__, __LINE__, ##__VA_ARGS__); \
} \
}
#define DEBUGPRINT(x, format, ...) \
if ( x ) { \
if ( debug_fd != NULL ) { \
fprintf(debug_fd, format, ##__VA_ARGS__); \
} \
else { \
fprintf(stderr, format, ##__VA_ARGS__); \
} \
}
#else /* HAVE_C99 */
void DEBUG(int enable, char *fmt, ...);
void DEBUGPRINT(int enable, char *fmt, ...);
#endif /* HAVE_C99 */
#else /* _DEBUG */
#define DEBUG(x, format, ...)
#define DEBUGPRINT(x, format, ...)
#endif /* _DEBUG */
#endif /* _DEBUG_H_ */
Have a look at this thread:
How to make a variadic macro (variable number of arguments)
It should answer your question.
This is what I use:
inline void DPRINTF(int level, char *format, ...)
{
# ifdef _DEBUG_LOG
va_list args;
va_start(args, format);
if(debugPrint & level) {
vfprintf(stdout, format, args);
}
va_end(args);
# endif /* _DEBUG_LOG */
}
which costs absolutely nothing at run-time when the _DEBUG_LOG flag is turned off.
This is a TCHAR version of user's answer, so it will work as ASCII (normal), or Unicode mode (more or less).
#define DEBUG_OUT( fmt, ...) DEBUG_OUT_TCHAR( \
TEXT(##fmt), ##__VA_ARGS__ )
#define DEBUG_OUT_TCHAR( fmt, ...) \
Trace( TEXT("[DEBUG]") #fmt, \
##__VA_ARGS__ )
void Trace(LPCTSTR format, ...)
{
LPTSTR OutputBuf;
OutputBuf = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, \
(size_t)(4096 * sizeof(TCHAR)));
va_list args;
va_start(args, format);
int nBuf;
_vstprintf_s(OutputBuf, 4095, format, args);
::OutputDebugString(OutputBuf);
va_end(args);
LocalFree(OutputBuf); // tyvm #sam shaw
}
I say, "more or less", because it won't automatically convert ASCII string arguments to WCHAR, but it should get you out of most Unicode scrapes without having to worry about wrapping the format string in TEXT() or preceding it with L.
Largely derived from MSDN: Retrieving the Last-Error Code
Not exactly what's asked in the question . But this code will be helpful for debugging purposes , it will print each variable's value along with it's name . This is completely type independent and supports variable number of arguments.
And can even display values of STL's nicely , given that you overload output operator for them
#define show(args...) describe(#args,args);
template<typename T>
void describe(string var_name,T value)
{
clog<<var_name<<" = "<<value<<" ";
}
template<typename T,typename... Args>
void describe(string var_names,T value,Args... args)
{
string::size_type pos = var_names.find(',');
string name = var_names.substr(0,pos);
var_names = var_names.substr(pos+1);
clog<<name<<" = "<<value<<" | ";
describe(var_names,args...);
}
Sample Use :
int main()
{
string a;
int b;
double c;
a="string here";
b = 7;
c= 3.14;
show(a,b,c);
}
Output :
a = string here | b = 7 | c = 3.14
Having come across the problem today, my solution is the following macro:
static TCHAR __DEBUG_BUF[1024];
#define DLog(fmt, ...) swprintf(__DEBUG_BUF, fmt, ##__VA_ARGS__); OutputDebugString(__DEBUG_BUF);
You can then call the function like this:
int value = 42;
DLog(L"The answer is: %d\n", value);