blob: ba64079a06bc22e7acb4815850fb0ab619ea1a1d [file] [log] [blame]
// 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