diff --git a/BUILD.bazel b/BUILD.bazel index 54aafe4a..922357a5 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,3 +1,5 @@ +load("@//tools/toolchain:toolchain.bzl", "if_opt", "if_not_opt") + cc_library( name = "cpp_redis", srcs = [ @@ -53,6 +55,11 @@ cc_library( strip_include_prefix = "includes", visibility = ["//visibility:public"], deps = ["@tacopie"], + defines = if_opt([ + "CPP_REDIS_ENABLE_EXCEPTION=0" + ]) + if_not_opt([ + "CPP_REDIS_ENABLE_EXCEPTION=1" + ]), ) cc_binary( diff --git a/CMakeLists.txt b/CMakeLists.txt index 3eb64968..80dfd7dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,8 @@ project(${PROJECT} CXX) ### # compilation options ### +option(CPP_REDIS_ENABLE_EXCEPTIONS "Build with exceptions" ON) + if (MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /O2 /bigobj") @@ -72,6 +74,12 @@ else () set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -W -Wall -Wextra -O3") endif () +if(NOT CPP_REDIS_ENABLE_EXCEPTIONS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") +endif() + +set(CPP_REDIS_ENABLE_EXCEPTION ${CPP_REDIS_ENABLE_EXCEPTIONS}) + ### # variables diff --git a/includes/cpp_redis/impl/types.hpp b/includes/cpp_redis/impl/types.hpp index 45fb0d86..ff3a4482 100644 --- a/includes/cpp_redis/impl/types.hpp +++ b/includes/cpp_redis/impl/types.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ namespace cpp_redis { class serializer_type { public: inline serializer_type() {} + virtual ~serializer_type() {} /** * @return the underlying string @@ -62,6 +64,8 @@ typedef std::shared_ptr serializer_ptr_t; template class message_impl { public: + virtual ~message_impl() {} + virtual const std::string get_id() const = 0; virtual const message_impl& set_id(std::string id) = 0; @@ -99,7 +103,7 @@ class message_type : public message_impl { if (it != m_values.end()) return it->second; else - throw "value not found"; + cpp_redis_throw_raw("value not found"); }; inline message_type& diff --git a/includes/cpp_redis/misc/convert.hpp b/includes/cpp_redis/misc/convert.hpp index 4813ad74..9f486957 100644 --- a/includes/cpp_redis/misc/convert.hpp +++ b/includes/cpp_redis/misc/convert.hpp @@ -24,6 +24,7 @@ #include #include +#include namespace cpp_redis { @@ -31,12 +32,12 @@ namespace cpp_redis { public: template static enableIf::value, optional_t > to_int(T value) { - try { + cpp_redis_try { std::stringstream stream(value); int64_t x; stream >> x; return optional_t(x); - } catch (std::exception &exc) { + } cpp_redis_catch (std::exception &exc,) { return {}; } } diff --git a/includes/cpp_redis/misc/macro.hpp b/includes/cpp_redis/misc/macro.hpp index 27f929c9..2934940c 100644 --- a/includes/cpp_redis/misc/macro.hpp +++ b/includes/cpp_redis/misc/macro.hpp @@ -29,3 +29,20 @@ #endif /* _WIN32 */ #define __CPP_REDIS_PRINT(...) printf(__VA_ARGS__) + +#if CPP_REDIS_ENABLE_EXCEPTION +#if !__cpp_exceptions && !__EXCEPTIONS +#undef CPP_REDIS_ENABLE_EXCEPTION +#define CPP_REDIS_ENABLE_EXCEPTION 0 +#endif +#endif + +#if CPP_REDIS_ENABLE_EXCEPTION +#define cpp_redis_try try +#define cpp_redis_catch(_decl, _stmt) catch (_decl) _stmt +#define cpp_redis_throw_raw(_exc...) throw _exc +#else +#define cpp_redis_try +#define cpp_redis_catch(_decl, _stmt) +#define cpp_redis_throw_raw(_exc...) __builtin_trap() +#endif diff --git a/sources/builders/builders_factory.cpp b/sources/builders/builders_factory.cpp index 3679552b..b95b0d5d 100644 --- a/sources/builders/builders_factory.cpp +++ b/sources/builders/builders_factory.cpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace cpp_redis { @@ -48,7 +49,7 @@ create_builder(char id) { return std::unique_ptr{new array_builder()}; default: __CPP_REDIS_LOG(error, "cpp_redis::builders::create_builder receives invalid data type"); - throw redis_error("Invalid data"); + cpp_redis_throw_raw(redis_error("Invalid data")); } } diff --git a/sources/builders/bulk_string_builder.cpp b/sources/builders/bulk_string_builder.cpp index 554953ac..3b14ff08 100644 --- a/sources/builders/bulk_string_builder.cpp +++ b/sources/builders/bulk_string_builder.cpp @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include "cpp_redis/misc/macro.hpp" #include #include #include @@ -69,7 +70,7 @@ bulk_string_builder::fetch_str(std::string& buffer) { if (buffer[m_str_size] != '\r' || buffer[m_str_size + 1] != '\n') { __CPP_REDIS_LOG(error, "cpp_redis::builders::bulk_string_builder receives invalid ending sequence"); - throw redis_error("Wrong ending sequence"); + cpp_redis_throw_raw(redis_error("Wrong ending sequence")); } m_str = buffer.substr(0, m_str_size); diff --git a/sources/builders/integer_builder.cpp b/sources/builders/integer_builder.cpp index c695c03e..72df1ada 100644 --- a/sources/builders/integer_builder.cpp +++ b/sources/builders/integer_builder.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace cpp_redis { @@ -53,7 +54,7 @@ integer_builder::operator<<(std::string& buffer) { } else if (!std::isdigit(buffer[i])) { __CPP_REDIS_LOG(error, "cpp_redis::builders::integer_builder receives invalid digit character"); - throw redis_error("Invalid character for integer redis reply"); + cpp_redis_throw_raw(redis_error("Invalid character for integer redis reply")); } m_nbr *= 10; diff --git a/sources/builders/reply_builder.cpp b/sources/builders/reply_builder.cpp index e2ea7831..46b32a98 100644 --- a/sources/builders/reply_builder.cpp +++ b/sources/builders/reply_builder.cpp @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include "cpp_redis/misc/macro.hpp" #include #include #include @@ -76,7 +77,7 @@ namespace cpp_redis { const reply & reply_builder::get_front() const { if (!reply_available()) - throw redis_error("No available reply"); + cpp_redis_throw_raw(redis_error("No available reply")); return m_available_replies.front(); } @@ -84,7 +85,7 @@ namespace cpp_redis { void reply_builder::pop_front() { if (!reply_available()) - throw redis_error("No available reply"); + cpp_redis_throw_raw(redis_error("No available reply")); m_available_replies.pop_front(); } diff --git a/sources/core/client.cpp b/sources/core/client.cpp index 659c0498..bc478048 100644 --- a/sources/core/client.cpp +++ b/sources/core/client.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace cpp_redis { @@ -83,7 +84,7 @@ namespace cpp_redis { if (m_sentinel.get_master_addr_by_name(name, m_redis_server, m_redis_port, true)) { connect(m_redis_server, m_redis_port, connect_callback, timeout_ms, max_reconnects, reconnect_interval_ms); } else { - throw redis_error("cpp_redis::client::connect() could not find master for m_name " + name); + cpp_redis_throw_raw(redis_error("cpp_redis::client::connect() could not find master for m_name " + name)); } } @@ -232,19 +233,19 @@ namespace cpp_redis { void client::try_commit() { - try { + cpp_redis_try { __CPP_REDIS_LOG(debug, "cpp_redis::client attempts to send pipelined commands"); m_client.commit(); __CPP_REDIS_LOG(info, "cpp_redis::client sent pipelined commands"); } - catch (const cpp_redis::redis_error &) { + cpp_redis_catch(const cpp_redis::redis_error &, { __CPP_REDIS_LOG(error, "cpp_redis::client could not send pipelined commands"); /** * ensure commands are flushed */ clear_callbacks(); throw; - } + }); } void @@ -452,11 +453,11 @@ namespace cpp_redis { /** * Try catch block because the redis client throws an error if connection cannot be made. */ - try { + cpp_redis_try { connect(m_redis_server, m_redis_port, m_connect_callback, m_connect_timeout_ms, m_max_reconnects, m_reconnect_interval_ms); } - catch (...) { + cpp_redis_catch (...,) { } if (!is_connected()) { diff --git a/sources/core/consumer.cpp b/sources/core/consumer.cpp index bb30eae6..53af1466 100644 --- a/sources/core/consumer.cpp +++ b/sources/core/consumer.cpp @@ -21,6 +21,7 @@ // SOFTWARE.#include "consumer.hpp" #include +#include #include @@ -110,7 +111,7 @@ namespace cpp_redis { for (auto &m : stream.Messages) { if (m_should_read_pending.load()) m_read_id = m.get_id(); - try { + cpp_redis_try { m_dispatch_queue->dispatch( m, [&](const message_type &message) { @@ -135,11 +136,11 @@ namespace cpp_redis { }).sync_commit(); return response; }); - } catch (std::exception &exc) { + } cpp_redis_catch (std::exception &exc, ) { __CPP_REDIS_LOG(1, "Processing failed for message id: " + m.get_id() + "\nDetails: " + exc.what()); - throw exc; + cpp_redis_throw_raw(exc); } } } diff --git a/sources/core/reply.cpp b/sources/core/reply.cpp index 19c8c98a..4eb542f5 100644 --- a/sources/core/reply.cpp +++ b/sources/core/reply.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace cpp_redis { @@ -79,7 +80,7 @@ namespace cpp_redis { const std::string & reply::error() const { if (!is_error()) - throw cpp_redis::redis_error("Reply is not an error"); + cpp_redis_throw_raw(cpp_redis::redis_error("Reply is not an error")); return as_string(); } @@ -157,7 +158,7 @@ namespace cpp_redis { const std::vector & reply::as_array() const { if (!is_array()) - throw cpp_redis::redis_error("Reply is not an array"); + cpp_redis_throw_raw(cpp_redis::redis_error("Reply is not an array")); return m_rows; } @@ -165,7 +166,7 @@ namespace cpp_redis { const std::string & reply::as_string() const { if (!is_string()) - throw cpp_redis::redis_error("Reply is not a string"); + cpp_redis_throw_raw(cpp_redis::redis_error("Reply is not a string")); return m_str_val; } @@ -173,7 +174,7 @@ namespace cpp_redis { int64_t reply::as_integer() const { if (!is_integer()) - throw cpp_redis::redis_error("Reply is not an integer"); + cpp_redis_throw_raw(cpp_redis::redis_error("Reply is not an integer")); return m_int_val; } diff --git a/sources/core/sentinel.cpp b/sources/core/sentinel.cpp index bd67d6f6..9ee403d3 100644 --- a/sources/core/sentinel.cpp +++ b/sources/core/sentinel.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace cpp_redis { @@ -66,20 +67,20 @@ sentinel::get_master_addr_by_name(const std::string& name, std::string& host, st //! we must have some sentinels to connect to if we are in autoconnect mode if (autoconnect && m_sentinels.size() == 0) { - throw redis_error("No sentinels available. Call add_sentinel() before get_master_addr_by_name()"); + cpp_redis_throw_raw(redis_error("No sentinels available. Call add_sentinel() before get_master_addr_by_name()")); } //! if we are not connected and we are not in autoconnect mode, we can't go further in the process if (!autoconnect && !is_connected()) { - throw redis_error("No sentinel connected. Call connect() first or enable autoconnect."); + cpp_redis_throw_raw(redis_error("No sentinel connected. Call connect() first or enable autoconnect.")); } if (autoconnect) { - try { + cpp_redis_try { //! Will round robin all attached sentinels until it finds one that is online. connect_sentinel(nullptr); } - catch (const redis_error&) { + cpp_redis_catch (const redis_error&, ) { } //! we failed to connect @@ -111,7 +112,7 @@ sentinel::get_master_addr_by_name(const std::string& name, std::string& host, st void sentinel::connect_sentinel(const sentinel_disconnect_handler_t& sentinel_disconnect_handler) { if (m_sentinels.size() == 0) { - throw redis_error("No sentinels available. Call add_sentinel() before connect_sentinel()"); + cpp_redis_throw_raw(redis_error("No sentinels available. Call add_sentinel() before connect_sentinel()")); } auto disconnect_handler = std::bind(&sentinel::connection_disconnect_handler, this, std::placeholders::_1); @@ -122,11 +123,11 @@ sentinel::connect_sentinel(const sentinel_disconnect_handler_t& sentinel_disconn bool not_connected = true; while (not_connected && it != m_sentinels.end()) { - try { + cpp_redis_try { __CPP_REDIS_LOG(debug, std::string("cpp_redis::sentinel attempting to connect to host ") + it->get_host()); m_client.connect(it->get_host(), it->get_port(), disconnect_handler, receive_handler, it->get_timeout_ms()); } - catch (const redis_error&) { + cpp_redis_catch (const redis_error&, ) { __CPP_REDIS_LOG(info, std::string("cpp_redis::sentinel unable to connect to sentinel host ") + it->get_host()); } @@ -143,7 +144,7 @@ sentinel::connect_sentinel(const sentinel_disconnect_handler_t& sentinel_disconn } if (not_connected) { - throw redis_error("Unable to connect to any sentinels"); + cpp_redis_throw_raw(redis_error("Unable to connect to any sentinels")); } m_disconnect_handler = sentinel_disconnect_handler; @@ -273,15 +274,15 @@ sentinel::sync_commit() { void sentinel::try_commit(void) { - try { + cpp_redis_try { __CPP_REDIS_LOG(debug, "cpp_redis::sentinel attempts to send pipelined commands"); m_client.commit(); __CPP_REDIS_LOG(info, "cpp_redis::sentinel sent pipelined commands"); } - catch (const cpp_redis::redis_error&) { + cpp_redis_catch (const cpp_redis::redis_error&, ) { __CPP_REDIS_LOG(error, "cpp_redis::sentinel could not send pipelined commands"); clear_callbacks(); - throw; + cpp_redis_throw_raw(); } } diff --git a/sources/core/subscriber.cpp b/sources/core/subscriber.cpp index 02678d47..ba773730 100644 --- a/sources/core/subscriber.cpp +++ b/sources/core/subscriber.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -82,7 +83,7 @@ subscriber::connect( connect(m_redis_server, m_redis_port, connect_callback, timeout_ms, max_reconnects, reconnect_interval_ms); } else { - throw redis_error("cpp_redis::subscriber::connect() could not find master for m_name " + name); + cpp_redis_throw_raw(redis_error("cpp_redis::subscriber::connect() could not find master for m_name " + name)); } } @@ -266,14 +267,14 @@ subscriber::punsubscribe(const std::string& pattern) { subscriber& subscriber::commit() { - try { + cpp_redis_try { __CPP_REDIS_LOG(debug, "cpp_redis::subscriber attempts to send pipelined commands"); m_client.commit(); __CPP_REDIS_LOG(info, "cpp_redis::subscriber sent pipelined commands"); } - catch (const cpp_redis::redis_error&) { + cpp_redis_catch (const cpp_redis::redis_error&, ) { __CPP_REDIS_LOG(error, "cpp_redis::subscriber could not send pipelined commands"); - throw; + cpp_redis_throw_raw(); } return *this; @@ -484,10 +485,10 @@ subscriber::reconnect() { } //! Try catch block because the redis subscriber throws an error if connection cannot be made. - try { + cpp_redis_try { connect(m_redis_server, m_redis_port, m_connect_callback, m_connect_timeout_ms, m_max_reconnects, m_reconnect_interval_ms); } - catch (...) { + cpp_redis_catch (..., ) { } if (!is_connected()) { diff --git a/sources/network/redis_connection.cpp b/sources/network/redis_connection.cpp index 20ac6305..783a96c9 100644 --- a/sources/network/redis_connection.cpp +++ b/sources/network/redis_connection.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #ifndef __CPP_REDIS_USE_CUSTOM_TCP_CLIENT @@ -57,7 +58,7 @@ namespace cpp_redis { const disconnection_handler_t &client_disconnection_handler, const reply_callback_t &client_reply_callback, std::uint32_t timeout_ms) { - try { + cpp_redis_try { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to connect"); /** @@ -76,9 +77,9 @@ namespace cpp_redis { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection connected"); } - catch (const std::exception &e) { + cpp_redis_catch (const std::exception &e, ) { __CPP_REDIS_LOG(error, std::string("cpp_redis::network::redis_connection ") + e.what()); - throw redis_error(e.what()); + cpp_redis_throw_raw(redis_error(e.what())); } m_reply_callback = client_reply_callback; @@ -144,13 +145,13 @@ namespace cpp_redis { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection attempts to send pipelined commands"); std::string buffer = std::move(m_buffer); - try { + cpp_redis_try { tcp_client_iface::write_request request = {std::vector{buffer.begin(), buffer.end()}, nullptr}; m_client->async_write(request); } - catch (const std::exception &e) { + cpp_redis_catch (const std::exception &e, ) { __CPP_REDIS_LOG(error, std::string("cpp_redis::network::redis_connection ") + e.what()); - throw redis_error(e.what()); + cpp_redis_throw_raw(redis_error(e.what())); } __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection sent pipelined commands"); @@ -170,11 +171,11 @@ namespace cpp_redis { redis_connection::tcp_client_receive_handler(const tcp_client_iface::read_result &result) { if (!result.success) { return; } - try { + cpp_redis_try { __CPP_REDIS_LOG(debug, "cpp_redis::network::redis_connection receives packet, attempts to build reply"); m_builder << std::string(result.buffer.begin(), result.buffer.end()); } - catch (const redis_error &) { + cpp_redis_catch (const redis_error &, ) { __CPP_REDIS_LOG(error, "cpp_redis::network::redis_connection could not build reply (invalid format), disconnecting"); call_disconnection_handler(); @@ -193,13 +194,13 @@ namespace cpp_redis { } } - try { + cpp_redis_try { tcp_client_iface::read_request request = {__CPP_REDIS_READ_SIZE, std::bind(&redis_connection::tcp_client_receive_handler, this, std::placeholders::_1)}; m_client->async_read(request); } - catch (const std::exception &) { + cpp_redis_catch (const std::exception &, ) { /** * Client disconnected in the meantime */