diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c55d4d..fadb168 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ configuration_setting(NAME FUTURE OPTIONS STD STD_EXPERIMENTAL + BOOST CUSTOM ) @@ -88,9 +89,20 @@ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") ################################################################################ find_library(zookeeper_LIBRARIES zookeeper_mt) +set(ZKPP_LIB_DEPENDENCIES ${ZKPP_LIB_DEPENDENCIES} ${zookeeper_LIBRARIES}) include_directories("${PROJECT_SOURCE_DIR}/src") +if (ZKPP_BUILD_SETTING_FUTURE STREQUAL "BOOST") + find_package(Boost + 1.52.0 + REQUIRED + thread) + set(ZKPP_LIB_DEPENDENCIES ${ZKPP_LIB_DEPENDENCIES} ${Boost_LIBRARIES}) +endif() + + + ################################################################################ # GTest # ################################################################################ @@ -146,8 +158,8 @@ build_module(NAME zkpp PATH src/zk NO_RECURSE LINK_LIBRARIES - ${zookeeper_LIBRARIES} - ) + ${ZKPP_LIB_DEPENDENCIES} + ) build_module(NAME zkpp-server PATH src/zk/server diff --git a/README.md b/README.md index cfdb76f..670b4ae 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Ultimately, the usage looks like this (assuming you have a ZooKeeper server runn /** All result types are printable for debugging purposes. **/ template - void print_thing(const std::future& result) + void print_thing(const zk::future& result) { try { diff --git a/src/zk/client.cpp b/src/zk/client.cpp index 83f102d..58943f7 100644 --- a/src/zk/client.cpp +++ b/src/zk/client.cpp @@ -2,6 +2,7 @@ #include "acl.hpp" #include "connection.hpp" #include "multi.hpp" +#include "exceptions.hpp" #include #include @@ -45,16 +46,16 @@ future client::connect(connection_params conn_params) else { // TODO: Test if future::then can be relied on and use that instead of std::async - return std::async + return zk::async ( - std::launch::async, + zk::launch::async, [state_change_fut = std::move(state_change_fut), conn = std::move(conn)] () mutable -> client { state s(state_change_fut.get()); if (s == state::connected) return client(conn); else - throw std::runtime_error(std::string("Unexpected state: ") + to_string(s)); + zk::throw_exception(std::runtime_error(std::string("Unexpected state: ") + to_string(s))); } ); } @@ -62,7 +63,7 @@ future client::connect(connection_params conn_params) catch (...) { promise p; - p.set_exception(std::current_exception()); + p.set_exception(zk::current_exception()); return p.get_future(); } } diff --git a/src/zk/connection.cpp b/src/zk/connection.cpp index ef474ef..717b265 100644 --- a/src/zk/connection.cpp +++ b/src/zk/connection.cpp @@ -2,6 +2,7 @@ #include "connection_zk.hpp" #include "error.hpp" #include "types.hpp" +#include "exceptions.hpp" #include #include @@ -47,7 +48,7 @@ void connection::on_session_event(zk::state new_state) auto ex = new_state == zk::state::expired_session ? get_exception_ptr_of(error_code::session_expired) : new_state == zk::state::authentication_failed ? get_exception_ptr_of(error_code::authentication_failed) - : std::exception_ptr(); + : zk::exception_ptr(); for (auto& p : l_state_change_promises) { @@ -105,7 +106,7 @@ static connection_params::host_list extract_host_list(string_view src) static bool extract_bool(string_view key, string_view val) { if (val.empty()) - throw std::invalid_argument(std::string("Key ") + std::string(key) + " has blank value"); + zk::throw_exception(std::invalid_argument(std::string("Key ") + std::string(key) + " has blank value")); switch (val[0]) { @@ -118,20 +119,20 @@ static bool extract_bool(string_view key, string_view val) case 'F': return false; default: - throw std::invalid_argument(std::string("Invalid value for ") + std::string(key) + std::string(" \"") + zk::throw_exception(std::invalid_argument(std::string("Invalid value for ") + std::string(key) + std::string(" \"") + std::string(val) + "\" -- expected a boolean" - ); + )); } } static std::chrono::milliseconds extract_millis(string_view key, string_view val) { if (val.empty()) - throw std::invalid_argument(std::string("Key ") + std::string(key) + " has blank value"); + zk::throw_exception(std::invalid_argument(std::string("Key ") + std::string(key) + " has blank value")); if (val[0] == 'P') { - throw std::invalid_argument("ISO 8601 duration is not supported (yet)."); + zk::throw_exception(std::invalid_argument("ISO 8601 duration is not supported (yet).")); } else { @@ -162,9 +163,9 @@ static void extract_advanced_options(string_view src, connection_params& out) { auto eq_it = std::find(qp_part.begin(), qp_part.end(), '='); if (eq_it == qp_part.end()) - throw std::invalid_argument("Invalid connection string -- query string must be specified as " + zk::throw_exception(std::invalid_argument("Invalid connection string -- query string must be specified as " "\"key1=value1&key2=value2...\"" - ); + )); auto key = qp_part.substr(0, std::distance(qp_part.begin(), eq_it)); auto val = qp_part.substr(std::distance(qp_part.begin(), eq_it) + 1); @@ -180,7 +181,7 @@ static void extract_advanced_options(string_view src, connection_params& out) }); if (!invalid_keys_msg.empty()) - throw std::invalid_argument(std::move(invalid_keys_msg)); + zk::throw_exception(std::invalid_argument(std::move(invalid_keys_msg))); } connection_params connection_params::parse(string_view conn_string) @@ -195,9 +196,9 @@ connection_params connection_params::parse(string_view conn_string) std::cmatch match; if (!std::regex_match(conn_string.begin(), conn_string.end(), match, expr)) - throw std::invalid_argument(std::string("Invalid connection string (") + std::string(conn_string) + zk::throw_exception(std::invalid_argument(std::string("Invalid connection string (") + std::string(conn_string) + " -- format is \"schema://[auth@]${host_addrs}/[path][?options]\"" - ); + )); connection_params out; out.connection_schema() = match[schema_idx].str(); diff --git a/src/zk/connection_zk.cpp b/src/zk/connection_zk.cpp index 9feeb95..c4e9dbc 100644 --- a/src/zk/connection_zk.cpp +++ b/src/zk/connection_zk.cpp @@ -1,4 +1,5 @@ #include "connection_zk.hpp" +#include "exceptions.hpp" #include #include @@ -168,7 +169,7 @@ connection_zk::connection_zk(const connection_params& params) : _handle(nullptr) { if (params.connection_schema() != "zk") - throw std::invalid_argument(std::string("Invalid connection string \"") + to_string(params) + "\""); + zk::throw_exception(std::invalid_argument(std::string("Invalid connection string \"") + to_string(params) + "\"")); auto conn_string = [&] () { @@ -254,7 +255,7 @@ class connection_zk::basic_watcher : watcher::deliver_event(std::move(ev)); } - void deliver_data(optional data, std::exception_ptr ex_ptr) + void deliver_data(optional data, zk::exception_ptr ex_ptr) { if (!_data_delivered.exchange(true, std::memory_order_relaxed)) { @@ -378,7 +379,7 @@ class connection_zk::data_watcher : self.deliver_data(watch_result(get_result(buffer(data, data + data_sz), stat_from_raw(*pstat)), self.get_event_future() ), - std::exception_ptr() + zk::exception_ptr() ); } else @@ -431,7 +432,7 @@ future connection_zk::get_children(string_view path) } catch (...) { - prom->set_exception(std::current_exception()); + prom->set_exception(zk::current_exception()); } }; @@ -482,12 +483,12 @@ class connection_zk::child_watcher : ), self.get_event_future() ), - std::exception_ptr() + zk::exception_ptr() ); } catch (...) { - self.deliver_data(nullopt, std::current_exception()); + self.deliver_data(nullopt, zk::current_exception()); } } @@ -561,13 +562,13 @@ class connection_zk::exists_watcher : if (rc == error_code::ok) { self.deliver_data(watch_exists_result(exists_result(stat_from_raw(*stat_in)), self.get_event_future()), - std::exception_ptr() + zk::exception_ptr() ); } else if (rc == error_code::no_entry) { self.deliver_data(watch_exists_result(exists_result(nullopt), self.get_event_future()), - std::exception_ptr() + zk::exception_ptr() ); } else @@ -853,12 +854,12 @@ struct connection_zk_commit_completer auto iter = std::partition_point(raw_results.begin(), raw_results.end(), [] (auto res) { return res.err == 0; } ); - throw transaction_failed(rc, std::size_t(std::distance(raw_results.begin(), iter))); + zk::throw_exception(transaction_failed(rc, std::size_t(std::distance(raw_results.begin(), iter)))); } } catch (...) { - prom.set_exception(std::current_exception()); + prom.set_exception(zk::current_exception()); } } }; @@ -944,9 +945,9 @@ future connection_zk::commit(multi_op&& txn_in) default: { using std::to_string; - throw std::invalid_argument("Invalid op_type at index=" + to_string(idx) + ": " + zk::throw_exception(std::invalid_argument("Invalid op_type at index=" + to_string(idx) + ": " + to_string(src_op.type()) - ); + )); } } } @@ -972,7 +973,7 @@ future connection_zk::commit(multi_op&& txn_in) } catch (...) { - pcompleter->prom.set_exception(std::current_exception()); + pcompleter->prom.set_exception(zk::current_exception()); return pcompleter->prom.get_future(); } } @@ -990,7 +991,7 @@ future connection_zk::load_fence() prom->set_exception(get_exception_ptr_of(rc)); }; - auto ppromise = std::make_unique>(); + auto ppromise = std::make_unique>(); auto rc = error_code_from_raw(::zoo_async(_handle, "/", callback, ppromise.get())); if (rc == error_code::ok) { diff --git a/src/zk/error.cpp b/src/zk/error.cpp index 713ba08..223e07b 100644 --- a/src/zk/error.cpp +++ b/src/zk/error.cpp @@ -1,4 +1,5 @@ #include "error.hpp" +#include "exceptions.hpp" #include #include @@ -32,30 +33,30 @@ void throw_error(error_code code) { switch (code) { - case error_code::connection_loss: throw connection_loss(); - case error_code::marshalling_error: throw marshalling_error(); - case error_code::not_implemented: throw not_implemented("unspecified"); - case error_code::invalid_arguments: throw invalid_arguments(); - case error_code::new_configuration_no_quorum: throw new_configuration_no_quorum(); - case error_code::reconfiguration_in_progress: throw reconfiguration_in_progress(); - case error_code::no_entry: throw no_entry(); - case error_code::not_authorized: throw not_authorized(); - case error_code::version_mismatch: throw version_mismatch(); - case error_code::no_children_for_ephemerals: throw no_children_for_ephemerals(); - case error_code::entry_exists: throw entry_exists(); - case error_code::not_empty: throw not_empty(); - case error_code::session_expired: throw session_expired(); - case error_code::authentication_failed: throw authentication_failed(); - case error_code::closed: throw closed(); - case error_code::read_only_connection: throw read_only_connection(); - case error_code::ephemeral_on_local_session: throw ephemeral_on_local_session(); - case error_code::reconfiguration_disabled: throw reconfiguration_disabled(); - case error_code::transaction_failed: throw transaction_failed(error_code::transaction_failed, 0U); - default: throw error(code, "unknown"); + case error_code::connection_loss: zk::throw_exception( connection_loss() ); + case error_code::marshalling_error: zk::throw_exception( marshalling_error() ); + case error_code::not_implemented: zk::throw_exception( not_implemented("unspecified") ); + case error_code::invalid_arguments: zk::throw_exception( invalid_arguments() ); + case error_code::new_configuration_no_quorum: zk::throw_exception( new_configuration_no_quorum() ); + case error_code::reconfiguration_in_progress: zk::throw_exception( reconfiguration_in_progress() ); + case error_code::no_entry: zk::throw_exception( no_entry() ); + case error_code::not_authorized: zk::throw_exception( not_authorized() ); + case error_code::version_mismatch: zk::throw_exception( version_mismatch() ); + case error_code::no_children_for_ephemerals: zk::throw_exception( no_children_for_ephemerals() ); + case error_code::entry_exists: zk::throw_exception( entry_exists() ); + case error_code::not_empty: zk::throw_exception( not_empty() ); + case error_code::session_expired: zk::throw_exception( session_expired() ); + case error_code::authentication_failed: zk::throw_exception( authentication_failed() ); + case error_code::closed: zk::throw_exception( closed() ); + case error_code::read_only_connection: zk::throw_exception( read_only_connection() ); + case error_code::ephemeral_on_local_session: zk::throw_exception( ephemeral_on_local_session() ); + case error_code::reconfiguration_disabled: zk::throw_exception( reconfiguration_disabled() ); + case error_code::transaction_failed: zk::throw_exception( transaction_failed(error_code::transaction_failed, 0U) ); + default: zk::throw_exception( error(code, "unknown") ); } } -std::exception_ptr get_exception_ptr_of(error_code code) +zk::exception_ptr get_exception_ptr_of(error_code code) { try { @@ -63,7 +64,7 @@ std::exception_ptr get_exception_ptr_of(error_code code) } catch (...) { - return std::current_exception(); + return zk::current_exception(); } } diff --git a/src/zk/error.hpp b/src/zk/error.hpp index b68c450..76e953d 100644 --- a/src/zk/error.hpp +++ b/src/zk/error.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include "exceptions.hpp" #include #include @@ -91,10 +92,10 @@ std::string to_string(const error_code&); [[noreturn]] void throw_error(error_code code); -/// Get an \c std::exception_ptr containing an exception with the proper type for the given \a code. +/// Get an \c zk::exception_ptr containing an exception with the proper type for the given \a code. /// /// \see throw_error -std::exception_ptr get_exception_ptr_of(error_code code); +zk::exception_ptr get_exception_ptr_of(error_code code); /// Get the \c std::error_category capable of describing ZooKeeper-provided error codes. /// @@ -138,7 +139,7 @@ class transport_error : /// the client can experience a \ref connection_loss before the server replies with OK. /// /// \see session_expired -class connection_loss final : +class connection_loss: public transport_error { public: @@ -153,7 +154,7 @@ class connection_loss final : /// ZooKeeper Administrator's Guide for more /// information and a stern warning). Another possible cause is the system running out of memory, but due to overcommit, /// OOM issues rarely manifest so cleanly. -class marshalling_error final : +class marshalling_error: public transport_error { public: @@ -164,7 +165,7 @@ class marshalling_error final : /// Operation was attempted that was not implemented. If you happen to be writing a \ref connection implementation, you /// are encouraged to raise this error in cases where you have not implemented an operation. -class not_implemented final : +class not_implemented: public error { public: @@ -192,7 +193,7 @@ class invalid_arguments : /// credentials to function. /// /// \see acl_rule -class authentication_failed final : +class authentication_failed: public invalid_arguments { public: @@ -216,7 +217,7 @@ class invalid_ensemble_state : /// Raised when attempting an ensemble reconfiguration, but the proposed new ensemble would not be able to form quorum. /// This happens when not enough time has passed for potential new servers to sync with the leader. If the proposed new /// ensemble is up and running, the solution is usually to simply wait longer and attempt reconfiguration later. -class new_configuration_no_quorum final : +class new_configuration_no_quorum: public invalid_ensemble_state { public: @@ -227,7 +228,7 @@ class new_configuration_no_quorum final : /// An attempt was made to reconfigure the ensemble, but there is already a reconfiguration in progress. Concurrent /// reconfiguration is not supported. -class reconfiguration_in_progress final : +class reconfiguration_in_progress: public invalid_ensemble_state { public: @@ -237,7 +238,7 @@ class reconfiguration_in_progress final : }; /// The ensemble does not support reconfiguration. -class reconfiguration_disabled final : +class reconfiguration_disabled: public invalid_ensemble_state { public: @@ -267,7 +268,7 @@ class invalid_connection_state : /// of resuming a session can happen even in cases of quorum loss, as session expiration requires a leader in order to /// proceed, so a client reconnecting soon enough after the ensemble forms quorum and elects a leader will resume the /// session, even if the quorum has been lost for days. -class session_expired final : +class session_expired: public invalid_connection_state { public: @@ -277,7 +278,7 @@ class session_expired final : }; /// An attempt was made to read or write to a ZNode when the connection does not have permission to do. -class not_authorized final : +class not_authorized: public invalid_connection_state { public: @@ -288,7 +289,7 @@ class not_authorized final : /// The connection is closed. This exception is delivered for all unfilled operations and watches when the connection is /// closing. -class closed final : +class closed: public invalid_connection_state { public: @@ -300,7 +301,7 @@ class closed final : /// An attempt was made to create an ephemeral entry, but the connection has a local session. /// /// \see connection_params::local -class ephemeral_on_local_session final : +class ephemeral_on_local_session: public invalid_connection_state { public: @@ -312,7 +313,7 @@ class ephemeral_on_local_session final : /// A write operation was attempted on a read-only connection. /// /// \see connection_params::read_only -class read_only_connection final : +class read_only_connection : public invalid_connection_state { public: @@ -333,7 +334,7 @@ class check_failed : }; /// Thrown from read operations when attempting to read a ZNode that does not exist. -class no_entry final : +class no_entry : public check_failed { public: @@ -343,7 +344,7 @@ class no_entry final : }; /// Thrown when attempting to create a ZNode, but one already exists at the specified path. -class entry_exists final : +class entry_exists : public check_failed { public: @@ -353,7 +354,7 @@ class entry_exists final : }; /// Thrown when attempting to erase a ZNode that has children. -class not_empty final : +class not_empty : public check_failed { public: @@ -364,7 +365,7 @@ class not_empty final : /// Thrown from modification operations when a version check is specified and the value in the database does not match /// the expected. -class version_mismatch final : +class version_mismatch : public check_failed { public: @@ -374,7 +375,7 @@ class version_mismatch final : }; /// Ephemeral ZNodes cannot have children. -class no_children_for_ephemerals final : +class no_children_for_ephemerals : public check_failed { public: @@ -385,7 +386,7 @@ class no_children_for_ephemerals final : /// Thrown from \ref client::commit when a transaction cannot be committed to the system. Check the /// \ref underlying_cause to see the specific error and \ref failed_op_index to see what operation failed. -class transaction_failed final : +class transaction_failed : public check_failed { public: diff --git a/src/zk/exceptions.cpp b/src/zk/exceptions.cpp new file mode 100644 index 0000000..422774e --- /dev/null +++ b/src/zk/exceptions.cpp @@ -0,0 +1,15 @@ +#include "exceptions.hpp" + +namespace zk +{ + +exception_ptr current_exception() noexcept +{ +#if ZKPP_FUTURE_USE_BOOST + return boost::current_exception(); +#else + return std::current_exception(); +#endif +} + +} diff --git a/src/zk/exceptions.hpp b/src/zk/exceptions.hpp new file mode 100644 index 0000000..441f02a --- /dev/null +++ b/src/zk/exceptions.hpp @@ -0,0 +1,51 @@ +/** \file + * Controls the throwing of exceptions and import of \c exception_ptr types and \c current_exception implementation. + * These are probably \c std::exception_ptr and \c std::current_exception(), but if boost future is used it will be + * boost's implementation. +**/ +#pragma once + +#include + +#if ZKPP_FUTURE_USE_BOOST +#include +#else +#include +#endif + +namespace zk +{ + +/// \addtogroup Client +/// \{ + + +#if ZKPP_FUTURE_USE_BOOST + +using exception_ptr = boost::exception_ptr; +exception_ptr current_exception() noexcept; + +template +[[noreturn]] inline void throw_exception(T const & e) +{ + throw boost::enable_current_exception(e); +} + + +#else + +using exception_ptr = std::exception_ptr; +exception_ptr current_exception() noexcept; + +template +[[noreturn]] inline void throw_exception(T const & e) +{ + throw e; +} + +#endif + +/// \} + +} + diff --git a/src/zk/future.hpp b/src/zk/future.hpp index 2840599..7bcb6c3 100644 --- a/src/zk/future.hpp +++ b/src/zk/future.hpp @@ -18,6 +18,14 @@ # define ZKPP_FUTURE_USE_STD_EXPERIMENTAL 0 #endif +/** \def ZKPP_FUTURE_USE_BOOST + * Set this to 1 to use \c boost::future and \c boost::promise as the backing types for + * \c zk::future and \c zk::promise. +**/ +#ifndef ZKPP_FUTURE_USE_BOOST +# define ZKPP_FUTURE_USE_BOOST 0 +#endif + /** \def ZKPP_FUTURE_USE_CUSTOM * Set this to 1 to use custom definitions of \c zk::future and \c zk::promise. If this is set, you must also set * \c ZKPP_FUTURE_TEMPLATE, \c ZKPP_PROMISE_TEMPLATE, and \c ZKPP_FUTURE_INCLUDE. @@ -28,6 +36,14 @@ * \def ZKPP_PROMISE_TEMPLATE * The template to use for \c zk::promise. This should be highly related to \c ZKPP_FUTURE_TEMPLATE * + * \def ZKPP_ASYNC_TEMPLATE + * The template to use for \c zk::async. This should be highly related to \c ZKPP_FUTURE_TEMPLATE and usually mapped + * to std::async or boost::async. + * + * \def ZKPP_LAUNCH_ENUM + * The enum to use for \c zk::async launch policy. This should be highly related to \c ZKPP_FUTURE_TEMPLATE and usually mapped + * to std::launch::async or boost::launch::async. + * * \def ZKPP_FUTURE_INCLUDE * The file to include to get the implementation for \c future and \c promise. If you define \c ZKPP_FUTURE_TEMPLATE * and \c ZKPP_PROMISE_TEMPLATE, you must also define this. @@ -41,7 +57,7 @@ * This is the default behavior. **/ #ifndef ZKPP_FUTURE_USE_STD -# if ZKPP_FUTURE_USE_STD_EXPERIMENTAL || ZKPP_FUTURE_USE_CUSTOM +# if ZKPP_FUTURE_USE_BOOST || ZKPP_FUTURE_USE_STD_EXPERIMENTAL || ZKPP_FUTURE_USE_CUSTOM # define ZKPP_FUTURE_USE_STD 0 # else # define ZKPP_FUTURE_USE_STD 1 @@ -52,10 +68,23 @@ # define ZKPP_FUTURE_INCLUDE # define ZKPP_FUTURE_TEMPLATE std::future # define ZKPP_PROMISE_TEMPLATE std::promise +# define ZKPP_ASYNC_TEMPLATE std::async +# define ZKPP_LAUNCH_ENUM std::launch #elif ZKPP_FUTURE_USE_STD_EXPERIMENTAL # define ZKPP_FUTURE_INCLUDE # define ZKPP_FUTURE_TEMPLATE std::experimental::future # define ZKPP_PROMISE_TEMPLATE std::experimental::promise +# define ZKPP_ASYNC_TEMPLATE std::async +# define ZKPP_LAUNCH_ENUM std::launch +#elif ZKPP_FUTURE_USE_BOOST +# define BOOST_THREAD_PROVIDES_FUTURE +# define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION +# define BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY +# define ZKPP_FUTURE_INCLUDE +# define ZKPP_FUTURE_TEMPLATE boost::future +# define ZKPP_PROMISE_TEMPLATE boost::promise +# define ZKPP_ASYNC_TEMPLATE boost::async +# define ZKPP_LAUNCH_ENUM boost::launch #elif ZKPP_FUTURE_USE_CUSTOM # if !defined ZKPP_FUTURE_TEMPLATE || !defined ZKPP_PROMISE_TEMPLATE || !defined ZKPP_FUTURE_INCLUDE # error "When ZKPP_FUTURE_USE_CUSTOM is set, you must also define ZKPP_FUTURE_TEMPLATE, ZKPP_PROMISE_TEMPLATE," @@ -82,6 +111,9 @@ using future = ZKPP_FUTURE_TEMPLATE; template using promise = ZKPP_PROMISE_TEMPLATE; +using ZKPP_LAUNCH_ENUM; +using ZKPP_ASYNC_TEMPLATE; + /** \} **/ } diff --git a/src/zk/multi.cpp b/src/zk/multi.cpp index cb94757..e07b3e4 100644 --- a/src/zk/multi.cpp +++ b/src/zk/multi.cpp @@ -1,4 +1,5 @@ #include "multi.hpp" +#include "exceptions.hpp" #include #include @@ -67,11 +68,11 @@ const T& op::as(ptr operation) const } catch (const std::bad_variant_access&) { - throw std::logic_error( std::string("Invalid op type for op::") + zk::throw_exception(std::logic_error( std::string("Invalid op type for op::") + std::string(operation) + std::string(": ") + to_string(type()) - ); + )); } } @@ -259,11 +260,11 @@ const T& multi_result::part::as(ptr operation) const } catch (const std::bad_variant_access&) { - throw std::logic_error( std::string("Invalid op type for multi_result::") + zk::throw_exception(std::logic_error( std::string("Invalid op type for multi_result::") + std::string(operation) + std::string(": ") + to_string(type()) - ); + )); } } diff --git a/src/zk/multi.hpp b/src/zk/multi.hpp index 38998ed..595a1b7 100644 --- a/src/zk/multi.hpp +++ b/src/zk/multi.hpp @@ -292,6 +292,9 @@ class multi_result final multi_result(std::vector parts) noexcept; + multi_result(multi_result&&) = default; + multi_result & operator=(multi_result&&) = default; + ~multi_result() noexcept; /// The number of results in this transaction bundle. diff --git a/src/zk/results.hpp b/src/zk/results.hpp index 09d7286..2a840a4 100644 --- a/src/zk/results.hpp +++ b/src/zk/results.hpp @@ -227,6 +227,8 @@ class watch_result final explicit watch_result(get_result initial, future next) noexcept; watch_result(watch_result&&) = default; + watch_result& operator=(watch_result&&) = default; + ~watch_result() noexcept; @@ -260,6 +262,8 @@ class watch_children_result final explicit watch_children_result(get_children_result initial, future next) noexcept; watch_children_result(watch_children_result&&) = default; + watch_children_result& operator=(watch_children_result&&) = default; + ~watch_children_result() noexcept; @@ -293,6 +297,7 @@ class watch_exists_result final explicit watch_exists_result(exists_result initial, future next) noexcept; watch_exists_result(watch_exists_result&&) = default; + watch_exists_result & operator=(watch_exists_result&&) = default; ~watch_exists_result() noexcept;