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 createCallbackAndContext(
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)}
70 template <
typename Initiation,
typename CompletionToken>
71 static auto initiate(
Client& client, Initiation&& initiation, CompletionToken&& token) {
72 static_assert(std::is_invocable_v<Initiation, UA_ClientAsyncServiceCallback, void*>);
76 auto& catcher = opcua::detail::getExceptionCatcher(client);
79 auto callbackAndContext = createCallbackAndContext(
80 catcher, std::forward<
decltype(handler)>(handler)
83 std::forward<Initiation>(initiation),
84 callbackAndContext.callback,
85 callbackAndContext.context.get()
89 callbackAndContext.context.release();
91 catcher.setException(std::current_exception());
94 std::forward<CompletionToken>(token)
100template <
typename Request,
typename Response,
typename CompletionToken>
101auto sendRequestAsync(Client& client,
const Request& request, CompletionToken&& token) {
102 return AsyncServiceAdapter<Response>::initiate(
104 [&](UA_ClientAsyncServiceCallback callback,
void* userdata) {
106 opcua::detail::getHandle(client),
108 &getDataType<Request>(),
110 &getDataType<Response>(),
115 std::forward<CompletionToken>(token)
120template <
typename Request,
typename Response>
121Response sendRequest(Client& client,
const Request& request)
noexcept {
124 opcua::detail::getHandle(client),
126 &getDataType<Request>(),
128 &getDataType<Response>()
void(* UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata, UA_UInt32 requestId, void *response)
void __UA_Client_Service(UA_Client *client, const void *request, const UA_DataType *requestType, void *response, const UA_DataType *responseType)
UA_StatusCode __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)
auto asyncInitiate(Initiation &&initiation, CompletionToken &&token, Args &&... args)
constexpr void throwIfBad(UA_StatusCode code)
Check the status code and throw a BadStatus exception if the status code is bad.