A typical application using libebt will produce errors along the lines of the following:
Caught exception: * When performing query 'app-editors/vim' from commandline: -> When calculating dependencies for 'app-editors/vim': -> When calculating dependencies for 'app-editors/vim-core': -> When loading versions for 'gentoo::app-editors/vim-core': -> When parsing version string '6.4_invalid_version_suffix': Version error: '6.4_invalid_version_suffix' is invalid (unknown format at around character offset 3)
This is far more useful for both end users and developers than a simple context-free error message, and does not require the use of a debugger to generate the backtrace.
Point to note:
/* vim: set sw=4 sts=4 et foldmethod=syntax : */ #include <libebt/libebt.hh> #include <libebt/libebt_util.hh> #include <string> #include <exception> #include <iostream> #include <fstream> #include <cstdlib> /* Tag for our exception context. This tag allows multiple libraries * to make use of libebt without accidentally causing collisions. */ struct ExceptionTag { }; /* A basic exception class. We inherit from libebt::Backtraceable to * provide the backtrace() method. */ class Exception : public libebt::Backtraceable<ExceptionTag>, public std::exception { protected: const std::string _message; public: Exception(const std::string & message) throw () : libebt::Backtraceable<ExceptionTag>(), std::exception(), _message(message) { } virtual ~Exception() throw () { } std::string message() const throw () { return _message; } }; /* Thrown if we can't open a file. */ class CannotOpenFileException : public Exception { public: CannotOpenFileException(const std::string & filename) throw () : Exception("Cannot open file '" + filename + "'") { } }; /* Thrown if we encounter a '#e' directive in a file. */ class FileHasErrorException : public Exception { public: FileHasErrorException(unsigned line_number, const std::string & line) throw () : Exception("Error directive on line " + libebt::stringify(line_number) + ": '" + line + "'") { } }; /* Thrown if we encounter a bad # line in a file. */ class UnknownDirectiveException : public Exception { public: UnknownDirectiveException(unsigned line_number, const std::string & line) throw () : Exception("Unknown directive on line " + libebt::stringify(line_number) + ": '" + line + "'") { } }; /* Thrown if we include too deeply (for example, recursive includes). */ class IncludeDepthTooDeepException : public Exception { public: IncludeDepthTooDeepException() throw () : Exception("Include depth too deep") { } }; /* Convenience definition for our backtrace context. */ typedef libebt::BacktraceContext<ExceptionTag, std::string> Context; /* Process a file. Handle directives, and echo everything else to stdout. */ void process_file(const std::string & filename, unsigned depth = 0) { /* Here's our first example of declaring exception context. It is * done through a simple variable declaration. */ Context c("In file '" + filename + "'"); /* Check that we're not too deeply nested. */ if (depth > 10) throw IncludeDepthTooDeepException(); /* Open and check the file in question. */ std::ifstream file(filename.c_str()); if (! file) throw CannotOpenFileException(filename); std::string line; unsigned line_number = 0; while (std::getline(file, line)) { ++line_number; if (0 == line.compare(0, 3, "#e ")) { /* We hit an #e directive. Throw an error. */ throw FileHasErrorException(line_number, line.substr(3)); } else if (0 == line.compare(0, 3, "#w ")) { /* We hit a #w directive. Provide a backtrace, but don't * exit. */ std::cerr << "Warning!\n * " << Context::backtrace(":\n * ") << "Warning directive: " << line.substr(3) << std::endl; } else if (0 == line.compare(0, 3, "#i ")) { /* Note how the exception context can be declared at a non-function * scope. This is useful in tight loops to avoid unnecessary * overhead. */ Context c2("From #i directive on line " + libebt::stringify(line_number)); process_file(line.substr(3), depth + 1); } else if (0 == line.compare(0, 1, "#")) throw UnknownDirectiveException(line_number, line); else std::cout << line << std::endl; } } int main(int argc, char *argv[]) { Context c("In main program"); try { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " filename" << std::endl; return EXIT_FAILURE; } for (int argi = 1 ; argi < argc ; ++argi) { /* Another example of context being specified in non-function * scope. */ Context c2("When handling commandline argument '" + libebt::stringify(argv[argi]) + "'"); process_file(argv[argi]); } return EXIT_SUCCESS; } catch (Exception & e) { /* Use the backtrace method to get a pretty-printed exception * context. */ std::cerr << "Error!\n * " << e.backtrace(":\n * ") << e.message() << " (" << e.what() << ")" << std::endl; return EXIT_FAILURE; } catch (...) { std::cerr << "Unknown exception!" << std::endl; return EXIT_FAILURE; } }
This gives backtraces like the following:
Error! * In main program: * When handling commandline argument 'first.txt': * In file 'first.txt': * From #i directive on line 2: * In file 'second.txt': * From #i directive on line 5: * In file 'third.txt': * Error directive on line 2: 'This is an error.' (21FileHasErrorException)
Further things possible with libebt include:
std::wstring)See the class documentation for details.
libebt is a pure template library, so there is no need to link your application against anything. Usually libebt is installed into the standard include path, so no CXXFLAGS are needed either.
./configure make make check sudo make install
If you have Doxygen, you can make some HTML documentation:
make doxygen
If you are using a Subversion checkout rather than a source tarball, you will probably need to run:
./autogen.bash
to create the configure script. This will require a full GNU autotools setup on your system.
libebt does not need any libraries beyond a C++ standard library implementation. If the Boost library Boost.Threads is available, it will be used for one of the test cases; however, Boost is not required. Similarly, ZThread and pthread, if available, will be used for test cases.
Headers for use with ZThread, Boost and pthread will be installed, but they are not used except in test cases or when explicitly #included.
Copyright © 2005 Ciaran McCreesh <ciaranm@gentoo.org>. See the Licence for redistribution conditions.
![]() |
![]() |