Skip to content

Commit

Permalink
closures templates documented
Browse files Browse the repository at this point in the history
  • Loading branch information
milyin committed Aug 3, 2023
1 parent 1509aee commit 09295d6
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 82 deletions.
1 change: 1 addition & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ API Reference

namespaces
templates
closures
enums
generic
keyexpr
Expand Down
80 changes: 80 additions & 0 deletions docs/closures.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
..
.. Copyright (c) 2023 ZettaScale Technology
..
.. This program and the accompanying materials are made available under the
.. terms of the Eclipse Public License 2.0 which is available at
.. http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
.. which is available at https://www.apache.org/licenses/LICENSE-2.0.
..
.. SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
..
.. Contributors:
.. ZettaScale Zenoh Team, <zenoh@zettascale.tech>
..
Closure templates
=================

zenoh-c closure is a structure with 3 fields: context pointer ``context``, callback ``call`` and finalizer ``drop``. Context is a pointer to closure data,
callback is a function pointer and finalizer is a function pointer which is called when closure is destroyed. Example of zenoh-c closure:

.. code-block:: c++

typedef struct z_owned_closure_query_t {
void *context;
void (*call)(const struct z_query_t*, void *context);
void (*drop)(void*);
} z_owned_closure_query_t;

zenoh-cpp closures are wrappers around zenoh-c closures. These wrappers allows to construct closures from any callable C++ object, like lambda or function pointer.

All zenoh-cpp closures are based on the one of the following templates:
:cpp:class:`zenohcxx::ClosureConstPtrParam` and :cpp:class:`zenohcxx::ClosureMoveParam`. First one accepts ``const ZCPP_PARAM&``, second one accepts ``ZCPP_PARAM&&``.

These templates allows to construct closures from any callable C++ object:

- function pointer of type ``void (func*)(const ZCPP_PARAM&)`` or ``void (func*)(ZCPP_PARAM&&)``

Example:


.. code-block:: c++

void on_query(const Query&) { ... }


.. code-block:: c++

session.declare_queryable("foo/bar", on_query);

- any object which can be called with corresponding parameter, e.g. lambda or custom object. If object is passed by
move, closure will take ownership of it, otherwise it will store reference to it.

Example:

.. code-block:: c++

session.declare_queryable("foo/bar", [](const Query&) { ... });

or

.. code-block:: c++

struct OnQuery {
void operator()(const Query&) { ... }
~OnQuery() { ... }
};

.. code-block:: c++

OnQuery on_query;
session.declare_queryable("foo/bar", std::move(on_query));


.. doxygenclass:: zenohcxx::ClosureConstRefParam
:members:
:membergroups: Constructors Operators Methods

.. doxygenclass:: zenohcxx::ClosureMoveParam
:members:
:membergroups: Constructors Operators Methods
4 changes: 2 additions & 2 deletions docs/templates.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
.. ZettaScale Zenoh Team, <zenoh@zettascale.tech>
..
Templates
=========
Base templates
==============

There are two kinds of classes in zenoh-cpp: Copyable and Owned.

Expand Down
156 changes: 76 additions & 80 deletions include/zenohcxx/base.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -116,46 +116,11 @@ class Owned {
ZC_OWNED_TYPE _0;
};

//
// Base types for C++ wrappers for Zenoh closures
// Template parameters:
// - ZC_CLOSURE_TYPE is zenoh-c stucture for closure, like '::z_owned_closure_reply_t`, `::z_owned_closure_query_t`,
// etc.
// - ZC_PARAM is zenoh-c parameter type for closure: `::z_owned_reply_t`, `::z_query_t`, etc.
// - ZCPP_PARAM is zenoh-cpp parameter type for closure: `Reply`, `Query`, etc.
//
// There are two base types: `ClosureConstPtrParam` and `ClosureOwnedParam`. First one accepts 'const ZCPP_PARAM&',
// second one accepts 'ZCPP_PARAM&&'.
//
/// zenoh-cpp closure can be constructed from the following objects:
//
// - function pointer of type `void (func*)(const ZCPP_PARAM&)` or `void (func*)(ZCPP_PARAM&&)`
//
// Example:
//
// void on_query(const Query&) { ... }
// ...
// session.declare_queryable("foo/bar", on_query);
//
// - any object which can be called with corresponding parameter, e.g. lambda or custom object. If object is passed by
// move, closure will take ownership of it,
// otherwise it will store reference to it.
//
// Example:
//
// session.declare_queryable("foo/bar", [](const Query&) { ... });
//
// or
//
// struct OnQuery {
// void operator()(const Query&) { ... }
// ~OnQuery() { ... }
// };
//
// OnQuery on_query;
// session.declare_queryable("foo/bar", std::move(on_query));
//

