c++ print stacktrace

Not needed in any interview…

Here’s a simple demo generating a back trace in a c++ source code. Not sure if it would work in a big code base.

#ifdef usage_demo

g++ -g -rdynamic backtrace.cpp # -rdynamic needed
./a.out | perl -pe 's/.*\((.*)\+.*\[(.*)\]/ `c++filt $1` . `addr2line $2` /e;'

#endif

#include <execinfo.h>
#include <stdexcept>
using namespace std;
int f2(){
        try{
                throw 1;
        }catch(...){
                void *array[10];
                size_t size = backtrace(array, 10);
                backtrace_symbols_fd(array, size, STDOUT_FILENO);
        }
}
int f1(){
        return f2();
}
int main(){
        f1();
}

reliably convert Any c++ source to C

More than one person asked me “Can you compile a c++ app if you only have a c compiler?”

Some of the foremost experts on c/c++ compiler said on http://www.edg.com/index.php?location=faq_q6_convert

If you mean “can you convert C++ source to C source, and run the C through a C compiler to get object code”, as a way to run C++ code on a system that has only a C compiler, yes it is possible to implement all of the features of ISO standard C++ by translation to C source code, and except for exception handling this produces object code with efficiency comparable to that of the code generated by a conventional compiler.

For exception handling, it is possible to do an implementation using setjmp/longjmp that is completely conformant, but the code generated will be 5-20% slower than code generated by a true c++ compiler.

##common c++ run time errors

In java, the undisputed #1 common run time error is NPE, so much so that half of all error checks are null-pointer-checks. In c++, divide-by-zero is similarly a must. But there are more…

– divide by zero
– pointer-move beyond bounds — remember all array sizes are compile-time constants, even with realloc()
– read/write a heap object (heapy thingy) via a reference variable after de-allocation
– return by reference an object allocated on the stack — bulldozed
– dereference (unwrap) a dangling pointer
– c-style cast failure
– double-free
– dereference (unwrap) a null pointer — undefined behavior, unlike java NPE. We should always check null before dereferencing.

—-less common
– delete a pointer to non-heap
– free a pointer to non-heap
– hold pointer/reference to a field of an object, not knowing it’s soon to be reclaimed/bulldozed. Object could be on heap or stack. More likely in multi-threaded programs.
– misusing delete/delete[] on a pointer created with new/new[]. The variable always looks the same — plain pointers.

————-
For any of the above, if it happens in a dtor, you are in double trouble, because the dtor could be executing due to another run time error.

The authoritative [[essential c++]] says that for every exception, there must be a “throw” you can find. Divide-by-zero is something directly done on “hardware” so no chance to throw. I feel many error conditions in C are treated same as in C, without “throw”. In contrast,

  • I feel operator-new is a typical “managed” low level operation that can (therefore does) use exception.
  • dynamic_cast() is anther “managed” low level operation added over C, so it does throw in some cases

Now, JVM “wraps” up all these runtime error Conditions into exceptions. Java creators generally prefer to push these error conditions to the compilation phase, to reduce the variety of Runtime errors. What remain are wrapped up as various RuntimeExceptions. It’s rather remarkable that JVM let’s you catch  all(?) of these exceptions. No undefined behavior.

eg – c++ exception-passing vs param-passing

Update — [[c++primer]] points out the key concept of ExceptionObject (exo for short) in a try/catch. This is the object thrown, assuming we don’t throw a pointer.

exo isn’t the object created (“object1”) and passed to the “throw” statement. That object1 can have a short lifespan and destroyed much sooner than the exo, which needs a long lifespan — described on P1039. Note exo is copy-constructed from object1. See R1 below.

exo isn’t the object caught either, unless you catch by reference. P1037.

exo IS the object re-thrown if you use the empty throw statement.
—–
Based on [[More effC++]] item on the difference between exception-passing and function-parameter-passing. Illustrates a few rules — R1 R2 R3

class B: public std::exception{};
class C: public B{}; //and so on
class D: public C{}; //and so on
class K: public I{}; //and so on

f(){ D* ex = new K();
  throw *ex; // Copy#1 created [R1] and thrown, sliced [1] to D
}
main(int){
  try{ f();}
  catch (exception * ex){} // silently ignored because no “address” is thrown — type mismatch [R2]

  //catch (B ex){} //sliced Copy#2a created, just like function parameter pbclone
  // we will leave the above catch clause commented without further discussion.

  catch(B & exCaught){ //Copy#1 caught without slicing
      //throw; //throws Copy#1 [R3], type D
      throw exCaught; //Copy#2b created [R1] and thrown, sliced[1] to B
}

[1] slicing by copy-ctor.

empty throw ^ missing throw in c++

Background – all exception specs beside these 2 are deprecated in c++11. Only these 2 forms proved useful. Let’s rephrase the question as “Real difference between 2 otherwise identical functions, one with empty throw() the other without throw”.

Short Answer — wrapping the empty-throw function call in try/catch is useless.

Short answer – empty throw means “should throw Nothing. Nothing expected“;
Short answer – missing throw means the opposite — “all exceptions expected

Long answer — suppose you wrap each function call in a try/catch (…) ie catch-all. For the function without throw, every single exception from it will fall into your catch(…). For the empty-throw function, every single exception always, always triggers unexpected(). Your try/catch is ignored by compiler — could be removed during compilation.

Now we are ready to look at a function with throw(A). According the ARM, if at runtime this function throws unlisted exceptions, it’s seen as a serious design fault, so serious that compiler will *disregard* your try/catch — paper over (huge) cracks.

Here’s a best practice from http://www.gotw.ca/publications/mill22.htm — Moral #1: Never write an exception specification. If you follow this advice, then your try/catch will work, ie won’t be stripped off by compiler

My advice — “Either don’t list the exceptions, or adhere to your list. If you bother to list the expected exceptions, then stick to it. Don’t throw anything else. “

Note unexpected() is called always, always due to an explicit exception-spec i.e. an explicit throw suffix. “Unexpected” means “function author informed compiler to *expect* some exceptions, but something unexpected came out from the function“.

Let’s repeat the message — whenever you notice unexpected() invoked, there’s a function with a broken exception spec — No smoke without fire. No throw suffix then no “unexpected()”.

Further, these 2 conditions always lead to each other —

function has an exception list, but unlisted exceptions generated unexpected() invocation

What about terminate()? It’s somewhat peripheral in the story of exception-spec. I will just describe one of the causes — an expected (i.e. listed) exception is thrown but not caught. Also, unexpected() calls terminate() by default.