You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2012/12/18 02:36:57 UTC

svn commit: r1423246 - in /subversion/trunk: build.conf subversion/bindings/cxxhl/include/svncxxhl/exception.hpp subversion/bindings/cxxhl/src/exception.cpp subversion/bindings/cxxhl/tests/ subversion/bindings/cxxhl/tests/test_exception.cpp

Author: brane
Date: Tue Dec 18 01:36:56 2012
New Revision: 1423246

URL: http://svn.apache.org/viewvc?rev=1423246&view=rev
Log:
In cxxhl bindings, improve translation of errors to exceptions and add
generation of error messages from said exceptions. Also add a simple
test for same.

* build.conf (cxxhl-tests): New build target.
* subversion/bindings/cxxhl/include/svncxxhl/exception.hpp: Extend
   the error class and add more documentation.
* subversion/bindings/cxxhl/src/exception.cpp: Implement the improvements.
   Make the error_description helper a bit more thread-safe.
* subversion/bindings/cxxhl/tests/test_exception.cpp: Simple test for
   error-to-exception generation and message translation.

Added:
    subversion/trunk/subversion/bindings/cxxhl/tests/
    subversion/trunk/subversion/bindings/cxxhl/tests/test_exception.cpp
Modified:
    subversion/trunk/build.conf
    subversion/trunk/subversion/bindings/cxxhl/include/svncxxhl/exception.hpp
    subversion/trunk/subversion/bindings/cxxhl/src/exception.cpp

Modified: subversion/trunk/build.conf
URL: http://svn.apache.org/viewvc/subversion/trunk/build.conf?rev=1423246&r1=1423245&r2=1423246&view=diff
==============================================================================
--- subversion/trunk/build.conf (original)
+++ subversion/trunk/build.conf Tue Dec 18 01:36:56 2012
@@ -642,6 +642,16 @@ msvc-static = yes
 compile-cmd = $(COMPILE_CXXHL_CXX)
 link-cmd = $(LINK_CXX_LIB)
 
+[cxxhl-tests]
+description = Unit tests for Subversion C++ HighLevel bindings
+type = exe
+path = subversion/bindings/cxxhl
+libs = libsvncxxhl libsvn_subr
+sources = tests/*.cpp
+install = tests
+compile-cmd = $(COMPILE_CXXHL_CXX)
+link-cmd = $(LINK_CXX)
+
 # ----------------------------------------------------------------------------
 #
 # TESTING TARGETS

Modified: subversion/trunk/subversion/bindings/cxxhl/include/svncxxhl/exception.hpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxxhl/include/svncxxhl/exception.hpp?rev=1423246&r1=1423245&r2=1423246&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxxhl/include/svncxxhl/exception.hpp (original)
+++ subversion/trunk/subversion/bindings/cxxhl/include/svncxxhl/exception.hpp Tue Dec 18 01:36:56 2012
@@ -29,16 +29,23 @@
 #define SVN_CXXHL_EXCEPTION_HPP
 
 #include <exception>
+#include <string>
+#include <utility>
+#include <vector>
 
 #include "svncxxhl/_compat.hpp"
 
+// Forward declaration of implementation-specific structure
+struct svn_error_t;
+
 namespace subversion {
 namespace cxxhl {
 
 namespace compat {} // Announce the compat namespace for shared_ptr lookup
 
 namespace detail {
-struct error_description;
+// Forward declaration of implementation-specific structure
+class error_description;
 } // namespace detail
 
 namespace version_1_9_dev {
@@ -48,33 +55,74 @@ class error : public std::exception
 public:
   typedef compat::shared_ptr<error> shared_ptr;
 
-  error(const char* description, int errno);
-  error(const char* description, int errno, shared_ptr nested);
+  error(const char* description, int error_code);
+  error(const char* description, int error_code, shared_ptr nested_error);
 
   error(const error& that) throw();
   error& operator=(const error& that) throw();
   virtual ~error() throw();
 
+  /**
+   * Returns the error code associated with the exception.
+   */
   virtual int code() const throw() { return m_errno; }
-  virtual const char* what() const throw();
+
+  /**
+   * Returns a shared pointer to the nested exception object, if any.
+   */
   virtual shared_ptr nested() const throw() { return m_nested; }
 
