CppBind: Breaking the Language Barrier or Designing Multiplatform Applications with C++.

7 min read Original article ↗

Vardges Hovhannisyan

Press enter or click to view image in full size

Multiplatform applications have become very common, providing a platform-native experience for users by delivering native applications using native technologies. At the same time, it is desired to have a shared experience through applications for all supported platforms. The straightforward way to achieve this is to simply duplicate the app logic by keeping consistency using different technologies, such as Swift for iOS and Mac, Kotlin for Android, Javascript for Web, Python, and C++ for Linux, Mac, and Windows, etc. An alternative is to host the common logic into the web backend if applicable to the solution.

What if the application has to complete high-performance on-device processing by implementing a highly efficient computational algorithm utilizing available hardware? In some cases, on-device specialized hardware should be used, such as GPU.

C/C++ are highly efficient native languages for almost all platforms. It’s common for dev teams to use C/C++ for application logic or performance-critical parts.

C/C++ remains a go-to technology for multimedia, gaming, and similar high-performance software providing low-level access to both CPU hardware and specialized hardware like GPU. There are tons of very efficient libraries developed using C/C++. These facts are viable points for using C/C++ even when developing on a single platform.

Most of the heavy-duty operations in different languages are implemented using C/C++. Higher-level languages are used to glue algorithms and logic together, and algorithms themselves are implemented in C/C++.

At Picsart, we heavily use C++ to provide the best performance to the end-user.

The language for implementing a platform-specific user experience varies across different platforms, and it’s not necessarily C++. It still might be required to use platform-specific languages like Kotlin, Swift, and Python.

Considering all points mentioned above, the architecture of an application that needs to use C++ core, might look like this:

Press enter or click to view image in full size

This looks promising if we don’t consider the implementation complexity behind the architecture. To call C/C++ code from different languages, a binding API is required.

Implementing and maintaining bindings separately for each language is not an easy task. At first, the developers should be experienced in C++ and the target languages that need to be bound. Additionally, it’s important to be sure that the binding API is more or less similar across different languages, there is no missing functionality, and the API behaves similarly. Each new API or change causes us to revisit bindings. It’s easy to see how this can become monotonous and feel very uncreative, especially if we must add new language support afterwards.

At Picsart, we had a similar problem to solve. After manually developing and maintaining our language bindings, we came to an idea to automate the entire language binding process with a generic tool that could be used wherever we have a code in C++ and want to access it from other high-level languages like Kotlin, Swift, Python, etc. We call it CppBind, the missing puzzle piece we were looking for, and we are so excited by it’s impact that Picsart decided to open-source CppBind to democratize C++ adoption across the platforms.

Picsart decided to open-source CppBind to democratize C++ adoption across the platforms.

Introducing CppBind

CppBind is a software development tool that automates language binding by generating language binding wrappers. It was developed and recently open-sourced by Picsart, under the MIT license.

The main features include:

  • Supports bindings for Python, Swift, and Kotlin
    – Javascript is coming
  • Easy integration
  • No knowledge of the bound language required
  • Highly customizable
  • Flexible annotation for generation instruction
    – In source code annotation
    – out of source code in the side file
  • Supports a set of C++ features, including:
    – Enum, Structures, Classes
    – Multi inheritance
    – Nested types
    – Shared references
    – Preserving original types
    – Operators
    – Functions overloading
    – Static and global functions
    – Standard containers (vector, map, pair)
    – Exception handling
    – Documentation forwarding to bound languages
    – Template support
    – Examples and comprehensive documentation
    – and much more.
  • Ability to be extended to support more languages

Let’s see it in action.

Document Scanner Example

To get you familiar with CppBind, we have prepared a sample application that scans a document from a given image by applying a perspective reconstruction.

It converts the image from this:

To this:

The application logic is developed in C++ by using the OpenCV library. To illustrate various aspects of CppBind, we will create bindings for some OpenCV classes and functions, as well as for the sample code. The goal is to have the same application for Swift, Kotlin, and Python.

There are two main classes:

  • DocCornerPointsExtractor is responsible for finding the original document’s corner points in the given image.
  • DocExtractor is responsible for drawing corner points and warping the document.

The application loads the specified image using OpenCV. It then uses DocCornerPointsExtractor class to deduce document corners and DocExtractor for final extraction.

In addition to functional methods, our classes also contain property accessors over C++ member variables or get/set function pairs. All this functionality we wanted to have in other languages like Python, Kotlin, and Swift. To achieve this while developing these classes, we know they will be used in other languages, they are our library binding API. In the Doxygen comment section, we have added some annotations to instruct CppBind to generate bindings for us.

For example, on top of classes, in Doxygen comments, we have added the following lines:

On top of constructors that we want to expose:

On top of methods:

For the complete list of available generation instructions, please read here.

CppBind will read the generation instructions in the code and figure out how to generate the bindings between different languages. CppBind supports various data structures — vectors, maps, and more. In our API we are using cv::Mat data structure which comes from an external library. Here CppBind needs extra help — we need to write an extra generation rule for the new external type. We can use CppBind to write our custom bindings or use CppBind to generate bindings for it.

To annotate third-party data types with no option to accomplish it in the source code file, the YAML configuration file can be used to specify the annotation instructions.

Our OpenCV Yaml configuration looks like this:

To have fine control over generated code, CppBind provides annotation variables in the Doxygen section and the Yaml configuration file. Annotation variables allow us to customize how generation is done. For example, it is possible to change function names in target languages, choose packages for classes, etc. For the list of available CppBind annotation variables, you can read here.

Well, actually that is it! Your day-to-day development will consume this much effort to expose binding API with CppBind.

To run the CppBind, a project configuration file needs to be written. The project configuration file contains configs like: which files should be processed by CppBind, the directory where the output will be generated, etc. To simplify the project configuration setup, we provide cppbind init command.

For this example, the configuration file has a slight modification to meet our needs. You can take a look at the file here.

To generate language wrapper files for language binding, run the following command:

cppbind run swift kotlin python

Generated files should be used with the project build system. In our case, we are using CMake. You can find details on how we have done it on GitHub. Additionally, CppBind can be extended to generate build files (Bazel or CMake) as well. As an example, you can look at our generation extension of the Swift module map file.

Let’s take a look at the usage code and pick one of them, for example, Kotlin.

As you can see, it is pretty much the same as it was in C++.

On GitHub, you can find full Android and IOS apps that takes an image from a camera and extracts the document image.

Conclusion

CppBind enables software teams to develop efficient and elegant ways to multiplatform part or all of their applications. You may like to use C++ to gain performance improvement, or keep the common code shared between the platform applications, as well as bring one of the existing C++ libraries into your platforms. In all these cases and more, CppBind can surely enable us. Single integration will allow you to generate bindings for many languages, and hopefully this set will grow with the help of the open-source community.

Extendability of CppBind does not limit Picsart only for on-device language binding. Imagine you write C++ code and with not much effort you can organize RPC on top of it.

To read more and get familiar with the tool, go here.

If you like CppBind and would like to contribute to its enhancement, Picsart invites you to its GitHub open-source space!