Replies: 1 comment
-
|
See the discussion under #2661. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Overview
The C++ client uses CXX to build a low level API from the files in
src/*.rs, which are then used to create a high-level client with the header file beinginclude/iggy.hppand implementations living insrc/*.cpp. Bazel is used as the build-system.Concepts
Shared vs opaque structs: In CXX, “shared” structs are structs whose internals are transparent to both the C++ as well as Rust side (the
Messagestruct insrc/lib.rsis an example of this). “Opaque” types are types that are only visible to one side, and we need to define functions so that the other side can fetch data from these (theClientorIdentifierinsrc/lib.rsare examples of this).Design
I iterated on the C++ client a few times and finally settled on the following ideas:
src/*.cppaims to present a C++-native interface to the user, but does this by introducing latency by converting data between Rust and C++. If the user is alright with working with non-native C++ code, they can use the low-level client.rules_rustas the primary build path; insteadBUILD.bazelinvokescargo buildwith a Bazel-provisioned Rust toolchain. Since Bazel requires an isolated environment, this approach helps us preserve the local path dependencyiggy = { path = "../../core/sdk" }in Cargo.toml without having to convert the whole project into one managed using Bazel instead of cargo.Implementation
include/iggy.hppis the main header file defining all of the structs and methods available to the user. The implementations live insrc/*.cppfor the high-level client, andsrc/*.rsfor the rust bindings.CompressionAlgorithm,Expiry,MaxTopicSizeandPollingStrategyprovide higher-level structures that enable the user to specify their preferred configurations. On the Rust side, some of these structs accept inputs for certain options likeExpiry::duration(value), and do not for some likeExpiry::server_default(). To avoid defining a shared struct for each of them, the Rust side accepts two values (_kind and _value) wherever such a pattern is observed and the code builds the required type internally. For options that do not accept a value likeExpiry::server_default, the C++-side client sends 0 as the default value, and it is discarded when building the required type on the Rust side.IggyMessageclass on the C++ side utilizes the shared structMessageand presents a unified class for receiving and sending messages. Thesend_message_flag controls what the user can access when sending or receiving messages. Further, since message ids are 128-bit numbers, the client uses abseil to work with them. ThePolledMessagesclass is similarly defined and utilized thePolledMessagesshared struct to handle messages.Client,Identifier,StreamDetails, andTopicDetails) are handled on the C++-side using RAII classes to safely deal with raw pointers. These also expose various methods for the user to interact with the Rust side, which are similar to the Python ffi.IggyExceptionis the unified error class that our library presents to the user. Allrust:Errorandstd::exceptionare cast toIggyExceptionbefore being presented to the user.Limitations
examples/example.cppis ~200 MB)Future Development / Ideas
Credits
The initial iteration of the client and the build system settings were largely inspired by fluss-rust’s C++ bindings
Beta Was this translation helpful? Give feedback.
All reactions