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_PREFIXfor specifying installation location. Without this parameter installation is performed to default system location/usr/localwhich 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-candzenoh-picolocated on the same level aszenoh-cppitself. WARNING: If you see building errors, make sure that you don't have obsoletezenoh-corzenoh-picodirectories nearby -
Libraries
zenoh-candzenoh-picoinstalled 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
ctestNotice 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 examplesExamples 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_pubThe z_pub should receive message sent by z_sub
./z_queryable./z_getThe z_get should receive the data from z_queryable
./z_sub_thr_cpp./z_pub_thr_cpp 1024After 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::sharedandzenohcxx::zenohc::static)are defined only if their backend library targets (zenohpico,zenohc::lib,zenohc::sharedandzenohc::staticare 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 (zenohpicoorzenohc) is aliased tozenohnamespace. 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) { ... });