Merge pull request #58 from ben-clayton/response-callbacks

Add Session::registerHandler() overloads that support response callbacks
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2ec3ac0..47cb5df 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -221,6 +221,7 @@
         ${CPPDAP_SRC_DIR}/rwmutex_test.cpp
         ${CPPDAP_SRC_DIR}/session_test.cpp
         ${CPPDAP_SRC_DIR}/socket_test.cpp
+        ${CPPDAP_SRC_DIR}/traits_test.cpp
         ${CPPDAP_SRC_DIR}/typeinfo_test.cpp
         ${CPPDAP_SRC_DIR}/variant_test.cpp
         ${CPPDAP_GOOGLETEST_DIR}/googletest/src/gtest-all.cc
diff --git a/include/dap/session.h b/include/dap/session.h
index e06957f..11d5560 100644
--- a/include/dap/session.h
+++ b/include/dap/session.h
@@ -17,6 +17,7 @@
 
 #include "future.h"
 #include "io.h"
+#include "traits.h"
 #include "typeinfo.h"
 #include "typeof.h"
 
@@ -29,33 +30,6 @@
 struct Response;
 struct Event;
 
-// internal functionality
-namespace detail {
-template <typename T>
-struct traits {
-  static constexpr bool isRequest = std::is_base_of<dap::Request, T>::value;
-  static constexpr bool isResponse = std::is_base_of<dap::Response, T>::value;
-  static constexpr bool isEvent = std::is_base_of<dap::Event, T>::value;
-};
-
-// ArgTy<F>::type resolves to the first argument type of the function F.
-// F can be a function, static member function, or lambda.
-template <typename F>
-struct ArgTy {
-  using type = typename ArgTy<decltype(&F::operator())>::type;
-};
-
-template <typename R, typename Arg>
-struct ArgTy<R (*)(Arg)> {
-  using type = typename std::decay<Arg>::type;
-};
-
-template <typename R, typename C, typename Arg>
-struct ArgTy<R (C::*)(Arg) const> {
-  using type = typename std::decay<Arg>::type;
-};
-}  // namespace detail
-
 ////////////////////////////////////////////////////////////////////////////////
 // Error
 ////////////////////////////////////////////////////////////////////////////////
@@ -137,14 +111,24 @@
 // (3) Bind the session to the remote endpoint with bind().
 // (4) Send requests or events with send().
 class Session {
-  template <typename T>
-  using IsRequest = typename std::enable_if<detail::traits<T>::isRequest>::type;
+  template <typename F, int N>
+  using ParamType = traits::ParameterType<F, N>;
 
   template <typename T>
-  using IsEvent = typename std::enable_if<detail::traits<T>::isEvent>::type;
+  using IsRequest = traits::EnableIfIsType<dap::Request, T>;
+
+  template <typename T>
+  using IsEvent = traits::EnableIfIsType<dap::Event, T>;
 
   template <typename F>
-  using ArgTy = typename detail::ArgTy<F>::type;
+  using IsRequestHandlerWithoutCallback = traits::EnableIf<
+      traits::CompatibleWith<F, std::function<void(dap::Request)>>::value>;
+
+  template <typename F, typename CallbackType>
+  using IsRequestHandlerWithCallback = traits::EnableIf<traits::CompatibleWith<
+      F,
+      std::function<void(dap::Request, std::function<void(CallbackType)>)>>::
+                                                            value>;
 
  public:
   virtual ~Session();
@@ -167,20 +151,47 @@
   //   ResponseOrError<ResponseType>(const RequestType&)
   //   ResponseType(const RequestType&)
   //   Error(const RequestType&)
-  template <typename F, typename RequestType = ArgTy<F>>
-  inline IsRequest<RequestType> registerHandler(F&& handler);
+  template <typename F, typename RequestType = ParamType<F, 0>>
+  inline IsRequestHandlerWithoutCallback<F> registerHandler(F&& handler);
+
+  // registerHandler() registers a request handler for a specific request type.
+  // The handler has a response callback function for the second argument of the
+  // handler function. This callback may be called after the handler has
+  // returned.
+  // The function F must have the following signature:
+  //   void(const RequestType& request,
+  //        std::function<void(ResponseType)> response)
+  template <typename F,
+            typename RequestType = ParamType<F, 0>,
+            typename ResponseType = typename RequestType::Response>
+  inline IsRequestHandlerWithCallback<F, ResponseType> registerHandler(
+      F&& handler);
+
+  // registerHandler() registers a request handler for a specific request type.
+  // The handler has a response callback function for the second argument of the
+  // handler function. This callback may be called after the handler has
+  // returned.
+  // The function F must have the following signature:
+  //   void(const RequestType& request,
+  //        std::function<void(ResponseOrError<ResponseType>)> response)
+  template <typename F,
+            typename RequestType = ParamType<F, 0>,
+            typename ResponseType = typename RequestType::Response>
+  inline IsRequestHandlerWithCallback<F, ResponseOrError<ResponseType>>
+  registerHandler(F&& handler);
 
   // registerHandler() registers a event handler for a specific event type.
   // The function F must have the following signature:
   //   void(const EventType&)
-  template <typename F, typename EventType = ArgTy<F>>
+  template <typename F, typename EventType = ParamType<F, 0>>
   inline IsEvent<EventType> registerHandler(F&& handler);
 
   // registerSentHandler() registers the function F to be called when a response
   // of the specific type has been sent.
   // The function F must have the following signature:
   //   void(const ResponseOrError<ResponseType>&)
-  template <typename F, typename ResponseType = typename ArgTy<F>::Request>
+  template <typename F,
+            typename ResponseType = typename ParamType<F, 0>::Request>
   inline void registerSentHandler(F&& handler);
 
   // send() sends the request to the connected endpoint and returns a
@@ -251,21 +262,57 @@
   virtual bool send(const TypeInfo*, const void* event) = 0;
 };
 