/// @brief Base type for C++ wrappers of Zenoh closures with owned parameter
/// @tparam ZC_CLOSURE_TYPE - zenoh-c closure type ``::z_owned_closure_XXX_t``
/// @tparam ZC_PARAM - zenoh-c parameter type which is passed to closure ``::z_owned_XXX_t``
/// @tparam ZCPP_PARAM - zenoh-cpp parameter type which wraps zenoh-c parameter type (e.g. ``Reply`` for
/// ``::z_owned_reply_t``)
template <typename ZC_CLOSURE_TYPE, typename ZC_PARAM, typename ZCPP_PARAM,
typename std::enable_if_t<std::is_base_of_v<ZC_PARAM, ZCPP_PARAM> && sizeof(ZC_PARAM) == sizeof(ZCPP_PARAM),
bool> = true>
Expand All @@ -165,10 +130,37 @@ class ClosureConstRefParam : public Owned<ZC_CLOSURE_TYPE> {
public:
using Owned<ZC_CLOSURE_TYPE>::Owned;

// Closure is valid if it can be called. The drop operation is optional
/// @name Constructors

/// @brief Construct uninitialized closure
ClosureConstRefParam() : Owned<ZC_CLOSURE_TYPE>(nullptr) {}

/// @brief Construct closure from the data handler: any object with ``operator()(const ZCPP_PARAM&)`` defined
/// @param on_call data handler - any object with ``operator()(const ZCPP_PARAM&)`` defined
template <typename C>
ClosureConstRefParam(C&& on_call) : Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<C>(on_call), []() {})) {}

/// @brief Construct closure from the data handler and the drop handler
/// @param on_call data handler - any object with operator()(const ZCPP_PARAM&) defined
/// @param on_drop drop handler - any object with operator()() defined
//
/// Drop handler is convenient when it's necessary to catch dropping of the closure costructed from function
/// pointer, object lvalue reference or lambda. If the closure holds the user's object, the additional drop handler
/// is probably excessive. The cleanup in this case may be done in the object's destructor.
template <typename C, typename D>
ClosureConstRefParam(C&& on_call, D&& on_drop)
: Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<C>(on_call), std::forward<D>(on_drop))) {}

/// @name Methods

/// @brief Check if closure is valid. Closure is valid if it can be called, i.e. ``call`` is not ``nullptr``.
/// ``drop`` operation is optional
/// @return true if closure is valid
bool check() const { return Owned<ZC_CLOSURE_TYPE>::_0.call != nullptr; }

// Call closure with pointer to C parameter
/// @brief Call closure with pointer to C parameter (``::z_owned_XXX_t``)
/// @param v - pointer to C parameter
/// @return value returned by closure
ZC_RETVAL call(const ZC_PARAM* v) {
if constexpr (std::is_same_v<ZC_RETVAL, void>) {
if (check()) Owned<ZC_CLOSURE_TYPE>::_0.call(v, Owned<ZC_CLOSURE_TYPE>::_0.context);
Expand All @@ -177,27 +169,10 @@ class ClosureConstRefParam : public Owned<ZC_CLOSURE_TYPE> {
}
}

// Call closure with const reference to C++ parameter
/// @brief Call closure with const reference to C++ parameter (wrapper for ``::z_owned_XXX_t``)
/// @param v - const reference to C++ parameter
ZC_RETVAL operator()(const ZCPP_PARAM& v) { return call(&(static_cast<const ZC_PARAM&>(v))); }

// Construct empty closure
ClosureConstRefParam() : Owned<ZC_CLOSURE_TYPE>(nullptr) {}

// Construct closure from the data handler: any object with operator()(const ZCPP_PARAM&) defined
template <typename C>
ClosureConstRefParam(C&& on_call) : Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<C>(on_call), []() {})) {}

// Construct closure from the data handler and the drop handler
// data handler is any object with operator()(const ZCPP_PARAM&) defined
// drop handler is any object with operator()() defined
//
// Drop handler is convenient when it's necessary to catch dropping of the closure costructed from function pointer,
// object lvalue reference or lambda. If the closure holds the user's object, the additional drop handler is
// probably excessive. The cleanup in this case may be done in the object's destructor.
template <typename C, typename D>
ClosureConstRefParam(C&& on_call, D&& on_drop)
: Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<C>(on_call), std::forward<D>(on_drop))) {}

private:
template <typename C, typename D>
ZC_CLOSURE_TYPE wrap_call(C&& on_call, D&& on_drop) {
Expand All @@ -218,6 +193,11 @@ class ClosureConstRefParam : public Owned<ZC_CLOSURE_TYPE> {
}
};