-private:
-  int m_errno;                ///< The (SVN or APR) error code
-  shared_ptr m_nested;        ///< Optional pointer to nessted error
+  /// Returns the message associated with this exception object.
+  virtual const char* what() const throw();
 
-  /// Error message; will be @c NULL if this is a trace link.
-  detail::error_description* m_description;
+  /**
+   * Error message description.
+   *
+   * The first element of this pair is the error code, the second the
+   * associated error message. If the error code is 0, the message
+   * describes the location in the source code where the error was
+   * generated from.
+   */
+  typedef std::pair<int, std::string> message;
 
   /**
-   * The location of the error in @a m_loc_file at @a m_loc_line.
+   * The list of messages associated with an error.
+   */
+  typedef std::vector<message> message_list;
+
+  /**
+   * Returns the complete list of error messages, including those from
+   * nested exceptions.
+   */
+  virtual message_list messages() const
+    {
+      return compile_messages(false);
+    }
+
+  /**
+   * Like error::messages(), but includes debugging traceback.
    *
-   * @a m_loc_file will be @c NULL if the location is not available
-   * (i.e., if the wrapped Subversion library was not compiled in
-   * maintaner mode.
+   * @note
+   * Traceback is only available if the Subversion libraries were
+   * compiled with tracing enabled.
    */
-  const char* m_loc_file;
-  int m_loc_line;
+  virtual message_list traced_messages() const
+    {
+      return compile_messages(true);
+    }
+
+public:
+  /** Used internally by the implementation. */
+  static void throw_svn_error(svn_error_t*);
+
+private:
+  error(int error_code, detail::error_description* description) throw();
+  std::vector<message> compile_messages(bool show_traces) const;
+
+  int m_errno;                /**< The (SVN or APR) error code. */
+  shared_ptr m_nested;        /**< Optional pointer to nessted error. */
+  /** Error description and trace location information. */
+  detail::error_description* m_description;
 };
 
 class canceled : public error {};

Modified: subversion/trunk/subversion/bindings/cxxhl/src/exception.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxxhl/src/exception.cpp?rev=1423246&r1=1423245&r2=1423246&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxxhl/src/exception.cpp (original)
+++ subversion/trunk/subversion/bindings/cxxhl/src/exception.cpp Tue Dec 18 01:36:56 2012
@@ -21,14 +21,19 @@
  * @endcopyright
  */
 
-#include <csignal>
+#include <algorithm>
 #include <cstddef>
 #include <cstring>
 #include <new>
+#include <sstream>
 
 #include "svncxxhl/exception.hpp"
 
 #include "svn_error.h"
+#include "svn_utf.h"
+#include "private/svn_atomic.h"
+#include "private/svn_error_private.h"
+#include "svn_private_config.h"
 #undef TRUE
 #undef FALSE
 
