Fix coverity warnings in VOM and VAPI
[vpp.git] / src / vpp-api / vapi / vapi.hpp
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 Cisco and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17
18 #ifndef vapi_hpp_included
19 #define vapi_hpp_included
20
21 #include <cstddef>
22 #include <vector>
23 #include <mutex>
24 #include <queue>
25 #include <cassert>
26 #include <functional>
27 #include <algorithm>
28 #include <atomic>
29 #include <vppinfra/types.h>
30 #include <vapi/vapi.h>
31 #include <vapi/vapi_internal.h>
32 #include <vapi/vapi_dbg.h>
33 #include <vapi/vpe.api.vapi.h>
34
35 #if VAPI_CPP_DEBUG_LEAKS
36 #include <unordered_set>
37 #endif
38
39 /**
40  * @file
41  * @brief C++ VPP API
42  */
43
44 namespace vapi
45 {
46
47 class Connection;
48
49 template <typename Req, typename Resp, typename... Args> class Request;
50 template <typename M> class Msg;
51 template <typename M> void vapi_swap_to_be (M *msg);
52 template <typename M> void vapi_swap_to_host (M *msg);
53 template <typename M, typename... Args>
54 M *vapi_alloc (Connection &con, Args...);
55 template <typename M> vapi_msg_id_t vapi_get_msg_id_t ();
56 template <typename M> class Event_registration;
57
58 class Unexpected_msg_id_exception : public std::exception
59 {
60 public:
61   virtual const char *what () const throw ()
62   {
63     return "unexpected message id";
64   }
65 };
66
67 class Msg_not_available_exception : public std::exception
68 {
69 public:
70   virtual const char *what () const throw ()
71   {
72     return "message unavailable";
73   }
74 };
75
76 typedef enum {
77   /** response not ready yet */
78   RESPONSE_NOT_READY,
79
80   /** response to request is ready */
81   RESPONSE_READY,
82
83   /** no response to request (will never come) */
84   RESPONSE_NO_RESPONSE,
85 } vapi_response_state_e;
86
87 /**
88  * Class representing common functionality of a request - response state
89  * and context
90  */
91 class Common_req
92 {
93 public:
94   virtual ~Common_req (){};
95
96   Connection &get_connection ()
97   {
98     return con;
99   };
100
101   vapi_response_state_e get_response_state (void) const
102   {
103     return response_state;
104   }
105
106 private:
107   Connection &con;
108   Common_req (Connection &con)
109       : con (con), context{0}, response_state{RESPONSE_NOT_READY}
110   {
111   }
112
113   void set_response_state (vapi_response_state_e state)
114   {
115     response_state = state;
116   }
117
118   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
119                                                           void *shm_data) = 0;
120
121   void set_context (u32 context)
122   {
123     this->context = context;
124   }
125
126   u32 get_context ()
127   {
128     return context;
129   }
130
131   u32 context;
132   vapi_response_state_e response_state;
133
134   friend class Connection;
135
136   template <typename M> friend class Msg;
137
138   template <typename Req, typename Resp, typename... Args>
139   friend class Request;
140
141   template <typename Req, typename Resp, typename... Args> friend class Dump;
142
143   template <typename M> friend class Event_registration;
144 };
145
146 /**
147  * Class representing a connection to VPP
148  *
149  * After creating a Connection object, call connect() to actually connect
150  * to VPP. Use is_msg_available to discover whether a specific message is known
151  * and supported by the VPP connected to.
152  */
153 class Connection
154 {
155 public:
156   Connection (void) : vapi_ctx{0}, event_count{0}
157   {
158
159     vapi_error_e rv = VAPI_OK;
160     if (!vapi_ctx)
161       {
162         if (VAPI_OK != (rv = vapi_ctx_alloc (&vapi_ctx)))
163           {
164             throw std::bad_alloc ();
165           }
166       }
167     events.reserve (vapi_get_message_count () + 1);
168   }
169
170   Connection (const Connection &) = delete;
171
172   ~Connection (void)
173   {
174     vapi_ctx_free (vapi_ctx);
175 #if VAPI_CPP_DEBUG_LEAKS
176     for (auto x : shm_data_set)
177       {
178         printf ("Leaked shm_data@%p!\n", x);
179       }
180 #endif
181   }
182
183   /**
184    * @brief check if message identified by it's message id is known by the
185    * vpp to which the connection is open
186    */
187   bool is_msg_available (vapi_msg_id_t type)
188   {
189     return vapi_is_msg_available (vapi_ctx, type);
190   }
191
192   /**
193    * @brief connect to vpp
194    *
195    * @param name application name
196    * @param chroot_prefix shared memory prefix
197    * @param max_queued_request max number of outstanding requests queued
198    *
199    * @return VAPI_OK on success, other error code on error
200    */
201   vapi_error_e connect (const char *name, const char *chroot_prefix,
202                         int max_outstanding_requests, int response_queue_size)
203   {
204     return vapi_connect (vapi_ctx, name, chroot_prefix,
205                          max_outstanding_requests, response_queue_size,
206                          VAPI_MODE_BLOCKING);
207   }
208
209   /**
210    * @brief disconnect from vpp
211    *
212    * @return VAPI_OK on success, other error code on error
213    */
214   vapi_error_e disconnect ()
215   {
216     auto x = requests.size ();
217     while (x > 0)
218       {
219         VAPI_DBG ("popping request @%p", requests.front ());
220         requests.pop_front ();
221         --x;
222       }
223     return vapi_disconnect (vapi_ctx);
224   };
225
226   /**
227    * @brief get event file descriptor
228    *
229    * @note this file descriptor becomes readable when messages (from vpp)
230    * are waiting in queue
231    *
232    * @param[out] fd pointer to result variable
233    *
234    * @return VAPI_OK on success, other error code on error
235    */
236   vapi_error_e get_fd (int *fd)
237   {
238     return vapi_get_fd (vapi_ctx, fd);
239   }
240
241   /**
242    * @brief wait for responses from vpp and assign them to appropriate objects
243    *
244    * @param limit stop dispatch after the limit object received it's response
245    *
246    * @return VAPI_OK on success, other error code on error
247    */
248   vapi_error_e dispatch (const Common_req *limit = nullptr)
249   {
250     std::lock_guard<std::mutex> lock (dispatch_mutex);
251     vapi_error_e rv = VAPI_OK;
252     bool loop_again = true;
253     while (loop_again)
254       {
255         void *shm_data;
256         size_t shm_data_size;
257         rv = vapi_recv (vapi_ctx, &shm_data, &shm_data_size);
258         if (VAPI_OK != rv)
259           {
260             return rv;
261           }
262 #if VAPI_CPP_DEBUG_LEAKS
263         on_shm_data_alloc (shm_data);
264 #endif
265         std::lock_guard<std::recursive_mutex> requests_lock (requests_mutex);
266         std::lock_guard<std::recursive_mutex> events_lock (events_mutex);
267         vapi_msg_id_t id = vapi_lookup_vapi_msg_id_t (
268             vapi_ctx, be16toh (*static_cast<u16 *> (shm_data)));
269         bool has_context = vapi_msg_is_with_context (id);
270         bool break_dispatch = false;
271         Common_req *matching_req = nullptr;
272         if (has_context)
273           {
274             u32 context = *reinterpret_cast<u32 *> (
275                 (static_cast<u8 *> (shm_data) + vapi_get_context_offset (id)));
276             const auto x = requests.front ();
277             matching_req = x;
278             if (context == x->context)
279               {
280                 std::tie (rv, break_dispatch) =
281                     x->assign_response (id, shm_data);
282               }
283             else
284               {
285                 std::tie (rv, break_dispatch) =
286                     x->assign_response (id, nullptr);
287               }
288             if (break_dispatch)
289               {
290                 requests.pop_front ();
291               }
292           }
293         else
294           {
295             if (events[id])
296               {
297                 std::tie (rv, break_dispatch) =
298                     events[id]->assign_response (id, shm_data);
299                 matching_req = events[id];
300               }
301             else
302               {
303                 msg_free (shm_data);
304               }
305           }
306         if ((matching_req && matching_req == limit && break_dispatch) ||
307             VAPI_OK != rv)
308           {
309             return rv;
310           }
311         loop_again = !requests.empty () || (event_count > 0);
312       }
313     return rv;
314   }
315
316   /**
317    * @brief convenience wrapper function
318    */
319   vapi_error_e dispatch (const Common_req &limit)
320   {
321     return dispatch (&limit);
322   }
323
324   /**
325    * @brief wait for response to a specific request
326    *
327    * @param req request to wait for response for
328    *
329    * @return VAPI_OK on success, other error code on error
330    */
331   vapi_error_e wait_for_response (const Common_req &req)
332   {
333     if (RESPONSE_READY == req.get_response_state ())
334       {
335         return VAPI_OK;
336       }
337     return dispatch (req);
338   }
339
340 private:
341   void msg_free (void *shm_data)
342   {
343 #if VAPI_CPP_DEBUG_LEAKS
344     on_shm_data_free (shm_data);
345 #endif
346     vapi_msg_free (vapi_ctx, shm_data);
347   }
348
349   template <template <typename XReq, typename XResp, typename... XArgs>
350             class X,
351             typename Req, typename Resp, typename... Args>
352   vapi_error_e send (X<Req, Resp, Args...> *req)
353   {
354     if (!req)
355       {
356         return VAPI_EINVAL;
357       }
358     u32 req_context =
359         req_context_counter.fetch_add (1, std::memory_order_relaxed);
360     req->request.shm_data->header.context = req_context;
361     vapi_swap_to_be<Req> (req->request.shm_data);
362     std::lock_guard<std::recursive_mutex> lock (requests_mutex);
363     vapi_error_e rv = vapi_send (vapi_ctx, req->request.shm_data);
364     if (VAPI_OK == rv)
365       {
366         VAPI_DBG ("Push %p", req);
367         requests.emplace_back (req);
368         req->set_context (req_context);
369 #if VAPI_CPP_DEBUG_LEAKS
370         on_shm_data_free (req->request.shm_data);
371 #endif
372         req->request.shm_data = nullptr; /* consumed by vapi_send */
373       }
374     else
375       {
376         vapi_swap_to_host<Req> (req->request.shm_data);
377       }
378     return rv;
379   }
380
381   template <template <typename XReq, typename XResp, typename... XArgs>
382             class X,
383             typename Req, typename Resp, typename... Args>
384   vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req)
385   {
386     if (!req)
387       {
388         return VAPI_EINVAL;
389       }
390     u32 req_context =
391         req_context_counter.fetch_add (1, std::memory_order_relaxed);
392     req->request.shm_data->header.context = req_context;
393     vapi_swap_to_be<Req> (req->request.shm_data);
394     std::lock_guard<std::recursive_mutex> lock (requests_mutex);
395     vapi_error_e rv = vapi_send_with_control_ping (
396         vapi_ctx, req->request.shm_data, req_context);
397     if (VAPI_OK == rv)
398       {
399         VAPI_DBG ("Push %p", req);
400         requests.emplace_back (req);
401         req->set_context (req_context);
402 #if VAPI_CPP_DEBUG_LEAKS
403         on_shm_data_free (req->request.shm_data);
404 #endif
405         req->request.shm_data = nullptr; /* consumed by vapi_send */
406       }
407     else
408       {
409         vapi_swap_to_host<Req> (req->request.shm_data);
410       }
411     return rv;
412   }
413
414   void unregister_request (Common_req *request)
415   {
416     std::lock_guard<std::recursive_mutex> lock (requests_mutex);
417     std::remove (requests.begin (), requests.end (), request);
418   }
419
420   template <typename M> void register_event (Event_registration<M> *event)
421   {
422     const vapi_msg_id_t id = M::get_msg_id ();
423     std::lock_guard<std::recursive_mutex> lock (events_mutex);
424     events[id] = event;
425     ++event_count;
426   }
427
428   template <typename M> void unregister_event (Event_registration<M> *event)
429   {
430     const vapi_msg_id_t id = M::get_msg_id ();
431     std::lock_guard<std::recursive_mutex> lock (events_mutex);
432     events[id] = nullptr;
433     --event_count;
434   }
435
436   vapi_ctx_t vapi_ctx;
437   std::atomic_ulong req_context_counter;
438   std::mutex dispatch_mutex;
439
440   std::recursive_mutex requests_mutex;
441   std::recursive_mutex events_mutex;
442   std::deque<Common_req *> requests;
443   std::vector<Common_req *> events;
444   int event_count;
445
446   template <typename Req, typename Resp, typename... Args>
447   friend class Request;
448
449   template <typename Req, typename Resp, typename... Args> friend class Dump;
450
451   template <typename M> friend class Result_set;
452
453   template <typename M> friend class Event_registration;
454
455   template <typename M, typename... Args>
456   friend M *vapi_alloc (Connection &con, Args...);
457
458   template <typename M> friend class Msg;
459
460 #if VAPI_CPP_DEBUG_LEAKS
461   void on_shm_data_alloc (void *shm_data)
462   {
463     if (shm_data)
464       {
465         auto pos = shm_data_set.find (shm_data);
466         if (pos == shm_data_set.end ())
467           {
468             shm_data_set.insert (shm_data);
469           }
470         else
471           {
472             printf ("Double-add shm_data @%p!\n", shm_data);
473           }
474       }
475   }
476
477   void on_shm_data_free (void *shm_data)
478   {
479     auto pos = shm_data_set.find (shm_data);
480     if (pos == shm_data_set.end ())
481       {
482         printf ("Freeing untracked shm_data @%p!\n", shm_data);
483       }
484     else
485       {
486         shm_data_set.erase (pos);
487       }
488   }
489   std::unordered_set<void *> shm_data_set;
490 #endif
491 };
492
493 template <typename Req, typename Resp, typename... Args> class Request;
494
495 template <typename Req, typename Resp, typename... Args> class Dump;
496
497 template <class, class = void> struct vapi_has_payload_trait : std::false_type
498 {
499 };
500
501 template <class... T> using vapi_void_t = void;
502
503 template <class T>
504 struct vapi_has_payload_trait<T, vapi_void_t<decltype (&T::payload)>>
505     : std::true_type
506 {
507 };
508
509 template <typename M> void vapi_msg_set_msg_id (vapi_msg_id_t id)
510 {
511   Msg<M>::set_msg_id (id);
512 }
513
514 /**
515  * Class representing a message stored in shared memory
516  */
517 template <typename M> class Msg
518 {
519 public:
520   Msg (const Msg &) = delete;
521
522   ~Msg ()
523   {
524     VAPI_DBG ("Destroy Msg<%s>@%p, shm_data@%p",
525               vapi_get_msg_name (get_msg_id ()), this, shm_data);
526     if (shm_data)
527       {
528         con.get ().msg_free (shm_data);
529         shm_data = nullptr;
530       }
531   }
532
533   static vapi_msg_id_t get_msg_id ()
534   {
535     return *msg_id_holder ();
536   }
537
538   template <typename X = M>
539   typename std::enable_if<vapi_has_payload_trait<X>::value,
540                           decltype (X::payload) &>::type
541   get_payload () const
542   {
543     return shm_data->payload;
544   }
545
546 private:
547   Msg (Msg<M> &&msg) : con{msg.con}
548   {
549     VAPI_DBG ("Move construct Msg<%s> from msg@%p to msg@%p, shm_data@%p",
550               vapi_get_msg_name (get_msg_id ()), &msg, this, msg.shm_data);
551     shm_data = msg.shm_data;
552     msg.shm_data = nullptr;
553   }
554
555   Msg<M> &operator= (Msg<M> &&msg)
556   {
557     VAPI_DBG ("Move assign Msg<%s> from msg@%p to msg@%p, shm_data@%p",
558               vapi_get_msg_name (get_msg_id ()), &msg, this, msg.shm_data);
559     con.get ().msg_free (shm_data);
560     con = msg.con;
561     shm_data = msg.shm_data;
562     msg.shm_data = nullptr;
563     return *this;
564   }
565
566   struct Msg_allocator : std::allocator<Msg<M>>
567   {
568     template <class U, class... Args> void construct (U *p, Args &&... args)
569     {
570       ::new ((void *)p) U (std::forward<Args> (args)...);
571     }
572
573     template <class U> struct rebind
574     {
575       typedef Msg_allocator other;
576     };
577   };
578
579   static void set_msg_id (vapi_msg_id_t id)
580   {
581     assert ((INVALID_MSG_ID == *msg_id_holder ()) ||
582             (id == *msg_id_holder ()));
583     *msg_id_holder () = id;
584   }
585
586   static vapi_msg_id_t *msg_id_holder ()
587   {
588     static vapi_msg_id_t my_id{INVALID_MSG_ID};
589     return &my_id;
590   }
591
592   Msg (Connection &con, void *shm_data) : con{con}
593   {
594     if (!con.is_msg_available (get_msg_id ()))
595       {
596         throw Msg_not_available_exception ();
597       }
598     this->shm_data = static_cast<shm_data_type *> (shm_data);
599     VAPI_DBG ("New Msg<%s>@%p shm_data@%p", vapi_get_msg_name (get_msg_id ()),
600               this, shm_data);
601   }
602
603   void assign_response (vapi_msg_id_t resp_id, void *shm_data)
604   {
605     assert (nullptr == this->shm_data);
606     if (resp_id != get_msg_id ())
607       {
608         throw Unexpected_msg_id_exception ();
609       }
610     this->shm_data = static_cast<M *> (shm_data);
611     vapi_swap_to_host<M> (this->shm_data);
612     VAPI_DBG ("Assign response to Msg<%s>@%p shm_data@%p",
613               vapi_get_msg_name (get_msg_id ()), this, shm_data);
614   }
615
616   std::reference_wrapper<Connection> con;
617   using shm_data_type = M;
618   shm_data_type *shm_data;
619
620   friend class Connection;
621
622   template <typename Req, typename Resp, typename... Args>
623   friend class Request;
624
625   template <typename Req, typename Resp, typename... Args> friend class Dump;
626
627   template <typename X> friend class Event_registration;
628
629   template <typename X> friend class Result_set;
630
631   friend struct Msg_allocator;
632
633   template <typename X> friend void vapi_msg_set_msg_id (vapi_msg_id_t id);
634 };
635
636 /**
637  * Class representing a simple request - with a single response message
638  */
639 template <typename Req, typename Resp, typename... Args>
640 class Request : public Common_req
641 {
642 public:
643   Request (Connection &con, Args... args,
644            std::function<vapi_error_e (Request<Req, Resp, Args...> &)>
645                callback = nullptr)
646       : Common_req{con}, callback{callback},
647         request{con, vapi_alloc<Req> (con, args...)}, response{con, nullptr}
648   {
649   }
650
651   Request (const Request &) = delete;
652
653   virtual ~Request ()
654   {
655     if (RESPONSE_NOT_READY == get_response_state ())
656       {
657         con.unregister_request (this);
658       }
659   }
660
661   vapi_error_e execute ()
662   {
663     return con.send (this);
664   }
665
666   const Msg<Req> &get_request (void) const
667   {
668     return request;
669   }
670
671   const Msg<Resp> &get_response (void)
672   {
673     return response;
674   }
675
676 private:
677   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
678                                                           void *shm_data)
679   {
680     assert (RESPONSE_NOT_READY == get_response_state ());
681     response.assign_response (id, shm_data);
682     set_response_state (RESPONSE_READY);
683     if (nullptr != callback)
684       {
685         return std::make_pair (callback (*this), true);
686       }
687     return std::make_pair (VAPI_OK, true);
688   }
689   std::function<vapi_error_e (Request<Req, Resp, Args...> &)> callback;
690   Msg<Req> request;
691   Msg<Resp> response;
692
693   friend class Connection;
694 };
695
696 /**
697  * Class representing iterable set of responses of the same type
698  */
699 template <typename M> class Result_set
700 {
701 public:
702   ~Result_set ()
703   {
704   }
705
706   Result_set (const Result_set &) = delete;
707
708   bool is_complete () const
709   {
710     return complete;
711   }
712
713   size_t size () const
714   {
715     return set.size ();
716   }
717
718   using const_iterator =
719       typename std::vector<Msg<M>,
720                            typename Msg<M>::Msg_allocator>::const_iterator;
721
722   const_iterator begin () const
723   {
724     return set.begin ();
725   }
726
727   const_iterator end () const
728   {
729     return set.end ();
730   }
731
732   void free_response (const_iterator pos)
733   {
734     set.erase (pos);
735   }
736
737   void free_all_responses ()
738   {
739     set.clear ();
740   }
741
742 private:
743   void mark_complete ()
744   {
745     complete = true;
746   }
747
748   void assign_response (vapi_msg_id_t resp_id, void *shm_data)
749   {
750     if (resp_id != Msg<M>::get_msg_id ())
751       {
752         {
753           throw Unexpected_msg_id_exception ();
754         }
755       }
756     else if (shm_data)
757       {
758         vapi_swap_to_host<M> (static_cast<M *> (shm_data));
759         set.emplace_back (con, shm_data);
760         VAPI_DBG ("Result_set@%p emplace_back shm_data@%p", this, shm_data);
761       }
762   }
763
764   Result_set (Connection &con) : con (con), complete{false}
765   {
766   }
767
768   Connection &con;
769   bool complete;
770   std::vector<Msg<M>, typename Msg<M>::Msg_allocator> set;
771
772   template <typename Req, typename Resp, typename... Args> friend class Dump;
773
774   template <typename X> friend class Event_registration;
775 };
776
777 /**
778  * Class representing a dump request - zero or more identical responses to a
779  * single request message
780  */
781 template <typename Req, typename Resp, typename... Args>
782 class Dump : public Common_req
783 {
784 public:
785   Dump (Connection &con, Args... args,
786         std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback =
787             nullptr)
788       : Common_req{con}, request{con, vapi_alloc<Req> (con, args...)},
789         result_set{con}, callback{callback}
790   {
791   }
792
793   Dump (const Dump &) = delete;
794
795   virtual ~Dump ()
796   {
797   }
798
799   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
800                                                           void *shm_data)
801   {
802     if (id == vapi_msg_id_control_ping_reply)
803       {
804         con.msg_free (shm_data);
805         result_set.mark_complete ();
806         set_response_state (RESPONSE_READY);
807         if (nullptr != callback)
808           {
809             return std::make_pair (callback (*this), true);
810           }
811         return std::make_pair (VAPI_OK, true);
812       }
813     else
814       {
815         result_set.assign_response (id, shm_data);
816       }
817     return std::make_pair (VAPI_OK, false);
818   }
819
820   vapi_error_e execute ()
821   {
822     return con.send_with_control_ping (this);
823   }
824
825   Msg<Req> &get_request (void)
826   {
827     return request;
828   }
829
830   using resp_type = typename Msg<Resp>::shm_data_type;
831
832   const Result_set<Resp> &get_result_set (void) const
833   {
834     return result_set;
835   }
836
837 private:
838   Msg<Req> request;
839   Result_set<resp_type> result_set;
840   std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback;
841
842   friend class Connection;
843 };
844
845 /**
846  * Class representing event registration - incoming events (messages) from
847  * vpp as a result of a subscription (typically a want_* simple request)
848  */
849 template <typename M> class Event_registration : public Common_req
850 {
851 public:
852   Event_registration (
853       Connection &con,
854       std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr)
855       : Common_req{con}, result_set{con}, callback{callback}
856   {
857     if (!con.is_msg_available (M::get_msg_id ()))
858       {
859         throw Msg_not_available_exception ();
860       }
861     con.register_event (this);
862   }
863
864   Event_registration (const Event_registration &) = delete;
865
866   virtual ~Event_registration ()
867   {
868     con.unregister_event (this);
869   }
870
871   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
872                                                           void *shm_data)
873   {
874     result_set.assign_response (id, shm_data);
875     if (nullptr != callback)
876       {
877         return std::make_pair (callback (*this), true);
878       }
879     return std::make_pair (VAPI_OK, true);
880   }
881
882   using resp_type = typename M::shm_data_type;
883
884   Result_set<resp_type> &get_result_set (void)
885   {
886     return result_set;
887   }
888
889 private:
890   Result_set<resp_type> result_set;
891   std::function<vapi_error_e (Event_registration<M> &)> callback;
892 };
893 };
894
895 #endif
896
897 /*
898  * fd.io coding-style-patch-verification: ON
899  *
900  * Local Variables:
901  * eval: (c-set-style "gnu")
902  * End:
903  */