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;
+}