From 2a3ba209374f7384e6a78a47bafbd14ec5eceab4 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Wed, 3 Feb 2021 19:08:27 +0000 Subject: [PATCH] Merge pull request #58 from ben-clayton/response-callbacks Add Session::registerHandler() overloads that support response callbacks --- CMakeLists.txt | 1 + include/dap/session.h | 135 ++++++++++----- include/dap/traits.h | 159 +++++++++++++++++ src/session.cpp | 4 +- src/session_test.cpp | 136 +++++++++++++++ src/traits_test.cpp | 387 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 776 insertions(+), 46 deletions(-) create mode 100644 include/dap/traits.h create mode 100644 src/traits_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ec3ac0..47cb5df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,6 +221,7 @@ if(CPPDAP_BUILD_TESTS) ${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 Request; struct Response; struct Event; -// internal functionality -namespace detail { -template -struct traits { - static constexpr bool isRequest = std::is_base_of::value; - static constexpr bool isResponse = std::is_base_of::value; - static constexpr bool isEvent = std::is_base_of::value; -}; - -// ArgTy::type resolves to the first argument type of the function F. -// F can be a function, static member function, or lambda. -template -struct ArgTy { - using type = typename ArgTy::type; -}; - -template -struct ArgTy { - using type = typename std::decay::type; -}; - -template -struct ArgTy { - using type = typename std::decay::type; -}; -} // namespace detail - //////////////////////////////////////////////////////////////////////////////// // Error //////////////////////////////////////////////////////////////////////////////// @@ -137,14 +111,24 @@ ResponseOrError& ResponseOrError::operator=(ResponseOrError&& other) { // (3) Bind the session to the remote endpoint with bind(). // (4) Send requests or events with send(). class Session { - template - using IsRequest = typename std::enable_if::isRequest>::type; + template + using ParamType = traits::ParameterType; template - using IsEvent = typename std::enable_if::isEvent>::type; + using IsRequest = traits::EnableIfIsType; + + template + using IsEvent = traits::EnableIfIsType; template - using ArgTy = typename detail::ArgTy::type; + using IsRequestHandlerWithoutCallback = traits::EnableIf< + traits::CompatibleWith>::value>; + + template + using IsRequestHandlerWithCallback = traits::EnableIf)>>:: + value>; public: virtual ~Session(); @@ -167,20 +151,47 @@ class Session { // ResponseOrError(const RequestType&) // ResponseType(const RequestType&) // Error(const RequestType&) - template > - inline IsRequest registerHandler(F&& handler); + template > + inline IsRequestHandlerWithoutCallback 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 response) + template , + typename ResponseType = typename RequestType::Response> + inline IsRequestHandlerWithCallback 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)> response) + template , + typename ResponseType = typename RequestType::Response> + inline IsRequestHandlerWithCallback> + 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 > + template > inline IsEvent 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&) - template ::Request> + template ::Request> inline void registerSentHandler(F&& handler); // send() sends the request to the connected endpoint and returns a @@ -251,21 +262,57 @@ class Session { virtual bool send(const TypeInfo*, const void* event) = 0; }; -template -Session::IsRequest Session::registerHandler(F&& handler) { - using ResponseType = typename T::Response; - auto cb = [handler](const void* args, const RequestSuccessCallback& onSuccess, - const RequestErrorCallback& onError) { +template +Session::IsRequestHandlerWithoutCallback Session::registerHandler( + F&& handler) { + using ResponseType = typename RequestType::Response; + const TypeInfo* typeinfo = TypeOf::type(); + registerHandler(typeinfo, [handler](const void* args, + const RequestSuccessCallback& onSuccess, + const RequestErrorCallback& onError) { ResponseOrError res = - handler(*reinterpret_cast(args)); + handler(*reinterpret_cast(args)); if (res.error) { onError(TypeOf::type(), res.error); } else { onSuccess(TypeOf::type(), &res.response); } - }; - const TypeInfo* typeinfo = TypeOf::type(); - registerHandler(typeinfo, cb); + }); +} + +template +Session::IsRequestHandlerWithCallback Session::registerHandler( + F&& handler) { + using CallbackType = ParamType; + registerHandler( + TypeOf::type(), + [handler](const void* args, const RequestSuccessCallback& onSuccess, + const RequestErrorCallback&) { + CallbackType responseCallback = [onSuccess](const ResponseType& res) { + onSuccess(TypeOf::type(), &res); + }; + handler(*reinterpret_cast(args), responseCallback); + }); +} + +template +Session::IsRequestHandlerWithCallback> +Session::registerHandler(F&& handler) { + using CallbackType = ParamType; + registerHandler( + TypeOf::type(), + [handler](const void* args, const RequestSuccessCallback& onSuccess, + const RequestErrorCallback& onError) { + CallbackType responseCallback = + [onError, onSuccess](const ResponseOrError& res) { + if (res.error) { + onError(TypeOf::type(), res.error); + } else { + onSuccess(TypeOf::type(), &res.response); + } + }; + handler(*reinterpret_cast(args), responseCallback); + }); } template 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 +#include + +namespace dap { +namespace traits { + +// NthTypeOf returns the `N`th type in `Types` +template +using NthTypeOf = typename std::tuple_element>::type; + +// `IsTypeOrDerived::value` is true iff `T` is of type `BASE`, or +// derives from `BASE`. +template +using IsTypeOrDerived = std::integral_constant< + bool, + std::is_base_of::type>::value || + std::is_same::type>::value>; + +// `EachIsTypeOrDerived::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 +struct EachIsTypeOrDerived { + using base = typename std::tuple_element::type; + using type = typename std::tuple_element::type; + using last_matches = IsTypeOrDerived; + using others_match = EachIsTypeOrDerived; + static constexpr bool value = last_matches::value && others_match::value; +}; + +// EachIsTypeOrDerived specialization for N = 1 +template +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::value; +}; + +// EachIsTypeOrDerived specialization for N = 0 +template +struct EachIsTypeOrDerived<0, BASES, TYPES> { + static constexpr bool value = true; +}; + +// Signature describes the signature of a function. +template +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; + // The type of the Nth parameter of function signature + template + using parameter = NthTypeOf; + // 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 +struct SignatureOf { + // The signature of the function-like object `F` + using type = typename SignatureOf::type; +}; + +// SignatureOf specialization for a regular function or static method. +template +struct SignatureOf { + // The signature of the function-like object `F` + using type = Signature::type, + typename std::decay::type...>; +}; + +// SignatureOf specialization for a non-static method. +template +struct SignatureOf { + // The signature of the function-like object `F` + using type = Signature::type, + typename std::decay::type...>; +}; + +// SignatureOf specialization for a non-static, const method. +template +struct SignatureOf { + // The signature of the function-like object `F` + using type = Signature::type, + typename std::decay::type...>; +}; + +// SignatureOfT is an alias to `typename SignatureOf::type`. +template +using SignatureOfT = typename SignatureOf::type; + +// ParameterType is an alias to `typename SignatureOf::type::parameter`. +template +using ParameterType = typename SignatureOfT::template parameter; + +// `HasSignature::value` is true iff the function-like `F` has a matching +// signature to the function-like `S`. +template +using HasSignature = std::integral_constant< + bool, + std::is_same, SignatureOfT>::value>; + +// `Min::value` resolves to the smaller value of A and B. +template +using Min = std::integral_constant; + +// `CompatibleWith::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 +using CompatibleWith = std::integral_constant< + bool, + (SignatureOfT::parameter_count == SignatureOfT::parameter_count) && + EachIsTypeOrDerived::parameter_count, + SignatureOfT::parameter_count>::value, + typename SignatureOfT::parameters, + typename SignatureOfT::parameters>::value>; + +// If `CONDITION` is true then EnableIf resolves to type T, otherwise an +// invalid type. +template +using EnableIf = typename std::enable_if::type; + +// If `BASE` is a base of `T` then EnableIfIsType resolves to type `TRUE`, +// otherwise an invalid type. +template +using EnableIfIsType = EnableIf::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 +using EnableIfHasSignature = EnableIf::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 @@ class Impl : public dap::Session { 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 @@ class Impl : public dap::Session { 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 @@ TEST_F(SessionTest, RequestResponseError) { ASSERT_EQ(got.error.message, "Oh noes!"); } +TEST_F(SessionTest, RequestCallbackResponse) { + using ResponseCallback = std::function; + + 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)>; + + 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)>; + + 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)>; + + ResponseCallback callback; + std::mutex mutex; + std::condition_variable cv; + + server->registerHandler( + [&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) { + std::unique_lock 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 lock(mutex); + cv.wait(lock, [&] { return static_cast(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)>; + + ResponseCallback callback; + std::mutex mutex; + std::condition_variable cv; + + server->registerHandler( + [&](const dap::SetBreakpointsRequest&, const ResponseCallback& cb) { + std::unique_lock lock(mutex); + callback = cb; + cv.notify_all(); + }); + + bind(); + + auto future = client->send(dap::SetBreakpointsRequest{}); + + { + // Wait for the handler to be called. + std::unique_lock lock(mutex); + cv.wait(lock, [&] { return static_cast(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, S>::value, ""); + static_assert(std::is_same, int>::value, ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, 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, S>::value, ""); + static_assert(std::is_same, int>::value, + ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, 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, S>::value, ""); + static_assert(std::is_same, int>::value, + ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, 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, S>::value, ""); + static_assert(std::is_same, int>::value, + ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, float>::value, + ""); +} + +TEST(ParameterType, FunctionLike) { + using F1 = std::function; + using F3 = std::function; + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, int>::value, ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, float>::value, ""); +} + +TEST(ParameterType, Lambda) { + auto l1 = [](S) {}; + auto l3 = [](int, S, float) {}; + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, int>::value, ""); + static_assert(std::is_same, S>::value, ""); + static_assert(std::is_same, float>::value, ""); +} + +TEST(HasSignature, Function) { + F1({}); // Avoid unused method warning + F3(0, {}, 0); // Avoid unused method warning + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::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::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::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::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::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::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); +} + +TEST(HasSignature, FunctionLike) { + using f1 = std::function; + using f3 = std::function; + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); +} + +TEST(HasSignature, Lambda) { + auto l1 = [](S) {}; + auto l3 = [](int, S, float) {}; + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::value, ""); + static_assert(!HasSignature::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::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::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::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::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::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::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::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); +} + +TEST(CompatibleWith, FunctionLike) { + using f1 = std::function; + using f3 = std::function; + using e1 = std::function; + using e3 = std::function; + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); +} + +TEST(CompatibleWith, Lambda) { + auto f1 = [](S) {}; + auto f3 = [](int, S, float) {}; + auto e1 = [](E) {}; + auto e3 = [](int, E, float) {}; + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + static_assert(CompatibleWith::value, ""); + + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); + static_assert(!CompatibleWith::value, ""); +} + +} // namespace traits +} // namespace dap