Comparing C, Low-Level C++, and High-Level C++ Language Bindings

C Bindings

  • Optimized to meet the needs of low-level systems programming, plus tight constraints around dependencies and toolchains. The compiler, bindings library, and code-generator are written in C++, while exposing a pure C interface to clients.
  • Represent data structures whose memory layout coincides with the wire format.
  • Support in-place access and construction of FIDL messages.
  • Generated structures are views of an underlying buffer; they do not own memory.
  • Provide convenience wrappers for message construction and calling for a limited subset of FIDL messages ([Layout = “Simple”] types).
  • Client is synchronous only. Two-way method calls will block.
  • As the Low-Level C++ Bindings mature, there are plans to re-implement the C bindings as a light-weight wrapper around the C++ bindings.

Low-Level C++ Bindings

  • Optimized to meet the needs of low-level systems programming while providing slightly more safety and features than the C bindings.
  • Represent data structures whose memory layout coincides with the wire format, i.e. satisfying C++ Standard Layout. This opens the door to in-place encoding and decoding.
  • Support in-place access and construction of FIDL messages.
  • Generated structures are views of an underlying buffer; they do not own memory.
  • Use owned handle types such as zx::handle. Note that since generated structures are views of an underlying buffer, a parent structure will only own child handles if it also owns their underlying buffer. For example, a FIDL struct owns all the handles stored inline, but a FIDL vector of structs containing handles will be represented as a vector view, which will not own the out-of-line handles.
  • Defer all memory allocation decisions to the client.
  • Code generator produces only type declarations, coding tables, simple inline functions, and pure virtual server interfaces.
  • Client may manually dispatch incoming method calls on protocols (write their own switch statement and invoke argument decode functions).
  • Similar to the C language bindings but using zero-cost C++ features such as namespaces, string views, and array containers.
  • Client is synchronous only. However, async client support is planned.

High-Level C++ Bindings

  • Optimized to meet the needs of high-level service programming.
  • Represent data structures using idiomatic C++ types such as std::vector, std::optional, and std::string.
  • Use smart pointers to manage heap allocated objects.
  • Use zx::handle (libzx) to manage handle ownership.
  • Can convert data from in-place FIDL buffers to idiomatic heap allocated objects.
  • Can convert data from idiomatic heap allocated objects (e.g. std::string) to in-place buffers (e.g. as a fidl::StringView).
  • Code generator produces more code compared to the low-level C++ bindings, and much more than the C bindings. This includes constructors, destructors, protocol proxies, protocol stubs, copy/move functions, and conversions to/from in-place buffers.
  • Client performs protocol dispatch by subclassing a provided stub and implementing the virtual methods for each operation.
  • Both async and synchronous clients are supported. However, the async clients are not thread-safe.

Summary

CategorySimple CLow-level C++High-level C++
audiencedriversdrivers and performance-critical applicationshigh-level services
abstraction overheadalmost zeroalmost zeroheap allocation, construction, destruction
type safe typesenums, structs, unionsenums, structs, unions, handles, protocolsenums, structs, unions, handles, protocols
storagestackstack, in-place buffer, or heapheap
lifecyclemanual free (POD)manual free memory; own handles via RAII [1]automatic free (RAII)
receive behaviorcopycopy or decode in-placedecode then move to heap
send behaviorcopycopy or encode in-placemove to buffer then encode
calling protocol methodsfree functionsfree functions or proxycall through proxies, register callbacks
implementing protocol methodsmanual dispatch or via ops tablemanual dispatch or implement stub interfaceimplement stub object, invoke callbacks
async clientnono (planned)yes
async serverlimited [2]yes (unbounded) [3]yes (unbounded)
parallel server dispatchnoyes [4]no
generated code footprintsmallmoderatelarge

Notes:

  1. Generated types own all handles stored inline. Out-of-line handles e.g. those behind a pointer indirection are not closed when the containing object of the pointer goes away. In those cases, the bindings provide a fidl::DecodedMessage object to manage all handles associated with a call.
  2. The bindings library can dispatch at most one in-flight transaction.
  3. The bindings library defined in lib/fidl-async can dispatch an unbounded number of in-flight transactions via fidl::AsyncBind defined in lib/fidl-async/cpp/async_bind.h.
  4. The bindings library lib/fidl-async enables parallel dispatch using the EnableNextDispatch() API defined in lib/fidl/llcpp/async_transaction.h.

Migrating From C Bindings To Low-Level C++

TODO