The Eclipse Zenoh: Zero Overhead Pub/sub, Store/Query and Compute.
Zenoh (pronounce /zeno/) unifies data in motion, data at rest and computations. It carefully blends traditional pub/sub with geo-distributed storages, queries and computations, while retaining a level of time and space efficiency that is well beyond any of the mainstream stacks.
Check the website zenoh.io and the roadmap for more detailed information.
The Zenoh C++ API is a C++ bindings for zenoh-c and zenoh-pico libraries. The library is headers only, but building zenoh-c requires Rust installed. Please check here to learn how to install it.
C++ bindings are still so the Zenoh team will highly appreciate any help in testing them on various platforms, system architecture, etc. and to report any issue you might encounter. This will help in greatly improving its maturity and robustness.
⚠️ WARNING⚠️ : Zenoh and its ecosystem are under active development. When you build from git, make sure you also build from git any other Zenoh repository you plan to use (e.g. binding, plugin, backend, etc.). It may happen that some changes in git are not compatible with the most recent packaged Zenoh release (e.g. deb, docker, pip). We put particular effort in mantaining compatibility between the various git repositories in the Zenoh project.
To install zenoh-cpp do the following steps:
-
Clone the sources
git clone https://github.com/eclipse-zenoh/zenoh-cpp.git
-
Do install. Neither zenoh-c nor zenoh-pico are required for the installation, but both are neccessary for building tests and examples. So, instead of the main project, it's faster to do install from "install" subproject.
Use option
CMAKE_INSTALL_PREFIX
for specifying installation location. Without this parameter installation is performed to default system location/usr/local
which requires root privileges.mkdir build && cd build cmake ../zenoh-cpp/install -DCMAKE_INSTALL_PREFIX=~/.local cmake --install .
The zenoh-cpp is header-only C++ library that wraps zenoh-c and zenoh-pico libraries. To make tests and examples it tries to find zenoh-c
and zenoh-pico
libraries in the following places:
-
Directories
zenoh-c
andzenoh-pico
located on the same level aszenoh-cpp
itself. WARNING: If you see building errors, make sure that you don't have obsoletezenoh-c
orzenoh-pico
directories nearby -
Libraries
zenoh-c
andzenoh-pico
installed to system. WARNING: If you see building errors, make sure that no old version of libraries installed to/usr/local/lib/cmake
-
Download zenoh-c and zenoh-pico from GitHub
mkdir -p build && cd build
cmake ../zenoh-cpp
cmake --build . --target tests
ctest
Notice that the output of cmake ../zenoh-cpp
shows where the dependencies were found
Examples are splitted into two subdirectories. Subdirectory universal
contains zenoh-cpp examples buildable with both zenoh-c and zenoh-pico backends. The zenohc
subdirectory contains examples with zenoh-c specific functionality.
The examples can be built in two ways. One is to select examples
as a build target of the main project:
mkdir -p build && cd build
cmake ../zenoh-cpp
cmake --build . --target examples
Examples are placed into build/examples/zenohc
and build/examples/zenohpico
directories.
Second way is to build examples
as a root project, which includes zenoh-cpp as subproject
mkdir -p build && cd build
cmake ../zenoh-cpp/examples
cmake --build .
Examples are placed into build/zenohc
and build/zenohpico
directories.
Change current directory to the variant you want (examples/zenohc
or examples/zenohpico
in the build directory)
See example sources for command line arguments (key expression, value, router address).
zenohc
examples can work standalone, but for zenohpico
examples the working zenoh router is required. So to run zenohpico
examples download zenoh project and run the router (Rust should be installed):
git clone https://github.com/eclipse-zenoh/zenoh
cd zenoh
cargo run
./z_sub
./z_pub
The z_pub
should receive message sent by z_sub
./z_queryable
./z_get
The z_get
should receive the data from z_queryable
./z_sub_thr_cpp
./z_pub_thr_cpp 1024
After 30-40 seconds delay the z_sub_thr
will start to show the throughput measure results
Below are the steps to include zenoh-cpp into CMake project. See also examples/simple directory for short examples of CMakeLists.txt.
-
include zenoh-c or zenoh-pico into your CMake project before dependency on zenoh-cpp itself. This is important as the library targets you need (
zenohcxx::zenohpico
,zenohcxx::zenohc::lib
,zenohcxx::zenohc::shared
andzenohcxx::zenohc::static)
are defined only if their backend library targets (zenohpico
,zenohc::lib
,zenohc::shared
andzenohc::static
are defined) -
include zenoh-cpp itself. This can be done with add_subdirectory, find_package or FetchContent CMake commands.
add_subdirectory(../zenoh-cpp)
find_package(zenohcxx)
FetchContent_Declare(zenohcxx GIT_REPOSITORY https://github.com/eclipse-zenoh/zenoh-cpp GIT_TAG main) FetchContent_MakeAvailable(zenohcxx)
-
add dependency on zenoh-cpp to your project in one of these ways:
target_link_libraries(yourproject PUBLIC zenohcxx::zenohpico)
target_link_libraries(yourproject PUBLIC zenohcxx::zenohc::lib)
target_link_libraries(yourproject PUBLIC zenohcxx::zenohc::shared)
target_link_libraries(yourproject PUBLIC zenohcxx::zenohc::static)
-
include the zenoh.hxx header and use namespace
zenoh
. Depending on preprocessor setting in library target the correct namespace (zenohpico
orzenohc
) is aliased tozenoh
namespace. This allows to write code compatible with both libraries without changes.#include "zenoh.hxx" using namespace zenoh;
or use headers zenohc.hxx or zenohpico.hxx directly
#include "zenohc.hxx" using namespace zenohc;
#include "zenohpico.hxx" using namespace zenohpico;
The documentation is on zenoh-cpp.readthedocs.io. You may also refer directly to base.hxx and api.hxx which contains all the definitions. Instruction how to build documentation locally is at docs/README.md.
There are three main kinds of wrapper classes / structures provided by zenoh-cpp. They are:
struct PutOptions : public Copyable<::z_put_options_t> {
...
}
These structures can be freely passed by value. They exacly matches corresponging C-library structures (z_put_options_t
in this case) and adds some necessary constructors and methods. For example PutOptions
default constructor calls the zenohc/zenohpico function z_put_options_default()
.
There are some copyable structures with View
postfix:
struct BytesView : public Copyable<::z_bytes_t> {
...
}
Such structures contains pointers to some external data. So they should be cared with caution, as they becoming invalid when their data source is destroyed. The same carefulness is required when handling std::string_view
from standard library for examlple.
class Config : public Owned<::z_owned_config_t> {
...
}
Classes which protects corresponding so-called owned
structures from copying, allowing move semantic only. Corresponding utility functions like z_check
, z_null
, z_drop
are integrated into the Owned
base template.
It's classes representing zenoh-c and zenoh-pico callback structures:
typedef ClosureMoveParam<::z_owned_closure_reply_t, ::z_owned_reply_t, Reply> ClosureReply;
typedef ClosureConstRefParam<::z_owned_closure_query_t, ::z_query_t, Query> ClosureQuery;
They allows to wrap C++ invocable objects (fuctions, lambdas, classes with operator() overloaded) and pass them as callbacks to zenoh.
The ClosureConstRefParam
types accepts objects, invocable with const Foo&
parameter. For example
session.declare_subscriber("foo/bar", [](const Sample& sample) { ... });
or
void on_query(const Query& query) { ... };
...
auto queryable = std::get<Queryable>(session.declare_queryable("foo/bar", on_query);
Instead of std::get
it makes sense to use zenoh::expect
here. It is a helper template which takes std::variant<T,ErrorMessage>
and
either returns T
or throws ErrorMessage
:
auto queryable = expect(session.declare_queryable("foo/bar", on_query));
The ClosureMoveParam
types accepts types, invocable with Foo
, Foo&
or Foo&&
. Callback may take ownership of the reference parameter passed
or do nothing and leave to caller to drop it.
session.get("foo/bar", "", [](Reply reply) { ... });
session.get("foo/bar", "", [](Reply& reply) { ... });
session.get("foo/bar", "", [](Reply&& reply) { ... });