| /* |
| *------------------------------------------------------------------ |
| * Copyright (c) 2017 Cisco and/or its affiliates. |
| * 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: |
| * |
| * http://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 vapi_hpp_included |
| #define vapi_hpp_included |
| |
| #include <cstddef> |
| #include <vector> |
| #include <mutex> |
| #include <queue> |
| #include <cassert> |
| #include <functional> |
| #include <algorithm> |
| #include <atomic> |
| #include <vppinfra/types.h> |
| #include <vapi/vapi.h> |
| #include <vapi/vapi_internal.h> |
| #include <vapi/vapi_dbg.h> |
| #include <vapi/vpe.api.vapi.h> |
| |
| #if VAPI_CPP_DEBUG_LEAKS |
| #include <unordered_set> |
| #endif |
| |
| /** |
| * @file |
| * @brief C++ VPP API |
| */ |
| |
| namespace vapi |
| { |
| |
| class Connection; |
| |
| template <typename Req, typename Resp, typename... Args> class Request; |
| template <typename M> class Msg; |
| template <typename M> void vapi_swap_to_be (M *msg); |
| template <typename M> void vapi_swap_to_host (M *msg); |
| template <typename M, typename... Args> |
| M *vapi_alloc (Connection &con, Args...); |
| template <typename M> vapi_msg_id_t vapi_get_msg_id_t (); |
| template <typename M> class Event_registration; |
| |
| class Unexpected_msg_id_exception : public std::exception |
| { |
| public: |
| virtual const char *what () const throw () |
| { |
| return "unexpected message id"; |
| } |
| }; |
| |
| class Msg_not_available_exception : public std::exception |
| { |
| public: |
| virtual const char *what () const throw () |
| { |
| return "message unavailable"; |
| } |
| }; |
| |
| typedef enum { |
| /** response not ready yet */ |
| RESPONSE_NOT_READY, |
| |
| /** response to request is ready */ |
| RESPONSE_READY, |
| |
| /** no response to request (will never come) */ |
| RESPONSE_NO_RESPONSE, |
| } vapi_response_state_e; |
| |
| /** |
| * Class representing common functionality of a request - response state |
| * and context |
| */ |
| class Common_req |
| { |
| public: |
| virtual ~Common_req (){}; |
| |
| Connection &get_connection () |
| { |
| return con; |
| }; |
| |
| vapi_response_state_e get_response_state (void) const |
| { |
| return response_state; |
| } |
| |
| private: |
| Connection &con; |
| Common_req (Connection &con) |
| : con (con), context{0}, response_state{RESPONSE_NOT_READY} |
| { |
| } |
| |
| void set_response_state (vapi_response_state_e state) |
| { |
| response_state = state; |
| } |
| |
| virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id, |
| void *shm_data) = 0; |
| |
| void set_context (u32 context) |
| { |
| this->context = context; |
| } |
| |
| u32 get_context () |
| { |
| return context; |
| } |
| |
| u32 context; |
| vapi_response_state_e response_state; |
| |
| friend class Connection; |
| |
| template <typename M> friend class Msg; |
| |
| template <typename Req, typename Resp, typename... Args> |
| friend class Request; |
| |
| template <typename Req, typename Resp, typename... Args> friend class Dump; |
| |
| template <typename M> friend class Event_registration; |
| }; |
| |
| /** |
| * Class representing a connection to VPP |
| * |
| * After creating a Connection object, call connect() to actually connect |
| * to VPP. Use is_msg_available to discover whether a specific message is known |
| * and supported by the VPP connected to. |
| */ |
| class Connection |
| { |
| public: |
| Connection (void) : vapi_ctx{0}, event_count{0} |
| { |
| |
| vapi_error_e rv = VAPI_OK; |
| if (!vapi_ctx) |
| { |
| if (VAPI_OK != (rv = vapi_ctx_alloc (&vapi_ctx))) |
| { |
| throw std::bad_alloc (); |
| } |
| } |
| events.reserve (vapi_get_message_count () + 1); |
| } |
| |
| Connection (const Connection &) = delete; |
| |
| ~Connection (void) |
| { |
| vapi_ctx_free (vapi_ctx); |
| #if VAPI_CPP_DEBUG_LEAKS |
| for (auto x : shm_data_set) |
| { |
| printf ("Leaked shm_data@%p!\n", x); |
| } |
| #endif |
| } |
| |
| /** |
| * @brief check if message identified by it's message id is known by the |
| * vpp to which the connection is open |
| */ |
| bool is_msg_available (vapi_msg_id_t type) |
| { |
| return vapi_is_msg_available (vapi_ctx, type); |
| } |
| |
| /** |
| * @brief connect to vpp |
| * |
| * @param name application name |
| * @param chroot_prefix shared memory prefix |
| * @param max_queued_request max number of outstanding requests queued |
| * |
| * @return VAPI_OK on success, other error code on error |
| */ |
| vapi_error_e connect (const char *name, const char *chroot_prefix, |
| int max_outstanding_requests, int response_queue_size) |
| { |
| return vapi_connect (vapi_ctx, name, chroot_prefix, |
| max_outstanding_requests, response_queue_size, |
| VAPI_MODE_BLOCKING); |
| } |
| |
| /** |
| * @brief disconnect from vpp |
| * |
| * @return VAPI_OK on success, other error code on error |
| */ |
| vapi_error_e disconnect () |
| { |
| auto x = requests.size (); |
| while (x > 0) |
| { |
| VAPI_DBG ("popping request @%p", requests.front ()); |
| requests.pop_front (); |
| --x; |
| } |
| return vapi_disconnect (vapi_ctx); |
| }; |
| |
| /** |
| * @brief get event file descriptor |
| * |
| * @note this file descriptor becomes readable when messages (from vpp) |
| * are waiting in queue |
| * |
| * @param[out] fd pointer to result variable |
| * |
| * @return VAPI_OK on success, other error code on error |
| */ |
| vapi_error_e get_fd (int *fd) |
| { |
| return vapi_get_fd (vapi_ctx, fd); |
| } |
| |
| /** |
| * @brief wait for responses from vpp and assign them to appropriate objects |
| * |
| * @param limit stop dispatch after the limit object received it's response |
| * |
| * @return VAPI_OK on success, other error code on error |
| */ |
| vapi_error_e dispatch (const Common_req *limit = nullptr) |
| { |
| std::lock_guard<std::mutex> lock (dispatch_mutex); |
| vapi_error_e rv = VAPI_OK; |
| bool loop_again = true; |
| while (loop_again) |
| { |
| void *shm_data; |
| size_t shm_data_size; |
| rv = vapi_recv (vapi_ctx, &shm_data, &shm_data_size); |
| if (VAPI_OK != rv) |
| { |
| return rv; |
| } |
| #if VAPI_CPP_DEBUG_LEAKS |
| on_shm_data_alloc (shm_data); |
| #endif |
| std::lock_guard<std::recursive_mutex> requests_lock (requests_mutex); |
| std::lock_guard<std::recursive_mutex> events_lock (events_mutex); |
| vapi_msg_id_t id = vapi_lookup_vapi_msg_id_t ( |
| vapi_ctx, be16toh (*static_cast<u16 *> (shm_data))); |
| bool has_context = vapi_msg_is_with_context (id); |
| bool break_dispatch = false; |
| Common_req *matching_req = nullptr; |
| if (has_context) |
| { |
| u32 context = *reinterpret_cast<u32 *> ( |
| (static_cast<u8 *> (shm_data) + vapi_get_context_offset (id))); |
| const auto x = requests.front (); |
| matching_req = x; |
| if (context == x->context) |
| { |
| std::tie (rv, break_dispatch) = |
| x->assign_response (id, shm_data); |
| } |
| else |
| { |
| std::tie (rv, break_dispatch) = |
| x->assign_response (id, nullptr); |
| } |
| if (break_dispatch) |
| { |
| requests.pop_front (); |
| } |
| } |
| else |
| { |
| if (events[id]) |
| { |
| std::tie (rv, break_dispatch) = |
| events[id]->assign_response (id, shm_data); |
| matching_req = events[id]; |
| } |
| else |
| { |
| msg_free (shm_data); |
| } |
| } |
| if ((matching_req && matching_req == limit && break_dispatch) || |
| VAPI_OK != rv) |
| { |
| return rv; |
| } |
| loop_again = !requests.empty () || (event_count > 0); |
| } |
| return rv; |
| } |
| |
| /** |
| * @brief convenience wrapper function |
| */ |
| vapi_error_e dispatch (const Common_req &limit) |
| { |
| return dispatch (&limit); |
| } |
| |
| /** |
| * @brief wait for response to a specific request |
| * |
| * @param req request to wait for response for |
| * |
| * @return VAPI_OK on success, other error code on error |
| */ |
| vapi_error_e wait_for_response (const Common_req &req) |
| { |
| if (RESPONSE_READY == req.get_response_state ()) |
| { |
| return VAPI_OK; |
| } |
| return dispatch (req); |
| } |
| |
| private: |
| void msg_free (void *shm_data) |
| { |
| #if VAPI_CPP_DEBUG_LEAKS |
| on_shm_data_free (shm_data); |
| #endif |
| vapi_msg_free (vapi_ctx, shm_data); |
| } |
| |
| template <template <typename XReq, typename XResp, typename... XArgs> |
| class X, |
| typename Req, typename Resp, typename... Args> |
| vapi_error_e send (X<Req, Resp, Args...> *req) |
| { |
| if (!req) |
| { |
| return VAPI_EINVAL; |
| } |
| u32 req_context = |
| req_context_counter.fetch_add (1, std::memory_order_relaxed); |
| req->request.shm_data->header.context = req_context; |
| vapi_swap_to_be<Req> (req->request.shm_data); |
| std::lock_guard<std::recursive_mutex> lock (requests_mutex); |
| vapi_error_e rv = vapi_send (vapi_ctx, req->request.shm_data); |
| if (VAPI_OK == rv) |
| { |
| VAPI_DBG ("Push %p", req); |
| requests.emplace_back (req); |
| req->set_context (req_context); |
| #if VAPI_CPP_DEBUG_LEAKS |
| on_shm_data_free (req->request.shm_data); |
| #endif |
| req->request.shm_data = nullptr; /* consumed by vapi_send */ |
| } |
| else |
| { |
| vapi_swap_to_host<Req> (req->request.shm_data); |
| } |
| return rv; |
| } |
| |
| template <template <typename XReq, typename XResp, typename... XArgs> |
| class X, |
| typename Req, typename Resp, typename... Args> |
| vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req) |
| { |
| if (!req) |
| { |
| return VAPI_EINVAL; |
| } |
| u32 req_context = |
| req_context_counter.fetch_add (1, std::memory_order_relaxed); |
| req->request.shm_data->header.context = req_context; |
| vapi_swap_to_be<Req> (req->request.shm_data); |
| std::lock_guard<std::recursive_mutex> lock (requests_mutex); |
| vapi_error_e rv = vapi_send_with_control_ping ( |
| vapi_ctx, req->request.shm_data, req_context); |
| if (VAPI_OK == rv) |
| { |
| VAPI_DBG ("Push %p", req); |
| requests.emplace_back (req); |
| req->set_context (req_context); |
| #if VAPI_CPP_DEBUG_LEAKS |
| on_shm_data_free (req->request.shm_data); |
| #endif |
| req->request.shm_data = nullptr; /* consumed by vapi_send */ |
| } |
| else |
| { |
| vapi_swap_to_host<Req> (req->request.shm_data); |
| } |
| return rv; |
| } |
| |
| void unregister_request (Common_req *request) |
| { |
| std::lock_guard<std::recursive_mutex> lock (requests_mutex); |
| std::remove (requests.begin (), requests.end (), request); |
| } |
| |
| template <typename M> void register_event (Event_registration<M> *event) |
| { |
| const vapi_msg_id_t id = M::get_msg_id (); |
| std::lock_guard<std::recursive_mutex> lock (events_mutex); |
| events[id] = event; |
| ++event_count; |
| } |
| |
| template <typename M> void unregister_event (Event_registration<M> *event) |
| { |
| const vapi_msg_id_t id = M::get_msg_id (); |
| std::lock_guard<std::recursive_mutex> lock (events_mutex); |
| events[id] = nullptr; |
| --event_count; |
| } |
| |
| vapi_ctx_t vapi_ctx; |
| std::atomic_ulong req_context_counter; |
| std::mutex dispatch_mutex; |
| |
| std::recursive_mutex requests_mutex; |
| std::recursive_mutex events_mutex; |
| std::deque<Common_req *> requests; |
| std::vector<Common_req *> events; |
| int event_count; |
| |
| template <typename Req, typename Resp, typename... Args> |
| friend class Request; |
| |
| template <typename Req, typename Resp, typename... Args> friend class Dump; |
| |
| template <typename M> friend class Result_set; |
| |
| template <typename M> friend class Event_registration; |
| |
| template <typename M, typename... Args> |
| friend M *vapi_alloc (Connection &con, Args...); |
| |
| template <typename M> friend class Msg; |
| |
| #if VAPI_CPP_DEBUG_LEAKS |
| void on_shm_data_alloc (void *shm_data) |
| { |
| if (shm_data) |
| { |
| auto pos = shm_data_set.find (shm_data); |
| if (pos == shm_data_set.end ()) |
| { |
| shm_data_set.insert (shm_data); |
| } |
| else |
| { |
| printf ("Double-add shm_data @%p!\n", shm_data); |
| } |
| } |
| } |
| |
| void on_shm_data_free (void *shm_data) |
| { |
| auto pos = shm_data_set.find (shm_data); |
| if (pos == shm_data_set.end ()) |
| { |
| printf ("Freeing untracked shm_data @%p!\n", shm_data); |
| } |
| else |
| { |
| shm_data_set.erase (pos); |
| } |
| } |
| std::unordered_set<void *> shm_data_set; |
| #endif |
| }; |
| |
| template <typename Req, typename Resp, typename... Args> class Request; |
| |
| template <typename Req, typename Resp, typename... Args> class Dump; |
| |
| template <class, class = void> struct vapi_has_payload_trait : std::false_type |
| { |
| }; |
| |
| template <class... T> using vapi_void_t = void; |
| |
| template <class T> |
| struct vapi_has_payload_trait<T, vapi_void_t<decltype (&T::payload)>> |
| : std::true_type |
| { |
| }; |
| |
| template <typename M> void vapi_msg_set_msg_id (vapi_msg_id_t id) |
| { |
| Msg<M>::set_msg_id (id); |
| } |
| |
| /** |
| * Class representing a message stored in shared memory |
| */ |
| template <typename M> class Msg |
| { |
| public: |
| Msg (const Msg &) = delete; |
| |
| ~Msg () |
| { |
| VAPI_DBG ("Destroy Msg<%s>@%p, shm_data@%p", |
| vapi_get_msg_name (get_msg_id ()), this, shm_data); |
| if (shm_data) |
| { |
| con.get ().msg_free (shm_data); |
| shm_data = nullptr; |
| } |
| } |
| |
| static vapi_msg_id_t get_msg_id () |
| { |
| return *msg_id_holder (); |
| } |
| |
| template <typename X = M> |
| typename std::enable_if<vapi_has_payload_trait<X>::value, |
| decltype (X::payload) &>::type |
| get_payload () const |
| { |
| return shm_data->payload; |
| } |
| |
| private: |
| Msg (Msg<M> &&msg) : con{msg.con} |
| { |
| VAPI_DBG ("Move construct Msg<%s> from msg@%p to msg@%p, shm_data@%p", |
| vapi_get_msg_name (get_msg_id ()), &msg, this, msg.shm_data); |
| shm_data = msg.shm_data; |
| msg.shm_data = nullptr; |
| } |
| |
| Msg<M> &operator= (Msg<M> &&msg) |
| { |
| VAPI_DBG ("Move assign Msg<%s> from msg@%p to msg@%p, shm_data@%p", |
| vapi_get_msg_name (get_msg_id ()), &msg, this, msg.shm_data); |
| con.get ().msg_free (shm_data); |
| con = msg.con; |
| shm_data = msg.shm_data; |
| msg.shm_data = nullptr; |
| return *this; |
| } |
| |
| struct Msg_allocator : std::allocator<Msg<M>> |
| { |
| template <class U, class... Args> void construct (U *p, Args &&... args) |
| { |
| ::new ((void *)p) U (std::forward<Args> (args)...); |
| } |
| |
| template <class U> struct rebind |
| { |
| typedef Msg_allocator other; |
| }; |
| }; |
| |
| static void set_msg_id (vapi_msg_id_t id) |
| { |
| assert ((INVALID_MSG_ID == *msg_id_holder ()) || |
| (id == *msg_id_holder ())); |
| *msg_id_holder () = id; |
| } |
| |
| static vapi_msg_id_t *msg_id_holder () |
| { |
| static vapi_msg_id_t my_id{INVALID_MSG_ID}; |
| return &my_id; |
| } |
| |
| Msg (Connection &con, void *shm_data) : con{con} |
| { |
| if (!con.is_msg_available (get_msg_id ())) |
| { |
| throw Msg_not_available_exception (); |
| } |
| this->shm_data = static_cast<shm_data_type *> (shm_data); |
| VAPI_DBG ("New Msg<%s>@%p shm_data@%p", vapi_get_msg_name (get_msg_id ()), |
| this, shm_data); |
| } |
| |
| void assign_response (vapi_msg_id_t resp_id, void *shm_data) |
| { |
| assert (nullptr == this->shm_data); |
| if (resp_id != get_msg_id ()) |
| { |
| throw Unexpected_msg_id_exception (); |
| } |
| this->shm_data = static_cast<M *> (shm_data); |
| vapi_swap_to_host<M> (this->shm_data); |
| VAPI_DBG ("Assign response to Msg<%s>@%p shm_data@%p", |
| vapi_get_msg_name (get_msg_id ()), this, shm_data); |
| } |
| |
| std::reference_wrapper<Connection> con; |
| using shm_data_type = M; |
| shm_data_type *shm_data; |
| |
| friend class Connection; |
| |
| template <typename Req, typename Resp, typename... Args> |
| friend class Request; |
| |
| template <typename Req, typename Resp, typename... Args> friend class Dump; |
| |
| template <typename X> friend class Event_registration; |
| |
| template <typename X> friend class Result_set; |
| |
| friend struct Msg_allocator; |
| |
| template <typename X> friend void vapi_msg_set_msg_id (vapi_msg_id_t id); |
| }; |
| |
| /** |
| * Class representing a simple request - with a single response message |
| */ |
| template <typename Req, typename Resp, typename... Args> |
| class Request : public Common_req |
| { |
| public: |
| Request (Connection &con, Args... args, |
| std::function<vapi_error_e (Request<Req, Resp, Args...> &)> |
| callback = nullptr) |
| : Common_req{con}, callback{callback}, |
| request{con, vapi_alloc<Req> (con, args...)}, response{con, nullptr} |
| { |
| } |
| |
| Request (const Request &) = delete; |
| |
| virtual ~Request () |
| { |
| if (RESPONSE_NOT_READY == get_response_state ()) |
| { |
| con.unregister_request (this); |
| } |
| } |
| |
| vapi_error_e execute () |
| { |
| return con.send (this); |
| } |
| |
| const Msg<Req> &get_request (void) const |
| { |
| return request; |
| } |
| |
| const Msg<Resp> &get_response (void) |
| { |
| return response; |
| } |
| |
| private: |
| virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id, |
| void *shm_data) |
| { |
| assert (RESPONSE_NOT_READY == get_response_state ()); |
| response.assign_response (id, shm_data); |
| set_response_state (RESPONSE_READY); |
| if (nullptr != callback) |
| { |
| return std::make_pair (callback (*this), true); |
| } |
| return std::make_pair (VAPI_OK, true); |
| } |
| std::function<vapi_error_e (Request<Req, Resp, Args...> &)> callback; |
| Msg<Req> request; |
| Msg<Resp> response; |
| |
| friend class Connection; |
| }; |
| |
| /** |
| * Class representing iterable set of responses of the same type |
| */ |
| template <typename M> class Result_set |
| { |
| public: |
| ~Result_set () |
| { |
| } |
| |
| Result_set (const Result_set &) = delete; |
| |
| bool is_complete () const |
| { |
| return complete; |
| } |
| |
| size_t size () const |
| { |
| return set.size (); |
| } |
| |
| using const_iterator = |
| typename std::vector<Msg<M>, |
| typename Msg<M>::Msg_allocator>::const_iterator; |
| |
| const_iterator begin () const |
| { |
| return set.begin (); |
| } |
| |
| const_iterator end () const |
| { |
| return set.end (); |
| } |
| |
| void free_response (const_iterator pos) |
| { |
| set.erase (pos); |
| } |
| |
| void free_all_responses () |
| { |
| set.clear (); |
| } |
| |
| private: |
| void mark_complete () |
| { |
| complete = true; |
| } |
| |
| void assign_response (vapi_msg_id_t resp_id, void *shm_data) |
| { |
| if (resp_id != Msg<M>::get_msg_id ()) |
| { |
| { |
| throw Unexpected_msg_id_exception (); |
| } |
| } |
| else if (shm_data) |
| { |
| vapi_swap_to_host<M> (static_cast<M *> (shm_data)); |
| set.emplace_back (con, shm_data); |
| VAPI_DBG ("Result_set@%p emplace_back shm_data@%p", this, shm_data); |
| } |
| } |
| |
| Result_set (Connection &con) : con (con), complete{false} |
| { |
| } |
| |
| Connection &con; |
| bool complete; |
| std::vector<Msg<M>, typename Msg<M>::Msg_allocator> set; |
| |
| template <typename Req, typename Resp, typename... Args> friend class Dump; |
| |
| template <typename X> friend class Event_registration; |
| }; |
| |
| /** |
| * Class representing a dump request - zero or more identical responses to a |
| * single request message |
| */ |
| template <typename Req, typename Resp, typename... Args> |
| class Dump : public Common_req |
| { |
| public: |
| Dump (Connection &con, Args... args, |
| std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback = |
| nullptr) |
| : Common_req{con}, request{con, vapi_alloc<Req> (con, args...)}, |
| result_set{con}, callback{callback} |
| { |
| } |
| |
| Dump (const Dump &) = delete; |
| |
| virtual ~Dump () |
| { |
| } |
| |
| virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id, |
| void *shm_data) |
| { |
| if (id == vapi_msg_id_control_ping_reply) |
| { |
| con.msg_free (shm_data); |
| result_set.mark_complete (); |
| set_response_state (RESPONSE_READY); |
| if (nullptr != callback) |
| { |
| return std::make_pair (callback (*this), true); |
| } |
| return std::make_pair (VAPI_OK, true); |
| } |
| else |
| { |
| result_set.assign_response (id, shm_data); |
| } |
| return std::make_pair (VAPI_OK, false); |
| } |
| |
| vapi_error_e execute () |
| { |
| return con.send_with_control_ping (this); |
| } |
| |
| Msg<Req> &get_request (void) |
| { |
| return request; |
| } |
| |
| using resp_type = typename Msg<Resp>::shm_data_type; |
| |
| const Result_set<Resp> &get_result_set (void) const |
| { |
| return result_set; |
| } |
| |
| private: |
| Msg<Req> request; |
| Result_set<resp_type> result_set; |
| std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback; |
| |
| friend class Connection; |
| }; |
| |
| /** |
| * Class representing event registration - incoming events (messages) from |
| * vpp as a result of a subscription (typically a want_* simple request) |
| */ |
| template <typename M> class Event_registration : public Common_req |
| { |
| public: |
| Event_registration ( |
| Connection &con, |
| std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr) |
| : Common_req{con}, result_set{con}, callback{callback} |
| { |
| if (!con.is_msg_available (M::get_msg_id ())) |
| { |
| throw Msg_not_available_exception (); |
| } |
| con.register_event (this); |
| } |
| |
| Event_registration (const Event_registration &) = delete; |
| |
| virtual ~Event_registration () |
| { |
| con.unregister_event (this); |
| } |
| |
| virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id, |
| void *shm_data) |
| { |
| result_set.assign_response (id, shm_data); |
| if (nullptr != callback) |
| { |
| return std::make_pair (callback (*this), true); |
| } |
| return std::make_pair (VAPI_OK, true); |
| } |
| |
| using resp_type = typename M::shm_data_type; |
| |
| Result_set<resp_type> &get_result_set (void) |
| { |
| return result_set; |
| } |
| |
| private: |
| Result_set<resp_type> result_set; |
| std::function<vapi_error_e (Event_registration<M> &)> callback; |
| }; |
| }; |
| |
| #endif |
| |
| /* |
| * fd.io coding-style-patch-verification: ON |
| * |
| * Local Variables: |
| * eval: (c-set-style "gnu") |
| * End: |
| */ |