Topic: Toward partial introspection for C++


Author: hattons@globalsymmetry.com ("Steven T. Hatton")
Date: Wed, 10 Aug 2005 06:02:27 GMT
Raw View
Gene Bushuyev wrote:

> ""Steven T. Hatton"" <hattons@globalsymmetry.com> wrote in message
> news:RJmdndu7n49a8mXfRVn-gQ@speakeasy.net...
>>I often what to use the name of the function, class, and namespace from
>> which an exception is thrown as part of the std::exception::what() return
>> NTBS.  My preference would be to define my exception in such a way that
>> it
>> would automatically detect and include such information.  For example I
>> would like something such as the following:
>
> Are you sure you are using exceptions for the right purposes. You are
> trying to defeat exception stack unwinding mechanism, which suggests me
> that you need to use assert. Where exactly exception is thrown from, class
> and function name, etc. is of no value for the code that catches it and
> performs recovery. But if you are catching bugs in your application then
> assert will do what you want, and will stop right on the spot, and you
> won't need to back trace stack to find the problem.
> Just a thought,
>
> Gene

The traditional use of asserts in C and C++ is not a good way to deal with
errors in many production environments.  That is, they typically result in
a system crash.  They do not need to, however.  They can be used to throw
exceptions.

Exceptions are not necessarily errors, but they are often errors.  There is
nothing in my example that would be required of every exception object.
The suggested mechanism would be made available for those cases where one
does want such information.  Actually, a similar method to that which I
described can be used to significant advantage in stack unwinding.  As the
stack is popped, every calling function can register its own state in a new
exception which has, as a member, the exception it caught.  What I
suggested is based on what is regularly done in Java, but my example
provides more information than is easily available in the analogous Java
context.  That is, unless things have changed, or I was unaware of all the
available features (both are quite possible), there is no way to
automatically capture information about the function call parameters where
an exception originated.

What I posted was really just an off the cuff example, and was not intended
to be overly precise in what a finalized design might entail.  The main
objective was to show what could be done if introspective information were
available, an hint at a means of providing that information.  Other than
the idea of automatically caputring call parameters, none of the ideas I
presented were, AFAIK, original.

--
STH
http://www.kdevelop.org
http://www.suse.com
http://www.mozilla.org

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: realnc@hotmail.com ("Nikos Chantziaras")
Date: Sun, 14 Aug 2005 12:42:07 GMT
Raw View
Steven T. Hatton wrote:
> [...]
> Exceptions are not necessarily errors, but they are often errors.

Exceptions should always be errors.  If an error is critical for a given
context though, that's another question.


> As the stack is popped, every calling function can register its
> own state in a new exception which has, as a member, the exception
> it caught.

What don't you just record the state in the already thrown exception and
then simply re-throw it?


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gcc@integrable-solutions.net (Gabriel Dos Reis)
Date: Sun, 14 Aug 2005 18:44:31 GMT
Raw View
realnc@hotmail.com ("Nikos Chantziaras") writes:

| Steven T. Hatton wrote:
| > [...]
| > Exceptions are not necessarily errors, but they are often errors.
|
| Exceptions should always be errors.

"new T[n]" may throw but then it is an error.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: spam@spamguard.com ("Gene Bushuyev")
Date: Sun, 14 Aug 2005 23:48:32 GMT
Raw View
""Steven T. Hatton"" <hattons@globalsymmetry.com> wrote in message
news:nLGdnd0d2KCiE2TfRVn-tg@speakeasy.net...
> Gene Bushuyev wrote:
>
>> ""Steven T. Hatton"" <hattons@globalsymmetry.com> wrote in message
>> news:RJmdndu7n49a8mXfRVn-gQ@speakeasy.net...
>>>I often what to use the name of the function, class, and namespace from
>>> which an exception is thrown as part of the std::exception::what() return
>>> NTBS.  My preference would be to define my exception in such a way that
>>> it
>>> would automatically detect and include such information.  For example I
>>> would like something such as the following:
>>
>> Are you sure you are using exceptions for the right purposes. You are
>> trying to defeat exception stack unwinding mechanism, which suggests me
>> that you need to use assert. Where exactly exception is thrown from, class
>> and function name, etc. is of no value for the code that catches it and
>> performs recovery. But if you are catching bugs in your application then
>> assert will do what you want, and will stop right on the spot, and you
>> won't need to back trace stack to find the problem.
>> Just a thought,
>>
>> Gene
>
> The traditional use of asserts in C and C++ is not a good way to deal with
> errors in many production environments.  That is, they typically result in
> a system crash.  They do not need to, however.  They can be used to throw
> exceptions.

