From cf1917f00d28381023d2e30f767adc5872b21084 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lo=C3=AFc=20Collignon?= Date: Wed, 2 Jan 2019 17:44:27 +0100 Subject: [PATCH] c++: New C++ API for bindings MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit You can implement a C++ binding API by inheriting the templated base class 'base_api_t' and overriding the methods you want. At the same time, the c++ files are now located in in their own subdirectory: afb/c++ Change-Id: Ie02535961ec6b4b5ae21390cb520acb1fdc44c9e Signed-off-by: Loïc Collignon --- include/afb/afb-binding | 3 +- include/afb/c++/binding | 19 ++ include/afb/c++/binding-object.hpp | 323 +++++++++++++++++++++ .../afb/{afb-binding.hpp => c++/binding-wrap.hpp} | 0 4 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 include/afb/c++/binding create mode 100644 include/afb/c++/binding-object.hpp rename include/afb/{afb-binding.hpp => c++/binding-wrap.hpp} (100%) diff --git a/include/afb/afb-binding b/include/afb/afb-binding index 96d2ec9c..a961a408 100644 --- a/include/afb/afb-binding +++ b/include/afb/afb-binding @@ -15,5 +15,6 @@ * limitations under the License. */ #pragma once -#include "afb-binding.hpp" +#include "c++/binding-wrap.hpp" +#include "c++/binding-object.hpp" diff --git a/include/afb/c++/binding b/include/afb/c++/binding new file mode 100644 index 00000000..5858dff7 --- /dev/null +++ b/include/afb/c++/binding @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2016, 2017, 2018 "IoT.bzh" + * Author: José Bollo + * + * 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. + */ +#pragma once +#include "binding-wrap.hpp" +#include "binding-object.hpp" diff --git a/include/afb/c++/binding-object.hpp b/include/afb/c++/binding-object.hpp new file mode 100644 index 00000000..f9c3bcab --- /dev/null +++ b/include/afb/c++/binding-object.hpp @@ -0,0 +1,323 @@ +#pragma once + +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon + * + * 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. + */ + +#include "../afb-binding.h" +#include +#include + +namespace afb +{ + /** + * @brief Create a new API. + * @tparam TApi The Api's concrete class to create an instance from. + * @param[in] handle Parent API. + * @param[in] name API's name. + * @param[in] info API's description. + * @param[in] noconcurrency Zero for a reentrant API, non-zero otherwise. + * @return The created API. + */ + template + TApi* new_api(afb_api_t handle, const std::string& name, const std::string& info = "", int noconcurrency = 1) + { + TApi* api = new TApi(); + afb_api_new_api( + handle, + name.c_str(), + info == "" ? nullptr : info.c_str(), + noconcurrency, + TApi::Traits::preinit, + api + ); + return api; + } + + /** + * @brief Default Api's traits implementation. + * @tparam TApi The Api's concrete class. + */ + template + struct ApiTraits + { + /** + * @brief TApi's method pointer. + */ + using TVerbCallback = void(TApi::*)(req); + + /*** + * @brief TApi's const method pointer. + */ + using TVerbCallbackConst = void(TApi::*)(req) const; + + /** + * @brief Pre-init callback for an api created using @c afb::api::new_api. + * @param[in] closure Pointer to the API object. + * @param[in] handle Handle of the API. + * @return Zero on success, non-zero otherwise. + */ + static int preinit(void* closure, afb_api_t handle) + { + assert(closure != nullptr); + assert(handle != nullptr); + + afb_api_set_userdata(handle, closure); + + TApi* api = reinterpret_cast(closure); + + if (afb_api_on_init(handle, TApi::Traits::init)) + { + AFB_API_ERROR(handle, "Failed to register init handler callback."); + return -1; + } + + if (afb_api_on_event(handle, TApi::Traits::event)) + { + AFB_API_ERROR(handle, "Failed to register event handler callback."); + return -2; + } + + api->handle_ = handle; + return api->preinit(handle); + } + + /** + * @brief Init callback for an api created using @c afb::api::new_api. + * @param[in] handle Handle to the API to initialize. + * @return Zero on success, non-zero otherwise. + */ + static int init(afb_api_t handle) + { + assert(handle != nullptr); + + void* userdata = afb_api_get_userdata(handle); + assert(userdata != nullptr); + + TApi* api = reinterpret_cast(userdata); + return api->init(); + } + + /** + * @brief Event callback for an api created using @c afb::api::new_api. + * @param[in] handle Handle to the API that is receiving an event. + * @param[in] event The event's name. + * @param[in] object The event's json argument. + */ + static void event(afb_api_t handle, const char* event, json_object* object) + { + assert(handle != nullptr); + + void* userdata = afb_api_get_userdata(handle); + assert(userdata != nullptr); + + TApi* api = reinterpret_cast(userdata); + api->event(event, object); + } + + /** + * @brief Verb callback for a verb added using @c afb::api::add_verb. + * @tparam callback TApi's method to call + * @param[in] r Request to handle. + */ + template + static void verb(afb_req_t r) + { + assert(r != nullptr); + + afb_api_t handle = afb_req_get_api(r); + if (handle) + { + void* userdata = afb_api_get_userdata(handle); + if (userdata) + { + TApi* api = reinterpret_cast(userdata); + (api->*callback)(afb::req(r)); + } + else + { + afb_req_fail(r, "Failed to get the API object!", nullptr); + } + } + else + { + afb_req_fail(r, "Failed to get the corresponding API from the query!", nullptr); + } + } + + /** + * @brief Verb callback for a verb added using @c afb::api::add_verb. + * @tparam callback TApi's const method to call. + * @param[in] req Request to handle. + */ + template + static void verb(afb_req_t r) + { + assert(r != nullptr); + + afb_api_t handle = afb_req_get_api(r); + if (handle) + { + void* userdata = afb_api_get_userdata(handle); + if (userdata) + { + TApi* api = reinterpret_cast(userdata); + (api->*callback)(afb::req(r)); + } + else + { + afb_req_fail(r, "Failed to get the API object!", nullptr); + } + } + else + { + afb_req_fail(r, "Failed to get the corresponding API from the query!", nullptr); + } + } + }; + + /** + * @brief Base class for API implementation. + * @tparam TApi The Api's concrete class. + * @tparam TTraits The Api's static callback implementation. + */ + template < + typename TApi, + typename TTraits = ApiTraits + > + class base_api_t + { + friend TTraits; + + public: + using Traits = TTraits; + + private: + // Non-copyable + base_api_t(const base_api_t&) = delete; + base_api_t& operator=(const base_api_t&) = delete; + + protected: + afb_api_t handle_; + + /** + * @brief Default constructor. + */ + explicit base_api_t() = default; + + /** + * @brief Move constructor. + */ + explicit base_api_t(base_api_t&&) = default; + + /** + * @brief Add a verb to an API. + * @param[in] api API on which the verb should be added. + * @param[in] verb Verb's name. + * @param[in] info Verb's description. + * @param[in] auth Verb's permissions required. + * @param[in] session Verb's session handling. + * @param[in] glob is the verb glob name. + * @return Zero if success, non-zero otherwise. + */ + template + 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) + { + return afb_api_add_verb( + handle_, + verb.c_str(), + info == "" ? nullptr : info.c_str(), + TTraits::template verb, + vcbdata, + auth, + session, + glob + ); + } + + /** + * @brief Add a verb to an API. + * @param[in] api API on which the verb should be added. + * @param[in] verb Verb's name. + * @param[in] info Verb's description. + * @param[in] auth Verb's permissions required. + * @param[in] session Verb's session handling. + * @param[in] glob is the verb glob name. + * @return Zero if success, non-zero otherwise. + */ + template + 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) + { + return afb_api_add_verb( + handle_, + verb.c_str(), + info == "" ? nullptr : info.c_str(), + TTraits::template verb, + vcbdata, + auth, + session, + glob + ); + } + + public: + /** + * @brief Move assignation operator. + */ + base_api_t& operator=(base_api_t&&) = default; + + /** + * @brief Get the API's handle. + * @return The API's handle. + */ + afb_api_t handle() const { return handle_; } + + /** + * @brief Implicit conversion to C handle. + * @return The API's handle. + */ + operator afb_api_t() const { return handle_; } + + /** + * @brief Destructor. + */ + virtual ~base_api_t() + { + if (handle_ && afb_api_delete_api(handle_)) + AFB_API_ERROR(handle_, "Failed to delete API."); + } + + /** + * @brief Called by the binder during the API's pre-init phase. + * @param[in] handle Handle representing the API on the binder's side. + * @return Zero if success, non-zero otherwise. + */ + virtual int preinit(afb_api_t handle) { return 0; } + + /** + * @brief Called by the binder during the API's init phase. + * @return Zero on success, non-zero otherwise. + */ + virtual int init() { return 0; } + + /** + * @brief Called by the binder when an event is received for this API. + * @param[in] name Event's name. + * @param[in] arg Event's argument. + */ + virtual void event(const std::string& name, json_object* arg) { } + }; +} diff --git a/include/afb/afb-binding.hpp b/include/afb/c++/binding-wrap.hpp similarity index 100% rename from include/afb/afb-binding.hpp rename to include/afb/c++/binding-wrap.hpp -- 2.16.6