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
83 return asyncInitiate<Response>(
84 [&client](
auto&& handler,
auto&& innerInitiation,
auto&&... innerArgs) {
85 auto& catcher = opcua::detail::getExceptionCatcher(client);
88 auto callbackAndContext = makeCallbackAndContext(
89 catcher, std::forward<
decltype(handler)>(handler)
92 std::forward<
decltype(innerInitiation)>(innerInitiation),
93 callbackAndContext.callback,
94 callbackAndContext.context.get(),
95 std::forward<
decltype(innerArgs)>(innerArgs)...
99 callbackAndContext.context.release();
101 catcher.setException(std::current_exception());
104 std::forward<CompletionToken>(token),
105 std::forward<Initiation>(initiation),
106 std::forward<Args>(args)...
112template <
typename Request,
typename Response,
typename CompletionToken>
113auto sendRequestAsync(Client& client,
const Request& request, CompletionToken&& token) {
114 return AsyncServiceAdapter<Response>::initiate(
117 UA_ClientAsyncServiceCallback callback,
void* userdata,
const Request& innerRequest
120 opcua::detail::getHandle(client),
122 &getDataType<Request>(),
124 &getDataType<Response>(),
129 std::forward<CompletionToken>(token),
135template <
typename Request,
typename Response>
136Response sendRequest(Client& client,
const Request& request)
noexcept {
139 opcua::detail::getHandle(client),
141 &getDataType<Request>(),
143 &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.