@@ -37,28 +42,72 @@ namespace cxxhl {
 
 namespace detail {
 
-struct error_description
+class error_description
 {
-  volatile std::sig_atomic_t m_refcount;
-  char m_message[1];
+public:
+  static error_description* create(const char* message,
+                                   const char *loc_file, long loc_line,
+                                   bool trace_link)
+    {
+      bool empty_message = (message == NULL);
+      const std::size_t length = (empty_message ? 0 : std::strlen(message));
+      void *memblock = ::operator new(length + sizeof(error_description));
+
+      error_description* description = new(memblock) error_description(
+          loc_file, loc_line, trace_link, empty_message);
+      if (length)
+        std::memcpy(description->m_message, message, length);
+      description->m_message[length] = 0;
+      return description;
+    }
 
-  static error_description* create(const char* message) throw(std::bad_alloc)
+  static error_description* create(const char* message)
     {
-      const std::size_t len = std::strlen(message);
-      void *memblock = ::operator new(len + sizeof(error_description));
+      return create(message, NULL, 0, false);
+    }
 
-      error_description* err = new(memblock) error_description;
-      std::memcpy(err->m_message, message, len);
-      err->m_message[len] = 0;
-      err->m_refcount = 0;
-      return err;
+  error_description* reference() throw()
+    {
+      if (this)
+        svn_atomic_inc(&m_refcount);
+      return this;
     }
 
-  static void destroy(error_description* message) throw()
+  error_description* dereference() throw()
     {
-      // TODO: DEBUG assert(message->m_refcount == 0)
-      ::operator delete(message, std::nothrow);
+      if (this && 0 == svn_atomic_dec(&m_refcount))
+        {
+          this->~error_description();
+          ::operator delete(this, std::nothrow);
+          return NULL;
+        }
+      return this;
     }
+
+  const char* what() const throw() { return (m_empty ? NULL : m_message); }
+  const char* file() const throw() { return m_loc_file; }
+  long line() const throw() { return m_loc_line; }
+  bool trace() const throw() { return m_trace; }
+
+private:
+  error_description(const char *loc_file, long loc_line,
+                    bool trace_link, bool empty_message) throw()
+    : m_loc_file(loc_file),
+      m_loc_line(loc_line),
+      m_trace(trace_link),
+      m_empty(empty_message),
+      m_refcount(0)
+    {}
+
+  ~error_description() throw() {}
+
+  const char* m_loc_file;
+  long m_loc_line;
+  bool m_trace;
+  bool m_empty;
+
+  volatile svn_atomic_t m_refcount;
+  char m_message[1];
 };
 
 } // namespace detail
@@ -68,27 +117,21 @@ namespace version_1_9_dev {
 
 error::error(const char* description, int error_code)
   : m_errno(error_code),
-    m_description(detail::error_description::create(description))
-{
-  ++m_description->m_refcount;
-}
+    m_description(detail::error_description::create(description)->reference())
+{}
 
 error::error(const char* description, int error_code,
              error::shared_ptr nested_error)
   : m_errno(error_code),
     m_nested(nested_error),
-    m_description(detail::error_description::create(description))
-{
-  ++m_description->m_refcount;
-}
+    m_description(detail::error_description::create(description)->reference())
+{}
 
 error::error(const error& that) throw()
   : m_errno(that.m_errno),
     m_nested(that.m_nested),
-    m_description(that.m_description)
-{
-  ++m_description->m_refcount;
-}
+    m_description(that.m_description->reference())
+{}
 
 error& error::operator=(const error& that) throw()
 {
@@ -104,13 +147,160 @@ error& error::operator=(const error& tha
 
 error::~error() throw()
 {
-  if (!--m_description->m_refcount)
-    detail::error_description::destroy(m_description);
+  m_description->dereference();
 }
 
 const char* error::what() const throw()
 {
-  return m_description->m_message;
+  return m_description->what();
+}
+
+error::error(int error_code, detail::error_description* description) throw()
+  : m_errno(error_code),
+    m_description(description)
+{}
+
+void error::throw_svn_error(svn_error_t* err)
+{
+  detail::error_description* description = NULL;
+  try
+    {
+      // Be very careful when creating the error descriptions, so that
+      // the exception unwinder can free them if an allocation fails.
+      // The private constructor does not increment the refcount
+      // precisely for this reason.
+      description = detail::error_description::create(
+          err->message, err->file, err->line,
+          svn_error__is_tracing_link(err));
+      description->reference();
+      error converted = error(err->apr_err, description);
+      description = NULL;
+
+      svn_error_t *prev = err;
+      error* current = &converted;
+      for (err = err->child; err; prev = err, err = err->child)
+        {
+          svn_error_clear(prev);
+          description = detail::error_description::create(
+              err->message, err->file, err->line,
+              svn_error__is_tracing_link(err));
+          description->reference();
+          current->m_nested.reset(new error(err->apr_err, description));
+          description = NULL;
+          current = current->m_nested.get();
+        }
+      svn_error_clear(prev);
+      throw converted;
+    }
+  catch (...)
+    {
+      description->dereference();
+      throw;
+    }
+}
+
+
+namespace {
+void handle_one_error(error::message_list& ml, bool show_traces,
+                      int error_code, detail::error_description* descr,
+                      apr_pool_t* pool)
+{
+  if (show_traces && descr->file())
+    {
+      const char* file_utf8 = NULL;
+      svn_error_t* err =
+        svn_utf_cstring_to_utf8(&file_utf8, descr->file(), pool);
+      if (err)
+        {
+          svn_error_clear(err);
+          file_utf8 = NULL;
+        }
+      std::ostringstream buffer;
+      if (file_utf8)
+        buffer << file_utf8 << ':' << descr->line();
+      else
+        buffer << "svn:<undefined>";
+      buffer << ": (apr_err=" << error_code << ')';
+      ml.push_back(error::message(0, buffer.str()));
+    }
+
+  if (descr->trace())
+    return;
+
+  const char *description = descr->what();
+  if (!description)
+    {
+      char errorbuf[512];
+
+      // Is this a Subversion-specific error code?
+      if (error_code > APR_OS_START_USEERR
+          && error_code <= APR_OS_START_CANONERR)
+        description = svn_strerror(error_code, errorbuf, sizeof(errorbuf));
+      // Otherwise, this must be an APR error code.
+      else
+        {
+          svn_error_t* err = svn_utf_cstring_to_utf8(
+              &description,
+              apr_strerror(error_code, errorbuf, sizeof(errorbuf)),
+              pool);
+          if (err)
+            {
+              svn_error_clear(err);
+              description = _("Can't recode error string from APR");
+            }
+        }
+    }
+  ml.push_back(error::message(error_code, std::string(description)));
+}
+} // anonymous namespace
+
+error::message_list error::compile_messages(bool show_traces) const
+{
+  // Determine the maximum size of the returned list
+  message_list::size_type max_length = 0;
+  for (const error* err = this; err; err = err->m_nested.get())
+    {
+      if (show_traces && m_description->file())
+        ++max_length;                   // We will display an error location
+      if (!m_description->trace())
+        ++max_length;                   // Traces do not emit a message line
+    }
+  message_list ml;
+  ml.reserve(max_length);
+
+  // This vector holds a list of all error codes that we've printed
+  // the generic description for.  See svn_handle_error2 for details.
+  std::vector<int> empties;
+  empties.reserve(max_length);
+
+  apr_pool_t* pool = NULL;
+  apr_pool_create(&pool, NULL);
+  try
+    {
+      for (const error* err = this; err; err = err->m_nested.get())
+        {
+          if (!err->m_description->what())
+            {
+              // Non-specific messages are printed only once.
+              std::vector<int>::iterator it = std::find(
+                  empties.begin(), empties.end(), err->m_errno);
+              if (it != empties.end())
+                continue;
+              empties.push_back(err->m_errno);
+            }
+          handle_one_error(ml, show_traces,
+                           err->m_errno, err->m_description,
+                           pool);
+        }
+    }
+  catch (...)
+    {
+      apr_pool_destroy(pool);
+      throw;
+    }
+
+  apr_pool_destroy(pool);
+  return ml;
 }
 
 } // namespace version_1_9_dev

Added: subversion/trunk/subversion/bindings/cxxhl/tests/test_exception.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxxhl/tests/test_exception.cpp?rev=1423246&view=auto
==============================================================================
--- subversion/trunk/subversion/bindings/cxxhl/tests/test_exception.cpp (added)
+++ subversion/trunk/subversion/bindings/cxxhl/tests/test_exception.cpp Tue Dec 18 01:36:56 2012
@@ -0,0 +1,52 @@
+#include <algorithm>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+
+#include "svncxxhl.hpp"
+
+#include <apr.h>
+#include "svn_error.h"
+
+namespace {
+void trace(const svn::error::message& msg)
+{
+  std::cout << "    ";
+  if (msg.first)
+    std::cout << "test_exception: E"
+              << std::setw(6) << std::setfill('0') << std::right
+              << msg.first << ':' << ' ';
+  std::cout << msg.second << std::endl;
+}
+} // anonymous namespace
+
+int main()
+{
+  apr_initialize();
+
+  try
+    {
+      svn_error_t* err;
+      err = svn_error_create(SVN_ERR_TEST_FAILED, NULL, "original message");
+      err = svn_error_create(SVN_ERR_BASE, err, "wrapper message");
+      err = svn_error_create(SVN_ERR_CANCELLED, err, NULL);
+      err = svn_error_create(SVN_ERR_CANCELLED, err, NULL);
+      err = svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, err, NULL);
+      err = svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, err, NULL);
+      err = svn_error_create(SVN_ERR_CANCELLED, err, NULL);
+      err = svn_error_trace(err);
+      svn::error::throw_svn_error(err);
+    }
+  catch (const svn::error& err)
+    {
+      typedef svn::error::message_list message_list;
+      std::cout << "Traced Messages:" << std::endl;
+      message_list ml = err.traced_messages();
+      std::for_each(ml.begin(), ml.end(), trace);
+      std::cout << "Just Messages:" << std::endl;
+      ml = err.messages();
+      std::for_each(ml.begin(), ml.end(), trace);
+    }
+
+  return 0;
+}