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