16namespace opcua::services::detail {
21template <
typename Response>
22struct AsyncServiceAdapter {
23 using ExceptionCatcher = opcua::detail::ExceptionCatcher;
25 template <
typename Context>
26 struct CallbackAndContext {
28 std::unique_ptr<Context> context;
31 template <
typename CompletionHandler>
32 static auto makeCallbackAndContext(
33 ExceptionCatcher& exceptionCatcher, CompletionHandler&& handler
35 static_assert(std::is_invocable_v<CompletionHandler, Response&>);
38 ExceptionCatcher* catcher;
39 std::decay_t<CompletionHandler> handler;
43 [](
UA_Client*,
void* userdata, uint32_t ,
void* responsePtr) {
44 std::unique_ptr<Context> context{
static_cast<Context*
>(userdata)};
45 assert(context !=
nullptr);
46 assert(context->catcher !=
nullptr);
47 context->catcher->invoke([context = context.get(), responsePtr] {
48 if (responsePtr == nullptr) {
49 throw BadStatus{UA_STATUSCODE_BADUNEXPECTEDERROR};
51 std::invoke(context->handler, *
static_cast<Response*
>(responsePtr));
55 return CallbackAndContext<Context>{
57 std::make_unique<Context>(
58 Context{&exceptionCatcher, std::forward<CompletionHandler>(handler)}
75 template <
typename Initiation,
typename CompletionToken,
typename... Args>
77 Client& client, Initiation&& initiation, CompletionToken&& token, Args&&... args
79 static_assert(std::is_invocable_v<
85 return asyncInitiate<Response>(
86 [&client](
auto&& handler,
auto&& innerInitiation,
auto&&... innerArgs) {
87 auto& catcher = opcua::detail::getExceptionCatcher(client);
90 auto callbackAndContext = makeCallbackAndContext(
91 catcher, std::forward<
decltype(handler)>(handler)
94 std::forward<
decltype(innerInitiation)>(innerInitiation),
95 callbackAndContext.callback,
96 callbackAndContext.context.get(),
97 std::forward<
decltype(innerArgs)>(innerArgs)...
101 callbackAndContext.context.release();
103 catcher.setException(std::current_exception());
106 std::forward<CompletionToken>(token),
107 std::forward<Initiation>(initiation),
108 std::forward<Args>(args)...
114template <
typename Request,
typename Response,
typename CompletionToken>
115auto sendRequestAsync(Client& client,
const Request& request, CompletionToken&& token) {
116 return AsyncServiceAdapter<Response>::initiate(
119 UA_ClientAsyncServiceCallback callback,
void* userdata,
const Request& innerRequest
122 opcua::detail::getHandle(client),
124 &getDataType<Request>(),
126 &getDataType<Response>(),
131 std::forward<CompletionToken>(token),
137template <
typename Request,
typename Response>
138Response sendRequest(Client& client,
const Request& request)
noexcept {
141 opcua::detail::getHandle(client),
143 &getDataType<Request>(),
145 &getDataType<Response>()
void(* UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response)
UA_StatusCode UA_THREADSAFE __UA_Client_AsyncService(UA_Client *client, const void *request, const UA_DataType *requestType, UA_ClientAsyncServiceCallback callback, const UA_DataType *responseType, void *userdata, UA_UInt32 *requestId)
void UA_THREADSAFE __UA_Client_Service(UA_Client *client, const void *request, const UA_DataType *requestType, void *response, const UA_DataType *responseType)
struct UA_Client UA_Client
constexpr void throwIfBad(UA_StatusCode code)
Check the status code and throw a BadStatus exception if the status code is bad.