This document is a description of the Fuchsia Interface Definition Language (FIDL) implementation for C++, including its libraries and code generator.
See FIDL: Overview for more information about FIDL's overall purpose, goals, and requirements, as well as links to related documents.
This specification builds on the FIDL: C Language Bindings and reuses many of its elements where appropriate.
Point for discussion: given how close the C++ native style is to the C style (mostly syntactic benefits), we might want to consider deferring its implementation or finding some other middle ground like in-place friendly non-POD with weak RAII semantics (don't traverse pointers).
This is the mapping from FIDL types to C types which the code generator produces.
TODO: discuss reserved words, name mangling
TODO: discuss generated namespaces, constants, enums, typedefs, encoding tables
Only depends on Zircon system headers, libzx, and a portion of the C and C++ standard libraries.
Does not depend on libftl or libmtl.
To be discussed.
The native bindings library uses C++ standard library style, eg. function names are lower-case with underscores.
The idiomatic bindings library could use Google C++ style to match FIDL v1.0 but though this may ultimately be more confusing, especially given style choices in Zircon so we may prefer to follow the C++ standard library style here as well.
class string { public: void init(size_t size, uint8_t* data); size_t size() const; bool empty() const; bool null() const; uint8_t* data(); const uint8_t* data() const; uint8_t& operator[](size_t pos); const uint8_t& operator[](size_t pos) const; iterator begin(); // etc... private: fidl_string string_; };
Holds a reference to a variable-length string stored within the buffer. C++ wrapper of fidl_string.
No constructor or destructor so this is POD.
template<typename T> class vector { public: void init(size_t size, T* data); size_t size() const; bool empty() const; bool null() const; T* data(); const T* data() const; T& operator[](size_t pos); const T& operator[](size_t pos) const; iterator begin(); // etc… private: fidl_vector vector_; };
Holds a reference to a variable-length vector of elements stored within the buffer. C++ wrapper of fidl_vector.
No constructor or destructor so this is POD.
template<typename T, size_t N> class array { public: size_t size() const; bool empty() const; bool null() const; T* data(); const T* data() const; T& operator[](size_t pos); const T& operator[](size_t pos) const; iterator begin(); // etc… private: T[N] array_; };
Holds a reference to a fixed-length array of elements stored within the buffer. Similar to std::array<T, N> but intended purely for in-place use.
No constructor or destructor so this is POD.
class buffer { public: buffer(size_t max_capacity = ZX_MAX_MESSAGE_SIZE); ~buffer(); template<typename T> T* append(); fidl::string append_string(size_t size); template<typename T> fidl::string append_string(const char* text); fidl::vector<T> append_vector(size_t size); const uint8_t* data() const; size_t size() const; ZX_status_t encode(const fidl_encoding_table* encoding_table, std::vector<zx_handle_t>* out_handles); ZX_status_t decode(const fidl_encoding_table* encoding_table, const std::vector<zx_handle_t>& out_handles); };
Helper for constructing messages laid out in depth-first traversal order. Generally takes care of the messy pointer arithmetic for building messages in-place.
TBD: It might be even more convenient to make a more specialized encode_buffer which knows the encoding table of the message being constructed. Maybe do something similar for incoming messages.
ZX_status_t say_hello( const zx::channel& channel, const char* text, zx::handle token) { assert(strlen(text) <= MAX_TEXT_SIZE); fidl::buffer buf(); auto header = buf.append<fidl_message_header>(); header->transaction_id = 1; header->flags = 0; header->ordinal = example_Animal_Say_ordinal; auto args = buf.append<example::Animal::Say_args>(); args->text = buf.append_string(text); args->token = std::move(token); std::vector<zx::handle> handles; ZX_status_t status = buf.encode(example::Animal::Say_args::encoding, &handles); if (status == NO_ERROR) { status = channel.write(0, buf.data(), buf.size(), reinterpret_cast<const zx_handle_t*>(handles.data()), handles.size()); } return status; }
TODO: adopt main ideas from FIDL 1.0
InterfacePtr / interface_ptr?
InterfaceRequest / interface_req?
async waiter
etc…
The FIDL v1 API for calling and implementing FIDL interfaces has generally been fairly effective so we would like to retain most of its structure in the idiomatic FIDL v2 bindings. However, there are a few areas that could be improved.
TODO: actually specify the intended API
Handling connection errors systematically has been a cause of concern for clients of FIDL v1 because method result callbacks and connection error callbacks are implemented by different parts of the client program.
It would be desirable to consider an API which allows for localized handling of connection errors at the point of method calls (in addition to interface level connection error handling as before).
See https://fuchsia-review.git.corp.google.com/#/c/23457/ for one example of how a client would otherwise work around the API deficiency.
One approach towards a better API may be constructed by taking advantage of the fact that std::function<> based callbacks are always destroyed even if they are not invoked (such as when a connection error occurs). It is possible to implement a callback wrapper which distinguishes these cases and allows clients to handle them more systematically. Unfortunately such an approach may not be able to readily distinguish between a connection error vs. proxy destruction.
Alternately we could wire in support for multiple forms of callbacks or for multiple callbacks.
Or we could change the API entirely in favor of a more explicit Promise-style mechanism.
There are lots of options here...
TBD (please feel free to amend / expand on this)