An open API service indexing awesome lists of open source software.

https://github.com/dynamium/lv_cxx_bindgen

A C++ binding generator for LVGL. Written in Rust (oh the irony)
https://github.com/dynamium/lv_cxx_bindgen

binding cpp cpp11 cpp14 cpp17 cpp20 cpp23 lvgl rust

Last synced: 6 months ago
JSON representation

A C++ binding generator for LVGL. Written in Rust (oh the irony)

Awesome Lists containing this project

README

          

# lv_cxx_bindgen
A C++ binding generator for LVGL. Written in Rust (oh the irony)

> This is still work-in-progress, it doesn't even generate any sources for
> now, but it's in the works.

This generator uses the LVGL JSON API map, that will later be analyzed, and used as
a basis for all autogenerated C++ code. Also, it supports different C++ target
versions, ranging from C++11 to C++23.

## Usage

`lv_cxx_bindgen` requires a config file, by default named `lv_cxx_bindgen.toml` and
looked for in CWD, but can be specified in the `-c` argument.

> TODO: Finish writing this section after CLI API is finished

## Configuration

All configuration is done in the `lv_cxx_bindgen.toml` file. An example file
looks like this:

```toml
[classes]
exclude = {
groups = ["anim"],
functions = ["obj_get_disp", "font_set_kerning"]
},
renames = [
["obj", "Object"]
],
inheritances = [
["img", "obj"],
["menu", "obj"]
]
namespaces = {
exclude = ["obj"],
renames = [
["anim", "animation"]
]
}
functions = {}
```

### Input

- `cwd` - current working directory.

### Generation

- `target` - target C++ version, by default C++23. All the differences between different
targets and generated output:

> Note: read the list from the start to your desired C++ target to fully understand what
> code will be generated

- C++11
- Functions that accept arrays in arguments have those arguments converted from pointers
to `std::vector` or `std::array`, depending on configuration
- Functions that accept function pointers in arguments have those arguments converted
to `std::function`, but as an overload, so there are options for `std::function`, and
normal function pointers
- Functions that can error out have `lvgl::expected` as their result type.
- Functions that can have `null` values use `lvgl::optional`.
- C++14 doesn't have any changes, but it's still a good idea to set the target to your
C++ version, so that in future updates it will not break
- C++17
- Instead of `lvgl::optional`, `std::optional` is used as an option type.
- C++20
- Now there is no header file in the output, only a `.cppm` file, which is a C++
module, that can be imported with `import lvgl;`. And yes, when you use import,
you lose access to the C API, but it still can be included via its header file
(that is not recommended tho, see FAQ).
- C++23
- Instead of `lvgl::expected`, `std::expected` is used as a result type.

> TODO: document `classes` and `namespaces` when those will be implemented

- `class.exclude` - function groups that are excluded from assigning to a class.
For example, if you specify `"obj"` in the list, any function that starts with
`lv_obj_` will not get assigned to any class
- `class.renames` -

## Process

In short, the whole process can be simplified to 3 main steps:

- Parsing
- Conversion
- Generation

The first step is the simplest one, it just parses a JSON API map file and extracts
all relevant data.

The second step converts that map file to High-level Abstract Syntax Tree (HL-AST),
which represents the entire C++ binding in a high-level format. That step is the
most interesting one, because this one does the fun stuff - converting
the C API into idiomatic C++ code.

And the (not-so) best for last, the third step consists of actually converting allat
into actual C++ code. That shit is done manually (without any codegen library), for
plain efficiency and also ease of understanding what is actually going on. Also, an
additional `clang-format` run can be named a "three and a half" step, because it's
part of codegen, but not part of the actual generator.

So, a full process can be described like this:

- Extraction
- Parsing of `lvgl.json` (a.k.a. LVGL API Map)
- Function list extraction
- Typedef/struct/enum/etc extraction and combination (combining anonymous struct
with its associated typedef for example)
- Conversion to C++ a.k.a. HL-AST (High-level Abstract Syntax Tree) generation
- Function processing/generation
- Argument conversion for more idiomatic C++
- Argument filtering (removal of singular void args, like `lv_init(void)`)
- Struct processing/generation
- Replace *char with std::string, function pointers with std::function,
and other stuff when applicable (configurable via CLI)
- Enum processing/generation
- Generate as enum classes
- Namespace generation
- Group by function/struct/enum namespace (lv_)
- Class generation
- Group by function/struct/enum namespace (lv_)
- Create constructors from lv__create functions
- Generation
- Conversion of HL-AST to LL-AST (Low-level Abstract Syntax Tree)
- LL-AST to source code conversion
- `clang-format` run over generated code (optional)

## FAQ

### Q: Are exceptions used in any shape or form?

A: No, never. That's the worst idea known to man, especially knowing that
LVGL is supposed to run on embedded systems. Exceptions introduce a lot of
overhead, and are generally a pain in the ass. As a replacement, `std::optional`,
`std::expected`, and similar types are used, depending on context.

### Q: Can I use the normal C API together with C++?

A: **TL;DR: Don't. Please.**

Full answer: Technically, yes, no one is stripping that right from you, but that is
far from recommended, use the C API only when you REALLY know what you are doing.
This is basically black&white, you should only use one API, no mixing of C and C++
APIs, no "grays. You're only making it harder for yourself by using both at the
same time.

## Technical

### Templating

If you have looked at this repository longer that 99% of people that go here, you've
probably noticed that `res/src/lvgl.hpp` has comments that look something like
`// MARKER: OBJ_CLASS_MEMBERS` all over it. And that is just for simple
templating built-in to `lv_cxx_binding` itself. All the templating engine does
is it looks for `// MARKER:` inside the source code, and if found, gets the
ID after the statement, and replaces the comment with what should be in that spot.
No dark magic or anything, simple search-and-replace :)

## Thanks

The implementation of `lvgl::optional` is basically
[martinmoene/optional-lite](https://github.com/martinmoene/optional-lite). Thanks
for this great piece of software @martinmoene!

Same goes for `lvgl::expected`, it is from
[martinmoene/expected-lite](https://github.com/martinmoene/expected-lite). Fabulous
piece of software.