Allow callback raw access to message

In some cases, the callback may care where the message came from. Allow
the callback to receive the raw sdbusplus::message along with the
automatically extracted arguments.

This means that the registration mechanism as well as the callback
mechanism need to be aware of a 'message as a first parameter' situation
and behave accordingly. The registration needs to strip the message
parameter off in order to expose a correct DBus signature. The callback
mechanism needs to strip of the message parameter in order to properly
unpack the message, but add it back in before calling apply to execute
the function.

Tested: Added an example to example/asio-example.cpp and checked to see
	that it builds. Was able to extract sender from message passed
	into the IPMI handler.

Change-Id: I520e494d3baad34271e748465f30274353554728
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/example/asio-example.cpp b/example/asio-example.cpp
index 82d713e..7477357 100644
--- a/example/asio-example.cpp
+++ b/example/asio-example.cpp
@@ -12,6 +12,11 @@
     return ++test;
 }
 
+int methodWithMessage(sdbusplus::message::message& m, int test)
+{
+    return ++test;
+}
+
 int main()
 {
     using GetSubTreeType = std::vector<std::pair<
@@ -60,7 +65,6 @@
             {
                 std::cout << item.first << "\n";
             }
-
         },
         "xyz.openbmc_project.ObjectMapper",
         "/xyz/openbmc_project/object_mapper",
@@ -115,6 +119,8 @@
 
     iface->register_method("TestFunction", foo);
 
+    iface->register_method("TestMethodWithMessage", methodWithMessage);
+
     iface->initialize();
     iface->set_property("int", 45);
     io.run();
diff --git a/sdbusplus/asio/object_server.hpp b/sdbusplus/asio/object_server.hpp
index 3c0f534..bdd910b 100644
--- a/sdbusplus/asio/object_server.hpp
+++ b/sdbusplus/asio/object_server.hpp
@@ -29,6 +29,12 @@
     virtual int set(const boost::any &value) = 0;
 };
 
+template <typename T>
+using FirstArgIsMessage =
+    std::is_same<typename utility::get_first_arg<typename utility::decay_tuple<
+                     boost::callable_traits::args_t<T>>::type>::type,
+                 message::message>;
+
 template <typename CallbackType>
 class callback_method_instance : public callback
 {
@@ -38,17 +44,8 @@
     }
     int call(message::message &m) override
     {
-        InputTupleType inputArgs;
-        if (!utility::read_into_tuple(inputArgs, m))
-        {
-            return -EINVAL;
-        }
-
-        auto ret = m.new_method_return();
-        callFunction<ResultType>(ret, inputArgs);
-        ret.method_return();
-        return 1;
-    };
+        return expandCall<CallbackType>(m);
+    }
 
   private:
     using CallbackSignature = boost::callable_traits::args_t<CallbackType>;
@@ -69,6 +66,42 @@
     {
         std::experimental::apply(func_, inputArgs);
     }
+    // optional message-first-argument callback
+    template <typename T>
+    std::enable_if_t<FirstArgIsMessage<T>::value, int>
+        expandCall(message::message &m)
+    {
+        using DbusTupleType =
+            typename utility::strip_first_arg<InputTupleType>::type;
+        DbusTupleType dbusArgs;
+        if (!utility::read_into_tuple(dbusArgs, m))
+        {
+            return -EINVAL;
+        }
+
+        auto ret = m.new_method_return();
+        InputTupleType inputArgs =
+            std::tuple_cat(std::forward_as_tuple(std::move(m)), dbusArgs);
+        callFunction<ResultType>(ret, inputArgs);
+        ret.method_return();
+        return 1;
+    };
+    // normal dbus-types-only callback
+    template <typename T>
+    std::enable_if_t<!FirstArgIsMessage<T>::value, int>
+        expandCall(message::message &m)
+    {
+        InputTupleType inputArgs;
+        if (!utility::read_into_tuple(inputArgs, m))
+        {
+            return -EINVAL;
+        }
+
+        auto ret = m.new_method_return();
+        callFunction<ResultType>(ret, inputArgs);
+        ret.method_return();
+        return 1;
+    };
 };
 
 template <typename PropertyType, typename CallbackType>
@@ -285,7 +318,38 @@
     }
 
     template <typename CallbackType>
-    bool register_method(const std::string &name, CallbackType &&handler)
+    std::enable_if_t<FirstArgIsMessage<CallbackType>::value, bool>
+        register_method(const std::string &name, CallbackType &&handler)
+    {
+        using CallbackSignature = typename utility::strip_first_arg<
+            boost::callable_traits::args_t<CallbackType>>::type;
+        using InputTupleType =
+            typename utility::decay_tuple<CallbackSignature>::type;
+        using ResultType = boost::callable_traits::return_type_t<CallbackType>;
+
+        if (initialized_)
+        {
+            return false;
+        }
+        static const auto argType = utility::strip_ends(
+            utility::tuple_to_array(message::types::type_id<InputTupleType>()));
+        static const auto resultType =
+            utility::tuple_to_array(message::types::type_id<ResultType>());
+
+        auto nameItr = methodNames_.emplace(methodNames_.end(), name);
+
+        callbacksMethod_[name] =
+            std::make_unique<callback_method_instance<CallbackType>>(
+                std::move(handler));
+
+        vtable_.emplace_back(vtable::method(nameItr->c_str(), argType.data(),
+                                            resultType.data(), method_handler));
+        return true;
+    }
+
+    template <typename CallbackType>
+    std::enable_if_t<!FirstArgIsMessage<CallbackType>::value, bool>
+        register_method(const std::string &name, CallbackType &&handler)
     {
         using CallbackSignature = boost::callable_traits::args_t<CallbackType>;
         using InputTupleType =
diff --git a/sdbusplus/utility/type_traits.hpp b/sdbusplus/utility/type_traits.hpp
index a88fcfa..e2e0b90 100644
--- a/sdbusplus/utility/type_traits.hpp
+++ b/sdbusplus/utility/type_traits.hpp
@@ -33,6 +33,17 @@
     using type = std::tuple<Rest...>;
 };
 
+// matching helper class to only return the first type
+template <typename T> struct get_first_arg
+{
+};
+
+template <typename FirstArg, typename... Rest>
+struct get_first_arg<std::tuple<FirstArg, Rest...>>
+{
+    using type = FirstArg;
+};
+
 // helper class to remove const and reference from types
 template <typename T> struct decay_tuple
 {