HLCPP bindings

Libraries

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.

Constants

All constants are generated as a constexpr. For example, the following constants:

const BOARD_SIZE uint8 = 9;
const NAME string = "Tic-Tac-Toe";

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.

Fields

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.

Built-in types

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.

User defined types

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.

Request, response, and event parameters

Whenever FIDL needs to generate a single type representing parameters for a request, response, or event (e.g. when generating fit::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.

Type definitions

Bits

Given the bits definition:

type FileMode = strict bits : uint16 {
    READ = 0b001;
    WRITE = 0b010;
    EXECUTE = 0b100;
};

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 like mode |= 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:

auto flags = fuchsia::examples::FileMode::READ | fuchsia::examples::FileMode::WRITE;
ASSERT_EQ(static_cast<uint16_t>(flags), 0b11);
flags |= fuchsia::examples::FileMode::EXECUTE;
ASSERT_EQ(flags, fuchsia::examples::FileModeMask);

Flexible bits

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 returns cpp17::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

Enums

Given the enum definition:

type LocationType = strict enum {
    MUSEUM = 1;
    AIRPORT = 2;
    RESTAURANT = 3;
};

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:

ASSERT_EQ(static_cast<uint32_t>(fuchsia::examples::LocationType::MUSEUM), 1u);

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], 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

Structs

Given a struct declaration:

type Color = struct {
    id uint32;
    name string:MAX_STRING_LENGTH = "red";
};

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 for name.
  • Methods:

    • static inline std::unique_ptr<Color> New(): returns a unique_ptr to a new Color.

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 to unique_ptr<Color>.

Structs may have additional members if they represent the response variant of a result.

Example usage:

fuchsia::examples::Color default_color;
ASSERT_EQ(default_color.id, 0u);
ASSERT_EQ(default_color.name, "red");

fuchsia::examples::Color blue = {1, "blue"};
ASSERT_EQ(blue.id, 1u);

Unions

Given the union definition:

type JsonValue = strict union {
    1: reserved;
    2: int_value int32;
    3: string_value string:MAX_STRING_LENGTH;
};

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 initially Tag::Invalid until the JsonValue is set to a specific variant. Using the WithFoo constructors should be preferred whenever possible.
  • ~JsonValue(): Default destructor
  • static JsonValue WithIntValue(int32&&) and static JsonValue WithStringValue(std::string&&): Static constructors that directly construct a specific variant of the union.
  • static inline std::unique_ptr<JsonValue> New(): Returns a unique_ptr to a new JsonValue
  • bool has_invalid_tag(): Returns true if the instance of JsonValue 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 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 std::string& string_value() const: Read-only accessor methods for each variant. These methods fail if JsonValue does not have the specified variant set
  • int32_t& int_value() and std::string& string_value(): Mutable accessor methods for each variant. If the JsonValue 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) and JsonValue& set_string_value(std::string): Setter methods for each variant.
  • Tag Which() const: returns the current tag of the JsonValue.
  • fidl_xunion_tag_t Ordinal() const: returns the raw fidl_xunion_tag_t tag. Prefer to use Which() unless the raw ordinal is required

JsonValue also has the following associated generated values:

  • JsonValuePtr: an alias to unique_ptr<Foo>.

Unions may have additional methods if they represent the response variant of a result.

Example usage:

auto int_val = fuchsia::examples::JsonValue::WithIntValue(1);
ASSERT_EQ(int_val.Which(), fuchsia::examples::JsonValue::Tag::kIntValue);
ASSERT_TRUE(int_val.is_int_value());

auto str_val = fuchsia::examples::JsonValue::WithStringValue("1");
ASSERT_EQ(str_val.Which(), fuchsia::examples::JsonValue::Tag::kStringValue);
ASSERT_TRUE(str_val.is_string_value());

fuchsia::examples::JsonValuePtr other_int_val = std::make_unique<fuchsia::examples::JsonValue>();
other_int_val->set_int_value(5);
ASSERT_EQ(other_int_val->int_value(), 5);

Flexible unions and unknown variants

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, or nullptr otherwise.
  • const vector<zx::handle>* UnknownHandles() const: Returns the handles of the union variant in traversal order if it is unknown, or nullptr 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, or nullptr 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.

Tables

Given the table definition:

type User = table {
    1: reserved;
    2: age uint8;
    3: name string:MAX_STRING_LENGTH;
};

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 and bool has_name() const: Returns whether a field is set.
  • const uint8_t& age() const and const std::string& name() const: Read-only field accessor methods. These fail if the field is not set.
  • uint8_t* mutable_age() and std::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) and User& set_name(std::string): Field setters.
  • void clear_age() and void 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 to unique_ptr<User>.

Example usage:

fuchsia::examples::User user;
ASSERT_FALSE(user.has_age());
user.set_age(100);
*user.mutable_age() += 100;
ASSERT_EQ(user.age(), 200);
user.clear_age();
ASSERT_TRUE(user.IsEmpty());

Inline layouts

The generated C++ code uses the the name reserved by fidlc for inline layouts.

Protocols

Given the protocol:

protocol TicTacToe {
    StartGame(struct {
        start_first bool;
    });
    MakeMove(struct {
        row uint8;
        col uint8;
    }) -> (struct {
        success bool;
        new_state box<GameState>;
    });
    -> OnOpponentMove(struct {
        new_state GameState;
    });
};

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 and OnOpponentMoveCallback: 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 aliases fit::function<void(bool, std::unique_ptr<GameState>)> and OnOpponentMoveCallback aliases fit::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 a zx_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 a zx_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.

Client

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.

Server

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.

Events

Client

For a TicTacToePtr tictactoe, tictactoe.events() returns a proxy class that contains the following public members:

  • OnOpponentMoveCallback OnOpponentMove: The callback handler for the OnOpponentMove 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.

Server

For a Binding<TicTacToe> tictactoe, tictactoe.events() returns a stub class that contains the following public members:

  • void OnOpponentMove(GameState new_state): Send an OnOpponentMove.

The tutorial has an example for obtaining a Binding.

Results

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 fit::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 fit::result<GameState, MoveError>. Using this feature, an example implementation of MakeMove on the server side could look like:

void  MakeMove(MakeMoveCallback callback) override {
    callback(fit::ok(game_state_.state()));
    // or, in the error case: callback(fit::error(Error::kInvalid);
}

An example of using this on the client side, in the async case would be:

async_game->MakeMove([&](fit::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 fit::result:

  • TicTacToe_MakeMove_Result(fit::result<GameState, MoveError>&& result): Move constructor from a fit::result.
  • TicTacToe_MakeMove_Result(fit::ok_result<GameState>&& result): Move constructor from a fit::ok_result.
  • TicTacToe_MakeMove_Result(fit::error_result<MoveError>&& result): Move constructor from a fit::error_result.
  • operator fit::result<GameState, MoveError>() &&: Conversion to a fit::result.

Note that the successful result type parameter of the fit::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 fit::result<std::tuple<...>, ...>, and if MakeMove returned an empty response, the result type would be fit::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.

Protocol composition

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.

Protocol and method attributes

Transitional

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.

Discoverable

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".

Test scaffolding

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.

Extensions

fostr is a separate library that provides utilities for formatting (pretty printing) FIDL types in HLCPP. Usage information can be found in the tutorial.