The Role of Language Bindings in the Future of the Qt Ecosystem.


Dr. CristiΓ‘n Maureira-Fredes
@cmaureir

Disclaimer

Motivation

x = x + 1

ALMA Common Software (ACS) Workshop, Chile 2009

Let's start with a simple question.

What is the best

programming language?

Maybe a better question...

What is the most popular*

programming language?

Source TIOBE index October 2023

Most Popular technologies - Source StackOverflow Survey 2023

Admired and Desired Programming languages - Source StackOverflow Survey 2023

What about

UI frameworks?

Most popular Frameworks - Source StackOverflow Survey 2023

Admired and Desired Frameworks - Source StackOverflow Survey 2023

Source Tauri Intro

The current language state

of the Qt Project

Languages in Qt

1996
2010
2011 (2018)*

What about other

languages?

Name Language Latest version Last commit License Company
PySide Python 6.6.0 - LGPLv3/Commercial The Qt Company
PyQt Python 6.6.0 - GPLv3/Commercial Riverbank Computing
CXX-Qt Rust 6.x* - MIT/Apache 2.0 KDAB
qmetaobject-rs Rust 6.5.0 2023.11.03 MIT Woboq
QtJambi Java/Kotlin 6.6.0 2023.10.17 LGPLv2 Omix Visualization
QML-zig Zig 5.15 2023.08.17 Apache 2.0 -
QML.jl Julia 6.5.2 2023.10.16 MIT JuliaGraphs
RingQt Ring 5.15.15 2023.09.27 MIT -
NodeGui Node.js 6.4.1 2023.08.28 MIT NodeGui
nimqt Nim 6.4.3 2023.08.22 GPL2 -

(updated on Nov 6th, 2023)

Other 26 projects (some inactive) in wiki.qt.io/Language_Bindings

This is nothing new

Yesterday's talk

What is the motivation?

  • The need of GUI frameworks
  • C++ flexibility for bindings
  • Language showcase
  • Qt's popularity ⭐

How is it done? (1/2)

How is it done? (2/2)

What other languages

bring to the table

Python's syntax


        #ifndef MAINWINDOW_H
        #define MAINWINDOW_H

        #include <QMainWindow>
        #include <QPushButton>

        class MainWindow : public QMainWindow
        {
            Q_OBJECT
            public:
                MainWindow(QWidget *parent = nullptr);
            private slots:
                void handleButton();
            private:
                QPushButton *m_button;
        };

        #endif // MAINWINDOW_H

        #include "mainwindow.h"

        MainWindow::MainWindow(QWidget *parent)
           : QMainWindow(parent)
        {
            m_button = new QPushButton("My Button", this);
            connect(m_button, SIGNAL(clicked()), this,
              SLOT(handleButton()));
        }

        void MainWindow::handleButton()
        {
            m_button->setText("Ready");
        }

        #include <QApplication>
        #include "mainwindow.h"

        int main(int argc, char *argv[])
        {
            QApplication app(argc, argv);
            MainWindow mainWindow;
            mainWindow.show();
            return app.exec(d);
        }

        import sys
        from PySide6.QtCore import Slot
        from PySide6.QtWidgets import (
          QApplication, QMainWindow, QPushButton
        )

        class MainWindow(QMainWindow):
            def __init__(self, parent=None):
                QMainWindow.__init__(self, parent)
                self.b = QPushButton("My Button", self)
                self.b.clicked.connect(self.handle_button)

            @Slot()
            def handle_button(self):
                self.b.setText("Ready")

        if __name__ == "__main__":
            app = QApplication(sys.argv)
            mainWindow = MainWindow()
            mainWindow.show()
            sys.exit(app.exec())

Python's heterogeneity

Web Development
Data
Science

Embedded systems

Python: package distribution

qtpip

qt.io/blog/qtpip-qtforpython-installer

More Qt for Python

Rust

  • Performance
    • Fast and memory efficient (no runtime or GC).
  • Reliability
    • Rich type system and ownership model (mem and thread safety)
  • Productivity
    • Docs, friendly compiler, package manager and build tool.

Rust and C++ (1/2)


    #include <iostream>

    using namespace std;

    int main() {
        int nums[] = {1,2,3,4,5,6};
        int five = nums[1] + nums[2];
        cout << "Result: " << five";
    }
    

    fn main() {
        let arr: [i32; 5] = [1, 2, 3, 4, 5];
        let five = arr[1] + arr[2];
        println!("Result: {}", five);
    }
    

Rust and with C++ (2/2)


    class Person {
      public:
        Person(string n, int a);
        void talk();
        string name;
        int age;
    };
    Person::Person(string n, int a){
        name = n;
        age = a;
    }
    void Person::talk(){
        cout << name << " says Hello!\n";
    }

    int main() {
        Person maria("Maria", 22);
        maria.talk();
    }
    

Side note

Not saying that we should re-write Qt...

CXX-Qt Rust ⇄ C++ bindings


    #[cxx_qt::bridge]
    pub mod qobject {
      unsafe extern "C++" {
        include!("cxx-qt-lib/qstring.h");
        type QString = cxx_qt_lib::QString;
      }
      unsafe extern "RustQt" {
        #[qobject]
        #[qml_element]
        #[qproperty(i32, number)]
        #[qproperty(QString, string)]
        type MyObject = super::MyObjectRust;
      }
      unsafe extern "RustQt" {
        #[qinvokable]
        fn increment_number(self: Pin<&mut MyObject>);
        #[qinvokable]
        fn say_hi(self: &MyObject,
                  string: &QString,
                  number: i32);
      }
    }
    

