From 54cb18e817a0ee8ae2bd72df46a0d38831236330 Mon Sep 17 00:00:00 2001 From: Martin Haefner Date: Wed, 28 Jul 2021 14:39:23 +0200 Subject: [PATCH] Implement user defined exception class with additional exception data --- include/simppl/detail/error.h | 22 ++++++++++++++++-- include/simppl/detail/util.h | 4 ++++ include/simppl/error.h | 2 +- include/simppl/objectpath.h | 8 +------ src/error.cpp | 7 +++--- src/skeletonbase.cpp | 7 ------ src/util.cpp | 23 ++++++++++++++++++ tests/errors.cpp | 44 +++++++++++++++++++++++++++++++++-- 8 files changed, 94 insertions(+), 23 deletions(-) diff --git a/include/simppl/detail/error.h b/include/simppl/detail/error.h index c51cacf..f6d2af8 100644 --- a/include/simppl/detail/error.h +++ b/include/simppl/detail/error.h @@ -5,6 +5,10 @@ #include "simppl/types.h" #include "simppl/string.h" +#include "util.h" + +#include + namespace simppl { @@ -16,6 +20,15 @@ namespace detail { +// FIXME get rid of redefinion +struct FreeDeleter { + template + void operator()(T* o) { + ::free(o); + } +}; + + template struct ErrorFactory { @@ -23,7 +36,12 @@ struct ErrorFactory static message_ptr_t reply(DBusMessage& msg, const Error& e) { - message_ptr_t rmsg = e.make_reply_for(msg); + std::unique_ptr name; + + if (!e.name()) + name.reset(make_error_name(abi::__cxa_demangle(typeid(ExceptionT).name(), nullptr, 0, nullptr))); + + message_ptr_t rmsg = e.make_reply_for(msg, name.get()); // encode arguments DBusMessageIter iter; @@ -51,7 +69,7 @@ struct ErrorFactory err.set_members(dbus_message_get_error_name(&msg), text.c_str(), dbus_message_get_reply_serial(&msg)); // any other unexpected dbus error, e.g. exception during method body - if (dbus_message_iter_has_next(&iter)) + if (dbus_message_iter_get_arg_type(&iter) != 0) { // and now the rest decode(iter, err); diff --git a/include/simppl/detail/util.h b/include/simppl/detail/util.h index 410b069..1bebb71 100644 --- a/include/simppl/detail/util.h +++ b/include/simppl/detail/util.h @@ -42,6 +42,10 @@ std::string make_interface_name(const char* begin, const char* end); */ const char* find_next_interface(const char* template_args); +/** + * Make dbus compatible name from Exception type. + */ +char* make_error_name(char*); } // namespace detail diff --git a/include/simppl/error.h b/include/simppl/error.h index 2cb4c7e..9357ccf 100644 --- a/include/simppl/error.h +++ b/include/simppl/error.h @@ -58,7 +58,7 @@ struct Error : public std::exception protected: - message_ptr_t make_reply_for(DBusMessage& req) const; + message_ptr_t make_reply_for(DBusMessage& req, const char* classname = nullptr) const; void set_members(const char* name, const char* msg, uint32_t serial); diff --git a/include/simppl/objectpath.h b/include/simppl/objectpath.h index ef74cb6..8fd5b3f 100644 --- a/include/simppl/objectpath.h +++ b/include/simppl/objectpath.h @@ -69,13 +69,7 @@ struct ObjectPath { return !(lhs < rhs); } - - inline - bool operator!=(const ObjectPath& rhs) const - { - return path != rhs.path; - } - + std::string path; }; diff --git a/src/error.cpp b/src/error.cpp index c997609..1824d14 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -12,7 +12,6 @@ namespace simppl namespace dbus { - Error::Error(const char* name, const char* msg, uint32_t serial) : name_and_message_(nullptr) , message_(nullptr) @@ -81,9 +80,9 @@ void Error::set_members(const char* name, const char* msg, uint32_t serial) } -message_ptr_t Error::make_reply_for(DBusMessage& req) const +message_ptr_t Error::make_reply_for(DBusMessage& req, const char* class_name) const { - return make_message(dbus_message_new_error(&req, name(), message())); + return make_message(dbus_message_new_error(&req, class_name ? class_name : name(), message())); } @@ -101,7 +100,7 @@ const char* Error::name() const const char* Error::message() const { - return message_; + return message_ ? message_ : ""; } diff --git a/src/skeletonbase.cpp b/src/skeletonbase.cpp index f8631f6..7b42538 100644 --- a/src/skeletonbase.cpp +++ b/src/skeletonbase.cpp @@ -28,13 +28,6 @@ namespace dbus namespace detail { -struct FreeDeleter { - template - void operator()(T* o) { - ::free(o); - } -}; - using DemangledNamePtr = std::unique_ptr; } // namespace detail diff --git a/src/util.cpp b/src/util.cpp index 55eca2d..8c00eea 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -120,6 +120,29 @@ const char* find_next_interface(const char* s) { } } + +char* make_error_name(char* str) +{ + char* to = str; + char* from = str; + + while(*from) + { + if (*from == ':') + { + *to++ = '.'; + from += 2; + } + else + *to++ = *from++; + } + + *to = '\0'; + + return str; +} + + } // namespace detail } // namespace dbus diff --git a/tests/errors.cpp b/tests/errors.cpp index 58aa319..6bedf40 100644 --- a/tests/errors.cpp +++ b/tests/errors.cpp @@ -23,12 +23,20 @@ namespace test * is it not possible to use the make_serializer here. * * But with boost::fusion everything works ok. + * + * @note that if mixing simppl code with raw DBus calls you must make sure + * that when using the exceptions feature, the dbus message must contain + * a message in addition to the error name, otherwise deserialization would + * yield an error. */ struct MyException : simppl::dbus::Error { - /// class needs a default constructor + /// class needs a default constructor which is used during deserialization. + /// If such an exception instance is returned on the server, the name + /// will be set via C++ RTTI. MyException() = default; + /// This is the instance that can be thrown in user code. MyException(int return_code) : simppl::dbus::Error("My.Exception") , rc(return_code) @@ -50,6 +58,7 @@ INTERFACE(Errors) #if SIMPPL_HAVE_BOOST_FUSION Method<_throw> hello2; Method<_throw> hello3; + Method<_throw> hello4; #endif inline @@ -60,6 +69,7 @@ INTERFACE(Errors) #if SIMPPL_HAVE_BOOST_FUSION , INIT(hello2) , INIT(hello3) + , INIT(hello4) #endif { // NOOP @@ -141,12 +151,16 @@ struct Server : simppl::dbus::Skeleton #if SIMPPL_HAVE_BOOST_FUSION hello2 >> [this](){ - respond_with(MyException()); + respond_with(MyException(42)); }; hello3 >> [this](){ throw std::runtime_error("Ooops"); }; + + hello4 >> [this](){ + respond_with(MyException()); // will produce an DBus error test.MyException + }; #endif } }; @@ -261,6 +275,32 @@ TEST(Errors, blocking) { ASSERT_FALSE(true); } + + try + { + stub.hello4(); + + // never reach + ASSERT_FALSE(true); + } + catch(const test::MyException& e) + { + EXPECT_STREQ(e.name(), "test.MyException"); + EXPECT_EQ(0, e.rc); + } + catch(const simppl::dbus::RuntimeError& e) + { + ASSERT_FALSE(true); + } + catch(const simppl::dbus::Error& e) + { + // does not reach the default exception handler + ASSERT_FALSE(true); + } + catch(...) + { + ASSERT_FALSE(true); + } #endif stub.stop();