| # Comparing new C++ and high-level C++ language bindings |
| |
| [TOC] |
| |
| ## Quick reference |
| |
| Here's how to recognize if a particular type/function/identifier in C++ code is |
| part of the new C++ bindings or high-level C++ bindings. |
| |
| Taking the [`examples.keyvaluestore.baseline`][kvstore] library as example: |
| |
| ```fidl |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/new/key_value_store/baseline/fidl/key_value_store.test.fidl" exclude_regexp="^//.*" %} |
| ``` |
| |
| Here are how the various FIDL elements will map to in the C++ bindings. Note |
| that in the table "C++" refers to the new C++ bindings, and applies equally to |
| both natural domain objects and wire domain objects. "Natural" refers to the |
| natural domain objects in the new C++ bindings. "Wire" refers to the wire domain |
| objects in the new C++ bindings. |
| |
| <div> |
| <table> |
| <thead> |
| <tr> |
| <th>FIDL element</th> |
| <th></th> |
| <th>C++ natural types</th> |
| <th>Comments</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr> |
| <td rowspan="2">Header include</td> |
| <td>C++</td> |
| <td>#include <fidl/examples.keyvaluestore.baseline/cpp/fidl.h></td> |
| <td>Format is `fidl/library name/cpp/fidl.h`</td> |
| </tr> |
| <tr> |
| <td>HLCPP</td> |
| <td>#include <examples/keyvaluestore/baseline/cpp/fidl.h></td> |
| <td>Format is `slash separated library name/cpp/fidl.h`</td> |
| </tr> |
| <tr> |
| <td rowspan="2">The library</td> |
| <td>C++</td> |
| <td>::examples_keyvaluestore_baseline</td> |
| <td rowspan="2">New C++ uses a single level namespace.<br>HLCPP uses nested namespaces.</td> |
| </tr> |
| <tr> |
| <td>HLCPP</td> |
| <td>::examples::keyvaluestore::baseline</td> |
| </tr> |
| <tr> |
| <td rowspan="3">Item struct</td> |
| <td>Natural</td> |
| <td>::examples_keyvaluestore_baseline::Item</td> |
| <td rowspan="3">On top of the namespace differences, the wire types are nested under "::wire".</td> |
| </tr> |
| <tr> |
| <td>Wire</td> |
| <td>::examples_keyvaluestore_baseline::wire::Item</td> |
| </tr> |
| <tr> |
| <td>HLCPP</td> |
| <td>::examples::keyvaluestore::baseline::Item</td> |
| </tr> |
| <tr> |
| <td rowspan="3">WriteError enum</td> |
| <td>Natural</td> |
| <td>::examples_keyvaluestore_baseline::WriteError</td> |
| <td rowspan="3">On top of the namespace differences, the wire types are nested under "::wire".<br>In case of enums and bits, the wire types and natural types are equivalent. There is just an extra type alias.<br></td> |
| </tr> |
| <tr> |
| <td>Wire</td> |
| <td>::examples_keyvaluestore_baseline::wire::WriteError</td> |
| </tr> |
| <tr> |
| <td>HLCPP</td> |
| <td>::examples::keyvaluestore::baseline::WriteError</td> |
| </tr> |
| <tr> |
| <td rowspan="3">string:128</td> |
| <td>Natural</td> |
| <td>std::string</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>Wire</td> |
| <td>fidl::StringView</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>HLCPP</td> |
| <td>std::string</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td rowspan="3">vector<byte>:64000</td> |
| <td>Natural</td> |
| <td>std::vector<uint8_t></td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>Wire</td> |
| <td>fidl::VectorView<uint8_t></td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>HLCPP</td> |
| <td>std::vector<uint8_t></td> |
| <td></td> |
| </tr> |
| <tr> |
| <td rowspan="2">protocol Store</td> |
| <td>C++</td> |
| <td>::examples_keyvaluestore_baseline::Store</td> |
| <td>A marker type that carries some information about the protocol</td> |
| </tr> |
| <tr> |
| <td>HLCPP</td> |
| <td>::examples::keyvaluestore::baseline::Store</td> |
| <td>An abstract base class that contains methods in the protocol</td> |
| </tr> |
| <tr> |
| <td rowspan="2">client_end:Store</td> |
| <td>C++</td> |
| <td>fidl::ClientEnd<Store> |
| <td rowspan="2"></td> |
| </tr> |
| <tr> |
| <td>HLCPP</td> |
| <td>fidl::InterfaceHandle<Store></td> |
| </tr> |
| <tr> |
| <td rowspan="2">server_end:Store</td> |
| <td>C++</td> |
| <td>fidl::ServerEnd<Store></td> |
| <td rowspan="2"></td> |
| </tr> |
| <tr> |
| <td>HLCPP</td> |
| <td>fidl::InterfaceRequest<Store></td> |
| </tr> |
| <tr> |
| <td rowspan="3">Client and server types<br>for the Store protocol<br></td> |
| <td>Natural</td> |
| <td colspan="2">Client: fidl::Client<Store><br>Synchronous client: fidl::SyncClient<Store><br>Server interface: fidl::Server<Store><br>Event handler interface: fidl::EventHandler<Store></td> |
| </tr> |
| <tr> |
| <td>Wire</td> |
| <td colspan="2">Client: fidl::WireClient<Store><br>Synchronous client: fidl::WireSyncClient<Store><br>Server interface: fidl::WireServer<Store><br>Event handler interface: fidl::WireEventHandler<Store></td> |
| </tr> |
| <tr> |
| <td>HLCPP</td> |
| <td colspan="2">Client: fidl::InterfacePtr<Store><br>Synchronous client: fidl::SynchronousInterfacePtr<Store><br>Server interface: Store<br>Event handler interface: N/A. InterfacePtr has setters that take one callback per event declaration.</td> |
| </tr> |
| </tbody> |
| </table> |
| </div> |
| |
| <!-- TODO(https://fxbug.dev/42062595): Replace the canvas reference with key value store |
| to align with the above, once the key value store example has landed. --> |
| |
| Here's the most common way to set up a client: |
| |
| <div> |
| <devsite-selector> |
| <!-- C++ (Natural) --> |
| <section> |
| <h3>C++ (Natural)</h3> |
| <pre class="prettyprint lang-cc">{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/new/canvas/baseline/cpp_natural/client/main.cc" region_tag="connect-protocol" %}</pre> |
| </section> |
| <!-- C++ (Wire) --> |
| <section> |
| <h3>C++ (Wire)</h3> |
| <pre class="prettyprint lang-cc">{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/new/canvas/baseline/cpp_wire/client/main.cc" region_tag="connect-protocol" %}</pre> |
| </section> |
| <!-- HLCPP --> |
| <section> |
| <h3>HLCPP</h3> |
| <pre class="prettyprint lang-cc">{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/new/canvas/baseline/hlcpp/client/main.cc" region_tag="connect-protocol" %}</pre> |
| </section> |
| </devsite-selector> |
| </div> |
| |
| See the [canvas][canvas] example for the full code listing and explanation. |
| |
| Here's the most common way to implement a server: |
| |
| <div> |
| <devsite-selector> |
| <!-- C++ (Natural) --> |
| <section> |
| <h3>C++ (Natural)</h3> |
| <pre class="prettyprint lang-cc">{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/new/canvas/baseline/cpp_natural/server/main.cc" region_tag="server-impl-short" %} |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/new/canvas/baseline/cpp_natural/server/main.cc" region_tag="addline-impl-short" %} |
| // ... |
| } |
| };</pre> |
| </section> |
| <!-- C++ (Wire) --> |
| <section> |
| <h3>C++ (Wire)</h3> |
| <pre class="prettyprint lang-cc">{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/new/canvas/baseline/cpp_wire/server/main.cc" region_tag="server-impl-short" %} |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/new/canvas/baseline/cpp_wire/server/main.cc" region_tag="addline-impl-short" %} |
| // ... |
| } |
| };</pre> |
| </section> |
| <!-- HLCPP --> |
| <section> |
| <h3>HLCPP</h3> |
| <pre class="prettyprint lang-cc">{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/new/canvas/baseline/hlcpp/server/main.cc" region_tag="server-impl-short" %} |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/new/canvas/baseline/hlcpp/server/main.cc" region_tag="addline-impl-short" %} |
| // ... |
| } |
| };</pre> |
| </section> |
| </devsite-selector> |
| </div> |
| |
| See the [canvas][canvas] example for the full code listing and explanation. |
| |
| ## New C++ bindings {#cpp} |
| |
| The new C++ bindings supports both low-level and high-level use cases, by |
| offering two families of generated domain objects, and corresponding client and |
| server APIs that speak those types. |
| |
| Note: prefer natural types unless optimizing for critical performance and |
| memory allocation. Refer to the [C++ tutorials][cpp-prefer-natural]. |
| |
| ### Natural types |
| |
| * Optimized to meet the needs of high-level service programming. |
| * Represent data structures using idiomatic C++ types such as `std::vector`, |
| `std::optional`, and `std::string`. |
| * Use smart pointers to manage heap allocated objects. |
| * Use `zx::handle` to manage handle ownership. |
| * Can convert data between their wire (e.g. `fidl::StringView`) and natural |
| type representations (e.g. `std::string`). |
| |
| ### Wire types |
| |
| * Optimized to meet the needs of low-level systems programming while providing |
| slightly more safety and features than the C bindings. |
| * Represent data structures whose memory layout coincides with the wire |
| format, i.e. satisfying C++ Standard Layout. This opens the door to |
| in-place encoding and decoding. |
| * Generated structures are views of an underlying buffer; they do not own |
| memory. |
| * Support in-place access of FIDL messages. |
| * Provide fine-grained control over memory allocation. |
| * Use owned handle types such as `zx::handle`. Note that since generated |
| structures are views of an underlying buffer, a parent structure will only |
| own child handles if it also owns their underlying buffer. For example, a |
| FIDL struct owns all the handles stored inline, but a FIDL vector of structs |
| containing handles will be represented as a vector view, which will not own |
| the out-of-line handles. |
| |
| ### Client and server APIs |
| |
| * Code generator produces more code compared to the C bindings. This includes |
| constructors, destructors, copy/move functions, conversions between domain |
| object families, protocol client implementations, and pure virtual server |
| interfaces. |
| * Users implement a server by sub-classing a provided server interface and |
| overriding the pure virtual methods for each operation. |
| * Clients supporting sync and async calls, and sync and async event handling. |
| * Requires C++17 or above. |
| |
| Refer to the [New C++ tutorial][cpp-tutorial] to get started. |
| |
| ## High-level C++ bindings |
| |
| <<../../../../_common/_hlcpp_notice.md>> |
| |
| * Optimized to meet the needs of high-level service programming. |
| * Represent data structures using idiomatic C++ types such as `std::vector`, |
| `std::optional`, and `std::string`. |
| * Use smart pointers to manage heap allocated objects. |
| * Use `zx::handle` (libzx) to manage handle ownership. |
| * Can convert data from in-place FIDL buffers to idiomatic heap allocated |
| objects. |
| * Can convert data from idiomatic heap allocated objects |
| (e.g. `std::string`) to in-place buffers (e.g. as a `fidl::StringView`). |
| * Code generator produces more code compared to the C bindings. This includes |
| constructors, destructors, protocol proxies, protocol stubs, copy/move |
| functions, and conversions to/from in-place buffers. |
| * Client performs protocol dispatch by sub-classing a provided stub and |
| implementing the virtual methods for each operation. |
| * Both async and synchronous clients are supported. However, the async clients |
| are not thread-safe. |
| * Requires C++14 or above. |
| |
| Refer to the [HLCPP tutorial][hlcpp-tutorial] to get started. |
| |
| <!-- TODO(https://fxbug.dev/NNNNN): Guide for migrating HLCPP to new C++. --> |
| |
| ## Summary |
| |
| Category | New C++ with wire types | New C++ with natural types | High-level C++ |
| -----------------------------------|-----------------------------------------------|--------------------------------------------|-------------------- |
| **audience** | drivers and performance-critical applications | high-level services | high-level services |
| **abstraction overhead** | RAII closing of handles [^1] | heap allocation, construction, destruction | heap allocation, construction, destruction |
| **type safe types** | enums, structs, unions, handles, protocols | enums, structs, unions, handles, protocols | enums, structs, unions, handles, protocols |
| **storage** | stack, user-provided buffer, or heap | heap | heap |
| **lifecycle** | manual or automatic free | automatic free (RAII) | automatic free (RAII) |
| **receive behavior** | decode in-place | decode into heap | decode then move to heap |
| **send behavior** | copy or vectorize | copy | copy |
| **calling protocol methods** | free functions or proxy | free functions or proxy | call through proxies, register callbacks |
| **implementing protocol methods** | manual dispatch or implement stub interface | implement stub interface | implement stub object, invoke callbacks |
| **async client** | yes | yes | yes |
| **async server** | yes (unbounded) [^2] | yes (unbounded) [^2] | yes (unbounded) |
| **parallel server dispatch** | yes [^3] | yes [^3] | no |
| **generated code footprint** | large | large | large |
| |
| <!-- footnotes. These must be 1 line; continuations indented 4 spaces. --> |
| |
| [^1]: Generated types own all handles stored inline. Out-of-line handles |
| e.g. those behind a pointer indirection are not closed when the containing |
| object of the pointer goes away. In those cases, the bindings provide a |
| `fidl::DecodedValue` object to manage all handles associated with a call. |
| |
| [^2]: The bindings library defined in [lib/fidl](/sdk/lib/fidl/cpp/wire) can |
| dispatch an unbounded number of in-flight transactions via `fidl::BindServer` |
| defined in |
| [lib/fidl/cpp/wire/channel.h](/sdk/lib/fidl/cpp/wire/include/lib/fidl/cpp/wire/channel.h). |
| |
| [^3]: The bindings library [lib/fidl](/sdk/lib/fidl/cpp/wire) enables parallel |
| dispatch using the `EnableNextDispatch()` API defined in |
| [lib/fidl/cpp/wire/async_transaction.h](/sdk/lib/fidl/cpp/wire/include/lib/fidl/cpp/wire/async_transaction.h). |
| |
| <!-- xrefs --> |
| [cpp-tutorial]: /docs/development/languages/fidl/tutorials/cpp |
| [cpp-prefer-natural]: /docs/development/languages/fidl/tutorials/cpp/README.md#natural_and_wire_domain_objects |
| [hlcpp-tutorial]: /docs/development/languages/fidl/tutorials/hlcpp |
| [kvstore]: /docs/development/languages/fidl/examples/key_value_store |
| [canvas]: /docs/development/languages/fidl/examples/canvas |