| # New C++ bindings |
| |
| <!-- TODO(https://fxbug.dev/42054534): Document natural bindings here too. --> |
| |
| The new C++ bindings come in **natural** and **wire** variants. The natural |
| bindings are optimized for safety and ergonomics, while the wire bindings are |
| optimized for performance. Most code should should use the natural bindings. |
| Only turn to the wire bindings when performance requirements demand it or when |
| you need precise control over memory allocation. |
| |
| Note: This page currently only documents the wire bindings. See the [New C++ |
| bindings tutorials] and [Comparing new C++ and high-level C++ language bindings] |
| for examples of the natural bindings. |
| |
| ## Libraries {#libraries} |
| |
| Given the library declaration: |
| |
| ```fidl |
| library fuchsia.examples; |
| ``` |
| |
| [Protocol types](#protocols) are generated in the `fuchsia_examples` namespace. |
| [Domain objects](#type-definitions) for this library are generated in the |
| `fuchsia_examples::wire` namespace, and [test scaffolding](#test-scaffolding) |
| is generated in the `fidl::testing` namespace. |
| |
| Generated type names are transformed to follow the |
| [Google C++ style guide][cpp-style]. |
| |
| ## Constants {#constants} |
| |
| [Constants][lang-constants] are generated as a `constexpr`. For example, the |
| following constants: |
| |
| ```fidl |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="consts" %} |
| ``` |
| |
| Are generated in the header file as: |
| |
| ```c++ |
| constexpr uint8_t kBoardSize = 9u; |
| extern const char[] kName; |
| ``` |
| |
| The correspondence between FIDL primitive types and C++ types is outlined in |
| [built-in types](#builtins). Instead of `constexpr`, strings are declared as an |
| `extern const char[]` in the header file, and defined in a `.cc` file. |
| |
| ## Fields |
| |
| This section describes how the FIDL toolchain converts FIDL types to C++ wire |
| types. These types can appear as members in an aggregate type or as parameters |
| to a protocol method. |
| |
| ### Built-in types {#builtins} |
| |
| The FIDL types are converted to C++ types based on the following table: |
| |
| |FIDL Type|C++ Wire Type| |
| |--- |--- | |
| |`bool`|`bool`, *(requires sizeof(bool) == 1)*| |
| |`int8`|`int8_t`| |
| |`int16`|`int16_t`| |
| |`int32`|`int32_t`| |
| |`int64`|`int64_t`| |
| |`uint8`|`uint8_t`| |
| |`uint16`|`uint16_t`| |
| |`uint32`|`uint32_t`| |
| |`uint64`|`uint64_t`| |
| |`float32`|`float`| |
| |`float64`|`double`| |
| |`array<T, N>`|`fidl::Array<T, N>`| |
| |`vector<T>:N`|`fidl::VectorView<T>`| |
| |`string`|`fidl::StringView`| |
| |`client_end:P` |`fidl::ClientEnd<P>`| |
| |`server_end:P` |`fidl::ServerEnd<P>`| |
| |`zx.Handle`|`zx::handle`| |
| |`zx.Handle:S`|The corresponding zx type is used whenever possible. For example, `zx::vmo` or `zx::channel`.| |
| |
| Optional vectors, strings, client/server ends, and handles have the same C++ |
| wire types as their non-optional counterparts. |
| |
| ### User defined types {#user-defined-types} |
| |
| The C++ wire bindings define a class for each user defined bits, enum, struct, |
| table, and union. For strict enums, they define an `enum class` instead of a |
| regular class. For unions, they use the same class to represent optional and |
| non-optional values. For boxed structs, they use `fidl::ObjectView`. See [Memory |
| ownership of wire domain objects] to learn more about `fidl::ObjectView`. |
| |
| ## Type definitions {#type-definitions} |
| |
| ### Bits {#bits} |
| |
| Given the [bits][lang-bits] definition: |
| |
| ```fidl |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="bits" %} |
| ``` |
| |
| The FIDL toolchain generates a `FileMode` class with a static member for each |
| flag, as well as a `kMask` member that contains a mask of all bits members (in |
| this example `0b111`): |
| |
| * `const static FileMode kRead` |
| * `const static FileMode kWrite` |
| * `const static FileMode kExecute` |
| * `const static FileMode kMask` |
| |
| `FileMode` provides the following methods: |
| |
| * `explicit constexpr FileMode(uint16_t)`: Constructs a value from an underlying |
| primitive value, preserving any unknown bit members. |
| * `constexpr static std::optional<FileMode> TryFrom(uint16_t value)`: Constructs |
| an instance of the bits from an underlying primitive value if the value does |
| not contain any unknown members, and returns `std::nullopt` otherwise. |
| * `constexpr static FileMode TruncatingUnknown(uint16_t value)`: Constructs an |
| instance of the bits from an underlying primitive value, clearing any unknown |
| members. |
| * Bitwise operators: Implementations for the `|`, `|=`, `&`, `&=`, `^`, `^=`, |
| and `~` operators are provided, allowing bitwise operations on the bits like |
| `mode |= FileMode::kExecute`. |
| * Comparison operators `==` and `!=`. |
| * Explicit conversion functions for `uint16_t` and `bool`. |
| |
| If `FileMode` is [flexible][lang-flexible], it will have the following |
| additional methods: |
| |
| * `constexpr FileMode unknown_bits() const`: Returns a bits value that contains |
| only the unknown members from this bits value. |
| * `constexpr bool has_unknown_bits() const`: Returns whether this value contains |
| any unknown bits. |
| |
| Note: When applying bitwise negation to bits values that contain unknown |
| members, the resulting bits value is only defined for the known bits. |
| |
| Example usage: |
| |
| ```c++ |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/domain_objects/main.cc" region_tag="wire-bits" adjust_indentation="auto" exclude_regexp="^TEST|^}" %} |
| ``` |
| |
| ### Enums {#enums} |
| |
| Given the [enum][lang-enums] definition: |
| |
| ```fidl |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="enums" %} |
| ``` |
| |
| The FIDL toolchain generates a C++ `enum class` using the specified underlying |
| type, or `uint32_t` if none is specified: |
| |
| ```c++ |
| enum class LocationType : uint32_t { |
| kMuseum = 1u; |
| kAirport = 2u; |
| kRestaurant = 3u; |
| }; |
| ``` |
| |
| Example usage: |
| |
| ```c++ |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/domain_objects/main.cc" region_tag="wire-enums" adjust_indentation="auto" exclude_regexp="^TEST|^}" %} |
| ``` |
| |
| #### Flexible enums {#flexible-enums} |
| |
| Flexible enums are implemented as a `class` instead of an `enum class`, with the |
| following methods: |
| |
| * `constexpr LocationType()`: Default constructor, which initializes the enum to |
| an unspecified unknown value. |
| * `constexpr LocationType(uint32_t value)`: Explicit constructor that takes in a |
| value of the underlying type of the enum. |
| * `constexpr bool IsUnknown()`: Returns whether the enum value is unknown. |
| * `constexpr static LocationType Unknown()`: Returns an enum value that is |
| guaranteed to be treated as unknown. If the enum has a member annotated with |
| [`[Unknown]`][unknown-attr], then the value of that member is returned. If |
| there is no such member, then the underlying value of the returned enum member |
| is unspecified. |
| * `explicit constexpr operator int32_t() const`: Converts the enum back to its |
| underlying value. |
| |
| The generated class contains a static member for each enum member, which are |
| guaranteed to match the members of the `enum class` in the equivalent |
| [strict][lang-flexible] enum: |
| |
| * `const static LocationType kMuseum` |
| * `const static LocationType kAirport` |
| * `const static LocationType kRestaurant` |
| |
| ### Structs {#structs} |
| |
| Given the [struct][lang-structs] declaration: |
| |
| ```fidl |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="structs" %} |
| ``` |
| |
| The FIDL toolchain generates an equivalent `struct`: |
| |
| ```c++ |
| struct Color { |
| uint32_t id = {}; |
| fidl::StringView name = {}; |
| } |
| ``` |
| |
| The C++ bindings do not support default values, and instead they zero-initialize |
| all fields of the struct. |
| |
| Example usage: |
| |
| ```c++ |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/domain_objects/main.cc" region_tag="wire-structs" adjust_indentation="auto" exclude_regexp="^TEST|^}" %} |
| ``` |
| |
| ### Unions {#unions} |
| |
| Given the union definition: |
| |
| ```fidl |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="unions" %} |
| ``` |
| |
| FIDL will generate a `JsonValue` class. `JsonValue` contains a public tag enum |
| class representing the possible [variants][union-lexicon]: |
| |
| ```c++ |
| enum class Tag : fidl_xunion_tag_t { |
| kIntValue = 2, |
| kStringValue = 3, |
| }; |
| ``` |
| |
| Each member of `Tag` has a value matching its ordinal specified in the `union` |
| definition. |
| |
| `JsonValue` provides the following methods: |
| |
| * `JsonValue()`: Default constructor. The constructed union is initially in an |
| "absent" state until a variant is set. The `WithFoo` constructors should be |
| preferred whenever possible. |
| * `~JsonValue()`: Destructor that clears the underlying union data. |
| * `JsonValue(JsonValue&&)`: Default move constructor. |
| * `JsonValue& operator=(JsonValue&&)`: Default move assignment |
| * `static JsonValue WithIntValue(fidl::ObjectView<int32>)` and `static |
| JsonValue WithStringValue(fidl::ObjectView<fidl::StringView>)`: Static |
| constructors that directly construct a specific variant of the union. |
| * `bool has_invalid_tag()`: Returns `true` if the instance of `JsonValue` does |
| not yet have a variant set. Calling this method without first setting the |
| variant leads to an assertion error. |
| * `bool is_int_value() const` and `bool is_string_value() const`: Each variant |
| has an associated method to check whether an instance of `JsonValue` is of |
| that variant |
| * `const int32_t& int_value() const` and `const fidl::StringView& string_value() |
| const`: Read-only accessor methods for each variant. Calling these methods |
| without first setting the variant leads to an assertion error. |
| * `int32_t& int_value()` and `fidl::StringView& string_value()`: Mutable |
| accessor methods for each variant. These methods will fail if `JsonValue` does |
| not have the specified variant set |
| * `Tag Which() const`: returns the current [tag][union-lexicon] of the |
| `JsonValue`. Calling this method without first setting the variant leads to an |
| assertion error. |
| |
| Example usage: |
| |
| ```c++ |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/domain_objects/main.cc" region_tag="wire-unions" adjust_indentation="auto" exclude_regexp="^TEST|^}" %} |
| ``` |
| |
| #### Flexible unions and unknown variants |
| |
| [Flexible][lang-flexible] unions have an extra variant in the generated `Tag` |
| class: |
| |
| ```c++ |
| enum class Tag : fidl_xunion_tag_t { |
| ... // other fields omitted |
| kUnknown = ::std::numeric_limits<::fidl_union_tag_t>::max(), |
| }; |
| ``` |
| |
| When a FIDL message containing a union with an unknown variant is decoded into |
| `JsonValue`, `JsonValue::Which()` will return `JsonValue::Tag::kUnknown`. |
| |
| The C++ bindings bindings do not store the raw bytes and handles of unknown |
| variants. |
| |
| Encoding a union with an unknown variant is not supported and will cause |
| an encoding failure. |
| |
| ### Tables {#tables} |
| |
| Given the [table][lang-tables] definition: |
| |
| ```fidl |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="tables" %} |
| ``` |
| |
| The FIDL toolchain generates a `User` class with the following methods: |
| |
| * `User()`: Default constructor, initializes an empty table with no fields set. |
| * `User::Builder(fidl::AnyArena& arena)`: Builder factory. |
| Returns a `fidl::WireTableBuilder<User>` that allocates the frame and members |
| from the supplied arena. |
| * `User::ExternalBuilder(fidl::ObjectView<fidl::WireTableFrame<User>> frame)`: |
| External builder factory. Returns a `fidl::WireTableExternalBuilder<User>` |
| with the supplied frame. This builder requires careful, memory management but |
| might occasionally be useful. _Caveat Emptor_. |
| * `User(User&&)`: Default move constructor. |
| * `~User()`: Default destructor. |
| * `User& operator=(User&&)`: Default move assignment. |
| * `bool IsEmpty() const`: Returns true if no fields are set. |
| * `bool has_age() const` and `bool has_name() const`: Returns whether a field is |
| set. |
| * `const uint8_t& age() const` and `const fidl::StringView& name() const`: |
| Read-only field accessor methods. Calling these methods without first setting |
| the field leads to an assertion error. |
| |
| In order to build a table, three additional class is generated: |
| `fidl::WireTableBuilder<User>`, `fidl::WireTableExternalBuilder<User>` and |
| `fidl::WireTableFrame<User>`. |
| |
| `fidl::WireTableFrame<User>` is a container for the table's internal storage, |
| and is allocated separately from the builder to maintain the object layout of |
| the underlying wire format. It is only use internally by builders. |
| |
| `fidl::WireTableFrame<User>` has the following methods: |
| |
| * `WireTableFrame()`: Default constructor. |
| |
| `fidl::WireTableExternalBuilder<User>` has the following methods: |
| |
| * `fidl::WireTableExternalBuilder<User> age(uint8_t)`: |
| set age by inlining it into the table frame. |
| * `fidl::WireTableExternalBuilder<User> name(fidl::ObjectView<fidl::StringView>)`: |
| set name with an already allocated value. |
| * `User Build()`: build and return the table object. After `Build()` is called |
| the builder must be discarded. |
| |
| `fidl::WireTableBuilder<User>` has all of the methods of |
| `fidl::WireTableExternalBuilder<User>` (but with the right return type from the |
| setters) and adds: |
| |
| * `fidl::WireTableBuilder<User> name(std::string_view)`: set name by allocating |
| a new `fidl::StringView` from the builder's arena and copying the supplied |
| string into it. |
| |
| |
| Example usage: |
| |
| ```c++ |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/cpp/domain_objects/main.cc" region_tag="wire-tables" adjust_indentation="auto" exclude_regexp="^TEST|^}" %} |
| ``` |
| |
| In addition to assigning fields with `fidl::ObjectView`, you can use any of the |
| allocation strategies described in [Memory ownership of wire domain objects]. |
| |
| Note: Tables with unknown fields will decode successfully but will fail to |
| encode. |
| |
| ### Inline layouts |
| |
| The generated C++ code uses the [the name chosen by `fidlc`][anon-names] for |
| inline layouts. |
| |
| The C++ bindings also generate scoped names to refer to inline layouts. For |
| example, for the FIDL: |
| |
| ```fidl |
| type Outer = struct { |
| inner struct {}; |
| }; |
| ``` |
| |
| The inner struct can be referred to using its globally unique name `Inner` as |
| well as the scoped name `Outer::Inner`. This can be useful when the top level |
| name is overridden using the [`@generated_name`][generated-name-attr] attribute, |
| for example in: |
| |
| ```fidl |
| type Outer = struct { |
| inner |
| @generated_name("SomeCustomName") struct {}; |
| }; |
| ``` |
| |
| the inner struct can be referred to as `SomeCustomName` or `Outer::Inner`. |
| |
| Another example of this is the [protocol result](#protocol-results) types: the |
| success and error variants of a type such as `TicTacToe_MakeMove_Result` can be |
| referenced as `TicTacToe_MakeMove_Result::Response` and |
| `TicTacToe_MakeMove_Result::Err`, respectively. |
| |
| ## Protocols {#protocols} |
| |
| Given the [protocol][lang-protocols]: |
| |
| ```fidl |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="protocols" %} |
| ``` |
| |
| Note: The `MakeMove` method above returns a bool representing success, and a |
| nullable response value. This is considered un-idiomatic, you should use an |
| [error type](#protocols-results) instead. |
| |
| FIDL will generate a `TicTacToe` class, which acts as an entry point for types |
| and classes that both clients and servers will use to interact with this |
| service. The members of this class are described in individual subsections in |
| the rest of this section. |
| |
| ### Typed channel endpoints {#typed-channels} |
| |
| The C++ bindings send and receive FIDL protocol messages over the [Zircon |
| channel][zircon-channel] transport, which carry arbitrary blobs of bytes and |
| handles. Rather than exposing raw endpoints, for instance `zx::channel`, the API |
| exposes three templated endpoint classes: |
| |
| * `fidl::ClientEnd<TicTacToe>`: the client endpoint of the `TicTacToe` protocol; |
| it owns a `zx::channel`. Client bindings that require exclusive ownership of |
| the channel would consume this type. For example, a |
| `fidl::WireClient<TicTacToe>` may be constructed from a |
| `fidl::ClientEnd<TicTacToe>`, also known as "binding the channel to the |
| message dispatcher". |
| * `fidl::UnownedClientEnd<TicTacToe>`: an unowned value borrowing some client |
| endpoint of the `TicTacToe` protocol. Client APIs that do not require |
| exclusive ownership of the channel would take this type. An `UnownedClientEnd` |
| may be derived from a `ClientEnd` of the same protocol type by calling |
| `borrow()`. The borrowed-from endpoint may be `std::move`-ed within the same |
| process, but cannot be dropped or transferred out-of-process, while there are |
| unowned borrows to it. |
| * `fidl::ServerEnd<TicTacToe>`: the server endpoint of the `TicTacToe` protocol; |
| it owns a `zx::channel`. Server bindings that require exclusive ownership of |
| the channel would consume this type. For example, a |
| `fidl::ServerEnd<TicTacToe>` may be provided to `fidl::BindServer<TicTacToe>` |
| among other parameters to create a server binding. |
| |
| There is no `UnownedServerEnd` as it is not yet needed to safely implement the |
| current set of features. |
| |
| A pair of client and server endpoint may be created using the |
| `::fidl::CreateEndpoints<TicTacToe>` library call. In a protocol request |
| pipelining scenario, one can immediately start performing operations on the |
| client endpoint after `std::move()`-ing the server endpoint to the remote |
| server. |
| |
| See the class documentation on these types for more details. |
| |
| ### Request and response types {#request-response-structs} |
| |
| The request type for a FIDL method or event can be accessed through a pair of |
| aliases, `fidl::WireRequest` and `fidl::WireEvent`: |
| |
| * `fidl::WireRequest<TicTacToe::StartGame>` |
| * `fidl::WireRequest<TicTacToe::MakeMove>` |
| * `fidl::WireEvent<TicTacToe::OnOpponentMove>` |
| |
| If the type used for the request or event is a named type, the alias will point |
| to that type. If the request type was an anonymous type, the alias will point to |
| the type name generated for that anonymous type. For both method requests and |
| events, the generated request type is named `[Method]Request`. |
| |
| Unlike requests, responses to two-way methods are generated as a new type, |
| `fidl::WireResult`: |
| |
| * `fidl::WireResult<TicTacToe::MakeMove>` |
| |
| The `fidl::WireResult` type inherits from `fidl::Status`, and its status tells |
| whether the call succeeded at the FIDL layer. If the method has a non-empty |
| response or uses FIDL error syntax, the generated `WireResult` type will also |
| have a set of accessors for accessing the return value or application-layer |
| error. The available accessors for the contained result are: |
| |
| * `WireResultUnwrapType<FidlMethod>* Unwrap()` |
| * `const WireResultUnwrapType<FidlMethod>* Unwrap() const` |
| * `WireResultUnwrapType<FidlMethod>& value()` |
| * `const WireResultUnwrapType<FidlMethod>& value() const` |
| * `WireResultUnwrapType<FidlMethod>* operator->()` |
| * `const WireResultUnwrapType<FidlMethod>* operator->() const` |
| * `WireResultUnwrapType<FidlMethod>& operator*()` |
| * `const WireResultUnwrapType<FidlMethod>& operator*() const` |
| |
| The `WireResultUnwrapType` is another type alias, which depends on whether the |
| method uses error syntax. Given this example library, |
| |
| ```fidl |
| library response.examples; |
| |
| protocol Test { |
| Foo() -> (struct { x int32; }); |
| Bar() -> () error int32; |
| Baz() -> (struct { x int32; }) error int32; |
| }; |
| ``` |
| |
| here is what the `fidl::WireResultUnwrapType` is for each method in the `Test` |
| protocol: |
| |
| * `fidl::WireResultUnwrapType<response_examples::Test::Foo> = response_examples::wire::TestFooResponse` |
| * `fidl::WireResultUnwrapType<response_examples::Test::Bar> = fit::result<int32_t>` |
| * `fidl::WireResultUnwrapType<response_examples::Test::Baz> = fit::result<int32_t, ::response_examples::wire::TestBazResponse*>` |
| |
| ### Client {#client} |
| |
| The C++ wire bindings bindings provides multiple ways to interact with a FIDL |
| protocol as a client: |
| |
| * `fidl::WireClient<TicTacToe>`: This class exposes thread-safe APIs for |
| outgoing asynchronous and synchronous calls as well as asynchronous event |
| handling. It owns the client end of the channel. An `async_dispatcher_t*` is |
| required to support the asynchronous APIs as well as event and error handling. |
| It must be used with a single-threaded dispatcher. Objects of this class must |
| be bound to the client endpoint and destroyed on the same thread that is |
| running the dispatcher. This is the recommended variant for most use cases, |
| except for those where an `async_dispatcher_t` cannot be used or when the |
| client needs to be moved between threads. |
| * `fidl::WireSharedClient<TicTacToe>`: This class has less opinions on threading |
| models compared to `WireClient`, but requires a two-phase shutdown pattern to |
| prevent use-after-frees. Objects of this class may be destroyed on an |
| arbitrary thread. It also supports use with a multi-threaded dispatcher. For |
| more details, see the [New C++ bindings threading guide]. |
| * `fidl::WireSyncClient<TicTacToe>`: This class exposes purely synchronous APIs |
| for outgoing calls as well as for event handling. It owns the client end of |
| the channel. |
| * `fidl::WireCall<TicTacToe>`: This class is identical to `WireSyncClient` |
| except that it does not have ownership of the client end of the channel. |
| `WireCall` may be preferable to `WireSyncClient` when implementing C APIs that |
| take raw `zx_handle_t`s. |
| |
| #### WireClient {#async-client} |
| |
| <!-- TODO(https://fxbug.dev/42136655) fidl::WireClient should be covered by generated docs --> |
| |
| `fidl::WireClient` is thread-safe and supports both synchronous and asynchronous |
| calls as well as asynchronous event handling. |
| |
| ##### Creation |
| |
| A client is created with a client-end `fidl::ClientEnd<P>` to the protocol `P`, |
| an `async_dispatcher_t*`, and an optional pointer to an |
| [`WireAsyncEventHandler`](#async-event-handlers) that defines the methods to be |
| called when a FIDL event is received or when the client is unbound. If the |
| virtual method for a particular event is not overridden, the event is ignored. |
| |
| ```cpp |
| class EventHandler : public fidl::WireAsyncEventHandler<TicTacToe> { |
| public: |
| EventHandler() = default; |
| |
| void OnOpponentMove(fidl::WireEvent<OnOpponentMove>* event) override { |
| /* ... */ |
| } |
| |
| void on_fidl_error(fidl::UnbindInfo unbind_info) override { /* ... */ } |
| }; |
| |
| fidl::ClientEnd<TicTacToe> client_end = /* logic to connect to the protocol */; |
| EventHandler event_handler; |
| fidl::WireClient<TicTacToe> client; |
| client.Bind(std::move(client_end), dispatcher, &event_handler); |
| ``` |
| |
| The binding may be torn down automatically in case of the server-end being |
| closed or due to an invalid message being received from the server. You may also |
| actively tear down the bindings by destroying the client object. |
| |
| ##### Outgoing FIDL methods |
| |
| You can invoke outgoing FIDL APIs through the `fidl::WireClient` instance. |
| Dereferencing a `fidl::WireClient` provides access to the following methods: |
| |
| * For `StartGame` (fire and forget): |
| |
| * `fidl::Status StartGame(bool start_first)`: Managed variant of a fire and |
| forget method. |
| |
| * For `MakeMove` (two way): |
| |
| * `[...] MakeMove(uint8_t row, uint8_t col)`: Managed variant of an |
| asynchronous two way method. It returns an internal type that must be used |
| to register the asynchronous continuation for receiving the result, such |
| as a callback. See [specifying asynchronous |
| continuation][specifying-asynchronous-continuation]. The continuation will |
| be executed on a dispatcher thread unless the dispatcher is shutting down. |
| |
| `fidl::WireClient::buffer` provides access to the following methods: |
| |
| * `fidl::Status StartGame(bool start_first)`: Caller-allocated variant of a fire |
| and forget method. |
| * `[...] MakeMove(uint8_t row, uint8_t col)`: Asynchronous, caller-allocated |
| variant of a two way method. It returns the same internal type as that from |
| the managed variant. |
| |
| `fidl::WireClient::sync` provides access to the following methods: |
| |
| * `fidl::WireResult<MakeMove> MakeMove(uint8_t row, uint8_t col)`: Synchronous, |
| managed variant of a two way method. The same method exists on |
| `WireSyncClient`. |
| |
| ##### Specifying asynchronous continuation {#specifying-asynchronous-continuation} |
| |
| See the corresponding C++ [documentation comments][wire-thenable-impl]. |
| |
| The continuation is called with a result object either representing a |
| successfully decoded response or an error. This is useful when the user needs to |
| propagate errors for each FIDL call to their originators. For example, a server |
| may need to make another FIDL call while handling an existing FIDL call, and |
| need to fail the original call in case of errors. |
| |
| The are a few methods on the returned object from a two way call: |
| |
| * `Then`: takes a callback, and invokes the callback at most once until the |
| client goes away. |
| |
| * `ThenExactlyOnce`: when passed a callback, the callback is executed exactly |
| once, either when the call succeeds or fails. However, because the callbacks |
| are invoked asynchronously, be ware of [use-after-free bugs when destroying a |
| client][result-callback-use-after-free]: the objects captured by the callback |
| may not be valid. |
| |
| * `ThenExactlyOnce` may also take a response context when control over |
| allocation is desired. `TicTacToe` has only one response context, |
| `fidl::WireResponseContext<TicTacToe::MakeMove>`, which has pure virtual |
| methods that should be overridden to handle the result of the call: |
| |
| ```c++ |
| virtual void OnResult(fidl::WireUnownedResult<MakeMove>& result) = 0; |
| ``` |
| |
| `OnResult` is called with a result object either representing a successfully |
| decoded response or an error. You are responsible for ensuring that the response |
| context object outlives the duration of the entire async call, since the |
| `fidl::WireClient` borrows the context object by address to avoid implicit |
| allocation. |
| |
| ##### Centralized error handler {#central-error-handler} |
| |
| When the binding is torn down due to an error, |
| `fidl::WireAsyncEventHandler<TicTacToe>::on_fidl_error` will be invoked from the |
| dispatcher thread with the detailed reason. When the error is dispatcher |
| shutdown, `on_fidl_error` will be invoked from the thread that is calling |
| dispatcher shutdown. It is recommended to put any central logic for logging or |
| releasing resources in that handler. |
| |
| #### WireSyncClient {#sync-client} |
| |
| `fidl::WireSyncClient<TicTacToe>` is a synchronous client which provides the |
| following methods: |
| |
| * `explicit WireSyncClient(fidl::ClientEnd<TicTacToe>)`: Constructor. |
| * `~WireSyncClient()`: Default destructor. |
| * `WireSyncClient(&&)`: Default move constructor. |
| * `WireSyncClient& operator=(WireSyncClient&&)`: Default move assignment. |
| * `const fidl::ClientEnd<TicTacToe>& client_end() const`: Returns the underlying |
| [client endpoint](#typed-channels). |
| * `fidl::Status StartGame(bool start_first)`: Managed variant of a fire and |
| forget method call. Buffer allocation for requests are entirely handled within |
| this function. |
| * `fidl::WireResult<TicTacToe::MakeMove> MakeMove(uint8_t row, uint8_t col)`: |
| Managed variant of a two way method call, which takes the parameters as |
| arguments and returns a `WireResult` object. Buffer allocation for requests |
| and responses are entirely handled within this function. The bindings |
| calculate a safe buffer size specific to this call at compile time based on |
| FIDL wire-format and maximum length constraints. The buffers are allocated on |
| the stack if they fit under 512 bytes, or else on the heap. See |
| [WireResult](#result) for details on buffer management. |
| * `fidl::Status HandleOneEvent(SyncEventHandler& event_handler)`: Blocks to |
| consume exactly one event from the channel. If the server has sent an epitaph, |
| the status contained within the epitaph is returned. See [Events](#events). |
| |
| `fidl::WireSyncClient<TicTacToe>::buffer` provides the following methods: |
| |
| * `fidl::WireUnownedResult<TicTacToe::StartGame> StartGame(bool start_first)`: |
| Caller-allocated variant of a fire and forget call, which takes in backing |
| storage for the request buffer passed as the argument to `buffer`, as well as |
| request parameters, and returns an `fidl::WireUnownedResult`. |
| |
| * `fidl::WireUnownedResult<TicTacToe::MakeMove> MakeMove(uint8_t row, uint8_t |
| col)`: Caller-allocated variant of a two way method, which requests both the |
| space for encoding the request and the space for receiving the response from |
| the same memory resource that is passed to the `buffer` method. |
| |
| Note that each method has both an owned and caller-allocated variant. In brief, |
| the owned variant of each method handles memory allocation for requests and |
| responses, whereas the caller-allocated variant allows the user to provide the |
| buffer themselves. The owned variant is easier to use, but may result in extra |
| allocation. |
| |
| #### WireCall {#client-call} |
| |
| `fidl::WireCall<TicTacToe>` provides similar methods to those found in |
| `WireSyncClient`, with the only difference being that `WireCall` can be |
| constructed with a `fidl::UnownedClientEnd<TicTacToe>` i.e. it borrows the |
| client endpoint: |
| |
| * `fidl::WireResult<StartGame> StartGame(bool start_first)`: Owned variant of |
| `StartGame`. |
| * `fidl::WireResult<MakeMove> MakeMove(uint8_t row, uint8_t col)`: Owned variant |
| of `MakeMove`. |
| |
| `fidl::WireCall<TicTacToe>(client_end).buffer` provides the following methods: |
| |
| * `fidl::WireUnownedResult<StartGame> StartGame(bool start_first)`: |
| Caller-allocated variant of `StartGame`. |
| * `fidl::WireUnownedResult<MakeMove> MakeMove(uint8_t row, uint8_t col);`: |
| Caller-allocated variant of `MakeMove`. |
| |
| #### Result, WireResult, and WireUnownedResult {#result} |
| |
| The managed variants of each method of `WireSyncClient` and `WireCall` all |
| return a `fidl::WireResult<Method>` type, whereas the caller-allocating variants |
| all return an `fidl::WireUnownedResult<Method>`. Fire and forget methods on |
| `fidl::WireClient` return a `fidl::Status`. These types define the same set of |
| methods: |
| |
| * `zx_status status() const` returns the transport status. it returns the |
| first error encountered during (if applicable) linearizing, encoding, making |
| a call on the underlying channel, and decoding the result. If the status is |
| `ZX_OK`, the call has succeeded, and vice versa. |
| * `fidl::Reason reason() const` returns details about which operation failed, |
| when `status()` is not `ZX_OK`. For example, if encoding failed, `reason()` |
| will return `fidl::Reason::kEncodeError`. `reason()` should not be called |
| when status is `ZX_OK`. |
| * `const char* error_message() const` contains a brief error message when |
| status is not `ZX_OK`. Otherwise, returns `nullptr`. |
| * **(only for WireResult and WireUnownedResult for two-way calls)** `T* Unwrap()` |
| returns a pointer to the [response struct](#request-response-structs). For |
| `WireResult`, the pointer points to memory owned by the result object. For |
| `WireUnownedResult`, the pointer points to the caller-provided buffer. |
| `Unwrap()` should only be called when the status is `ZX_OK`. |
| |
| Additionally, `WireResult` and `WireUnownedResult` for two-way calls will |
| implement dereference operators that return the response struct itself. |
| This allows code such as: |
| |
| ```cpp |
| fidl::WireResult result = client.sync()->MakeMove(0, 0); |
| auto* response = result.Unwrap(); |
| bool success = response->success; |
| ``` |
| |
| To be simplified to: |
| |
| ```cpp |
| fidl::WireResult result = client.sync()->MakeMove(0, 0); |
| bool success = result->success; |
| ``` |
| |
| > `WireResult<Method>` manages ownership of all buffer and handles, while |
| > `::Unwrap()` returns a view over it. Therefore, this object must outlive any |
| > references to the unwrapped response. |
| |
| ##### Allocation strategy And move semantics |
| |
| `WireResult` stores the response buffer inline if the message is guaranteed to |
| fit under 512 bytes. Since the result object is usually instantiated on the |
| caller's stack, this effectively means the response is stack-allocated when it |
| is reasonably small. If the maximal response size exceeds 512 bytes, |
| `WireResult` instead contains a heap-allocated buffer. |
| |
| Therefore, `std::move()` on `WireResult` is not supported. The content has to be |
| copied if the buffer is inline, and pointers to out-of-line objects have to be |
| updated to locations within the destination object, these are surprising |
| overheads for a move operation that is commonly understood to be low cost. |
| |
| If the result object need to be passed around multiple function calls, consider |
| pre-allocating a buffer in the outer-most function and use the caller-allocating |
| flavor. |
| |
| ### Server |
| |
| Implementing a server for a FIDL protocol involves providing a concrete |
| implementation of `TicTacToe`. |
| |
| The generated `fidl::WireServer<TicTacToe>` class has pure virtual methods |
| corresponding to the method calls defined in the FIDL protocol. Users implement |
| a `TicTacToe` server by providing a concrete implementation of |
| `fidl::WireServer<TicTacToe>`, which has the following pure virtual methods: |
| |
| * `virtual void StartGame(StartGameRequestView request, StartGameCompleter::Sync& |
| completer)` |
| * `virtual void MakeMove(MakeMoveRequestView request, MakeMoveCompleter::Sync& |
| completer)` |
| |
| Refer to the [example C++ server][cpp-server-example] for how to bind and |
| set up a server implementation. |
| |
| The C++ wire bindings also provide functions for manually dispatching a message |
| given an implementation, `fidl::WireDispatch<TicTacToe>`: |
| |
| * `void fidl::WireDispatch<TicTacToe>(fidl::WireServer<TicTacToe>* impl, |
| fidl::IncomingMessage&& msg, ::fidl::Transaction* txn)`: Dispatches the |
| incoming message. If there is no matching handler, it closes all handles in |
| the message and notifies `txn` of an error. |
| |
| #### Requests {#server-requests} |
| |
| The request is provided as the first argument of each generated FIDL method |
| handler. This a view of the request (a pointer). All the request arguments are |
| accessed using the arrow operator and the argument name. |
| |
| For example: |
| |
| * `request->start_first` |
| * `request->row` |
| |
| See [Memory ownership of wire domain objects] for notes on request lifetime. |
| |
| #### Completers {#server-completers} |
| |
| A completer is provided as the last argument of each generated FIDL method |
| handler, after all the FIDL request parameters for that method. The completer |
| classes capture the various ways one can complete a FIDL transaction, e.g. by |
| sending a reply, closing the channel with an epitaph, etc, and come in both |
| synchronous and asynchronous versions (though the `::Sync` class is provided as |
| an argument by default). In this example, this completers are: |
| |
| * `fidl::WireServer<TicTacToe>::StartGameCompleter::Sync` |
| * `fidl::WireServer<TicTacToe>::StartGameCompleter::Async` |
| * `fidl::WireServer<TicTacToe>::MakeMoveCompleter::Sync` |
| * `fidl::WireServer<TicTacToe>::MakeMoveCompleter::Async` |
| |
| All completer classes provide the following methods: |
| |
| * `void Close(zx_status_t status)`: Close the channel and send `status` as the |
| epitaph. |
| |
| In addition, two way methods will provide two versions of a `Reply` method for |
| replying to a response: a managed variant and a caller-allocating variant. These |
| correspond to the variants present in the [client API](#client). For example, |
| both `MakeMoveCompleter::Sync` and `MakeMoveCompleter::Async` provide the |
| following `Reply` methods: |
| |
| * `::fidl::Status Reply(bool success, fidl::ObjectView<GameState> new_state)` |
| * `::fidl::Status Reply(fidl::BufferSpan _buffer, bool success, |
| fidl::ObjectView<GameState> new_state)` |
| |
| Because the status returned by Reply is identical to the unbinding status, it |
| can be safely ignored. |
| |
| Finally, sync completers for two way methods can be converted to an async |
| completer using the `ToAsync()` method. Async completers can out-live the scope |
| of the handler by e.g. moving it into a lambda capture, allowing the server to |
| respond to requests asynchronously. The async completer has the same methods for |
| responding to the client as the sync completer. See [Responding to requests |
| asynchronously] for example usage |
| |
| Note: Each `Completer` object must only be accessed by one thread at a time. |
| Simultaneous access from multiple threads will result in a crash. |
| |
| ##### Parallel message handling |
| |
| By default, messages from a single binding are handled sequentially, i.e. a |
| single thread attached to the dispatcher (run loop) is woken up if necessary, |
| reads the message, executes the handler, and returns back to the dispatcher. The |
| `::Sync` completer provides an additional API, `EnableNextDispatch()`, which may |
| be used to selectively break this restriction. Specifically, a call to this API |
| will enable another thread waiting on the dispatcher to handle the next message |
| on the binding while the first thread is still in the handler. Note that |
| repeated calls to `EnableNextDispatch()` on the same `Completer` are idempotent. |
| |
| Note: This use-case is currently possible only using the |
| [lib/fidl](/sdk/lib/fidl/cpp/wire) bindings. |
| |
| ```cpp |
| void DirectedScan(int16_t heading, ScanForPlanetsCompleter::Sync& completer) override { |
| // Suppose directed scans can be done in parallel. It would be suboptimal to block one scan until |
| // another has completed. |
| completer.EnableNextDispatch(); |
| fidl::VectorView<Planet> discovered_planets = /* perform a directed planet scan */; |
| completer.Reply(std::move(discovered_planets)); |
| } |
| ``` |
| |
| ### Caller-allocated methods |
| |
| A number of the APIs above provide owned and caller-allocated variants of |
| generated methods. |
| |
| The caller-allocated variant defers all memory allocation responsibilities to |
| the caller. The type `fidl::BufferSpan` references a buffer address and size. It |
| will be used by the bindings library to construct the FIDL request, hence it |
| must be sufficiently large. The method parameters (e.g. `heading`) are |
| *linearized* to appropriate locations within the buffer. There are a number of |
| ways to create the buffer: |
| |
| ```cpp |
| // 1. On the stack |
| using StartGame = TicTacToe::StartGame; |
| fidl::SyncClientBuffer<StartGame> buffer; |
| auto result = client.buffer(buffer.view())->StartGame(true); |
| |
| // 2. On the heap |
| auto buffer = std::make_unique<fidl::SyncClientBuffer<StartGame>>(); |
| auto result = client.buffer(buffer->view())->StartGame(true); |
| |
| // 3. Some other means, e.g. thread-local storage |
| constexpr uint32_t buffer_size = fidl::SyncClientMethodBufferSizeInChannel<StartGame>(); |
| uint8_t* buffer = allocate_buffer_of_size(buffer_size); |
| fidl::BufferSpan buffer_span(/* data = */buffer, /* capacity = */request_size); |
| auto result = client.buffer(buffer_span)->StartGame(true); |
| |
| // Check the transport status (encoding error, channel writing error, etc.) |
| if (result.status() != ZX_OK) { |
| // Handle error... |
| } |
| |
| // Don't forget to free the buffer at the end if approach #3 was used... |
| ``` |
| |
| > When the caller-allocating flavor is used, the `result` object borrows the |
| > request and response buffers (hence its type is under `WireUnownedResult`). |
| > Make sure the buffers outlive the `result` object. |
| > See [WireUnownedResult](#result). |
| |
| Note: buffers passed to the bindings must be aligned to 8 bytes. The |
| `fidl::SyncClientBuffer` helper class does this automatically. For an |
| asynchronous client, use `fidl::AsyncClientBuffer`. Failure to align would |
| result in a run-time error. |
| |
| ### Events {#events} |
| |
| In the C++ bindings, events can be handled asynchronously or synchronously, |
| depending on the type of [client](#client) being used. |
| |
| #### Async client {#async-event-handlers} |
| |
| When using a `fidl::WireClient`, events can be handled asynchronously by passing |
| the class a `fidl::WireAsyncEventHandler<TicTacToe>*`. The |
| `WireAsyncEventHandler` class has the following members: |
| |
| * `virtual void OnOpponentMove(fidl::WireEvent<OnOpponentMove>* event) {}`: |
| handler for the OnOpponentMove event (one method per event). |
| |
| * `virtual on_fidl_error(::fidl::UnbindInfo info) {}`: method called when the |
| client encounters a terminal error. |
| |
| To be able to handle events and errors, a class that inherits from |
| `fidl::WireAsyncEventHandler<TicTacToe>` must be defined. |
| |
| #### Sync client {#sync-event-handlers} |
| |
| In `WireSyncClient`, events are handled synchronously by calling |
| a `HandleOneEvent` function and passing it a |
| `fidl::WireSyncEventHandler<TicTacToe>`. |
| |
| `WireSyncEventHandler` is a class that contains a pure virtual method for each |
| event. In this example, it consists of the following member: |
| |
| * `virtual void OnOpponentMove(fidl::WireEvent<TicTacToe::OnOpponentMove>* |
| event) = 0`: The handle for the OnOpponentMove event. |
| |
| To be able to handle events, a class that inherits from `WireSyncEventHandler` |
| must be defined. This class must define the virtual methods for all the events |
| in the protocol. Then an instance of this class must be created. |
| |
| There are two ways to handle one event. Each one use an instance of the user |
| defined event handler class: |
| |
| * `::fidl::Status fidl::WireSyncClient<TicTacToe>::HandleOneEvent( |
| SyncEventHandler& event_handler)`: A bound version for sync clients. |
| * `::fidl::Status fidl::WireSyncEventHandler<TicTacToe>::HandleOneEvent( |
| fidl::UnownedClientEnd<TicTacToe> client_end)`: An unbound version that |
| uses an `fidl::UnownedClientEnd<TicTacToe>` to handle one event for a specific |
| handler. |
| |
| For each call to `HandleOneEvent`, the method waits on the channel for exactly |
| one incoming message. Then the message is decoded. If the result is |
| `fidl::Status::Ok()` then exactly one virtual method has been called. Otherwise, |
| no virtual method has been called and the status indicates the error. |
| |
| If the handlers are always the same (from one call to `HandleOneEvent` to the |
| other), the `WireSyncEventHandler` object should be constructed once and used |
| each time you need to call `HandleOneEvent`. |
| |
| If an event is marked as transitional, then a default implementation is |
| provided, which causes `HandleOneEvent` to return an error upon receiving a |
| transitional event that is not handled by the user. |
| |
| #### Server |
| |
| `fidl::WireSendEvent` is used to send events from the server side. There are two |
| overloads: |
| |
| * `fidl::WireSendEvent(const fidl::ServerBindingRef<Protocol>& binding_ref)` |
| to send events over a server binding reference. |
| * `fidl::WireSendEvent(const fidl::ServerEnd<Protocol>& endpoint)` |
| to send events over an endpoint. |
| |
| ##### Sending events using a server binding object {#bound-event-sending} |
| |
| When binding a server implementation to a channel, `fidl::BindServer` returns a |
| `fidl::ServerBindingRef<Protocol>`, which is the means by which you may interact |
| safely with a server binding. |
| |
| Calling `fidl::WireSendEvent` with a binding reference returns an interface to |
| send events. |
| |
| The event sender interface contains methods for sending each event. As a |
| concrete example, the event sender interface for `TicTacToe` provides the |
| following methods: |
| |
| * `fidl::Status OnOpponentMove(GameState new_state)`: Managed flavor. |
| |
| Calling `.buffer(...)` returns a similar interface for the caller-allocating |
| flavor, allocating encoding buffers from the memory resource passed to |
| `.buffer`, analogous to the [client API](#client) as well as the [server |
| completers](#server-completers). |
| |
| ##### Sending events with a ServerEnd object |
| |
| A server endpoint by itself is represented by `fidl::ServerEnd<Protocol>`. |
| |
| [Sending events using a server binding object](#bound-event-sending) is the |
| standard approach to sending events while the server endpoint is bound to an |
| implementation. However, there may be occasions which call for sending events |
| on a `fidl::ServerEnd<TicTacToe>` object directly, without setting up a server |
| binding. |
| |
| `fidl::WireSendEvent` takes a constant reference to `fidl::ServerEnd<Protocol>`. |
| It does not support `zx::unowned_channel`, to reduce the chances of using an |
| endpoint after the handle has been closed elsewhere. |
| |
| ### Results {#protocols-results} |
| |
| Given a method: |
| |
| ```fidl |
| protocol TicTacToe { |
| MakeMove(struct { |
| row uint8; |
| col uint8; |
| }) -> (struct { |
| new_state GameState; |
| }) error MoveError; |
| }; |
| ``` |
| |
| FIDL will generate convenience methods on the [completers](#server-completers) |
| corresponding to methods with an error type. Depending on the Reply "variant", |
| the completer will have `ReplySuccess`, `ReplyError`, or both methods to respond |
| directly with the success or error data, without having to construct a union. |
| |
| For the managed flavor, both methods are available: |
| |
| * `void ReplySuccess(GameState new_state)` |
| * `void ReplyError(MoveError error)` |
| |
| Since `ReplyError` doesn't require heap allocation, only `ReplySuccess` exists |
| for the caller-allocated flavor: |
| |
| * `void ReplySuccess(fidl::BufferSpan _buffer, GameState new_state)` |
| |
| Note that the passed in buffer is used to hold the *entire* response, not just |
| the data corresponding to the success variant. |
| |
| The regularly generated `Reply` methods are available as well: |
| |
| * `void Reply(TicTacToe_MakeMove_Result result)`: Owned variant. |
| * `void Reply(fidl::BufferSpan _buffer, TicTacToe_MakeMove_Result result)`: |
| Caller-allocated variant. |
| |
| The owned and caller-allocated variant use a parameter of |
| `TicTacToe_MakeMove_Result`, which is generated as a [union](#unions) with two |
| variants: `Response`, which is a `TicTacToe_MakeMove_Response`, and `Err`, which |
| is a `MoveError`. `TicTacToe_MakeMove_Response` is generated as a |
| [struct](#structs) with the response parameters as its fields. In this case, it |
| has a single field `new_state`, which is a `GameState`. |
| |
| ### Unknown interaction handling {#unknown-interaction-handling} |
| |
| #### Server-side |
| |
| When a protocol is declared as `open` or `ajar`, the generated |
| `fidl::WireServer<Protocol>` type will also inherit from |
| `fidl::UnknownMethodHandler<Protocol>`. The `UnknownMethodHandler` defines a |
| single abstract method which the server must implement, called |
| `handle_unknown_method`, with this signature: |
| |
| ```c++ |
| virtual void handle_unknown_method(UnknownMethodMetadata<Protocol> metadata, |
| UnknownMethodCompleter::Sync& completer) = 0; |
| ``` |
| |
| The provided `UnknownMethodMetadata` is a struct with one or two fields |
| depending on if the protocol is `ajar` or `open`. Here's what the metadata |
| struct looks like, with template arguments omitted for simplicity: |
| |
| ```c++ |
| struct UnknownMethodMetadata { |
| // Ordinal of the method that was called. |
| uint64_t method_ordinal; |
| // Whether the method that was called was a one-way method or a two-way |
| // method. This field is only defined if the protocol is open, since ajar |
| // protocols only handle one-way methods. |
| UnknownMethodType unknown_method_type; |
| }; |
| ``` |
| |
| `UnknownMethodType` is an enum with two variants, `kOneWay` and `kTwoWay`, which |
| tells which kind of method was called. |
| |
| The `UnknownMethodCompleter` is the same completer type as is used for one-way |
| methods. |
| |
| #### Client-side |
| |
| There is no way for the client to tell if a `flexible` one-way method was known to |
| the server or not. For `flexible` two-way methods, if the method is not known to |
| the server the `fidl::WireResult` will have a `fidl::Status` of |
| `ZX_ERR_NOT_SUPPORTED` with a reason of `fidl::Reason::kUnknownMethod`. The |
| `kUnknownMethod` reason is only possible for a flexible two-way method. |
| |
| Aside from the possibility of getting an error with `Reason` of |
| `kUnknownMethod`, there are no API differences between `strict` and `flexible` |
| methods on the client. |
| |
| For `open` and `ajar` protocols, the generated |
| `fidl::WireAsyncEventHandler<Protocol>` and |
| `fidl::WireSyncEventHandler<Protocol>` will inherit from |
| `fidl::UnknownEventHandler<Protocol>`. `UnknownEventHandler` defines a single |
| method which event handlers must implement, called `handle_unknown_event`, with |
| this signature: |
| |
| ```c++ |
| virtual void handle_unknown_event(UnknownEventMetadata<Protocol> metadata) = 0; |
| ``` |
| |
| The `UnknownEventMetadata` has this layout, with template arguments omitted for |
| simplicity: |
| |
| ```c++ |
| struct UnknownEventMetadata { |
| // Ordinal of the event that was received. |
| uint64_t event_ordinal; |
| }; |
| ``` |
| |
| ### Protocol composition {#protocol-composition} |
| |
| FIDL does not have a concept of inheritance, and generates full code as |
| described above for all [composed protocols][lang-protocol-composition]. In |
| other words, the code generated for |
| |
| ```fidl |
| protocol A { |
| Foo(); |
| }; |
| |
| protocol B { |
| compose A; |
| Bar(); |
| }; |
| ``` |
| |
| Provides the same API as the code generated for: |
| |
| ```fidl |
| protocol A { |
| Foo(); |
| }; |
| |
| protocol B { |
| Foo(); |
| Bar(); |
| }; |
| ``` |
| |
| The generated code is identical except for the method ordinals. |
| |
| ### Protocol and method attributes {#protocol-and-method-attributes} |
| |
| #### Transitional |
| |
| For protocol methods annotated with the |
| [`@transitional`](/docs/reference/fidl/language/attributes.md#transitional) |
| attribute, the `virtual` methods on the protocol class come with a default |
| `Close(ZX_NOT_SUPPORTED)` implementation. This allows implementations of the |
| protocol class with missing method overrides to compile successfully. |
| |
| #### Discoverable |
| |
| A protocol annotated with the |
| [`@discoverable`](/docs/reference/fidl/language/attributes.md#discoverable) |
| attribute causes the FIDL toolchain to generate an additional `static const char |
| Name[]` field on the protocol class, containing the full protocol name. |
| |
| ## Persistence, and standalone use of the FIDL wire format |
| |
| Standalone use of the FIDL wire format, such as encoding and decoding individual |
| FIDL domain objects, are not yet supported (https://fxbug.dev/42163274). |
| |
| ## Test scaffolding {#test-scaffolding} |
| |
| The FIDL toolchain also generates a file named `wire_test_base.h` that contains |
| convenience code for testing FIDL client and server implementations. To use |
| these headers, depend on the bindings target label with a `_testing` suffix |
| (`my_library_cpp_testing` instead of `my_library_cpp`). |
| |
| ### Server test base |
| |
| The test base header contains a class for each protocol that provides stub |
| implementations for each of the class’ methods, making it possible to implement |
| only the methods that are used during testing. These classes are template |
| specializations of `fidl::testing::WireTestBase<Protocol>` or |
| `fidl::testing::TestBase<Protocol>` where `Protocol` is the FIDL protocol that |
| is stubbed (e.g. for protocol `games.tictactoe/TicTacToe`, the test bases are |
| `fidl::testing::WireTestBase<games_tictactoe::TicTacToe>` or |
| `fidl::testing::TestBase<games_tictactoe::TicTacToe>`). |
| |
| For the same `TicTacToe` protocol listed above, generated test base subclasses |
| `fidl::WireServer<TicTacToe>` and `fidl::Server<TicTacToe>` (see |
| [Protocols](#protocols)), offering the following methods: |
| |
| * `virtual ~WireTestBase() = default` or `virtual ~TestBase() = default` : |
| Destructor. |
| * `virtual void NotImplemented_(const std::string& name, ::fidl::CompleterBase& |
| completer) = 0`: Pure virtual method that is overridden to define behavior for |
| unimplemented methods. |
| |
| The test base provides an implementation for the virtual protocol methods |
| `StartGame` and `MakeMove`, which are implemented to just call |
| `NotImplemented_("StartGame", completer)` and `NotImplemented_("MakeMove", |
| completer)`, respectively. |
| |
| ### Synchronous event handler test base |
| |
| The test base header contains a class for each protocol that provides stub |
| implementations for each of the class’ events, making it possible to implement |
| only the events that are used during testing. Similar to the server test base, |
| these classes are template specializations of |
| `fidl::testing::WireSyncEventHandlerTestBase<Protocol>` where `Protocol` is the |
| FIDL protocol that is stubbed. |
| |
| For the same `TicTacToe` protocol listed above, generated test base subclasses |
| `fidl::WireSyncEventHandler<TicTacToe>` (see [Protocols](#protocols)), offering |
| the following events: |
| |
| * `virtual ~WireSyncEventHandlerTestBase() = default`: Destructor. |
| * `virtual void NotImplemented_(const std::string& name) = 0`: Pure virtual |
| method that is overridden to define behavior for unimplemented events. |
| |
| The test base provides an implementation for the virtual protocol events |
| `OnOpponentMove`, which is implemented to just call |
| `NotImplemented_("OnOpponentMove")`. |
| |
| [anon-names]: /docs/reference/fidl/language/language.md#inline-layouts |
| [cpp-style]: https://google.github.io/styleguide/cppguide.html#Naming |
| [generated-name-attr]: /docs/reference/fidl/language/attributes.md#generated-name |
| [Memory ownership of wire domain objects]: /docs/development/languages/fidl/tutorials/cpp/topics/wire-memory-ownership.md |
| [Responding to requests asynchronously]: /docs/development/languages/fidl/tutorials/cpp/topics/async-completer.md |
| [New C++ bindings threading guide]: /docs/development/languages/fidl/tutorials/cpp/topics/threading.md |
| [New C++ bindings tutorials]: /docs/development/languages/fidl/tutorials/cpp |
| [Comparing new C++ and high-level C++ language bindings]: /docs/development/languages/fidl/guides/c-family-comparison.md |
| [cpp-server-example]: /examples/fidl/cpp/server/wire |
| [lang-constants]: /docs/reference/fidl/language/language.md#constants |
| [lang-bits]: /docs/reference/fidl/language/language.md#bits |
| [lang-enums]: /docs/reference/fidl/language/language.md#enums |
| [lang-flexible]: /docs/reference/fidl/language/language.md#strict-vs-flexible |
| [lang-structs]: /docs/reference/fidl/language/language.md#structs |
| [lang-tables]: /docs/reference/fidl/language/language.md#tables |
| [lang-unions]: /docs/reference/fidl/language/language.md#unions |
| [lang-resource]: /docs/reference/fidl/language/language.md#value-vs-resource |
| [lang-protocols]: /docs/reference/fidl/language/language.md#protocols |
| [lang-protocol-composition]: /docs/reference/fidl/language/language.md#protocol-composition |
| [result-callback-use-after-free]: /docs/development/languages/fidl/tutorials/cpp/topics/threading.md#additional_use-after-free_risks_with_thenexactlyonce |
| [specifying-asynchronous-continuation]: #specifying-asynchronous-continuation |
| [union-lexicon]: /docs/reference/fidl/language/lexicon.md#union-terms |
| [unknown-attr]: /docs/reference/fidl/language/attributes.md#unknown |
| [wire-thenable-impl]: https://cs.opensource.google/fuchsia/fuchsia/+/main:sdk/lib/fidl/cpp/wire/include/lib/fidl/cpp/wire/internal/thenable.h;l=34?q=wirethenable&ss=fuchsia%2Ffuchsia |
| [zircon-channel]: /docs/reference/kernel_objects/channel.md |