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-gen
is part of the seaqt
family of projects each targeting a different aspect of Qt binding development:
seaqt-gen
usesclang
to parse Qt C++ headers and outputs a correspondingC
interface - if you have your ownC++
-based components that you wish to expose toC
, 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
containsQt
binding code forC
generated byseaqt-gen
- this is the main repository to use if you want to consume the bindings in your application or languagenim-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!
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 spawnedDOtherSide
-C
bindings focusing on the QML subset of Qt - similarly aims to be a base for multiple languagesQtJambi
-java
bindings that similar tomiqt
uses a generator approach for broad Qt coverage along with garbage-collection supportcxx-qt
- more on lifetime design and metaobject integration
Same as miqt
, the bindings are MIT-licensed but you have to take care to follow Qt's licensing as well.
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.
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 similargo
-specific constructs have been removed along with the generatedgo
code itself - while the binding generator itself remains written ingo
, the generated code has no dependencies beyondQt
itself (andpkg-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 theGo
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.
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.
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 runsgenbindings
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 forkmake 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 ingen
, updating the "official"qt-<version>
branches - Update the PR to point to these commits
- Merge
- During the PR process, fork
- When this repository is rebased against
miqt
, theqt-<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.
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 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).