Skip to main content

Autogenerating Cwtch Bindings

· 5 min read
Sarah Jamie Lewis

The C-bindings for Cwtch evolved as part of Cwtch UI development. After two years of prototyping, development, new features, and revisiting first-implementations we have reached the point where we have a good understanding of what the bindings need to do, and how they should do it. To that end we have produced a first-cut of a workflow to automatically generate these bindings: cwtch-autobindings.

This this development log we will introduced autobindings, the motivation behind them, and how we plan to use them on the path to Cwtch Stable.

A Brief History of Cwtch Bindings

Prior to the modern Flutter-based UI application, the first Cwtch UI prototype was based on Qt, with the bindings automatically generated by therecipe/qt. However, after encountering numerous crash-bugs on the compiled Arm version for Android, and a few weeks of prototyping different approaches, we settled on Flutter as a replacement UI framework.

As part of early prototyping efforts for Flutter we built out a first version of libCwtch-go, and over the two years of beta development we have evolved that prototype into a functional set of Cwtch bindings.

This approach has not been without side effects. There is still code from those early prototypes floating around in libCwtch-go, inconsistencies in how functions - in particular experimental features - handle settings, duplication of logic between Cwtch and libCwtch-go, and special behaviour in libCwtch-go that better belongs in the core Cwtch library.

As part of a broader effort to refine the Cwtch API in preparation for Cwtch Stable we have taken the opportunity to fix many of these problems.

Cwtch Autobindings

The current lib.go file that encapsulates the vast majority of libCwtch-go currently sits at 1500+ lines of code. However, much of that code is boilerplate calling conventions e.g. the BlockContact API implementation is:

	//export c_BlockContact
func c_BlockContact(profilePtr *C.char, profileLen C.int, conversation_id C.int) {
BlockContact(C.GoStringN(profilePtr, profileLen), int(conversation_id))
}

func BlockContact(profileOnion string, conversationID int) {
profile := application.GetPeer(profileOnion)
if profile != nil {
profile.BlockConversation(conversationID)
}
}

All that code is doing is defining a C-compatible API, performing some basic checking of parameters, and passing the result into the core Cwtch library. The two functions themselves support the C-bindings and Java-bindings respectively.

In the new cwtch-autobindings we reduce these multiple lines to a single one:

profile BlockConversation conversation

Defining a profile-level function, called BlockConversation which takes in a single parameter of type conversation.

Using a similar boilerplate-reduction for the reset of lib.go yields 5-basic function prototypes:

  • Application-level functions e.g. CreateProfile
  • Profile-level functions e.g. BlockConversation
  • Profile-level functions that return data e.g. GetMessage
  • Experimental Profile-level feature functions e.g. DownloadFile
  • Experimental Profile-level feature functions that return data e.g. ShareFile

Once aggregated and itemized the full set of bindings for Cwtch applications, profile interactions, and experiments can be described in fewer than 50 lines, including comments. Even including the code necessary to generate the bindings from this specification file (~400 lines), and the code needed to initialize the bindings themselves (~300 lines). This cuts the amount of coded needed by 60%, and eliminates many classes of error and inconsistencies associated with maintaining bindings (e.g. regularizing function calls / checking experiment status / handling error conditions etc.).

Next Steps

Cwtch autobindings work today, are API-compatible with the existing libCwtch-go implements, and can be fully integrated into an existing Cwtch application with minimal effort. However, there are a few areas which need to be addressed prior to a full rollout:

  • Application-level experiments (of which there is only one: Desktop Server Hosting) are not currently supported. This functionality is only tangentially related to the rest of the Cwtch bindings, and necessarily introduces additional dependencies (e.g. on cwtch-server). In the coming weeks we will allow optional application experiments to be enabled at compile time, to allow us to produce smaller bindings for platforms that don't support the experiment, and to allow us to build new kinds of platform-targeted experiments that can take advantage of platform specific features.
  • Dart Library generation: since we now have a formal description of the bindings interface, we can move ahead with also autogenerating the Dart-side of the bindings interface, giving a boost to UI integration of new features, and allowing us to generate tailored versions of the UI interface e.g. one compiled without experiment support. We can also extend the same logic to other downstream interfaces e.g. libcwtch-rs
  • Documentation generation: another benefit of a formal description of the bindings interface, we can easily generate documentation compatible with docs.cwtch.im.
  • Cwtch API: This first cut of autobindings is based on an unreleased version of the core Cwtch library that implements much of the Cwtch Stable API redesign. In a short while we will be merging these features into Cwtch, in preparation for Cwtch 1.11, and beyond.

Help us go further!

We couldn't do what we do without all the wonderful community support we get, from one-off donations to recurring support via Patreon.

If you want to see us move faster on some of these goals and are in a position to, please donate. If you happen to be at a company that wants to do more for the community and this aligns, please consider donating or sponsoring a developer.

Donations of $5 or more can opt to receive stickers as a thank-you gift!

For more information about donating to Open Privacy and claiming a thank you gift please visit the Open Privacy Donate page.

A Photo of Cwtch Stickers