Reputation: 203
I want to handle errors in my c++ program, so I created some exception classes to manage those errors, but I want to specify at which line in my program the error occurred.
I passed LINE macro to the constructor of my exception class.
For example:
void f(int i){ // LINE A
if(i<0)
throw(OutOfRange("message", __LINE__); // LINE B
}
void main(){
try{
f(-6); // LINE C
}
catch(const OutOfRange& error){
//do something
}
}
In this example I can only get the LINE B number, but I want to get LINE A and LINE C numbers.
Any idea, where and how to use LINE macro ??
Thanks.
Upvotes: 13
Views: 20738
Reputation: 3149
In addition to __LINE__
, you can also use __func__
and __FILE__
to give you more information.
__func__
will give you line A and you can at least get a line inside the catch
-block by rethrowing from there, but I don't know another way to get line C.
It would probably help you to create a backtrace using standard C++11, i.e. cross-platform and without the need for a debugger or cumbersome logging. In the years since this question was asked, some useful features have been added to C++. You can trace the call stack that led to an exception using:
std::nested_exception
and std::throw_with_nested
It is described on StackOverflow here and here
This will, however, require that you insert try/catch
statements at the functions you wish to trace (i.e. functions without this will not appear in your trace).
You could automate this with macros, reducing the amount of code you have to write/change.
Since you can do this with any derived exception class, you can add a lot of information to such a backtrace! You may also take a look at my MWE on GitHub, where a backtrace would look something like this:
Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
Upvotes: 0
Reputation: 6982
The CPPUNit framework uses macros instead of functions. That way you can easily get the line number at the same place where the macro is called.
I don't think it is a valid approach in a general sense, but you may find it interesting to take a look at the way the CPPUnit developers did it.
Upvotes: 1
Reputation: 114481
You are looking for a stack trace and there's no portable way to get it. Something somewhat similar can be achieved with:
struct SourcePoint
{
const char *filename;
int line;
SourcePoint(const char *filename, int line)
: filename(filename), line(line)
{ }
};
std::vector<SourcePoint> callstack;
struct SourcePointMarker
{
SourcePointMarker(const char *filename, int line)
{
callstack.push_back(SourcePoint(filename, line);
}
~SourcePointMarker()
{
callstack.pop_back();
}
}
#define MARK_FUNCTION \
SourcePointMarker sourcepointmarker(__FILE__, __LINE__);
Then right after the beginning of each function (or point of interest) you just add a line... for example
int myFunction(int x)
{
MARK_FUNCTION
...
}
Using this approach in your error handlers you can know who was called by who and so on (of course you will know only functions or places that have been instrumented with MARK_FUNCTION). If this is needed only during testing (and not in production) then probably you should just enable core dumps and learn how to run a debugger in post-mortem analysis.
Upvotes: 8
Reputation: 76529
Line C would be near impossible (I can't think of a way... except by passing a second argument to f
, __LINE__
.
Line A as follows:
void f(int i){ const int lineA = __LINE__;
if(i<0)
throw(OutOfRange("message", __LINE__); // LINE B
}
Upvotes: 1
Reputation: 146930
You need a stack trace and a debugger. There's no way in Standard C++ that you could find line C without passing it in as an argument (f(-6, __LINE__)
), and no way at all that you could find Line A.
Upvotes: 1