-template <typename F, typename T>
-Session::IsRequest<T> Session::registerHandler(F&& handler) {
-  using ResponseType = typename T::Response;
-  auto cb = [handler](const void* args, const RequestSuccessCallback& onSuccess,
-                      const RequestErrorCallback& onError) {
+template <typename F, typename RequestType>
+Session::IsRequestHandlerWithoutCallback<F> Session::registerHandler(
+    F&& handler) {
+  using ResponseType = typename RequestType::Response;
+  const TypeInfo* typeinfo = TypeOf<RequestType>::type();
+  registerHandler(typeinfo, [handler](const void* args,
+                                      const RequestSuccessCallback& onSuccess,
+                                      const RequestErrorCallback& onError) {
     ResponseOrError<ResponseType> res =
-        handler(*reinterpret_cast<const T*>(args));
+        handler(*reinterpret_cast<const RequestType*>(args));
     if (res.error) {
       onError(TypeOf<ResponseType>::type(), res.error);
     } else {
       onSuccess(TypeOf<ResponseType>::type(), &res.response);
     }
-  };
-  const TypeInfo* typeinfo = TypeOf<T>::type();
-  registerHandler(typeinfo, cb);
+  });
+}
+
+template <typename F, typename RequestType, typename ResponseType>
+Session::IsRequestHandlerWithCallback<F, ResponseType> Session::registerHandler(
+    F&& handler) {
+  using CallbackType = ParamType<F, 1>;
+  registerHandler(
+      TypeOf<RequestType>::type(),
+      [handler](const void* args, const RequestSuccessCallback& onSuccess,
+                const RequestErrorCallback&) {
+        CallbackType responseCallback = [onSuccess](const ResponseType& res) {
+          onSuccess(TypeOf<ResponseType>::type(), &res);
+        };
+        handler(*reinterpret_cast<const RequestType*>(args), responseCallback);
+      });
+}
+
+template <typename F, typename RequestType, typename ResponseType>
+Session::IsRequestHandlerWithCallback<F, ResponseOrError<ResponseType>>
+Session::registerHandler(F&& handler) {
+  using CallbackType = ParamType<F, 1>;
+  registerHandler(
+      TypeOf<RequestType>::type(),
+      [handler](const void* args, const RequestSuccessCallback& onSuccess,
+                const RequestErrorCallback& onError) {
+        CallbackType responseCallback =
+            [onError, onSuccess](const ResponseOrError<ResponseType>& res) {
+              if (res.error) {
+                onError(TypeOf<ResponseType>::type(), res.error);
+              } else {
+                onSuccess(TypeOf<ResponseType>::type(), &res.response);
+              }
+            };
+        handler(*reinterpret_cast<const RequestType*>(args), responseCallback);
+      });
 }
 
 template <typename F, typename T>
diff --git a/include/dap/traits.h b/include/dap/traits.h
new file mode 100644
index 0000000..cbc7d39
--- /dev/null
+++ b/include/dap/traits.h
@@ -0,0 +1,159 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef dap_traits_h
+#define dap_traits_h
+
+#include <tuple>
+#include <type_traits>
+
+namespace dap {
+namespace traits {
+
+// NthTypeOf returns the `N`th type in `Types`
+template <int N, typename... Types>
+using NthTypeOf = typename std::tuple_element<N, std::tuple<Types...>>::type;
+
+// `IsTypeOrDerived<BASE, T>::value` is true iff `T` is of type `BASE`, or
+// derives from `BASE`.
+template <typename BASE, typename T>
+using IsTypeOrDerived = std::integral_constant<
+    bool,
+    std::is_base_of<BASE, typename std::decay<T>::type>::value ||
+        std::is_same<BASE, typename std::decay<T>::type>::value>;
+
+// `EachIsTypeOrDerived<N, BASES, TYPES>::value` is true iff all of the types in
+// the std::tuple `TYPES` is of, or derives from the corresponding indexed type
+// in the std::tuple `BASES`.
+// `N` must be equal to the number of types in both the std::tuple `BASES` and
+// `TYPES`.
+template <int N, typename BASES, typename TYPES>
+struct EachIsTypeOrDerived {
+  using base = typename std::tuple_element<N - 1, BASES>::type;
+  using type = typename std::tuple_element<N - 1, TYPES>::type;
+  using last_matches = IsTypeOrDerived<base, type>;
+  using others_match = EachIsTypeOrDerived<N - 1, BASES, TYPES>;
+  static constexpr bool value = last_matches::value && others_match::value;
+};
+
+// EachIsTypeOrDerived specialization for N = 1
+template <typename BASES, typename TYPES>
+struct EachIsTypeOrDerived<1, BASES, TYPES> {
+  using base = typename std::tuple_element<0, BASES>::type;
+  using type = typename std::tuple_element<0, TYPES>::type;
+  static constexpr bool value = IsTypeOrDerived<base, type>::value;
+};
+
+// EachIsTypeOrDerived specialization for N = 0
+template <typename BASES, typename TYPES>
+struct EachIsTypeOrDerived<0, BASES, TYPES> {
+  static constexpr bool value = true;
+};
+
+// Signature describes the signature of a function.
+template <typename RETURN, typename... PARAMETERS>
+struct Signature {
+  // The return type of the function signature
+  using ret = RETURN;
+  // The parameters of the function signature held in a std::tuple
+  using parameters = std::tuple<PARAMETERS...>;
+  // The type of the Nth parameter of function signature
+  template <std::size_t N>
+  using parameter = NthTypeOf<N, PARAMETERS...>;
+  // The total number of parameters
+  static constexpr std::size_t parameter_count = sizeof...(PARAMETERS);
+};
+
+// SignatureOf is a traits helper that infers the signature of the function,
+// method, static method, lambda, or function-like object `F`.
+template <typename F>
+struct SignatureOf {
+  // The signature of the function-like object `F`
+  using type = typename SignatureOf<decltype(&F::operator())>::type;
+};
+
+// SignatureOf specialization for a regular function or static method.
+template <typename R, typename... ARGS>
+struct SignatureOf<R (*)(ARGS...)> {
+  // The signature of the function-like object `F`
+  using type = Signature<typename std::decay<R>::type,
+                         typename std::decay<ARGS>::type...>;
+};
+
+// SignatureOf specialization for a non-static method.
+template <typename R, typename C, typename... ARGS>
+struct SignatureOf<R (C::*)(ARGS...)> {
+  // The signature of the function-like object `F`
+  using type = Signature<typename std::decay<R>::type,
+                         typename std::decay<ARGS>::type...>;
+};
+
+// SignatureOf specialization for a non-static, const method.
+template <typename R, typename C, typename... ARGS>
+struct SignatureOf<R (C::*)(ARGS...) const> {
+  // The signature of the function-like object `F`
+  using type = Signature<typename std::decay<R>::type,
+                         typename std::decay<ARGS>::type...>;
+};
+
+// SignatureOfT is an alias to `typename SignatureOf<F>::type`.
+template <typename F>
+using SignatureOfT = typename SignatureOf<F>::type;
+
+// ParameterType is an alias to `typename SignatureOf<F>::type::parameter<N>`.
+template <typename F, std::size_t N>
+using ParameterType = typename SignatureOfT<F>::template parameter<N>;
+
+// `HasSignature<F, S>::value` is true iff the function-like `F` has a matching
+// signature to the function-like `S`.
+template <typename F, typename S>
+using HasSignature = std::integral_constant<
+    bool,
+    std::is_same<SignatureOfT<F>, SignatureOfT<S>>::value>;
+
+// `Min<A, B>::value` resolves to the smaller value of A and B.
+template <std::size_t A, std::size_t B>
+using Min = std::integral_constant<std::size_t, (A < B ? A : B)>;
+
+// `CompatibleWith<F, S>::value` is true iff the function-like `F`
+// can be called with the argument types of the function-like `S`. Return type
+// of the two functions are not considered.
+template <typename F, typename S>
+using CompatibleWith = std::integral_constant<
+    bool,
+    (SignatureOfT<S>::parameter_count == SignatureOfT<F>::parameter_count) &&
+        EachIsTypeOrDerived<Min<SignatureOfT<S>::parameter_count,
+                                SignatureOfT<F>::parameter_count>::value,
+                            typename SignatureOfT<S>::parameters,
+                            typename SignatureOfT<F>::parameters>::value>;
+
+// If `CONDITION` is true then EnableIf resolves to type T, otherwise an
+// invalid type.
+template <bool CONDITION, typename T = void>
+using EnableIf = typename std::enable_if<CONDITION, T>::type;
+
+// If `BASE` is a base of `T` then EnableIfIsType resolves to type `TRUE`,
+// otherwise an invalid type.
+template <typename BASE, typename T, typename TRUE = void>
+using EnableIfIsType = EnableIf<IsTypeOrDerived<BASE, T>::value, TRUE>;
+
+// If the function-like `F` has a matching signature to the function-like `S`
+// then EnableIfHasSignature resolves to type `TRUE`, otherwise an invalid type.
+template <typename F, typename S, typename TRUE = void>
+using EnableIfHasSignature = EnableIf<HasSignature<F, S>::value, TRUE>;
+
+}  // namespace traits
+}  // namespace dap
+
+#endif  // dap_traits_h
diff --git a/src/session.cpp b/src/session.cpp
index ad6597c..95b30a0 100644
--- a/src/session.cpp
+++ b/src/session.cpp
@@ -335,7 +335,7 @@
     return [=] {
       handler(
           data,
-          [&](const dap::TypeInfo* typeinfo, const void* data) {
+          [=](const dap::TypeInfo* typeinfo, const void* data) {
             // onSuccess
             dap::json::Serializer s;
             s.object([&](dap::FieldSerializer* fs) {
@@ -354,7 +354,7 @@
               handler(data, nullptr);
             }
           },
-          [&](const dap::TypeInfo* typeinfo, const dap::Error& error) {
+          [=](const dap::TypeInfo* typeinfo, const dap::Error& error) {
             // onError
             dap::json::Serializer s;
             s.object([&](dap::FieldSerializer* fs) {
diff --git a/src/session_test.cpp b/src/session_test.cpp
index 21b73d6..eeb8fe3 100644
--- a/src/session_test.cpp
+++ b/src/session_test.cpp
@@ -269,6 +269,142 @@
   ASSERT_EQ(got.error.message, "Oh noes!");
 }
 
+TEST_F(SessionTest, RequestCallbackResponse) {
+  using ResponseCallback = std::function<void(dap::SetBreakpointsResponse)>;
+
+  server->registerHandler(
+      [&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) {
+        dap::SetBreakpointsResponse response;
+        dap::Breakpoint bp;
+        bp.line = 2;
+        response.breakpoints.emplace_back(std::move(bp));
+        callback(response);
+      });
+
+  bind();
+
+  auto got = client->send(dap::SetBreakpointsRequest{}).get();
+
+  // Check response was received correctly.
+  ASSERT_EQ(got.error, false);
+  ASSERT_EQ(got.response.breakpoints.size(), 1U);
+}
+
+TEST_F(SessionTest, RequestCallbackResponseOrError) {
+  using ResponseCallback =
+      std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
+
+  server->registerHandler(
+      [&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) {
+        dap::SetBreakpointsResponse response;
+        dap::Breakpoint bp;
+        bp.line = 2;
+        response.breakpoints.emplace_back(std::move(bp));
+        callback(response);
+      });
+
+  bind();
+
+  auto got = client->send(dap::SetBreakpointsRequest{}).get();
+
+  // Check response was received correctly.
+  ASSERT_EQ(got.error, false);
+  ASSERT_EQ(got.response.breakpoints.size(), 1U);
+}
+
+TEST_F(SessionTest, RequestCallbackError) {
+  using ResponseCallback =
+      std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
+
+  server->registerHandler(
+      [&](const dap::SetBreakpointsRequest&, const ResponseCallback& callback) {
+        callback(dap::Error("Oh noes!"));
+      });
+
+  bind();
+
+  auto got = client->send(dap::SetBreakpointsRequest{}).get();
+
+  // Check response was received correctly.
+  ASSERT_EQ(got.error, true);
+  ASSERT_EQ(got.error.message, "Oh noes!");
+}
+
+TEST_F(SessionTest, RequestCallbackSuccessAfterReturn) {
+  using ResponseCallback =
+      std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
+
+  ResponseCallback callback;
+  std::mutex mutex;
+  std::condition_variable cv;
+
+  server->registerHandler(
+      [&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) {
+        std::unique_lock<std::mutex> lock(mutex);
+        callback = cb;
+        cv.notify_all();
+      });
+
+  bind();
+
+  auto future = client->send(dap::SetBreakpointsRequest{});
+
+  {
+    dap::SetBreakpointsResponse response;
+    dap::Breakpoint bp;
+    bp.line = 2;
+    response.breakpoints.emplace_back(std::move(bp));
+
+    // Wait for the handler to be called.
+    std::unique_lock<std::mutex> lock(mutex);
+    cv.wait(lock, [&] { return static_cast<bool>(callback); });
+
+    // Issue the callback
+    callback(response);
+  }
+
+  auto got = future.get();
+
+  // Check response was received correctly.
+  ASSERT_EQ(got.error, false);
+  ASSERT_EQ(got.response.breakpoints.size(), 1U);
+}
+
+TEST_F(SessionTest, RequestCallbackErrorAfterReturn) {
+  using ResponseCallback =
+      std::function<void(dap::ResponseOrError<dap::SetBreakpointsResponse>)>;
+
+  ResponseCallback callback;
+  std::mutex mutex;
+  std::condition_variable cv;
+
+  server->registerHandler(
+      [&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) {
+        std::unique_lock<std::mutex> lock(mutex);
+        callback = cb;
+        cv.notify_all();
+      });
+
+  bind();
+
+  auto future = client->send(dap::SetBreakpointsRequest{});
+
+  {
+    // Wait for the handler to be called.
+    std::unique_lock<std::mutex> lock(mutex);
+    cv.wait(lock, [&] { return static_cast<bool>(callback); });
+
+    // Issue the callback
+    callback(dap::Error("Oh noes!"));
+  }
+
+  auto got = future.get();
+
+  // Check response was received correctly.
+  ASSERT_EQ(got.error, true);
+  ASSERT_EQ(got.error.message, "Oh noes!");
+}
+
 TEST_F(SessionTest, ResponseSentHandlerSuccess) {
   const auto response = createResponse();
 
diff --git a/src/traits_test.cpp b/src/traits_test.cpp
new file mode 100644
index 0000000..aafca04
--- /dev/null
+++ b/src/traits_test.cpp
@@ -0,0 +1,387 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dap/traits.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace dap {
+namespace traits {
+
+namespace {
+struct S {};
+struct E : S {};
+void F1(S) {}
+void F3(int, S, float) {}
+void E1(E) {}
+void E3(int, E, float) {}
+}  // namespace
+
+TEST(ParameterType, Function) {
+  F1({});        // Avoid unused method warning
+  F3(0, {}, 0);  // Avoid unused method warning
+  static_assert(std::is_same<ParameterType<decltype(&F1), 0>, S>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(&F3), 0>, int>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(&F3), 1>, S>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(&F3), 2>, float>::value,
+                "");
+}
+
+TEST(ParameterType, Method) {
+  class C {
+   public:
+    void F1(S) {}
+    void F3(int, S, float) {}
+  };
+  C().F1({});        // Avoid unused method warning
+  C().F3(0, {}, 0);  // Avoid unused method warning
+  static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
+                "");
+  static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
+                "");
+}
+
+TEST(ParameterType, ConstMethod) {
+  class C {
+   public:
+    void F1(S) const {}
+    void F3(int, S, float) const {}
+  };
+  C().F1({});        // Avoid unused method warning
+  C().F3(0, {}, 0);  // Avoid unused method warning
+  static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
+                "");
+  static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
+                "");
+}
+
+TEST(ParameterType, StaticMethod) {
+  class C {
+   public:
+    static void F1(S) {}
+    static void F3(int, S, float) {}
+  };
+  C::F1({});        // Avoid unused method warning
+  C::F3(0, {}, 0);  // Avoid unused method warning
+  static_assert(std::is_same<ParameterType<decltype(&C::F1), 0>, S>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(&C::F3), 0>, int>::value,
+                "");
+  static_assert(std::is_same<ParameterType<decltype(&C::F3), 1>, S>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(&C::F3), 2>, float>::value,
+                "");
+}
+
+TEST(ParameterType, FunctionLike) {
+  using F1 = std::function<void(S)>;
+  using F3 = std::function<void(int, S, float)>;
+  static_assert(std::is_same<ParameterType<F1, 0>, S>::value, "");
+  static_assert(std::is_same<ParameterType<F3, 0>, int>::value, "");
+  static_assert(std::is_same<ParameterType<F3, 1>, S>::value, "");
+  static_assert(std::is_same<ParameterType<F3, 2>, float>::value, "");
+}
+
+TEST(ParameterType, Lambda) {
+  auto l1 = [](S) {};
+  auto l3 = [](int, S, float) {};
+  static_assert(std::is_same<ParameterType<decltype(l1), 0>, S>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(l3), 0>, int>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(l3), 1>, S>::value, "");
+  static_assert(std::is_same<ParameterType<decltype(l3), 2>, float>::value, "");
+}
+
+TEST(HasSignature, Function) {
+  F1({});        // Avoid unused method warning
+  F3(0, {}, 0);  // Avoid unused method warning
+  static_assert(HasSignature<decltype(&F1), decltype(&F1)>::value, "");
+  static_assert(HasSignature<decltype(&F3), decltype(&F3)>::value, "");
+  static_assert(HasSignature<decltype(&F3), decltype(&F3)>::value, "");
+  static_assert(HasSignature<decltype(&F3), decltype(&F3)>::value, "");
+  static_assert(!HasSignature<decltype(&F1), decltype(&F3)>::value, "");
+  static_assert(!HasSignature<decltype(&F3), decltype(&F1)>::value, "");
+  static_assert(!HasSignature<decltype(&F3), decltype(&F1)>::value, "");
+  static_assert(!HasSignature<decltype(&F3), decltype(&F1)>::value, "");
+}
+
+TEST(HasSignature, Method) {
+  class C {
+   public:
+    void F1(S) {}
+    void F3(int, S, float) {}
+  };
+  C().F1({});        // Avoid unused method warning
+  C().F3(0, {}, 0);  // Avoid unused method warning
+
+  static_assert(HasSignature<decltype(&C::F1), decltype(&F1)>::value, "");
+  static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F1), decltype(&F3)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
+}
+
+TEST(HasSignature, ConstMethod) {
+  class C {
+   public:
+    void F1(S) const {}
+    void F3(int, S, float) const {}
+  };
+  C().F1({});        // Avoid unused method warning
+  C().F3(0, {}, 0);  // Avoid unused method warning
+
+  static_assert(HasSignature<decltype(&C::F1), decltype(&F1)>::value, "");
+  static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F1), decltype(&F3)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
+}
+
+TEST(HasSignature, StaticMethod) {
+  class C {
+   public:
+    static void F1(S) {}
+    static void F3(int, S, float) {}
+  };
+  C::F1({});        // Avoid unused method warning
+  C::F3(0, {}, 0);  // Avoid unused method warning
+
+  static_assert(HasSignature<decltype(&C::F1), decltype(&F1)>::value, "");
+  static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(HasSignature<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F1), decltype(&F3)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!HasSignature<decltype(&C::F3), decltype(&F1)>::value, "");
+}
+
+TEST(HasSignature, FunctionLike) {
+  using f1 = std::function<void(S)>;
+  using f3 = std::function<void(int, S, float)>;
+  static_assert(HasSignature<f1, decltype(&F1)>::value, "");
+  static_assert(HasSignature<f3, decltype(&F3)>::value, "");
+  static_assert(HasSignature<f3, decltype(&F3)>::value, "");
+  static_assert(HasSignature<f3, decltype(&F3)>::value, "");
+  static_assert(!HasSignature<f1, decltype(&F3)>::value, "");
+  static_assert(!HasSignature<f3, decltype(&F1)>::value, "");
+  static_assert(!HasSignature<f3, decltype(&F1)>::value, "");
+  static_assert(!HasSignature<f3, decltype(&F1)>::value, "");
+}
+
+TEST(HasSignature, Lambda) {
+  auto l1 = [](S) {};
+  auto l3 = [](int, S, float) {};
+  static_assert(HasSignature<decltype(l1), decltype(&F1)>::value, "");
+  static_assert(HasSignature<decltype(l3), decltype(&F3)>::value, "");
+  static_assert(HasSignature<decltype(l3), decltype(&F3)>::value, "");
+  static_assert(HasSignature<decltype(l3), decltype(&F3)>::value, "");
+  static_assert(!HasSignature<decltype(l1), decltype(&F3)>::value, "");
+  static_assert(!HasSignature<decltype(l3), decltype(&F1)>::value, "");
+  static_assert(!HasSignature<decltype(l3), decltype(&F1)>::value, "");
+  static_assert(!HasSignature<decltype(l3), decltype(&F1)>::value, "");
+}
+
+////
+
+TEST(CompatibleWith, Function) {
+  F1({});        // Avoid unused method warning
+  F3(0, {}, 0);  // Avoid unused method warning
+  E1({});        // Avoid unused method warning
+  E3(0, {}, 0);  // Avoid unused method warning
+  static_assert(CompatibleWith<decltype(&F1), decltype(&F1)>::value, "");
+  static_assert(CompatibleWith<decltype(&F3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&F3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&F3), decltype(&F3)>::value, "");
+
+  static_assert(!CompatibleWith<decltype(&F1), decltype(&F3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&F3), decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&F3), decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&F3), decltype(&F1)>::value, "");
+
+  static_assert(CompatibleWith<decltype(&E1), decltype(&F1)>::value, "");
+  static_assert(CompatibleWith<decltype(&E3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&E3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&E3), decltype(&F3)>::value, "");
+
+  static_assert(!CompatibleWith<decltype(&F1), decltype(&E1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&F3), decltype(&E3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&F3), decltype(&E3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&F3), decltype(&E3)>::value, "");
+}
+
+TEST(CompatibleWith, Method) {
+  class C {
+   public:
+    void F1(S) {}
+    void F3(int, S, float) {}
+    void E1(E) {}
+    void E3(int, E, float) {}
+  };
+  C().F1({});        // Avoid unused method warning
+  C().F3(0, {}, 0);  // Avoid unused method warning
+  C().E1({});        // Avoid unused method warning
+  C().E3(0, {}, 0);  // Avoid unused method warning
+
+  static_assert(CompatibleWith<decltype(&C::F1), decltype(&F1)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
+
+  static_assert(!CompatibleWith<decltype(&C::F1), decltype(&F3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
+
+  static_assert(CompatibleWith<decltype(&C::E1), decltype(&C::F1)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
+
+  static_assert(!CompatibleWith<decltype(&C::F1), decltype(&C::E1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
+}
+
+TEST(CompatibleWith, ConstMethod) {
+  class C {
+   public:
+    void F1(S) const {}
+    void F3(int, S, float) const {}
+    void E1(E) const {}
+    void E3(int, E, float) const {}
+  };
+  C().F1({});        // Avoid unused method warning
+  C().F3(0, {}, 0);  // Avoid unused method warning
+  C().E1({});        // Avoid unused method warning
+  C().E3(0, {}, 0);  // Avoid unused method warning
+
+  static_assert(CompatibleWith<decltype(&C::F1), decltype(&F1)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
+
+  static_assert(!CompatibleWith<decltype(&C::F1), decltype(&F3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
+
+  static_assert(CompatibleWith<decltype(&C::E1), decltype(&C::F1)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
+
+  static_assert(!CompatibleWith<decltype(&C::F1), decltype(&C::E1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
+}
+
+TEST(CompatibleWith, StaticMethod) {
+  class C {
+   public:
+    static void F1(S) {}
+    static void F3(int, S, float) {}
+    static void E1(E) {}
+    static void E3(int, E, float) {}
+  };
+  C::F1({});        // Avoid unused method warning
+  C::F3(0, {}, 0);  // Avoid unused method warning
+  C::E1({});        // Avoid unused method warning
+  C::E3(0, {}, 0);  // Avoid unused method warning
+
+  static_assert(CompatibleWith<decltype(&C::F1), decltype(&F1)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::F3), decltype(&F3)>::value, "");
+
+  static_assert(!CompatibleWith<decltype(&C::F1), decltype(&F3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&F1)>::value, "");
+
+  static_assert(CompatibleWith<decltype(&C::E1), decltype(&C::F1)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
+  static_assert(CompatibleWith<decltype(&C::E3), decltype(&C::F3)>::value, "");
+
+  static_assert(!CompatibleWith<decltype(&C::F1), decltype(&C::E1)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
+  static_assert(!CompatibleWith<decltype(&C::F3), decltype(&C::E3)>::value, "");
+}
+
+TEST(CompatibleWith, FunctionLike) {
+  using f1 = std::function<void(S)>;
+  using f3 = std::function<void(int, S, float)>;
+  using e1 = std::function<void(E)>;
+  using e3 = std::function<void(int, E, float)>;
+  static_assert(CompatibleWith<f1, decltype(&F1)>::value, "");
+  static_assert(CompatibleWith<f3, decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<f3, decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<f3, decltype(&F3)>::value, "");
+
+  static_assert(!CompatibleWith<f1, decltype(&F3)>::value, "");
+  static_assert(!CompatibleWith<f3, decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<f3, decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<f3, decltype(&F1)>::value, "");
+
+  static_assert(CompatibleWith<e1, f1>::value, "");
+  static_assert(CompatibleWith<e3, f3>::value, "");
+  static_assert(CompatibleWith<e3, f3>::value, "");
+  static_assert(CompatibleWith<e3, f3>::value, "");
+
+  static_assert(!CompatibleWith<f1, e1>::value, "");
+  static_assert(!CompatibleWith<f3, e3>::value, "");
+  static_assert(!CompatibleWith<f3, e3>::value, "");
+  static_assert(!CompatibleWith<f3, e3>::value, "");
+}
+
+TEST(CompatibleWith, Lambda) {
+  auto f1 = [](S) {};
+  auto f3 = [](int, S, float) {};
+  auto e1 = [](E) {};
+  auto e3 = [](int, E, float) {};
+  static_assert(CompatibleWith<decltype(f1), decltype(&F1)>::value, "");
+  static_assert(CompatibleWith<decltype(f3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(f3), decltype(&F3)>::value, "");
+  static_assert(CompatibleWith<decltype(f3), decltype(&F3)>::value, "");
+
+  static_assert(!CompatibleWith<decltype(f1), decltype(&F3)>::value, "");
+  static_assert(!CompatibleWith<decltype(f3), decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<decltype(f3), decltype(&F1)>::value, "");
+  static_assert(!CompatibleWith<decltype(f3), decltype(&F1)>::value, "");
+
+  static_assert(CompatibleWith<decltype(e1), decltype(f1)>::value, "");
+  static_assert(CompatibleWith<decltype(e3), decltype(f3)>::value, "");
+  static_assert(CompatibleWith<decltype(e3), decltype(f3)>::value, "");
+  static_assert(CompatibleWith<decltype(e3), decltype(f3)>::value, "");
+
+  static_assert(!CompatibleWith<decltype(f1), decltype(e1)>::value, "");
+  static_assert(!CompatibleWith<decltype(f3), decltype(e3)>::value, "");
+  static_assert(!CompatibleWith<decltype(f3), decltype(e3)>::value, "");
+  static_assert(!CompatibleWith<decltype(f3), decltype(e3)>::value, "");
+}
+
+}  // namespace traits
+}  // namespace dap