The most important question you need to ask if you want to decide on error
mechanism is whether you want stack being unwound. I think your question
indicated "no." If the answer is no, then exceptions is the wrong mechanism, as
you're now coping with stack unwinding instead of taking advantage of it.
There is nothing wrong with process termination that failed assertion leads to.
Firstly, it indicates an internal implementation error and should be debugged,
the program wasn't designed and cannot recover from it. Secondly, it doesn't
mean that you cannot log and report the error the way you want it. And you don't
have to use assert() macro if you don't want to, you can choose another way to
terminate the program without stack being unwound, because that's what you want
for debugging of that program bug.
Exceptions on the other hand provide mechanism for recovery or graceful
termination. If the code is designed correctly, the catch site doesn't need to
know where exception originated and it doesn't need to know the stack trace.
It's sufficient to know that the program is still in a valid state, the
automatic objects were destroyed, and it knows what to do with the exception.

>
> Exceptions are not necessarily errors, but they are often errors.  There is
> nothing in my example that would be required of every exception object.
> The suggested mechanism would be made available for those cases where one
> does want such information.  Actually, a similar method to that which I

I don't think there are such legitimate cases. This information is of no use for
a correct exception design, I can only think of possible use when debugging
buggy exception paths.

> described can be used to significant advantage in stack unwinding.  As the
> stack is popped, every calling function can register its own state in a new
> exception which has, as a member, the exception it caught.  What I
> suggested is based on what is regularly done in Java, but my example
> provides more information than is easily available in the analogous Java
> context.  That is, unless things have changed, or I was unaware of all the

There were lots of funny stuff I've seen written about Java designs. I'm no
expert in that language, so I wouldn't comment on whether the language is
wanting or the programmers or something else. It just doesn't translate into a
C++ requirements.

> available features (both are quite possible), there is no way to
> automatically capture information about the function call parameters where
> an exception originated.
>
> What I posted was really just an off the cuff example, and was not intended
> to be overly precise in what a finalized design might entail.  The main
> objective was to show what could be done if introspective information were
> available, an hint at a means of providing that information.  Other than
> the idea of automatically caputring call parameters, none of the ideas I
> presented were, AFAIK, original.

Now, you can with some additional typing capture the stack unwinding if you want
to. Below is an EXCEPTION_GUARD macro that can be added at the beginning of the
functions and can report stack trace. It comes with a price, so it's enabled
only in debug build.

class ExceptionGuard
{
  const char* file;
  const char* func;
  const unsigned    line;
  const bool exception_in_progress;
public:
  ExceptionGuard(const char* file, const char* func, unsigned line)
    : file(file), func(func), line(line),
    exception_in_progress(std::uncaught_exception()) {}
  ~ExceptionGuard() throw()
  {
    if(!exception_in_progress && std::uncaught_exception())
    {
        // log or report exception during stack unwinding
    }
  }
};

#ifdef _DEBUG
#define EXCEPTION_GUARD \
  ExceptionGuard function_exception_guard(__FILE__, __FUNCTION__, __LINE__)
#else
#define NXT_EXCEPTION_GUARD (void)0
#endif

P.S. I'm not against adding introspection to the language. I just don't see any
value of it in exception mechanism.

- gene

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dave@boost-consulting.com (David Abrahams)
Date: Tue, 16 Aug 2005 13:25:28 GMT
Raw View
realnc@hotmail.com ("Nikos Chantziaras") writes:

> Steven T. Hatton wrote:
>> [...]
>> Exceptions are not necessarily errors, but they are often errors.
>
> Exceptions should always be errors.

