Skip to content

seaqt/seaqt-gen

Repository files navigation

seaqt-gen

seaqt-gen is a C and Nim binding generator for Qt evolved from the C bindings used in miqt.

The bindings are still going through significant changes, including in naming and structure - this repository is a preview intended for feedback.

The code may occasionally be rebased before the dust settles on a generally useful initial release.

seaqt family

seaqt-gen is part of the seaqt family of projects each targeting a different aspect of Qt binding development:

  • seaqt-gen uses clang to parse Qt C++ headers and outputs a corresponding C interface - if you have your own C++-based components that you wish to expose to C, the generator can similarly be used to wrap that (wip) - you can ignore this repository if all you want to do is use the bindings.
  • seaqt contains Qt binding code for C generated by seaqt-gen - this is the main repository to use if you want to consume the bindings in your application or language
  • nim-seaqt contains binding code for the Nim programming language - similarly, if all you want to do is to use Nim from Qt, you can ignore the other repos

Future repositories may be added to cover additional Qt-based libraries (such as Scintilla) as well as additional tooling. Get in touch to add yours!

Related projects

Qt has been around for a while and as such, there are many binding projects to choose from! seaqt blends ideas from all of them, but in particular:

  • miqt - original set of bindings from which this project spawned
  • DOtherSide - C bindings focusing on the QML subset of Qt - similarly aims to be a base for multiple languages
  • QtJambi - java bindings that similar to miqt uses a generator approach for broad Qt coverage along with garbage-collection support
  • cxx-qt - more on lifetime design and metaobject integration

License

Same as miqt, the bindings are MIT-licensed but you have to take care to follow Qt's licensing as well.

Usage

To run the generator, you need to have docker and go installed. The make command will compile cmd/genbindings, then run it inside a docker container to generate output that goes into the gen/ folder. The libseaqt folders are then overlaid on top of the automatically generated code.

The generated code can then be compiled or included in a project, as the examples show - generally, the binding code can be used with any Qt version equal or newer version that it was generated for.

See miqt documentation for general usage documentation.

To keep the generated code stable, bindings get generated inside a Docker container with a fixed Qt version installed.

Upstream changelog

The aim of seaqt-gen is to provide excellent C language bindings for Qt that can be used either on their own or as the base for other FFI-based language projections. While miqt focuses on Qt and Go specifically, seaqt-gen can be viewed as a general C binding generator for C++ that has special support for Qt.

seaqt-gen is currently developed as a set of patches against the upstream miqt repository that - a non-exhaustive list of changes includes:

  • Binding output is directed to a separate repositories/branches to reduce clone size for users
  • Versioning and module organisation more closely follows that of upstream Qt - in particular, modules follow pkg-config/dll boundaries and branches are used to track versions
  • cgo and similar go-specific constructs have been removed along with the generated go code itself - while the binding generator itself remains written in go, the generated code has no dependencies beyond Qt itself (and pkg-config).
  • Non-core components such as Scintilla have been dropped for now - the aim is for such libraries to be supported using command-line flags.
  • As a consequence of focusing on C in its own right, the generated code has features that are not directly needed by the Go bindings but that are useful for "native" usage or when used with other languages

As miqt evolves, so will this repository - for now, seaqt-gen gets rebased against upstream regularly - keep this in mind when forking.

Acknowledgements and contributions

seaqt-gen is built on top of the miqt binding generator and wouldn't be possible without it - consider contributing to upstream directly such that the changes may benefit both.

See the relevant upstream issue for more information.

Contributing to seaqt-gen

Contributions to seaqt-gen are welcome. When contributing, there are a few things to keep in mind:

  • The generated code lives in gen in submodules
  • To update the submodules, use make gencommits which runs genbindings then creates a commit in each submodule
  • To submit a PR and get to green CI, the submodules need to point to a commit that contains the updates to the generated code
    • During the PR process, fork seaqt and generate commits there, then update .gitmodules to point to your fork
      • make genbranches will create a similarly named branch as the main repository in each of the submodules
    • It's ok to skip this step in the interest of convenience, for early PR discussion
    • Merging is a somewhat messy multi-step process
      • check out the PR commit
      • run make gencommits to generate new commits in gen, updating the "official" qt-<version> branches
      • Update the PR to point to these commits
      • Merge
  • When this repository is rebased against miqt, the qt-<version> branches in the submodules are recreated from scratch, one commit for every seaqt commit that changes them
    • The first commit is used to seed these branches

The above workflow is likely to be replaced with something more smooth as the project evolves.

C translation details

Code layout

For each Qt module, we generate corresponding binding code in a directory named after the module.

Each Qt header file corresponds to a header and implementation pair in the output folder - the header can be used from both C and C++ while the implementation requires a C++ compiler to compile.

An amalgamation file is included for every module, for ease of compilation.

Inheritance

Inheritance is implemented using a wrapper type (named VirtualXxx for Xxx) which implements each virtual function redirecting the call to a function pointer in a vtable. The vtable is set per instance at construction time.

In addition to the vtable, the caller can request additional memory to be allocated with each instance simulating the typical memory layout of C++ inheritance where storage for the derived members simply extend the memory area reserved for the base type.

The vtable is simply a struct containing a pointer for each virtual function, including the virtual destructor which automatically gets called when the instance is being deleted.

Using QObject as example:

| -----------------------------------------------|
| sizeof(VirtualQObject) + alignment | vdata ... |
|------------------------------------|-----------|
| QObject | const QOjbect_VTable*    | ....      |
|------------------------------------|-----------|

Given a pointer to QObject, the QObject_vdata function returns a pointer to the data section while vdata_QObject performs the reverse similar to how static_cast adjusts pointers to correct for multiple inheritance.

Callers will typically keep a const vtable instance for each derived type they wish to create, using the vdata part for per-instance data (such as when mapping the C++ instance to a host language instance).

About

Binding generator for Qt

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 9