/// @brief Base type for C++ wrappers of Zenoh closures with const pointer parameter
/// @tparam ZC_CLOSURE_TYPE - zenoh-c closure type ``::z_owned_closure_XXX_t``
/// @tparam ZC_PARAM - zenoh-c parameter type which is passed to closure ``::z_XXX_t``
/// @tparam ZCPP_PARAM - zenoh-cpp parameter type which wraps zenoh-c parameter type (e.g. ``Sample`` for
/// ``::z_sample_t``)
template <typename ZC_CLOSURE_TYPE, typename ZC_PARAM, typename ZCPP_PARAM,
typename std::enable_if_t<
std::is_base_of_v<Owned<ZC_PARAM>, ZCPP_PARAM> && sizeof(ZC_PARAM) == sizeof(ZCPP_PARAM), bool> = true>
Expand All @@ -228,10 +208,37 @@ class ClosureMoveParam : public Owned<ZC_CLOSURE_TYPE> {
public:
using Owned<ZC_CLOSURE_TYPE>::Owned;

// Closure is valid if it can be called. The drop operation is optional
/// @name Constructors

/// @brief Construct uninitialized closure
ClosureMoveParam() : Owned<ZC_CLOSURE_TYPE>(nullptr) {}

/// @brief Construct closure from the data handler: any object with ``operator()(ZCPP_PARAM&)`` or
/// ``operator()(ZCPP_PARAM&&)`` defined
/// @param on_call data handler - any object with ``operator()(ZCPP_PARAM&)`` or ``operator()(ZCPP_PARAM&&)``
template <typename T>
ClosureMoveParam(T&& on_call) : Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<T>(on_call), []() {})) {}

/// @brief Construct closure from the data handler and the drop handler
/// @param on_call data handler - any object with ``operator()(const ZCPP_PARAM&)`` or
/// ``operator()(ZCPP_PARAM&&)`` defined
/// @param on_drop drop handler - any object with ``operator()()`` defined
///
/// Drop handler is convenient when it's necessary to catch dropping of the closure costructed from function
/// pointer, object lvalue reference or lambda. If the closure holds the user's object, the additional drop handler
/// is probably excessive. The cleanup in this case may be done in the object's destructor.
template <typename T, typename D>
ClosureMoveParam(T&& on_call, D&& on_drop)
: Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<T>(on_call), std::forward<D>(on_drop))) {}

/// @brief Check if closure is valid. Closure is valid if it can be called, i.e. ``call`` is not ``nullptr``.
/// ``drop`` operation is optional
/// @return true if closure is valid
bool check() const { return Owned<ZC_CLOSURE_TYPE>::_0.call != nullptr; }

// Call closure with pointer to C parameter
/// @brief Call closure with pointer to C parameter (``::z_XXX_t``)
/// @param v - pointer to C parameter
/// @return value returned by closure
ZC_RETVAL call(ZC_PARAM* v) {
if constexpr (std::is_same_v<ZC_RETVAL, void>) {
if (check()) Owned<ZC_CLOSURE_TYPE>::_0.call(v, Owned<ZC_CLOSURE_TYPE>::_0.context);
Expand All @@ -240,27 +247,16 @@ class ClosureMoveParam : public Owned<ZC_CLOSURE_TYPE> {
}
}

// Call closure with movable reference to C++ parameter
/// @brief Call closure with rvalue reference to C++ parameter
/// @param v - rvalue reference to C++ parameter
/// @return value returned by closure
ZC_RETVAL operator()(ZCPP_PARAM&& v) { return call((&static_cast<ZC_PARAM&>(v))); }

// Call closure with lvalue reference to C++ parameter
/// @brief Call closure with lvalue reference to C++ parameter
/// @param v - lvalue reference to C++ parameter
/// @return value returned by closure
ZC_RETVAL operator()(ZCPP_PARAM& v) { return call((&static_cast<ZC_PARAM&>(v))); }

// Construct closure from the data handler: any object with operator()(ZCPP_PARAM&&) defined
template <typename T>
ClosureMoveParam(T&& on_call) : Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<T>(on_call), []() {})) {}

// Construct closure from the data handler and drop handler:
// data handler: any object with operator()(ZCPP_PARAM&&) defined
// drop handler: any object with operator()() defined
//
// Drop handler is convenient when it's necessary to catch dropping of the closure costructed from function pointer,
// object lvalue reference or lambda. If the closure holds the user's object, the additional drop handler is
// probably excessive. The cleanup in this case may be done in the object's destructor.
template <typename T, typename D>
ClosureMoveParam(T&& on_call, D&& on_drop)
: Owned<ZC_CLOSURE_TYPE>(wrap_call(std::forward<T>(on_call), std::forward<D>(on_drop))) {}

private:
template <typename C, typename D>
ZC_CLOSURE_TYPE wrap_call(C&& on_call, D&& on_drop) {
Expand Down

0 comments on commit 09295d6

Please sign in to comment.