Exceptions can legitimately be used for things other than errors.  For
example, throwing an exception is often the most efficient way to exit
a long-running performance-intensive process.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: hattons@globalsymmetry.com ("Steven T. Hatton")
Date: Tue, 9 Aug 2005 14:01:30 GMT
Raw View
I often what to use the name of the function, class, and namespace from
which an exception is thrown as part of the std::exception::what() return
NTBS.  My preference would be to define my exception in such a way that it
would automatically detect and include such information.  For example I
would like something such as the following:

namespace exception {
  /*! type_value_t a string representation of a value and its type
   */
  template<typename T>
  struct type_value_t {

    /// @todo specialize per type

    type_value_t(const T& value){}

    const std::string& get_type() const
    { return "type not available."; }

    const std::string& get_value() const
    { return "value not printable"; }
  };

  /*! type_value_t helper function to instaniate type_value_t
   */
  template<typename T>
  type_value_t<T> type_value(cosnt T& value)
  { return type_value_t<T>(value); }

  /*! argument triplet of type, name, value strings
   */
  struct argument {
    argument(const std::string& type
             ,const std::string& name
             ,const std::string& value)
      :m_type (type)
      ,m_name (name)
      ,m_value(value)
    {}

    argument(){}//for containers

    const std::string& get_type () const { return m_type; }
    const std::string& get_name () const { return m_name; }
    const std::string& get_value() const { return m_value; }

    const std::string m_type;
    const std::string m_name;
    const std::string m_value;
  };

  /*! argument_list list of actual parameters of a function call
   */
  typedef std::vector<argument> argument_list;

  /*! call_info describes a function call
   */
  class call_info_if {

    /// @todo derive implementation(s) so user only supplies message.

  public:
    virtual std::string&   get_return_type() const = 0;
    virtual std::string&   get_namespace  () const = 0;
    virtual std::string&   get_class      () const = 0;
    virtual std::string&   get_signature  () const = 0;
    virtual argument_list& get_arguments  () const = 0;
    virtual std::string&   get_message    () const = 0;
  };

  /*! call_info describes a function call
   */
  class call_exception: std::exception {
  public:
    call_exception(const call_info_if& caller){
      m_what += "exception::call_exception\n" // or this->get_class(), etc.
        +  caller.get_return_type() + " "
        +  caller.get_namespace  () + "::"
        +  caller.get_class      () + "::"
        +  caller.get_signature  () + "\n";
      argument_list& args(caller.get_arguments());
      for(size_t i = 0; i < args.size(); ++i)
        m_what += "  ["+args[i].name()+" = "+args[i].value()+"]\n";

      m_what+= caller.get_message();
    }

    virtual const char* what() const throw() { return m_what.c_str(); }

    virtual ~call_exception() throw() {}
  protected:
    std::string m_what;
  };
}

struct call_info: public call_info_if {
  call_info(const std::string& message):m_message(message){}
  /// @todo extract other data from the context
  const std::string m_message;
};

struct foo{
  void bar(int i) {
    throw call_exception(call_info("I threw and exception!"));
  }
};

The real trick here is to get the compiler to extract the information about
the class, namespace, and function call.  Perhaps a context sensitive
preprocessor.
--





Author: spam@smapguard.com ("Gene Bushuyev")
Date: Wed, 10 Aug 2005 00:44:13 GMT
Raw View
""Steven T. Hatton"" <hattons@globalsymmetry.com> wrote in message
news:RJmdndu7n49a8mXfRVn-gQ@speakeasy.net...
>I often what to use the name of the function, class, and namespace from
> which an exception is thrown as part of the std::exception::what() return
> NTBS.  My preference would be to define my exception in such a way that it
> would automatically detect and include such information.  For example I
> would like something such as the following:

Are you sure you are using exceptions for the right purposes. You are trying
to defeat exception stack unwinding mechanism, which suggests me that you
need to use assert. Where exactly exception is thrown from, class and
function name, etc. is of no value for the code that catches it and performs
recovery. But if you are catching bugs in your application then assert will
do what you want, and will stop right on the spot, and you won't need to
back trace stack to find the problem.
Just a thought,

Gene

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]