diff --git a/ChangeLog b/ChangeLog index b5bc499..6cdd462 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +* Tue Apr 27 2004 Marc Wäckerlin - mrw-c++-0.91 (mrw) + - mrw/arg.hpp: Command line argument evaluation + - mrw/smartpointer.hpp: Smart pointer implementation + - configure: Correction for Solaris * Fri Apr 24 2004 Marc Wäckerlin - mrw-c++-0.90 (mrw) - initial rpm with two spec files - test cases for libmrwexcstderr and libmrwexclog4cxx diff --git a/NEWS b/NEWS index 3112486..4418edc 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,5 @@ +New: Command line argument evaluation and SmartPointer + This is a preliminary release. Especially tests, configure environment, packages and documentation are not yet finished. It should already work, even though it may be not yet perfect. diff --git a/configure.in b/configure.in index 5b25db9..3d5b2e4 100644 --- a/configure.in +++ b/configure.in @@ -2,7 +2,7 @@ AC_INIT([mrw/mrw.hpp.in]) PACKAGENAME=mrw-c++ MAJOR=0 -MINOR=90 +MINOR=91 SUPPORT=beta AM_INIT_AUTOMAKE(@PACKAGENAME@, @MAJOR@.@MINOR@, [marc@waeckerlin.org]) @@ -32,11 +32,11 @@ AC_CHECK_HEADER(sys/old_procfs.h, [AM_CPPFLAGS=-D__solaris__]) AC_SEARCH_LIBS(cplus_demangle, iberty, [AC_MSG_RESULT([OK])], [AC_MSG_ERROR([Library iberty is required!])]) AC_SEARCH_LIBS(bfd_arch_list, bfd, [AC_MSG_RESULT([OK])], - [ - AC_SEARCH_LIBS(bfd_arch_list, bfd, [AC_MSG_RESULT([OK])], - [AC_MSG_ERROR([BFD library libbfd is required])], - [-lintl]) - ]) + [no_bfd=true]) +if [ -n "$no_bfd" ]; then + AC_SEARCH_LIBS(bfd_arch_list, bfd, [AC_MSG_RESULT([OK])], + [AC_MSG_ERROR([BFD library libbfd is required])], [-lintl]) +fi # Arguments AM_MAINTAINER_MODE diff --git a/makefile.am b/makefile.am index 007170a..314f0b3 100644 --- a/makefile.am +++ b/makefile.am @@ -1,5 +1,7 @@ SUBDIRS = mrw +EXTRA_DIST = bootstrap.sh + nobase_include_HEADERS = mrw/auto.hpp mrw/unistd.hpp \ mrw/stacktrace.hpp mrw/exception.hpp \ mrw/exec.hpp diff --git a/mrw/arg.hpp b/mrw/arg.hpp new file mode 100644 index 0000000..8571ee9 --- /dev/null +++ b/mrw/arg.hpp @@ -0,0 +1,686 @@ +#include +#include +#include +#include +#include // exit +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mrw { + + /** @defgroup arguments C++ Evaluation of Command Line Arguments + + @brief These classes do simple and easy command line argment evaluation + in C++. + + Features: + - every argument has a long and a short option + - all arguments are optional and provide a default value + - the order of options is not important + - every option can take any (fixed) number of additional parameter of + type + - string + - integer + - boolean (@c "yes", @c "on", @c "true" evaluates to @c true) + - short options can be combined, instead of + @c -a @c -b @c -c @c 15 you can simply write @c -abc @c 15 + - automated help display (support for option @c -h) + + @c mrw::Args is the main user interface class that represents + all command line options with their arguments. It is implemented + as singleton, so the same instance can be accessed from + everywhere in the code. It mst be setup just in the beginning of + the @c main() function. + + The other important class for the end user is @c mrw::Opt, one + possible option with additional parameter. The end user needs @c + mrw::Opt to setup all allowed command line options in the + beginning, bevore evaluation of the user given command line is + done (before @c argc and @c argv is shifted into @c mrw::Args. + + The third class a user should know is @c mrw::Param. It + represents the arguments to one option. Every instance of @c + mrw::Opt owns one instance of @c mrw::Param that is either empty + or list of (mandatory) arguments of type @c std::string, @c int + or @c bool. + + The classes are normally used this way: + + @code + // this program may be called e.g. with the following arguments: + // ./a.out --coordinates 13 1 -vo out.txt -n MyName + int main(int argv, const char * const * const argv) { + try { + mrw::Args::instance() + // setup the possible options + < + + A new mandatory parameter is added to the list of parameter, by + shifting the default value into the instance of @c + mrw::Param. E.g. add a string, that defaults to @c "noname", an + integer, that defaults to @c 4, another integer that defaults to + @2 and a boolean that defaults to @c "true": + + @code + // if you need the instance as variable: + mrw::Param p(); + p<<"noname"<<4<<2<toString(); + int firstInteger = args[1]->toInt(); + int secondInteger = args[2]->toInt(); + bool theBoolean = args[3]->toBool(); + @endcode + + @section argParts Setup Command Line from Different Program Parts + + If your software is large and splitted into different parts (or + sub projects or modules, ...), all with their own parameter, you + can use the following trick: Statical variables are initialized + before the @c main() function is called. + + In part Abc write in a code file (not in a header file): + + @code + class AbcArguments { + public: + AbcArguments() { + mrw::Args::instance() + < + */ + class Value { + + public: + + virtual ~Value() {} + + /** @brief If the instance is a @c std::string, return that + string, otherwise throw an exception. + @throw mrw::bad_cast if the instance is not a string + @return the string, if the instance is a string + */ + virtual const std::string& toString() const throw(mrw::exception) { + throw mrw::bad_cast(); + } + + /** @brief If the instance is an @c int, return that integer, + otherwise throw an exception. + @throw mrw::bad_cast if the instance is not a integer + @return the integer, if the instance is a integer + */ + virtual int toInt() const throw(mrw::exception) { + throw mrw::bad_cast(); + } + + /** @brief If the instance is an @c bool, return that boolean, + otherwise throw an exception. + @note the following typings are converted to @c true: + - true + - yes + - on + Everything else is converted to @c false. + @throw mrw::bad_cast if the instance is not a boolean + @return the boolean, if the instance is a boolean + */ + virtual bool toBool() const throw(mrw::exception) { + throw mrw::bad_cast(); + } + + /// @brief returns a printable representation of the value + virtual std::string printable() const throw(mrw::bad_exception) = 0; + + /// @brief returns a printable typename of the value + virtual const std::string& typestr() const throw(mrw::bad_exception)=0; + + protected: + friend class Args; // allow assign for Param + virtual void operator=(const std::string&) throw(mrw::exception) = 0; + }; + + private: + + class StringValue: public Value { + public: + virtual ~StringValue() {} + StringValue(const std::string& s) throw(mrw::bad_exception): _s(s) { + } + virtual const std::string& toString() const throw(mrw::exception) { + return _s; + } + virtual const std::string& typestr() const throw(mrw::bad_exception) { + static std::string name("string"); + return name; + } + virtual std::string printable() const throw(mrw::bad_exception) { + return _s; + } + protected: + virtual void operator=(const std::string& s) throw(mrw::exception) { + _s = s; + } + private: + std::string _s; + }; + + class IntValue: public Value { + public: + virtual ~IntValue() {} + IntValue(int i) throw(mrw::bad_exception): _i(i) { + } + virtual int toInt() const throw(mrw::exception) { + return _i; + } + virtual const std::string& typestr() const throw(mrw::bad_exception) { + static std::string name("integer"); + return name; + } + virtual std::string printable() const throw(mrw::bad_exception) { + return ((std::stringstream&)(std::stringstream()<<_i)).str(); + } + protected: + virtual void operator=(const std::string& s) throw(mrw::exception) { + if (!(std::stringstream(s)>>_i)) throw mrw::bad_cast(); + } + private: + int _i; + }; + + class BoolValue: public Value { + public: + virtual ~BoolValue() {} + BoolValue(bool b) throw(mrw::bad_exception): _b(b) { + } + virtual bool toBool() const throw(mrw::exception) { + return _b; + } + virtual const std::string& typestr() const throw(mrw::bad_exception) { + static std::string name("boolean (\"yes\" or \"no\")"); + return name; + } + virtual std::string printable() const throw(mrw::bad_exception) { + return _b?"yes":"no"; + } + protected: + virtual void operator=(const std::string& s) throw(mrw::exception) { + _b = s=="true" || s=="yes" || s=="on"; + } + private: + bool _b; + }; + + typedef std::vector< mrw::SmartPointer > Params; + Params _params; + + public: + + /// @brief returns the number of (mandatory) parameter + int size() const throw(std::bad_exception) { + return _params.size(); + } + + /// @brief add one more mandatory string parameter + Param& operator<<(const char* const s) throw(mrw::bad_exception) { + _params.push_back(new StringValue(s)); + return *this; + } + + /// @brief add one more mandatory string parameter + Param& operator<<(const std::string& s) throw(mrw::bad_exception) { + _params.push_back(new StringValue(s)); + return *this; + } + + /// @brief add one more mandatory integer parameter + Param& operator<<(int i) throw(mrw::bad_exception) { + _params.push_back(new IntValue(i)); + return *this; + } + + // @brief add one more mandatory boolean parameter + Param& operator<<(bool b) throw(mrw::bad_exception) { + _params.push_back(new BoolValue(b)); + return *this; + } + + /** @brief get parameter number @i + @throw mrw::out_of_range if @c i is too big */ + const mrw::SmartPointer& operator[](unsigned int i) const + throw(mrw::exception) { + if (i<_params.size()) return _params[i]; + throw mrw::out_of_range + (((std::stringstream&) + (std::stringstream()<<"check failed: " + <& setable(unsigned int i) + throw(mrw::exception) { + if (i<_params.size()) return _params[i]; + throw mrw::out_of_range + (((std::stringstream&) + (std::stringstream()<<"check failed: " + < + + The library user needs this class when setting up the list of + supported command line ooptions: Simply shift one instance of @c + mrw::Opt per supported command line option into @c + mrw::Args::instance(), e.g.: + + @code + mrw::Args::instance() + <& operator[](unsigned int i) const + throw(mrw::exception) { + return _param[i]; + } + + private: + + friend class Args; // is allowed to set values + void set() const throw(mrw::bad_exception) { + _set = true; + } + Param& args() const throw(mrw::bad_exception) { + return _param; + } + mutable bool _set; + char _shortname; + std::string _longname; + mutable Param _param; + std::string _help; + }; + + /** @brief handle command line arguments + @pre #include + + This class handles command line arguments. It is a + singleton. Get the one and only instance of this class with @c + mrw::Args::instance(). It is setup by shifting values into this + class. The order is important, be sure that you shift in @c argc + and @c argv as last parameters. + + Example setup: + + @code + mrw::Args::instance() + < OtherArgs; + + /// @brief get the one and only instance + static Args& instance() throw(mrw::bad_exception) { // singleton + static Args _instance; + return _instance; + } + + /** @brief setup an acceptable option + + Setup an acceptable user option. + + Example: + + @code + mrw::Args::instance() + <second; + } + + /** @brief get an option, given the long option name + @throw mrw::out_of_range if the option does not exist + */ + const Opt& find(const std::string& s) const throw(mrw::exception) { + LongOpts::const_iterator it(_longopts.find(s)); + if (it==_longopts.end()) throw mrw::out_of_range(s); + return *it->second; + } + + /** @brief get all non interpreted options + + All user options that don't fit the defined and interpreted + options. The meaning for this is, that a user may append, + e.g. a list of file names. + */ + const OtherArgs& otherArgs() { + return _otherargs; + } + + /** @brief get the file name of the executable, that's @c argv[0] */ + const std::string& filename() throw(mrw::bad_exception) { + return _filename; + } + + /** @brief print the help text, then exit */ + void help() { + std::cout<<"USAGE: "<_shortname<<" | "<_longname; + for (int i(0); i_param.size(); ++i) + std::cout<<" <"<<(*it)[i]->typestr()<<">"; + if (it->_param.size()>0) std::cout<<" (default: "; + for (int i(0); i_param.size()-1; ++i) + std::cout<<(*it)[i]->printable()<<" "; + if (it->_param.size()>0) + std::cout<<(*it)[it->_param.size()-1]->printable()<<")"; + std::cout<help()<0) + std::cout<<"DESCRIPTION:"<0) _filename = argv[0]; + for (int i(1); isecond->args().size()>=argc) + throw mrw::invalid_argument(arg); + it->second->set(); + for (int j(0), l(it->second->args().size()); jsecond->args().setable(j)) = argv[++i]; + } + } else if (arg.find("-")==0) { // short arguments + // first check all, then set all + for (int j(1), l(arg.size()); jsecond->args().size()>0 && + (j+1!=l || i+it->second->args().size()>=argc)) + throw mrw::invalid_argument(arg); + } + for (int j(1), l(arg.size()); jsecond->set(); + if (j+1==l && it->second->args().size()>0) { + for (int k(0); k < it->second->args().size(); ++k) { + *(it->second->args().setable(k)) = argv[++i]; + } + } + } + } else { + if (arg!="--") _otherargs.push_back(arg); + while (++i Options; + typedef std::map ShortOpts; + typedef std::map LongOpts; + Args(): _argc(-1), _help(0) {} // singleton + Args& operator=(const Args&); // singleton, not implemented + std::string _filename; + Options _options; + ShortOpts _shortopts; + LongOpts _longopts; + OtherArgs _otherargs; + int _argc; + char _help; + std::string _description; + }; + + //@} +} diff --git a/mrw/auto_test.cpp b/mrw/auto_test.cpp index 0822b46..a7e9a8c 100644 --- a/mrw/auto_test.cpp +++ b/mrw/auto_test.cpp @@ -42,5 +42,3 @@ int main() { runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest()); return runner.run() ? 0 : 1; } - -static char* c = new char[100]; diff --git a/mrw/doxyfile.in b/mrw/doxyfile.in index be9dfba..6ad8421 100644 --- a/mrw/doxyfile.in +++ b/mrw/doxyfile.in @@ -96,7 +96,7 @@ HIDE_UNDOC_CLASSES = NO # If set to NO (the default) these declarations will be included in the # documentation. -HIDE_FRIEND_COMPOUNDS = NO +HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. diff --git a/mrw/examples/smartpointer.cpp b/mrw/examples/smartpointer.cpp new file mode 100644 index 0000000..e6bc1e2 --- /dev/null +++ b/mrw/examples/smartpointer.cpp @@ -0,0 +1,25 @@ +#include + +class A { + public: int a; + protected: virtual void fn() {} // dynamic_cast requires a virtual function +}; + +class B: virtual public A { + public: int b; +}; + +int main(int, char**) { + utl::SmartPointer i1 = new int; + *i1 = 13; + utl::SmartPointer i2 = i1; + utl::SmartPointer i3; + if (!i3) i3 = i2; + *i2 = 666; // *i1 is now 666 too + utl::SmartPointer b1 = new B; + utl::SmartPointer b2 = b1; // b1 and b2 point to the same instance + b1->a = 0; // b1->b does not compile + b2->a = 1; + b2->b = 2; + return 0; +} // memory is automatically freed diff --git a/mrw/exception.hpp b/mrw/exception.hpp index f61c7f1..e6bdaeb 100644 --- a/mrw/exception.hpp +++ b/mrw/exception.hpp @@ -2,6 +2,8 @@ #define __MRW_EXCEPTION_HPP__ #include +#include +#include #include namespace mrw { @@ -167,11 +169,140 @@ call of fn0 successful public: exception() throw(std::bad_exception); virtual ~exception() throw(); + virtual const char* what() const throw() { + return std::exception::what(); + } const std::string& stacktrace() const throw(std::bad_exception); private: StackTrace* _stacktrace; }; + /// Replacement for @c std::bad_alloc, but with stack trace + class bad_alloc: + virtual public mrw::exception, virtual public std::bad_alloc { + public: + virtual const char* what() const throw() { + return std::bad_alloc::what(); + } + }; + + /// Replacement for @c std::bad_cast, but with stack trace + class bad_cast: + virtual public mrw::exception, virtual public std::bad_cast { + public: + virtual const char* what() const throw() { + return std::bad_cast::what(); + } + }; + + /// Replacement for @c std::bad_exception, but with stack trace + class bad_exception: + virtual public mrw::exception, virtual public std::bad_exception { + public: + virtual const char* what() const throw() { + return std::bad_exception::what(); + } + }; + + /// Replacement for @c std::bad_typeid, but with stack trace + class bad_typeid: + virtual public mrw::exception, virtual public std::bad_typeid { + public: + virtual const char* what() const throw() { + return std::bad_typeid::what(); + } + }; + + /// Replacement for @c std::logic_error, but with stack trace + class logic_error: + virtual public mrw::exception, virtual public std::logic_error { + public: + logic_error(const std::string& arg): std::logic_error(arg) {} + virtual const char* what() const throw() { + return std::logic_error::what(); + } + }; + + /// Replacement for @c std::domain_error, but with stack trace + class domain_error: + virtual public mrw::exception, virtual public std::domain_error { + public: + domain_error(const std::string& arg): std::domain_error(arg) {} + virtual const char* what() const throw() { + return std::domain_error::what(); + } + }; + + /// Replacement for @c std::invalid_argument, but with stack trace + class invalid_argument: + virtual public mrw::exception, virtual public std::invalid_argument { + public: + invalid_argument(const std::string& arg): std::invalid_argument(arg) {} + virtual const char* what() const throw() { + return std::invalid_argument::what(); + } + }; + + /// Replacement for @c std::length_error, but with stack trace + class length_error: + virtual public mrw::exception, virtual public std::length_error { + public: + length_error(const std::string& arg): std::length_error(arg) {} + virtual const char* what() const throw() { + return std::length_error::what(); + } + }; + + /// Replacement for @c std::out_of_range, but with stack trace + class out_of_range: + virtual public mrw::exception, virtual public std::out_of_range { + public: + out_of_range(const std::string& arg): std::out_of_range(arg) {} + virtual const char* what() const throw() { + return std::out_of_range::what(); + } + }; + + /// Replacement for @c std::runtime_error, but with stack trace + class runtime_error: + virtual public mrw::exception, virtual public std::runtime_error { + public: + runtime_error(const std::string& arg): std::runtime_error(arg) {} + virtual const char* what() const throw() { + return std::runtime_error::what(); + } + }; + + /// Replacement for @c std::overflow_error, but with stack trace + class overflow_error: + virtual public mrw::exception, virtual public std::overflow_error { + public: + overflow_error(const std::string& arg): std::overflow_error(arg) {} + virtual const char* what() const throw() { + return std::overflow_error::what(); + } + }; + + /// Replacement for @c std::range_error, but with stack trace + class range_error: + virtual public mrw::exception, virtual public std::range_error { + public: + range_error(const std::string& arg): std::range_error(arg) {} + virtual const char* what() const throw() { + return std::range_error::what(); + } + }; + + /// Replacement for @c std::underflow_error, but with stack trace + class underflow_error: + virtual public mrw::exception, virtual public std::underflow_error { + public: + underflow_error(const std::string& arg): std::underflow_error(arg) {} + virtual const char* what() const throw() { + return std::underflow_error::what(); + } + }; + //@} } diff --git a/mrw/makefile.am b/mrw/makefile.am index b77b7d9..6e60a46 100644 --- a/mrw/makefile.am +++ b/mrw/makefile.am @@ -12,9 +12,10 @@ EXTRA_DIST = test.dat ${examples_DATA} ${html_DATA} ${pdf_DATA} lib_LTLIBRARIES = libmrw.la libmrwexcstderr.la libmrwexclog4cxx.la libmrw_la_SOURCES = mrw.hpp \ - auto.hpp auto.cpp unistd.hpp \ + auto.hpp auto.cpp unistd.hpp smartpointer.hpp \ stacktrace.hpp stacktrace.cpp exception.hpp exception.cpp \ - exec.hpp exec.cpp + exec.hpp exec.cpp \ + arg.hpp libmrw_la_LDFLAGS = -version-info @MAJOR@:@MINOR@ libmrwexcstderr_la_SOURCES = autostacktracestderr.cpp @@ -25,10 +26,14 @@ libmrwexclog4cxx_la_SOURCES = autostacktracelog4cxx.cpp libmrwexclog4cxx_la_LDFLAGS = -version-info @MAJOR@:@MINOR@ libmrwexclog4cxx_la_LIBADD = -lmrw -check_PROGRAMS = auto_test exec_test stacktrace_test mrwexcstderr_test mrwexclog4cxx_test +check_PROGRAMS = auto_test smartpointer_test exec_test \ + stacktrace_test mrwexcstderr_test mrwexclog4cxx_test auto_test_SOURCES = auto_test.cpp auto_test_CPPFLAGS = -I.. -g3 auto_test_LDADD = -lmrw -lcppunit +smartpointer_test_SOURCES = smartpointer_test.cpp +smartpointer_test_CPPFLAGS = -I.. -g3 +smartpointer_test_LDADD = -lmrw -lcppunit exec_test_SOURCES = exec_test.cpp exec_test_CPPFLAGS = -I.. -g3 exec_test_LDADD = -lmrw -lcppunit diff --git a/mrw/simpletrace.hpp b/mrw/simpletrace.hpp new file mode 100644 index 0000000..f5a4eed --- /dev/null +++ b/mrw/simpletrace.hpp @@ -0,0 +1,72 @@ +#ifndef __MRW_SIMPLETRACE_HPP__ +#define __MRW_SIMPLETRACE_HPP__ +#include +#include +#include + +// GENERIC TRACER FOR TESTS --------------------------------------------------- +// (without file/line) +#ifndef __GNUG__ +#define METHOD(name) mrw::FnTrace fnTrace(this, name) +#define FUNCTION(name) mrw::FnTrace fnTrace(0, name) +#else +#define METHOD mrw::FnTrace fnTrace(this, __FUNCTION__) +#define FUNCTION mrw::FnTrace fnTrace(0, __FUNCTION__) +#endif +#define CALL(name) fnTrace.call(name) +#define TRACE(name) fnTrace.trace(name) +#define TRACE_OFF mrw::FnTrace::off() +#define TRACE_ON mrw::FnTrace::on() +#define NO_TRACE mrw::NoTrace noTrace; + +namespace mrw { + class FnTrace { + public: + FnTrace(const void* addr, const std::string& name): + _addr(addr), _name(name) { + if (_off==0) + std::cout< "<0) --_off; + } + private: + const void* _addr; + const std::string _name; + static unsigned int _level; + static unsigned int _off; + }; + unsigned int FnTrace::_level(0); + unsigned int FnTrace::_off(0); + class NoTrace { + public: + NoTrace() {TRACE_OFF;} + ~NoTrace() {TRACE_ON;} + }; +} +#endif diff --git a/mrw/smartpointer.hpp b/mrw/smartpointer.hpp new file mode 100644 index 0000000..b693702 --- /dev/null +++ b/mrw/smartpointer.hpp @@ -0,0 +1,137 @@ +#ifndef __MRW__SMARTPOINTER_HPP__ +#define __MRW__SMARTPOINTER_HPP__ + +namespace mrw { + + class PointerCounter { + private: + unsigned int _cnt; + public: + PointerCounter(): + _cnt(1) { + } + PointerCounter* incr() { + ++_cnt; + return this; + } + int decr() { + return --_cnt; + } + int get() { + return _cnt; + } + }; + + class SmartPointerParent { + protected: + template + PointerCounter* getCounter(TYPE& sp) { + return sp._cnt; + } + template + typename TYPE::Pointer getPointer(TYPE& sp) { + return sp._ptr; + } + }; + + /** @addtogroup AutoTools */ + //@{ + + /** @brief Smart Pointer Implementation + @pre #include + + This is a smart pointer that can be casted withing the + inheritance of the pointer it is storing. + + A smart pointer is a pointer that is automatically cleaned up + when it is no more referenced. Therefore you only allocate + memory, but you never free it, just as in Java, cleaning up is + done behind the scenes. If you assign a smart pointer to another + smart pointer, both point to the same memory. A counter counts + the number of references to the object and frees it as soon as + the last smart pointer pointing to the same memory has been + destroyed. + + A smart pointer is used just as a normal pointer, except that + you can assign it only to other smart pointers, not to ordinary + pointers, and you don't have to delete the memory allocated. Any + memory assigned to a smart pointer is consumed and mustn't be + used any more. Never allocate a smart pointer with + new, but only the pointer that is stored in the + smart pointer! + + @note memory assigned to a smart pointer is consumed + */ + template class SmartPointer: public SmartPointerParent { + private: + typedef TYPE* Pointer; + PointerCounter* _cnt; + TYPE* _ptr; + private: + void drop() { + if (_cnt && !_cnt->decr()) { + delete _cnt; _cnt=0; + delete _ptr; _ptr=0; + } + } + private: + friend class SmartPointerParent; + friend class SmartPointerTest; + public: + SmartPointer(): + _cnt(0), _ptr(0) { + } + SmartPointer(const SmartPointer& o): + _cnt(o._cnt?o._cnt->incr():0), _ptr(o._ptr) { + } + SmartPointer(TYPE* ptr): + _cnt(ptr ? new PointerCounter : 0), _ptr(ptr) { + } + template SmartPointer(const SmartPointer& o): + _cnt(0), _ptr(dynamic_cast(getPointer(o))) { + if (_ptr) _cnt = getCounter(o)->incr(); + } + ~SmartPointer() { + drop(); + } + SmartPointer& operator=(const SmartPointer& o) { + if (o._ptr==_ptr) return *this; + drop(); + _cnt = o._cnt ? o._cnt->incr() : 0; + _ptr = o._ptr; + return *this; + } + SmartPointer& operator=(TYPE* ptr) { + if (ptr==_ptr) return *this; + drop(); + _cnt = ptr ? new PointerCounter : 0; + _ptr = ptr; + return *this; + } + template + SmartPointer& operator=(const SmartPointer& o) { + if (getPointer(o)==_ptr) return *this; + drop(); + _ptr = dynamic_cast(getPointer(o)); + _cnt = _ptr ? getCounter(o)->incr() : 0; + return *this; + } + TYPE& operator*() { + return *_ptr; + } + const TYPE& operator*() const { + return *_ptr; + } + TYPE* const operator->() { + return _ptr; + } + const TYPE* const operator->() const { + return _ptr; + } + operator bool() { + return _ptr!=0; + } + }; + //@} +} +#endif diff --git a/mrw/stacktrace.hpp b/mrw/stacktrace.hpp index 1b22457..7eb7a4e 100644 --- a/mrw/stacktrace.hpp +++ b/mrw/stacktrace.hpp @@ -53,8 +53,36 @@ namespace mrw { - a system with ELF binaries (LINUX, Solaris, ...) - debug information, compile option @c -g - it must be linked with @c -libery and @c -lbfd + */ + //@{ + + /** @brief store and print a stack trace of the actual position in code + @pre #include + + In the constructor, a stack trace is stored, but not yet + evaluated. Therefore storing a stack trace is relatively + fast. The evaluation is done when the stack trace is printed on + a stream or converted to a string. "Evaluation" means, that the + addresses are mapped to the correspoding symbols, the method + names, sorce file names and line numbers are evaluated. + + @note Method StackTrace::createSymtable must be called exactely + once, before evaluating the first stack trace.Best place is the + first line of the @c main function. + + @note This class requires libbfd an libiberty. Debug information + is required for compiling. You nee the compile option @c -g, or + even better @c -ggdb3. To link, you need @c -lmrw, @c -lbfd and + @c -liberty. - @subsection sttech Technology + @note The stack trace is known to work perfectly on Linux and + Solaris both with GNU gcc compiler. But it should work with the + GNU compiler on all systems, or wherever there is a glibc + library. + + @note Symbol evaluation requires the ELF library and an ELF system. + + @section sttech Technology On GNU glibc based systems (Linux), the stack trace is collected with GNU glibc's function @c backtrace(). On other systems @@ -75,7 +103,7 @@ namespace mrw { executable file name as an argument to @c mrw::StackTrace::createSymtable(). - @subsection stdrawbacks Draw Backs + @section stdrawbacks Draw Backs Unfortunately it is not possible to extract the source file name and line number information if the executable was not compiled @@ -91,34 +119,6 @@ namespace mrw { @todo Add support for alternative symbol evaluation using @c backtrace_symbols. */ - //@{ - - /** @brief store and print a stack trace of the actual position in code - @pre #include - - In the constructor, a stack trace is stored, but not yet - evaluated. Therefore storing a stack trace is relatively - fast. The evaluation is done when the stack trace is printed on - a stream or converted to a string. "Evaluation" means, that the - addresses are mapped to the correspoding symbols, the method - names, sorce file names and line numbers are evaluated. - - @note Method StackTrace::createSymtable must be called exactely - once, before evaluating the first stack trace.Best place is the - first line of the @c main function. - - @note This class requires libbfd an libiberty. Debug information - is required for compiling. You nee the compile option @c -g, or - even better @c -ggdb3. To link, you need @c -lmrw, @c -lbfd and - @c -liberty. - - @note The stack trace is known to work perfectly on Linux and - Solaris both with GNU gcc compiler. But it should work with the - GNU compiler on all systems, or wherever there is a glibc - library. - - @note Symbol evaluation requires the ELF library and an ELF system. - */ class StackTrace { public: //............................................................... typedefs