Given the library declaration:
library fuchsia.examples;
All code for this library is generated in the fuchsia::examples
namespace, and
test scaffolding is generated in
fuchsia::examples::testing
.
All constants are generated as a constexpr
. For example, the
following constants:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="consts" %}
Are generated in the header file as:
constexpr uint8_t BOARD_SIZE = 9u;
extern const char[] NAME;
The correspondence between FIDL primitive types and C++ types is outlined in
built-in types. Instead of constexpr
, strings are declared as an
extern const char[]
in the header file, and defined in a .cc
file.
This section describes how the FIDL toolchain converts FIDL types to native types in HLCPP. These types can appear as members in an aggregate type or as parameters to a protocol method.
The FIDL types are converted to C++ types based on the following table:
FIDL Type | HLCPP Type |
---|---|
bool |
bool |
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> |
std::array |
vector<T>:N |
std::vector |
vector<T>:<N, optional> |
fidl::VectorPtr |
string |
std::string |
string:optional |
fidl::StringPtr |
server_end:P , server_end:<P, optional> |
fidl::InterfaceRequest |
client_end:P , client_end:<P, optional> |
fidl::InterfaceHandle |
zx.handle , zx.handle:optional |
zx::handle |
zx.handle:S , zx.handle:<S, optional> |
The corresponding zx type is used. For example, zx::vmo or zx::channel . |
In HLCPP, a user defined type (bits, enum, constant, struct, union, or table) is
referred to in the bindings using the generated class or variable (see Type
Definitions). For a nullable user-defined type T
,
unique_ptr
of the equivalent generated type is used.
Whenever FIDL needs to generate a single type representing parameters for a
request, response, or event (e.g. when generating fpromise::result
compatible result types),
it uses the following rules:
- Multiple arguments are generated as an
std::tuple
of the parameter types. - A single parameter is just referred to using the parameter type itself.
- An empty set of parameters is represented using
void
.
Given the bits definition:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="bits" %}
The FIDL toolchain generates a C++ enum class
using the specified underlying
type, or uint32_t
if none is specified:
enum class FileMode : uint16_t {
READ = 1u;
WRITE = 2u;
EXECUTE = 4u;
};
In addition, FIDL generates the following methods for FileMode
:
- Bitwise operators: implementations for the
|
,|=
,&
,&=
,^
,^=
, and~
operators are generated, allowing bitwise operations on the bits likemode |= FileMode::EXECUTE
.
FIDL also generates a const static FileMode FileModeMask
variable. This is a
bitmask containing all of the bits in the enum class, which can be used to get
rid of any unused bit values from a raw underlying uint16_t
(or whichever type
the bits
are based on). In the above example, FileModeMask
has a value of
0b111
.
Example usage:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/unittests/main.cc" region_tag="bits" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
Flexible bits are implemented as a class
instead of an enum class
, with the following additional methods:
constexpr FileMode()
: Default constructor that initializes a value with no bits set.constexpr FileMode(uint16_t)
: Constructs a value from an underlying primitive value, preserving any unknown bit members.constexpr cpp17::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 returnscpp17::nullopt
otherwise.constexpr FileMode TruncatingUnknown(uint16_t value)
: Constructs an instance of the bits from an underlying primitive value, clearing any unknown members.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.explicit constexpr operator uint16_t() const
: Converts the bits value back to its underlying primitive value.explicit constexpr operator bool() const
: Returns whether any bits are set.
The generated class contains a static number for each bits member as well as
for the bits mask. These correspond exactly with the members of the enum class
value, with the addition a kMask
member that replaces FileModeMask
.
const static FileMode READ
const static FileMode WRITE
const static FileMode EXECUTE
const static FileMode kMask
Note: When applying bitwise negation to bits values that contain unknown members, the resulting bits value is only defined for the known bits.
Given the enum definition:
{% 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:
enum class LocationType : uint32_t {
MUSEUM = 1u;
AIRPORT = 2u;
RESTAURANT = 3u;
};
Example usage:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/unittests/main.cc" region_tag="enums" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
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]
, 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 enum:
const static LocationType MUSEUM
const static LocationType AIRPORT
const static LocationType RESTAURANT
Given a struct declaration:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="structs" %}
The FIDL toolchain generates a Color
type with public members and methods.
-
public
members:uint32_t id{}
: This field is zero-initialized since no default value is provided.std::string name = "red"
: The corresponding field forname
.
-
Methods:
static inline std::unique_ptr<Color> New()
: returns aunique_ptr
to a newColor
.
The 6 special members of Color
(default, copy and move constructor,
destructor, copy and move assignment) are implicitly defined.
Color
also has the following associated generated values:
ColorPtr
: an alias tounique_ptr<Color>
.
Structs may have additional members if they represent the response variant of a result.
Example usage:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/unittests/main.cc" region_tag="structs" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
Given the union definition:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/fuchsia.examples/types.test.fidl" region_tag="unions" %}
FIDL generates a JsonValue
class. JsonValue
contains a public tag enum
representing the possible variants:
enum Tag : fidl_xunion_tag_t {
kIntValue = 2,
kStringValue = 3,
Invalid = std::numeric_limits<fidl_xunion_tag_t>::max(),
};
Each member of Tag
has a value matching its ordinal specified
in the union
definition. Reserved fields do not have any generated code. In
addition, there is an Invalid
field, which is the initial value used for a
JsonValue
that has no variant set yet.
JsonValue
provides the following methods:
JsonValue()
: Default constructor. The tag is initiallyTag::Invalid
until theJsonValue
is set to a specific variant. Using theWithFoo
constructors should be preferred whenever possible.~JsonValue()
: Default destructorstatic JsonValue WithIntValue(int32&&)
andstatic JsonValue WithStringValue(std::string&&)
: Static constructors that directly construct a specific variant of the union.static inline std::unique_ptr<JsonValue> New()
: Returns aunique_ptr
to a newJsonValue
bool has_invalid_tag()
: Returnstrue
if the instance ofJsonValue
does not yet have a variant set. Users should not access a union until a variant is set - doing so should be considered undefined behavior.bool is_int_value() const
andbool is_string_value() const
: Each variant has an associated method to check whether an instance ofJsonValue
is of that variantconst int32_t& int_value() const
andconst std::string& string_value() const
: Read-only accessor methods for each variant. These methods fail ifJsonValue
does not have the specified variant setint32_t& int_value()
andstd::string& string_value()
: Mutable accessor methods for each variant. If theJsonValue
has a different variant than the called accessor method, it will destroy its current data and re-initialize it as the specified variant.JsonValue& set_int_value(int32_t)
andJsonValue& set_string_value(std::string)
: Setter methods for each variant.Tag Which() const
: returns the current tag of theJsonValue
.fidl_xunion_tag_t Ordinal() const
: returns the rawfidl_xunion_tag_t
tag. Prefer to useWhich()
unless the raw ordinal is required
JsonValue
also has the following associated generated values:
JsonValuePtr
: an alias tounique_ptr<Foo>
.
Unions may have additional methods if they represent the response variant of a result.
Example usage:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/unittests/main.cc" region_tag="unions" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
Flexible unions have an extra variant in the generated Tag
class:
enum Tag : fidl_xunion_tag_t {
kUnknown = 0,
... // other fields omitted
};
When a FIDL message containing a union with an unknown variant is decoded into
JsonValue
, JsonValue::Which()
returns JsonValue::Tag::kUnknown
, and
JsonValue::Ordinal()
returns the unknown ordinal.
A flexible JsonValue
type will have extra methods for interacting with unknown
data that will depend on whether the type is a
value or resource type. Value types will not have
unknown data methods that reference zx::handle
.
A flexible JsonValue
that is a resource type has the
following extra methods:
const vector<uint8_t>* UnknownBytes() const
: Returns the raw bytes of the union variant if it is unknown, ornullptr
otherwise.const vector<zx::handle>* UnknownHandles() const
: Returns the handles of the union variant in traversal order if it is unknown, ornullptr
otherwise.JsonValue& SetUnknownData(fidl_xunion_tag_t ordinal, vector<uint8_t> bytes, vector<zx::handle> handles)
: Similar to the setter methods for the known members, this sets the union to an unknown variant with the specified ordinal, bytes, and handles. This method should only be used for testing, e.g. to ensure that code can handle unknown data correctly.
A flexible JsonValue
that is a value type has the following
extra methods:
const vector<uint8_t>* UnknownBytes() const
: Returns the raw bytes of the union variant if it is unknown, ornullptr
otherwise.JsonValue& SetUnknownData(fidl_xunion_tag_t ordinal, vector<uint8_t> bytes)
: Similar to the setter methods for the known members, this sets the union to an unknown variant with the specified ordinal and bytes. This method should only be used for testing, e.g. to ensure that code can handle unknown data correctly.
Encoding a union with an unknown variant writes the unknown data and the original ordinal back onto the wire.
Strict unions fail when decoding an unknown variant. Flexible unions that are value types fail when decoding an unknown variant with handles.
Given the table definition:
{% 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 with all fields unset.User(User&&)
: Move constructor.~User()
: Destructor.User& User::operator=(User&&)
: Move assignment.bool IsEmpty() const
: Returns true if no fields are set.bool has_age() const
andbool has_name() const
: Returns whether a field is set.const uint8_t& age() const
andconst std::string& name() const
: Read-only field accessor methods. These fail if the field is not set.uint8_t* mutable_age()
andstd::string* mutable_name()
: Mutable field accessor methods. If the field is not set, a default one will be constructed, set, and returned.User& set_age(uint8_t)
andUser& set_name(std::string)
: Field setters.void clear_age()
andvoid clear_name()
: Clear the value of a field by calling its destructor
The User
class will also provide methods for interacting with unknown fields
which will depend on whether the type is a value or resource type.
Tables that are a value type will not have unknown
data methods that reference zx::handle
, and will fail to decode data with
unknown fields that contain handles.
If User
is a resource type, it will have the following
methods:
const std::map<uint64_t, fidl::UnknownData>>& UnknownData() const
: Returns a map from ordinal to bytes and handles. The handles are guaranteed to be in traversal order.void SetUnknownDataEntry(uint32_t ordinal, fidl::UnknownData&& data)
: Set the bytes and handles of an unknown field if it doesn't already exist. This method should only be used for testing, e.g. to check that tables with unknown fields are handled correctly.
If User
is a value type, it will have the following methods:
const std::map<uint64_t, vector<uint8_t>& UnknownData() const
: Returns a map from ordinal to bytes.void SetUnknownDataEntry(uint32_t ordinal, vector<uint8_t>&& data)
: Set the bytes of an unknown field if it doesn't already exist. This method should only be used for testing, e.g. to check that tables with unknown fields are handled correctly.
User
also has the following associated generated values:
UserPtr
: an alias tounique_ptr<User>
.
Example usage:
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/hlcpp/unittests/main.cc" region_tag="tables" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
The generated C++ code uses the the name reserved by fidlc
for
inline layouts.
Given the protocol:
{% 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
instead.
FIDL generates a TicTacToe
class, which acts as an entry point for interacting
with the protocol and defines the interface of the service used by
clients to proxy calls to the server, and for the server for implementing the
protocol. Synchronous clients use a different virtual interface, TicTacToe_Sync
.
TicTacToe
contains the following member types:
MakeMoveCallback
andOnOpponentMoveCallback
: Each response and event has a member type generated that represents the type of the callback for handling that response or event. In the above example,MakeMoveCallback
aliasesfit::function<void(bool, std::unique_ptr<GameState>)>
andOnOpponentMoveCallback
aliasesfit::function<void(GameState)>
.
TicTacToe
additionally has the following pure virtual methods, corresponding
to the methods in the protocol definition:
virtual void StartGame(bool start_first)
: Pure virtual method for a fire and forget protocol method. It takes as arguments the request parameters.virtual void MakeMove(uint8_t row, uint8_t col, MakeMoveCallback callback)
: Pure virtual method for a two way protocol method. It takes as arguments the request parameters followed by the response handler callback.
TicTacToe_Sync
has the following pure virtual methods, corresponding to the
methods in the protocol definition:
virtual zx_status_t StartGame(bool start_first)
: Pure virtual method for a fire and forget protocol method. It takes as arguments the request parameters, and returns azx_status_t
representing whether the request was sent successfully.virtual zx_status_t MakeMove(uint8_t row, uint8_t col, bool* out_success, std::unique_ptr<GameState>* out_new_state)
: Pure virtual method for a two way method protocol. It takes as arguments the request parameters, followed by output pointers for each of the response parameters. It returns azx_status_t
representing whether the method call was made successfully.
Other code may be generated depending on the attributes applied to the protocol or its methods.
The FIDL toolchain generates two aliases for the classes used to make calls to a
TicTacToe
server : TicTacToePtr
, which aliases
fidl::InterfacePtr<TicTacToe>
representing an async client, and
TicTacToeSyncPtr
, which aliases fidl::SynchronousInterfacePtr<TicTacToe>
representing a synchronous client.
When dereferenced, TicTacToePtr
and TicTacToeSyncPtr
return a proxy class
that implements TicTacToe
and TicTacToe_Sync
, respectively, which proxies
requests to the server. In this example, given a TicTacToePtr
called
async_tictactoe
, requests could be made by calling
async_tictactoe->StartGame(start_first)
or async_tictactoe->MakeMove(row, col, callback)
.
Examples on how to set up and bind an InterfacePtr
or a
SynchronousInterfacePtr
to a channel are covered in the
HLCPP tutorial.
The fidl::InterfacePtr
type is thread-hostile. All calls to an instance of
this type must be made from the same thread. The fidl::SynchronousInterfacePtr
type is thread-compatible. Once an instance of this type is bound it can be used
from multiple threads simultaneously. The fidl::InterfaceHandle
type can be
used to safely transfer a channel handle between threads. See the class
documentation on these types for more details.
Implementing a server for a FIDL protocol involves providing a concrete
implementation of TicTacToe
.
Examples on how to set up and bind a server implementation are covered in the HLCPP tutorial.
For a TicTacToePtr
tictactoe
, tictactoe.events()
returns a proxy class
that contains the following public members:
OnOpponentMoveCallback OnOpponentMove
: The callback handler for theOnOpponentMove
event.
Clients can handle events by setting the members of this class to the desired event handlers.
Refer to the top-level generated protocol code for details on the callback types.
For a Binding<TicTacToe>
tictactoe
, tictactoe.events()
returns a stub
class that contains the following public members:
void OnOpponentMove(GameState new_state)
: Send anOnOpponentMove
.
The tutorial has an example for obtaining a Binding
.
Given the method with an error type:
protocol TicTacToe {
MakeMove(struct {
row uint8;
col uint8;
}) -> (struct {
new_state GameState;
}) error MoveError;
};
FIDL generates code so that clients and servers can use fpromise::result
in
place of the generated MakeMove
response type. This is done by generating a
TicTacToe_MakeMove_Result
class to represent the response that is
interchangeable with fpromise::result<GameState, MoveError>
. Using this
feature, an example implementation of MakeMove
on the server side could look
like:
void MakeMove(MakeMoveCallback callback) override {
callback(fpromise::ok(game_state_.state()));
// or, in the error case: callback(fpromise::error(Error::kInvalid);
}
An example of using this on the client side, in the async case would be:
async_game->MakeMove([&](fpromise::result<GameState, MoveError>> response) { ... });
When generating code, the FIDL toolchain treats TicTacToe_MakeMove_Result
as a
union
with two variants: response
, which is a generated type described
below, and err
, which is the error type (in this case uint32
), which means
that it provides all the methods available to a regular union. In
addition, TicTacToe_MakeMove_Result
provides methods that allow interop with
fpromise::result
:
TicTacToe_MakeMove_Result(fpromise::result<GameState, MoveError>&& result)
: Move constructor from afpromise::result
.TicTacToe_MakeMove_Result(fpromise::ok_result<GameState>&& result)
: Move constructor from afpromise::ok_result
.TicTacToe_MakeMove_Result(fpromise::error_result<MoveError>&& result)
: Move constructor from afpromise::error_result
.operator fpromise::result<GameState, MoveError>() &&
: Conversion to afpromise::result
.
Note that the successful result type parameter of the fpromise::result
follows the
parameter type conversion rules: if
MakeMove
returned multiple values on success, the result type would be a tuple
of the response parameters fpromise::result<std::tuple<...>, ...>
, and if
MakeMove
returned an empty response, the result type would be
fpromise::result<void, ...>
.
The FIDL toolchain also generates a TicTacToe_MakeMove_Response
class, which
is the type of the response
variant of TicTacToe_MakeMove_Result
. This class
is treated as a FIDL struct with fields corresponding to each parameter of the
successful response. In addition to the methods and members available to a
regular struct, TicTacToe_MakeMove_Response
provides additional
methods that allow interop with std::tuple
:
explicit TicTacToe_MakeMove_Response(std::tuple<GameState> _value_tuple)
: Constructor from a tuple.operator std::tuple<GameState>() &&
: Conversion operator for a tuple.
FIDL does not have a concept of inheritance, and generates full code as described above for all composed protocols. In other words, the code generated for
protocol A {
Foo();
};
protocol B {
compose A;
Bar();
};
Provides the same API as the code generated for:
protocol A {
Foo();
};
protocol B {
Foo();
Bar();
};
The generated code is identical except for the method ordinals.
For protocol methods annotated with the
@transitional
attribute, the virtual
methods on the protocol class are not pure. This allows
implementations of the protocol class with missing method overrides to compile
successfully.
A protocol annotated with the
@discoverable
attribute causes the FIDL toolchain to generate an additional static const char Name_[]
field on the protocol class, containing the full protocol name. For a
protocol Baz
in the library foo.bar
, the generated name is "foo.bar.Baz"
.
The FIDL toolchain also generates a file suffixed with _test_base.h
that
contains convenience code for testing FIDL server implementations. This file
contains a class for each protocol that provides stub implementations for each
of the class’s methods, making it possible to implement only the methods that
are used during testing. These classes are generated into a testing
namespace
that is inside of the generated library’s namespace (e.g. for library
games.tictactoe
, these classes are generated into
games::tictactoe::testing
).
For the same TicTacToe
protocol listed above, the FIDL toolchain generates a
TicTacToe_TestBase
class that subclasses TicTacToe
(see
Protocols), offering the following methods:
virtual ~TicTacToe_TestBase() {}
: Destructor.virtual void NotImplemented_(const std::string& name) = 0
: Pure virtual method that is overridden to define behavior for unimplemented methods.
TicTacToe_TestBase
provides an implementation for the virtual protocol methods
StartGame
and MakeMove
, which are implemented to just call
NotImplemented_("StartGame")
and NotImplemented_("MakeMove")
, respectively.
fostr
is a separate library that provides utilities for formatting
(pretty printing) FIDL types in HLCPP. Usage information can be found in the
tutorial.