Snippet from: github.com/KDAB/cxx-qt/tree/main/examples/qml_minimal

Rust - Highlights (1/3)

Growing ecosystem

Rust - Highlights (2/3)

Tooling (cargo)


$ cargo new yourapp
  Created binary (application) `yourapp` package

$ tree yourapp
yourapp
β”œβ”€β”€ Cargo.toml
└── src
    └── main.rs

2 directories, 2 files
  

~/yourapp  $ cargo build
   Compiling yourapp v0.1.0 (/home/cmaureir/yourapp)
    Finished dev [unoptimized + debuginfo] target(s) in 0.68s
  

~/yourapp  $ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/yourapp`
Hello, world!
  

Rust - Highlights (3/3)

  • Memory management (Ownership & Borrowing)
  • Quickly evolving

Zig

  • No hidden control flow, nor memory alloc
  • No preprocessor, no macros
  • Metaprogramming: compile-time code execution and lazy evaluation (comptime)
  • Tests, test, test!

Zig and C++


    #include <iostream>
    using namespace std;
    class Rectangle {
      public:
        float x;
        float y;
        Rectangle(float x, float y) :
          x(x), y(y) {}

        float area() {
            return x * y;
        }
    };
    int main(int argc, char *argv[]) {
        Rectangle r = Rectangle(2.2, 3.3);
        cout << "Rectangle area: "
             << r.area();
        return 0;
    }
    

Zig - Highlights (1/5)

Integration with C-libraries without FFI/bindings


  const c = @cImport({
      // See github.com/ziglang/zig/issues/515
      @cDefine("_NO_CRT_STDIO_INLINE", "1");
      @cInclude("stdio.h");
  });
  pub fn main() void {
      _ = c.printf("hello\n");
  }
  

Zig - Highlights (2/5)

comptime and test


  fn max(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
  }
  fn getBiggerFloat(a: f32, b: f32) f32 {
    return max(f32, a, b);
  }
  fn getBiggerInt(a: u64, b: u64) u64 {
    return max(u64, a, b);
  }
  

  // array literal
  const m = [_]u8{ 'h', 'e', 'l', 'l', 'o' };

  // get the size of an array
  comptime {
    assert(m.len == 5);
  }
  

Zig - Highlights (3/5)

Tools


$ zig init-exe
info: Created build.zig
info: Created src/main.zig
info: Next, try `zig build --help` or `zig build run`
$ tree
.
β”œβ”€β”€ build.zig
└── src
    └── main.zig

2 directories, 2 files
  

$ zig build run
All your codebase are belong to us.
Run `zig build test` to run the tests.

$ tree zig-out
zig-out
└── bin
    └── yourapp2
  

Zig - Highlights (4/5)

Cross compilation


$  zig build-exe src/main.zig -target x86_64-windows-gnu
$  file main.exe
main.exe: PE32+ executable (console) x86-64, for MS Windows, 7 sections
  

$  zig build-exe src/main.zig -target aarch64-linux-musl
$  file main
main: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, with debug_info, not stripped
  

Zig - Highlights (5/5)

  • custom allocators
  • Faster and safer than C (*)
  • active development

Carbon

  • In development
  • Succesor approach
  • Interoperability (C++ ⇄ Carbon)
  • Modern Generic system
  • Safety strategy (Migration plan)

Carbon and C++


    // C++
    #include <math.h>
    #include <iostream>
    #include <span>
    #include <vector>

    struct Circle {
      float r;
    }

    void PrintTotalArea(std::span circles) {
      float area = 0;
      for (const Circle& c: circles) {
        area += M_PI * c.r * c.r;
      }
      std::cout << "Total area: " << area << "\n";
    }

    auto main(int argc, char** argv) -> int {
      std::vector circles = {{1.0}, {2.0}};
      // Implicitly converts `vector` to `span`
      PrintTotalArea(circles);
      return 0;
    }
    

    // Carbon
    package Geometry api;

    import Math;

    class Circle {
      var r: f32;
    }

    fn PrintTotalArea(circles: Slice(Circle)) {
      var area: f32 = 0;
      for (c: Circle in circles) {
        area += Math.Pi * c.r * c.r;
      }
      Print("Total area: {0}", area);
    }

    fn Main() -> i32 {
      // A dynamically sized array, like `std::vector`.
      var circles: Array(Circle) = ({.r = 1.0}, {.r = 2.0});
      // Implicitly converts `Array` to `Slice`
      PrintTotalArea(circles);
      return 0;
    }
    

Carbon - Highlights

  • Simpler syntax
  • Safer/modern C++ succesor
  • Safety: only way to radically change the std library
  • Still experimental

Many other languages to check

How should Qt

look like in the future

Imagine a Qt framework

  • βœ… Memory safe
  • βœ… Easy to test
  • βœ… Straightforward syntax
  • βœ… Modern Generics
  • βœ… Backward compatible 🀯

pick 5

What can we (*) do?

(*) you all, and me

Languages

Be aware of other languages, no need to be an expert.

API Design

Keep in mind other languages when new API is being written down (or updated).

Documentation

Try to add a general explanation of the method/class/module then a more language specific one.

Tooling

Get inspired by what's out there. Innovation is not always necessary.

Outreach

Spreading the knowledge should be priority 1, and making everything accesible priority 2.

What does it mean to have more languages in Qt?

More languages mean

more communities

More languages mean

more experience

More languages mean

a better Qt

But most importantly...

Let's bring Qt

everywhere

Q&A

The Role of Language Bindings in the Future of the Qt Ecosystem.


Dr. CristiΓ‘n Maureira-Fredes
@cmaureir

Slides: qtinfo.dev/qtws23