| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #pragma once |
| |
| #include <functional> |
| #include <type_traits> |
| |
| #include <fbl/macros.h> |
| #include <lib/async/dispatcher.h> |
| #include <lib/fidl-async/bind.h> |
| #include <lib/zx/channel.h> |
| #include <zircon/assert.h> |
| #include <zircon/fidl.h> |
| |
| namespace fidl { |
| |
| // A wrapper class which helps binding operations to a channel, and which |
| // helps creating C-compatible member-binding functions. |
| // |
| // This class ensures that the "void*" context value passed to |fidl_bind| |
| // is the same value used to dispatch to member functions. |
| template <typename T> |
| struct Binder { |
| // Bind a member of a base class to a FIDL dispatch function, to allow |
| // compatibility with the C bindings. |
| // |
| // For example FIDL functions: |
| // |
| // 1: MyFunction(Args) -> (ReturnValue); |
| // 2: MyNonReturning(Args); |
| // |
| // The following C signatures will be generated by the FIDL compiler, and are expected in a |
| // server's dispatch table that wishes to implement this interface: |
| // |
| // zx_status_t MyFunction(void* ctx, Args... args, fidl_txn_t* txn); |
| // zx_status_t MyNonReturning(void* ctx, Args... args); |
| // |
| // This functionality can be implemented with this helper like so: |
| // |
| // class MyClass { |
| // public: |
| // zx_status_t FunctionImplementation(Args... args, fidl_txn_t* txn) { ... } |
| // zx_status_t NonReturningImplementation(Args... args) { ... } |
| // }; |
| // |
| // fidl_MyInterface_ops_t ops = { |
| // .MyFunction = Binder<MyClass>::BindMember<&MyClass::FunctionImplementation>, |
| // .MyNonReturning = Binder<MyClass>::BindMember<&MyClass::NonReturningImplementation>, |
| // }; |
| // |
| // Which will instantiate functions with signatures matching the auto-generated "My*Function" |
| // C bindings, that automatically invoke the *FunctionImplementation member functions, using a |
| // "MyClass" instance as context. |
| template <auto Fn, typename... Args> |
| static zx_status_t BindMember(Args... args) { |
| auto memfn = std::mem_fn(Fn); |
| return BindInternal(memfn, args...); |
| } |
| |
| // Bind a disambiguated, overloaded member of a base class to a FIDL dispatch function, to allow |
| // compatibility with the C bindings. |
| // |
| // For a FIDL function: |
| // |
| // 1: MyFunction(Args) -> (ReturnValue); |
| // |
| // The following C signature will be generated by the FIDL compiler, and is expected in a |
| // server's dispatch table that wishes to implement this interface: |
| // |
| // zx_status_t MyFunction(void* ctx, Args... args, fidl_txn_t* txn); |
| // |
| // This functionality can be implemented with this helper like so: |
| // |
| // class MyClass { |
| // public: |
| // zx_status_t FunctionOverloaded(Args... args, fidl_txn_t* txn) { ... } |
| // zx_status_t FunctionOverloaded(OtherArgs... other_args); |
| // }; |
| // |
| // fidl_MyInterface_ops_t ops = { |
| // .MyFunction = Binder<MyClass>::BindMember<zx_status_t(Args..., fidl_txn_t*), |
| // &MyClass::FunctionOverloaded> |
| // }; |
| // |
| // Which will instantiate a function with a signature matching the auto-generated "MyFunction" |
| // C binding, that automatically invokes the FunctionOverloaded member function matching the |
| // specified signature, using a "MyClass" instance as context. |
| template <typename FnSig, FnSig T::*Fn, typename... Args> |
| static zx_status_t BindMember(Args... args) { |
| auto memfn = std::mem_fn<FnSig, T>(Fn); |
| return BindInternal(memfn, args...); |
| } |
| |
| // A utility function which simplifies binding methods of derived methods to FIDL |
| // dispatch functions. |
| // |
| // A typical use case would look like the following: |
| // |
| // FIDL: |
| // |
| // 1: MyFunction(Args) -> (ReturnValue); |
| // |
| // C++: |
| // |
| // class MyClass { |
| // public: |
| // zx_status_t FunctionImplementation(Args... args, fidl_txn_t* txn) { |
| // ...; |
| // } |
| // |
| // zx_status_t Bind(async_dispatcher_t* dispatcher, zx::channel channel) { |
| // static constexpr Interface_ops_t kOps = { |
| // .MyFunction = Binder<MyClass>::BindMember<&MyClass::FunctionImplementation>, |
| // }; |
| // return Binder<MyClass>::BindOps<Dispatch>(dispatcher, channel, this, &kOps); |
| // } |
| // } |
| // |
| // ... |
| // |
| // MyClass instance; |
| // instance.Bind(dispatcher, channel); |
| template <auto Dispatch, typename Ops> |
| static zx_status_t BindOps(async_dispatcher_t* dispatcher, zx::channel channel, T* ctx, |
| const Ops* ops) { |
| static_assert(std::is_same<decltype(Dispatch), zx_status_t (*)(void*, fidl_txn_t*, fidl_msg_t*, |
| const Ops* ops)>::value, |
| "Invalid dispatch function"); |
| return fidl_bind(dispatcher, channel.release(), reinterpret_cast<fidl_dispatch_t*>(Dispatch), |
| ctx, ops); |
| } |
| |
| private: |
| template <typename MemFn, typename... Args> |
| static zx_status_t BindInternal(MemFn memfn, void* ctx, Args... args) { |
| auto instance = static_cast<T*>(ctx); |
| return memfn(instance, args...); |
| } |
| }; |
| |
| // Wrapper class around |fidl_async_txn_t|. This allows a transaction to be |
| // initialized, moved, reset, and completed without unintentionally "double completing" |
| // or "forgetting to complete". |
| class AsyncTransaction { |
| public: |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(AsyncTransaction); |
| |
| AsyncTransaction() : txn_(nullptr) {} |
| explicit AsyncTransaction(fidl_txn_t* txn) : txn_(fidl_async_txn_create(txn)) {} |
| explicit AsyncTransaction(AsyncTransaction&& other) : txn_(other.release()) {} |
| AsyncTransaction& operator=(AsyncTransaction&& other) { |
| Reinitialize(other.release()); |
| return *this; |
| } |
| |
| ~AsyncTransaction() { Reinitialize(); } |
| |
| // Acquires a reference to the |fidl_txn_t| backing this txn object. |
| // This reference will be invalidated if any non-const operations are called |
| // on |AsyncTransaction|. |
| // |
| // Should not be called if the underlying transaction is invalid. |
| fidl_txn_t* Transaction() const { |
| ZX_DEBUG_ASSERT(HasTransaction()); |
| return fidl_async_txn_borrow(txn_); |
| } |
| |
| // Completes the transaction and rebinds the underlying channel against the binding. |
| // |
| // Causes the transaction to become invalid. |
| // |
| // Should not be called if the underlying transaction is invalid. |
| zx_status_t Rebind() { |
| ZX_DEBUG_ASSERT(HasTransaction()); |
| return fidl_async_txn_complete(release(), true); |
| } |
| |
| // Completes the current transaction, if one exists, and causes |AsyncTransaction| to |
| // track a new |txn|. |
| // |
| // If |txn| is nullptr, this function causes the underlying transaction to become invalid. |
| void Reset(fidl_txn_t* txn = nullptr) { |
| fidl_async_txn_t* async_txn = txn ? fidl_async_txn_create(txn) : nullptr; |
| Reinitialize(async_txn); |
| } |
| |
| private: |
| bool HasTransaction() const { return txn_ != nullptr; } |
| |
| __attribute__((warn_unused_result)) fidl_async_txn_t* release() { |
| fidl_async_txn_t* txn = txn_; |
| txn_ = nullptr; |
| return txn; |
| } |
| |
| void Reinitialize(fidl_async_txn_t* txn = nullptr) { |
| if (HasTransaction()) { |
| fidl_async_txn_complete(release(), false); |
| } |
| txn_ = txn; |
| } |
| |
| fidl_async_txn_t* txn_; |
| }; |
| |
| } // namespace fidl |