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 2018/12/27 00:29:00 UTC
svn commit: r1849782 - in /subversion/trunk/subversion/bindings/cxx:
include/svnxx/exception.hpp src/exception.cpp src/init.cpp
src/private/exception-private.hpp tests/test_exceptions.cpp
Author: brane
Date: Thu Dec 27 00:28:59 2018
New Revision: 1849782
URL: http://svn.apache.org/viewvc?rev=1849782&view=rev
Log:
More SVN++ exceptions.
- Renamed 'svn::canceled' back to 'svn::cancelled', grammar
police please note.
- Renamed 'svn::cancel' to 'svn::stop_iteration' (thanks danielsh).
- Changed inhertiance of 'svn::allocation_failed'.
- Avoid allocating a 'wrapped-error' object when creating smart
pointers to svn_error_t.
- More tests etc.
[in subversion/bindings/cxx]
* include/svnxx/exception.hpp: Update docstring.
(allocation_failed): Derive from std::bad_alloc.
(detail::svn_error): Renamed from svn_error_t.
(detail::error_ptr): Updated.
(cancelled): Renamed from 'canceled'. All uses udpated.
(stop_iteration): Renamed from 'cancel'. All uses updated.
* private/exception-private.hpp
(detail::checked_call): Update docstring.
(detail::iteration_stopped): New.
* src/exception.cpp
(detail::svn_error): Define.
(checked_call): Create detail::error_ptr with custom deleters.
(best_message, error::code, error::name, error::compile_message):
Update for different structure of detail::error_ptr.
(stop_iteration::what): Implement.
* src/init.cpp
(allocation_failed_builder): New, used for constructing
svn::allocation_failed which now has no public constructors.
(handle_failed_allocation, create_root_pool): Use it.
* tests/test_exceptions.cpp
(thrown_error, propagate_error,
thtrown_cancelled, propagate_cancelled,
iteration_stopped_cancels, iteration_stopped): New test cases.
(catch_error, catch_cancelled): Updated.
Modified:
subversion/trunk/subversion/bindings/cxx/include/svnxx/exception.hpp
subversion/trunk/subversion/bindings/cxx/src/exception.cpp
subversion/trunk/subversion/bindings/cxx/src/init.cpp
subversion/trunk/subversion/bindings/cxx/src/private/exception-private.hpp
subversion/trunk/subversion/bindings/cxx/tests/test_exceptions.cpp
Modified: subversion/trunk/subversion/bindings/cxx/include/svnxx/exception.hpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/include/svnxx/exception.hpp?rev=1849782&r1=1849781&r2=1849782&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/include/svnxx/exception.hpp (original)
+++ subversion/trunk/subversion/bindings/cxx/include/svnxx/exception.hpp Thu Dec 27 00:28:59 2018
@@ -25,10 +25,10 @@
#define SVNXX_EXCEPTION_HPP
#include <exception>
-#include <stdexcept>
#include <memory>
+#include <new>
+#include <stdexcept>
#include <string>
-#include <utility>
#include <vector>
/**
@@ -58,14 +58,17 @@
* All SVN++ exceptions are ultimately derived from @c std:::exception.
*
* * <em>std::exception</em>
- * + <em>std::runtime_error</em>
+ * + <em>std::bad_alloc</em>
* - apache::subversion::svnxx::allocation_failed\n
* Thrown when memory cannot be allocated from an APR pool
- * + apache::subversion::svnxx::error\n
- * Thrown when an operation failed (see @ref svn_error_t)
- * - apache::subversion::svnxx::canceled\n
- * Thrown when an operation was canceled
- * + apache::subversion::svnxx::cancel\n
+ * + <em>std::runtime_error</em>
+ * - apache::subversion::svnxx::error\n
+ * Thrown when an operation failed (see @ref svn_error_t)
+ * - apache::subversion::svnxx::cancelled\n
+ * Thrown when an operation was cancelled, including by the
+ * user code throwing a @c stop_iteration exception (see
+ * below)
+ * + apache::subversion::svnxx::stop_iteration\n
* Thrown by user callbacks to terminate iteration
*
* @{
@@ -78,23 +81,28 @@ namespace svnxx {
/**
* @brief Exception type that will be thrown when memory allocation fails.
*/
-class allocation_failed : public std::runtime_error
+class allocation_failed : public std::bad_alloc
{
public:
- explicit allocation_failed(const std::string& what_arg)
- : std::runtime_error(what_arg)
- {}
+ virtual ~allocation_failed() noexcept {}
- explicit allocation_failed(const char* what_arg)
- : std::runtime_error(what_arg)
+ virtual const char* what() const noexcept override
+ {
+ return reason;
+ }
+
+protected:
+ explicit allocation_failed(const char* what_arg) noexcept
+ : reason(what_arg)
{}
- virtual ~allocation_failed() {}
+private:
+ const char* reason;
};
namespace detail {
-struct wrapped_error;
-using error_ptr = std::shared_ptr<wrapped_error>;
+struct svn_error;
+using error_ptr = std::shared_ptr<svn_error>;
} // namespace detail
/**
@@ -110,10 +118,10 @@ public:
* Returns the message associated with the top-level error that
* caused the exception.
*
- * @note the returned string is valid onlu as long as the @c error
+ * @note the returned string is valid only as long as this @c error
* object is in scope.
*/
- virtual const char* what() const noexcept;
+ virtual const char* what() const noexcept override;
/**
* Returns the error code associated with the top-level error that
@@ -198,7 +206,7 @@ public:
}
protected:
- error(detail::error_ptr svn_error);
+ error(detail::error_ptr err);
const char* const m_message;
std::vector<message> compile_messages(bool show_traces) const;
};
@@ -207,14 +215,14 @@ protected:
* Thrown instead of Error when the error chain contains a
* @c SVN_ERR_CANCELLED error code.
*/
-class canceled : public error
+class cancelled : public error
{
public:
- virtual ~canceled() {}
+ virtual ~cancelled() {}
protected:
- canceled(detail::error_ptr svn_error)
- : error(svn_error)
+ cancelled(detail::error_ptr err)
+ : error(err)
{}
};
@@ -222,12 +230,12 @@ protected:
* User code should throw this exception from callbacks to
* cancel an operation.
*/
-class cancel : public std::exception
+class stop_iteration : public std::exception
{
public:
- cancel() {}
- virtual ~cancel() {}
- virtual const char* what() const noexcept;
+ stop_iteration() {}
+ virtual ~stop_iteration() {}
+ virtual const char* what() const noexcept override;
};
} // namespace svnxx
Modified: subversion/trunk/subversion/bindings/cxx/src/exception.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/exception.cpp?rev=1849782&r1=1849781&r2=1849782&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/src/exception.cpp (original)
+++ subversion/trunk/subversion/bindings/cxx/src/exception.cpp Thu Dec 27 00:28:59 2018
@@ -37,16 +37,49 @@ namespace apache {
namespace subversion {
namespace svnxx {
+//
+// checked_call
+//
+
namespace detail {
-struct wrapped_error final
+
+struct svn_error final : svn_error_t {};
+
+void checked_call(svn_error_t* const err)
{
- svn_error_t* err;
- wrapped_error(svn_error_t* const& e) : err(e) {}
- ~wrapped_error()
+ if (!err)
+ return;
+
+ struct error_builder final : public error
+ {
+ explicit error_builder(error_ptr err_)
+ : error(err_)
+ {}
+ };
+
+ struct cancelled_builder final : public cancelled
+ {
+ explicit cancelled_builder(error_ptr err_)
+ : cancelled(err_)
+ {}
+ };
+
+ static const auto error_deleter =
+ [](svn_error* ptr) noexcept
+ {
+ svn_error_clear(ptr);
+ };
+
+ auto err_ptr = error_ptr(static_cast<svn_error*>(err), error_deleter);
+ for (auto next = err; next; next = next->child)
{
- svn_error_clear(err);
+ if (next->apr_err == SVN_ERR_CANCELLED
+ || next->apr_err == SVN_ERR_ITER_BREAK)
+ throw cancelled_builder(err_ptr);
}
-};
+ throw error_builder(err_ptr);
+}
+
} // namespace detail
//
@@ -54,20 +87,20 @@ struct wrapped_error final
//
namespace {
-inline const char* best_message(const detail::error_ptr& wrapper)
+inline const char* best_message(const detail::error_ptr& err)
{
- if (!wrapper || !wrapper->err)
+ if (!err)
return "";
const apr_size_t bufsize = 512;
- char* buf = static_cast<char*>(apr_palloc(wrapper->err->pool, bufsize));
- return svn_err_best_message(wrapper->err, buf, bufsize);
+ char* buf = static_cast<char*>(apr_palloc(err->pool, bufsize));
+ return svn_err_best_message(err.get(), buf, bufsize);
}
} // anonymous namspace
-error::error(detail::error_ptr svn_error)
- : detail::error_ptr(svn_error),
- m_message(best_message(svn_error))
+error::error(detail::error_ptr err)
+ : detail::error_ptr(err),
+ m_message(best_message(err))
{}
const char* error::what() const noexcept
@@ -77,18 +110,17 @@ const char* error::what() const noexcept
int error::code() const noexcept
{
- const auto wrapper = get();
- if (!wrapper || !wrapper->err)
+ const auto err = detail::error_ptr::get();
+ if (!err)
return 0;
- return static_cast<int>(wrapper->err->apr_err);
+ return static_cast<int>(err->apr_err);
}
const char* error::name() const noexcept
{
- const auto wrapper = get();
- return svn_error_symbolic_name(!wrapper || !wrapper->err ? 0
- : wrapper->err->apr_err);
+ const auto err = detail::error_ptr::get();
+ return svn_error_symbolic_name(!err ? 0 : err->apr_err);
}
namespace {
@@ -167,7 +199,7 @@ std::vector<error::message> error::compi
{
// Determine the maximum size of the returned list
std::vector<message>::size_type max_length = 0;
- for (svn_error_t* err = get()->err; err; err = err->child)
+ for (svn_error_t* err = detail::error_ptr::get(); err; err = err->child)
{
if (show_traces && err->file)
++max_length; // We will display an error location
@@ -184,7 +216,7 @@ std::vector<error::message> error::compi
empties.reserve(max_length);
apr::pool iterbase;
- for (svn_error_t* err = get()->err; err; err = err->child)
+ for (svn_error_t* err = detail::error_ptr::get(); err; err = err->child)
{
apr::pool::iteration iterpool(iterbase);
@@ -207,47 +239,14 @@ std::string error::message::generic_text
}
//
-// Class cancel
+// Class stop_iteration
//
-const char* cancel::what() const noexcept
+const char* stop_iteration::what() const noexcept
{
- return "svn::cancel";
+ return "svn::stop_iteration";
}
-//
-// checked_call
-//
-
-namespace detail {
-void checked_call(svn_error_t* const err)
-{
- struct error_builder final : public error
- {
- explicit error_builder(error_ptr svn_error)
- : error(svn_error)
- {}
- };
-
- struct canceled_builder final : public canceled
- {
- explicit canceled_builder(error_ptr svn_error)
- : canceled(svn_error)
- {}
- };
-
- if (!err)
- return;
-
- auto wrapper = std::make_shared<wrapped_error>(err);
- for (svn_error_t* next = err; next; next = next->child)
- {
- if (next->apr_err == SVN_ERR_CANCELLED)
- throw canceled_builder(wrapper);
- }
- throw error_builder(wrapper);
-}
-} // namespace detail
} // namespace svnxx
} // namespace subversion
} // namespace apache
Modified: subversion/trunk/subversion/bindings/cxx/src/init.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/init.cpp?rev=1849782&r1=1849781&r2=1849782&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/src/init.cpp (original)
+++ subversion/trunk/subversion/bindings/cxx/src/init.cpp Thu Dec 27 00:28:59 2018
@@ -40,9 +40,16 @@ init::init()
namespace detail {
namespace {
+struct allocation_failed_builder final : public allocation_failed
+{
+ explicit allocation_failed_builder(const char* what_arg) noexcept
+ : allocation_failed(what_arg)
+ {}
+};
+
int handle_failed_allocation(int)
{
- throw allocation_failed("");
+ throw allocation_failed_builder("svn::allocation_failed");
}
apr_pool_t* create_root_pool()
@@ -51,14 +58,14 @@ apr_pool_t* create_root_pool()
apr_allocator_t *allocator = nullptr;
auto status = apr_allocator_create(&allocator);
if (status || !allocator)
- throw allocation_failed("svn++ creating pool allocator");
+ throw allocation_failed_builder("svn++ creating pool allocator");
// Create the root pool.
apr_pool_t* root_pool = nullptr;
status = apr_pool_create_ex(&root_pool, nullptr,
handle_failed_allocation, allocator);
if (status || !root_pool)
- throw allocation_failed("svn++ creating root pool");
+ throw allocation_failed_builder("svn++ creating root pool");
#if APR_POOL_DEBUG
apr_pool_tag(root_pool, "svn++ root pool");
@@ -69,7 +76,7 @@ apr_pool_t* create_root_pool()
apr_thread_mutex_t *mutex = nullptr;
status = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, root_pool);
if (status || !mutex)
- throw allocation_failed("svn++ creating allocator mutex");
+ throw allocation_failed_builder("svn++ creating allocator mutex");
apr_allocator_mutex_set(allocator, mutex);
#endif
Modified: subversion/trunk/subversion/bindings/cxx/src/private/exception-private.hpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/src/private/exception-private.hpp?rev=1849782&r1=1849781&r2=1849782&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/src/private/exception-private.hpp (original)
+++ subversion/trunk/subversion/bindings/cxx/src/private/exception-private.hpp Thu Dec 27 00:28:59 2018
@@ -37,10 +37,18 @@ namespace detail {
/**
* Given a @a err, if it is not @c nullptr, convert it to a and throw an
- * svn::error or svn::canceled exception; otherwise do nothing.
+ * svn::error or svn::cancelled exception; otherwise do nothing.
*/
void checked_call(svn_error_t* const err);
+/**
+ * Call this when a callback throws svn::stop_iteration to return
+ * an appropriate error.
+ */
+inline svn_error_t* iteration_etopped()
+{
+ return svn_error_create(SVN_ERR_ITER_BREAK, nullptr, nullptr);
+}
} // namespace detail
} // namespace svnxx
} // namespace subversion
Modified: subversion/trunk/subversion/bindings/cxx/tests/test_exceptions.cpp
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/bindings/cxx/tests/test_exceptions.cpp?rev=1849782&r1=1849781&r2=1849782&view=diff
==============================================================================
--- subversion/trunk/subversion/bindings/cxx/tests/test_exceptions.cpp (original)
+++ subversion/trunk/subversion/bindings/cxx/tests/test_exceptions.cpp Thu Dec 27 00:28:59 2018
@@ -50,6 +50,13 @@ svn_error_t* make_error_test_error()
}
} // anonymous namespace
+BOOST_AUTO_TEST_CASE(thrown_error)
+{
+ BOOST_CHECK_THROW(
+ svn::detail::checked_call(make_error_test_error()),
+ svn::error);
+}
+
BOOST_AUTO_TEST_CASE(catch_error)
{
BOOST_CHECK_THROW(
@@ -62,6 +69,10 @@ BOOST_AUTO_TEST_CASE(catch_error)
}
catch (const svn::error& err)
{
+ BOOST_TEST(err.code() == SVN_ERR_UNSUPPORTED_FEATURE);
+ BOOST_TEST(err.name() == "SVN_ERR_UNSUPPORTED_FEATURE");
+ BOOST_TEST(err.what() == "Trying to use an unsupported feature");
+
auto ml = err.messages();
BOOST_TEST(ml.size() == 3);
BOOST_TEST(ml[0].code() == SVN_ERR_UNSUPPORTED_FEATURE);
@@ -93,6 +104,21 @@ BOOST_AUTO_TEST_CASE(catch_error)
}
}
+BOOST_AUTO_TEST_CASE(propagate_error)
+{
+ std::exception_ptr xptr;
+ try
+ {
+ svn::detail::checked_call(make_error_test_error());
+ }
+ catch (...)
+ {
+ xptr = std::current_exception();
+ }
+ BOOST_REQUIRE(xptr);
+ BOOST_CHECK_THROW(std::rethrow_exception(xptr), svn::error);
+}
+
namespace {
svn_error_t* make_cancel_test_error()
@@ -108,18 +134,25 @@ svn_error_t* make_cancel_test_error()
}
} // anonymous namespace
-BOOST_AUTO_TEST_CASE(catch_canceled)
+BOOST_AUTO_TEST_CASE(thtrown_cancelled)
{
BOOST_CHECK_THROW(
svn::detail::checked_call(make_cancel_test_error()),
- svn::canceled);
+ svn::cancelled);
+}
+BOOST_AUTO_TEST_CASE(catch_cancelled)
+{
try
{
svn::detail::checked_call(make_cancel_test_error());
}
- catch (const svn::canceled& err)
+ catch (const svn::cancelled& err)
{
+ BOOST_TEST(err.code() == SVN_ERR_BASE);
+ BOOST_TEST(err.name() == "SVN_ERR_BASE");
+ BOOST_TEST(err.what() == "wrapper message");
+
auto ml = err.messages();
BOOST_TEST(ml.size() == 3);
BOOST_TEST(ml[0].code() == SVN_ERR_BASE);
@@ -151,4 +184,46 @@ BOOST_AUTO_TEST_CASE(catch_canceled)
}
}
+BOOST_AUTO_TEST_CASE(propagate_cancelled)
+{
+ std::exception_ptr xptr;
+ try
+ {
+ svn::detail::checked_call(make_cancel_test_error());
+ }
+ catch (...)
+ {
+ xptr = std::current_exception();
+ }
+ BOOST_REQUIRE(xptr);
+ BOOST_CHECK_THROW(std::rethrow_exception(xptr), svn::cancelled);
+}
+
+BOOST_AUTO_TEST_CASE(iteration_stopped_cancels)
+{
+ BOOST_CHECK_THROW(
+ svn::detail::checked_call(svn::detail::iteration_etopped()),
+ svn::cancelled);
+}
+
+BOOST_AUTO_TEST_CASE(iteration_stopped)
+{
+ try
+ {
+ svn::detail::checked_call(svn::detail::iteration_etopped());
+ }
+ catch (const svn::cancelled& err)
+ {
+ BOOST_TEST(err.code() == SVN_ERR_ITER_BREAK);
+ BOOST_TEST(err.name() == "SVN_ERR_ITER_BREAK");
+ BOOST_TEST(err.what() == "Iteration terminated before completion");
+
+ auto ml = err.messages();
+ BOOST_TEST(ml.size() == 1);
+ BOOST_TEST(ml[0].code() == SVN_ERR_ITER_BREAK);
+ BOOST_TEST(ml[0].name() == "SVN_ERR_ITER_BREAK");
+ BOOST_TEST(ml[0].text() == "Iteration terminated before completion");
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END();