Reputation: 585
Let's say I have a function called verify()
inside verify.c
and it is used to evaluate a condition and print debug message if the check fails.
void verify(int expected)
{
if (expected < 10) {
fprintf(stderr,"Verification failed for %d\n", expected);
abort();
}
}
Now, I am adding this header file verify.h
in various c files throughout SW package, and many functions from different c files are calling verify()
. If the check fails, verify()
will print out the error message but how do I exactly find out where it is happening (i.e. which file and which function is actually calling failed verify()
)?
I want to find this during I compile my code and without using a debugger such as gdb
.
Upvotes: 1
Views: 1341
Reputation: 93476
Given verify.h declaring:
#define verify(e) verify_impl( (e), __FILE__, __func__, __LINE__ )
void verify_impl( int expected, const char* file, const char* fn, int line ) ;
And verify.c defining:
void verify_impl(int expected, const char* file, const char* fn, int line ) ; )
{
if (expected < 10)
{
fprintf( stderr,"Verification failed for %d at %s:%s(%d)\n",
expected, file, fn, line );
abort();
}
}
The when verify(9) ;
is called from foo()
at line 20 of bar.c for example, you will get:
Verification failed for 9 at bar.c:foo(20)
However a more generally useful assertion mechanism is possible. Consider:
verify.h:
#define verify(e) verify_impl( (e), #e, __FILE__, __func__, __LINE__ )
void verify_impl( int exp, const char* expression,
const char* file, const char* fn, int line ) ;
verify.c
void verify_impl( int exp, const char* expression,
const char* file, const char* fn, int line ) ; )
{
if( !exp )
{
fprintf( stderr,"Verification that %s failed at from %s:%s(%d)\n",
expression, file, fn, line );
abort();
}
}
Then when verify( x >= 10 ) ;
is called with x == 9
from foo()
at line 20 of bar.c for example, you will get the more useful:
Verification that x >= 10 failed at bar.c:foo(20)
Any Boolean expression can be verified rather than the hard coded exp < 10
. If you still need the original hard test, then you can define another macro in terms of the new more flexible one:
#define verify_notlessthan10( e ) verify( (e) >= 10 )
Note the inversion of the logic; you are verifying that the expression is true, and aborting when false. The semantics being that of assert.
Upvotes: 4
Reputation: 31316
One solution is to modify the code like this:
void verify(int expected, char * str)
{
if (expected < 10) {
fprintf(stderr,"Verification failed for %d\n", expected);
if(str)
fprintf(stderr, str);
abort();
}
}
With this approach, you'll need to modify all calls. A way to get around that is to rename the function to verify_aux
or something, and then write this:
void verify(int expected)
{
verify_aux(expected, NULL);
}
This way, you could change verify to verify_aux if you feel the need to do so at a particular point.
Upvotes: 0
Reputation: 45045
If you're using gcc, you should be able to use the __FILE__
, __LINE__
, and __FUNCTION__
preprocessor macros, and pass them to your verify()
routine to issue more useful debugging messages. As tadman points out in his comment, it might make sense to define a macro that acts as a wrapper for verify()
, capturing the file, line, and function information before passing it to verify()
. Then, at the point of use, you only need to call the wrapper and pass in the expected
argument, letting the preprocessor fill in the extra information while it's still available in context, then passing it all to your verify()
function.
Upvotes: 3