Maximum call stack depth
June 10th, 2008Tried on g++ 4.1.3 on linux:
#include <iostream>
void x()
{
static int i = 0;
std::cout << "Depth " << i++ << std::endl;
x();
}
int main()
{
x();
}
There is a segmentation fault at “Depth 261811” ![]()
Tried on g++ 4.1.3 on linux:
#include <iostream>
void x()
{
static int i = 0;
std::cout << "Depth " << i++ << std::endl;
x();
}
int main()
{
x();
}
There is a segmentation fault at “Depth 261811” ![]()
I follow this guideline (which I read somewhere): assert around things that could never ever happen and throw exception for things that *might* happen. A short example:
int main(char* argv[], int argc)
{
// argv[0] always contains the program name, so argc always >= 1
assert(argc >= 1);
if ( argc < 2 )
{
// two parameters are expected.
throw std::runtime_exception("not enough parameters");
}
}
By default WinDbg will only break on second chance exceptions (that is there was no handle for the exception).
In order to break on first chance exceptions (where the exception was originally thrown) the right command is: sxe *
This will stop the debugger whenever any exception is thrown.
To undo: sxd * and the debugger will only stop on second chance exceptions.
Limited access destructors can be useful in order to enforce ownership of an object.
In the most obvious case a destructor is “public” and that is the case we all know.
When a destructor is declare as “protected” it cannot be destroyed from outside the class. That is you cannot declare an automatic variable of that type and if you allocate space on heap then “delete obj;” won’t work. The destructor is protected and the “delete” operator doesn’t have access to it. However you can call “delete” from inside the object by using some scheme of ownership management counted ref or other (ie when using a COM interface you first need to call AddRef and Release when you’re done).
If the destructor is private the compiler won’t allow inheriting that respective class. Alas I don’t think private destructors are very useful.
Exceptions are the way one should handle results of operations in C++ (I would say the *only* way). Nobody bothers anymore with the old c-ish way of checking the function result error code because: lots of tests/ifs make your code harder to read (thus maintain) and second because, if not careful, it could lead to memory leaks.
Welcome the wonderful world of exceptions. Except in C++ there is no base exception class for everyone to use (yes, there is std::exception but IMO it was designed specifically for the standard library). We want a class that facilitates declaring exceptions, throwing them and that gathers a maximum of information about the context in which the exception was thrown initially.
I tried to keep the base exception class as simple as possible.
/**
Base class for all exceptions.</font>
*/
class Exception
{
public:
Exception(int code,
const char* file,
const char* line
const char* reason)
: m_code(code), m_file(file), m_line(line), m_reason(reason)
{
}
void Print(std::ostream& where) const
{
where << "Exception " << m_reason << "("
<< m_code << ") occured at "
<< m_file << ":" << m_line << std::endl;
}
void SetFile(const std::string& file)
{
m_file = file;
}
void SetLine(const std::string& line)
{
m_line = line;
}
void SetReason(const std::string& reason)
{
m_reason = reason;
}
const std::string& GetReason() const
{
return m_reason;
}
virtual ~Exception()
{
}
private:
int m_code;
std::string m_file;
std::string m_line;
std::string m_reason;
};
As you can see the exception class allows us to assign an exception code, store the file/line where the exception occured and even the reason for which the problem appeared. Of course the user of the class is not expected to put in explicitly the file name and the line no as parameters to the constructor but we will use preprocessor macros for that.
#ifndef STRINGIZE#define STRINGIZE(N) _STRINGIZE(N)
#define _STRINGIZE(N) #N
#endif#define THROW(code, reason) throw Exception(code, __FILE__, STRINGIZE(__LINE__), reason);
Don’t worry about the STRINGIZE thingy, special preprocessor incantation to convert the __LINE__ no.
So now, how would one go about using this class. Well, you should either derive your special exception class from Exception or throw Exception with a special code everytime. I tend to combine the two in my code.
So, for example, in order to best handle exception in say, networking layer, I would derive special Exception in the network namespace base class. Then when I can’t read from a socket I would write THROW(READ_ERROR, “Can’t read from socket”)
No, of course you can’t do that in C++. However, there’s a neat and simple trick that you can use. Instead of typedefing create a class with the name of the type and make it inherit the type you wanted to alias.
So, for example, if you wanted to typedef Ints std::vector<int> you can create a new class:
class Ints : public std::vector<int> {};
There will be no problem forward declaring that.
Of course, if you choose to do this Ints will be a real class and not merely an alias (like typedef creates).
Ever wanted to break into debugger at that precise moment where module load/unload happens?
Using Windbg type: sxe ld [module name] for breakpoint on load or sxe ud [module name] for breakpoint on unload. Note that if module name isn’t present the breakpoint will be hit anytime a dll is loaded/unloaded. Oh and module name mustn’t contain .dll extension.
There are cases when you don’t want to allow static or automatic allocation (on the stack). In that case just make the destructor of your object protected. That way, if your code does:
MyObject p; // MyObject has a protected destructor -> compile error.
AnotherObject o(&p);
The first line will issue a compile error.
…just don’t!
Never, ever throw an exception inside your destructor. It *might* work if you’re not inside a stack-unwinding process, but if you are your whole program will go boom!
More informations here: faqlite.
If you intend to use a struct as just a simple collection of data don’t define any virtual functions (destructor included, it won’t be used polymorphically). Also don’t define any of the Big Three (assignment operator, copy constructor and destructor) but let the compiler generate them for you.