13

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.

lolando
  • 1,721
  • 1
  • 18
  • 22
CHAKRI
  • 203
  • 1
  • 3
  • 9

5 Answers5

8

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.

6502
  • 112,025
  • 15
  • 165
  • 265
1

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
}
Puppy
  • 144,682
  • 38
  • 256
  • 465
rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • you mean void f(int i){ const int lineA = __LINE__; if(i<0) throw(OutOfRange("message", lineA ); // LINE B } – CHAKRI Dec 30 '10 at 11:26
  • thanks for your answer, but I think for "LINE C" is not impossible cause most compiler use it !! – CHAKRI Dec 30 '10 at 11:28
  • 1
    @CHAKRI: The compiler can do many, many, many things that you can't do in Standard C++. – Puppy Dec 30 '10 at 11:29
1

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.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • +1, but I'd say "a stack trace *or* a debugger", since a stack trace can be obtained even without an external debugger (see e.g. the `backtrace` function). – Matteo Italia Dec 30 '10 at 11:28
1

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.

Antonio Pérez
  • 6,702
  • 4
  • 36
  • 61
  • +1. Look at the implementation of the assert() macro; I have created a similar assertion() macro, which throws a std::logic_error exception instead of calling std::abort() on failure of an assertion. – Raedwald Dec 30 '10 at 11:59
0

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"
GPMueller
  • 2,881
  • 2
  • 27
  • 36