c++: New C++ API for bindings
[src/app-framework-binder.git] / include / afb / c++ / binding-object.hpp
1 #pragma once
2
3 /*
4  * Copyright (C) 2018 "IoT.bzh"
5  * Author Loïc Collignon <loic.collignon@iot.bzh>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include "../afb-binding.h"
21 #include <cassert>
22 #include <string>
23
24 namespace afb
25 {
26         /**
27          * @brief Create a new API.
28          * @tparam TApi The Api's concrete class to create an instance from.
29          * @param[in] handle Parent API.
30          * @param[in] name API's name.
31          * @param[in] info API's description.
32          * @param[in] noconcurrency Zero for a reentrant API, non-zero otherwise.
33          * @return The created API.
34          */
35         template <typename TApi>
36         TApi* new_api(afb_api_t handle, const std::string& name, const std::string& info = "", int noconcurrency = 1)
37         {
38                 TApi* api = new TApi();
39                 afb_api_new_api(
40                         handle,
41                         name.c_str(),
42                         info == "" ? nullptr : info.c_str(),
43                         noconcurrency,
44                         TApi::Traits::preinit,
45                         api
46                 );
47                 return api;
48         }
49
50         /**
51          * @brief Default Api's traits implementation.
52          * @tparam TApi The Api's concrete class.
53          */
54         template <typename TApi>
55         struct ApiTraits
56         {
57                 /**
58                  * @brief TApi's method pointer.
59                  */
60                 using TVerbCallback = void(TApi::*)(req);
61
62                 /***
63                  * @brief TApi's const method pointer.
64                  */
65                 using TVerbCallbackConst = void(TApi::*)(req) const;
66
67                 /**
68                  * @brief Pre-init callback for an api created using @c afb::api::new_api.
69                  * @param[in] closure Pointer to the API object.
70                  * @param[in] handle Handle of the API.
71                  * @return Zero on success, non-zero otherwise.
72                  */
73                 static int preinit(void* closure, afb_api_t handle)
74                 {
75                         assert(closure != nullptr);
76                         assert(handle != nullptr);
77
78                         afb_api_set_userdata(handle, closure);
79
80                         TApi* api = reinterpret_cast<TApi*>(closure);
81
82                         if (afb_api_on_init(handle, TApi::Traits::init))
83                         {
84                                 AFB_API_ERROR(handle, "Failed to register init handler callback.");
85                                 return -1;
86                         }
87
88                         if (afb_api_on_event(handle, TApi::Traits::event))
89                         {
90                                 AFB_API_ERROR(handle, "Failed to register event handler callback.");
91                                 return -2;
92                         }
93
94                         api->handle_ = handle;
95                         return api->preinit(handle);
96                 }
97
98                 /**
99                  * @brief Init callback for an api created using @c afb::api::new_api.
100                  * @param[in] handle Handle to the API to initialize.
101                  * @return Zero on success, non-zero otherwise.
102                  */
103                 static int init(afb_api_t handle)
104                 {
105                         assert(handle != nullptr);
106
107                         void* userdata = afb_api_get_userdata(handle);
108                         assert(userdata != nullptr);
109
110                         TApi* api = reinterpret_cast<TApi*>(userdata);
111                         return api->init();
112                 }
113
114                 /**
115                  * @brief Event callback for an api created using @c afb::api::new_api.
116                  * @param[in] handle Handle to the API that is receiving an event.
117                  * @param[in] event The event's name.
118                  * @param[in] object The event's json argument.
119                  */
120                 static void event(afb_api_t handle, const char* event, json_object* object)
121                 {
122                         assert(handle != nullptr);
123
124                         void* userdata = afb_api_get_userdata(handle);
125                         assert(userdata != nullptr);
126
127                         TApi* api = reinterpret_cast<TApi*>(userdata);
128                         api->event(event, object);
129                 }
130
131                 /**
132                  * @brief Verb callback for a verb added using @c afb::api::add_verb.
133                  * @tparam callback TApi's method to call
134                  * @param[in] r Request to handle.
135                  */
136                 template <TVerbCallback callback>
137                 static void verb(afb_req_t r)
138                 {
139                         assert(r != nullptr);
140
141                         afb_api_t handle = afb_req_get_api(r);
142                         if (handle)
143                         {
144                                 void* userdata = afb_api_get_userdata(handle);
145                                 if (userdata)
146                                 {
147                                         TApi* api = reinterpret_cast<TApi*>(userdata);
148                                         (api->*callback)(afb::req(r));
149                                 }
150                                 else
151                                 {
152                                         afb_req_fail(r, "Failed to get the API object!", nullptr);
153                                 }
154                         }
155                         else
156                         {
157                                 afb_req_fail(r, "Failed to get the corresponding API from the query!", nullptr);
158                         }
159                 }
160
161                 /**
162                  * @brief Verb callback for a verb added using @c afb::api::add_verb.
163                  * @tparam callback TApi's const method to call.
164                  * @param[in] req Request to handle.
165                  */
166                 template <TVerbCallbackConst callback>
167                 static void verb(afb_req_t r)
168                 {
169                         assert(r != nullptr);
170
171                         afb_api_t handle = afb_req_get_api(r);
172                         if (handle)
173                         {
174                                 void* userdata = afb_api_get_userdata(handle);
175                                 if (userdata)
176                                 {
177                                         TApi* api = reinterpret_cast<TApi*>(userdata);
178                                         (api->*callback)(afb::req(r));
179                                 }
180                                 else
181                                 {
182                                         afb_req_fail(r, "Failed to get the API object!", nullptr);
183                                 }
184                         }
185                         else
186                         {
187                                 afb_req_fail(r, "Failed to get the corresponding API from the query!", nullptr);
188                         }
189                 }
190         };
191
192         /**
193          * @brief Base class for API implementation.
194          * @tparam TApi The Api's concrete class.
195          * @tparam TTraits The Api's static callback implementation.
196          */
197         template <
198                 typename TApi,
199                 typename TTraits = ApiTraits<TApi>
200         >
201         class base_api_t
202         {
203                 friend TTraits;
204
205         public:
206                 using Traits = TTraits;
207
208         private:
209                 // Non-copyable
210                 base_api_t(const base_api_t&) = delete;
211                 base_api_t& operator=(const base_api_t&) = delete;
212
213         protected:
214                 afb_api_t handle_;
215
216                 /**
217                  * @brief Default constructor.
218                  */
219                 explicit base_api_t() = default;
220
221                 /**
222                  * @brief Move constructor.
223                  */
224                 explicit base_api_t(base_api_t&&) = default;
225
226                 /**
227                  * @brief Add a verb to an API.
228                  * @param[in] api API on which the verb should be added.
229                  * @param[in] verb Verb's name.
230                  * @param[in] info Verb's description.
231                  * @param[in] auth Verb's permissions required.
232                  * @param[in] session Verb's session handling.
233                  * @param[in] glob is the verb glob name.
234                  * @return Zero if success, non-zero otherwise.
235                  */
236                 template <typename TTraits::TVerbCallback Callback>
237                 int add_verb(const std::string& verb, const std::string& info, void* vcbdata = nullptr, const struct afb_auth* auth = nullptr, uint32_t session = AFB_SESSION_NONE_X2, int glob = 0)
238                 {
239                         return afb_api_add_verb(
240                                 handle_,
241                                 verb.c_str(),
242                                 info == "" ? nullptr : info.c_str(),
243                                 TTraits::template verb<Callback>,
244                                 vcbdata,
245                                 auth,
246                                 session,
247                                 glob
248                         );
249                 }
250
251                 /**
252                  * @brief Add a verb to an API.
253                  * @param[in] api API on which the verb should be added.
254                  * @param[in] verb Verb's name.
255                  * @param[in] info Verb's description.
256                  * @param[in] auth Verb's permissions required.
257                  * @param[in] session Verb's session handling.
258                  * @param[in] glob is the verb glob name.
259                  * @return Zero if success, non-zero otherwise.
260                  */
261                 template <typename TTraits::TVerbCallbackConst Callback>
262                 int add_verb(const std::string& verb, const std::string& info, void* vcbdata = nullptr, const struct afb_auth* auth = nullptr, uint32_t session = AFB_SESSION_NONE_X2, int glob = 0)
263                 {
264                         return afb_api_add_verb(
265                                 handle_,
266                                 verb.c_str(),
267                                 info == "" ? nullptr : info.c_str(),
268                                 TTraits::template verb<Callback>,
269                                 vcbdata,
270                                 auth,
271                                 session,
272                                 glob
273                         );
274                 }
275
276         public:
277                 /**
278                  * @brief Move assignation operator.
279                  */
280                 base_api_t& operator=(base_api_t&&) = default;
281
282                 /**
283                  * @brief Get the API's handle.
284                  * @return The API's handle.
285                  */
286                 afb_api_t handle() const { return handle_; }
287
288                 /**
289                  * @brief Implicit conversion to C handle.
290                  * @return The API's handle.
291                  */
292                 operator afb_api_t() const { return handle_; }
293
294                 /**
295                  * @brief Destructor.
296                  */
297                 virtual ~base_api_t()
298                 {
299                         if (handle_ && afb_api_delete_api(handle_))
300                                 AFB_API_ERROR(handle_, "Failed to delete API.");
301                 }
302
303                 /**
304                  * @brief Called by the binder during the API's pre-init phase.
305                  * @param[in] handle Handle representing the API on the binder's side.
306                  * @return Zero if success, non-zero otherwise.
307                  */
308                 virtual int preinit(afb_api_t handle) { return 0; }
309
310                 /**
311                  * @brief Called by the binder during the API's init phase.
312                  * @return Zero on success, non-zero otherwise.
313                  */
314                 virtual int init() { return 0; }
315
316                 /**
317                  * @brief Called by the binder when an event is received for this API.
318                  * @param[in] name Event's name.
319                  * @param[in] arg Event's argument.
320                  */
321                 virtual void event(const std::string& name, json_object* arg) { }
322         };
323 }