-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Hello, thank you for the collaboration invite =D
I have been investigating + experimenting with converting C++ headers to Dlang extern (C++) bindings and also more recently to Nim.
Maybe I can share some thoughts/findings with you 🙂
libclang vs LibTooling
Probably not so important for just one library, but I have been doing reading on which approach is best for translating headers to other languages + bindgen/codegen.
- http://clang-developers.42468.n3.nabble.com/libclang-or-libtooling-for-transpiler-td4064875.html
- (bottom of post) https://forum.dlang.org/post/lvwnenjrcgvzdqozfcgy@forum.dlang.org
- Migrate away from libclang and use libtooling instead google/sandboxed-api#29
There are some screenshots of experiences from the authors of c2rust and dpp:
(I am not certain, but I think the newer
libclang-cppshared lib that contains all of the clang-based libraries also contains theLibToolingstuff and would be much smaller.)
# For MinGW we only enable shared library if LLVM_LINK_LLVM_DYLIB=ON.
# Without that option resulting library is too close to 2^16 DLL exports limit.
if(UNIX OR (MINGW AND LLVM_LINK_LLVM_DYLIB))
add_clang_subdirectory(clang-shlib)
endif()- https://github.com/llvm/llvm-project/blob/main/clang/tools/CMakeLists.txt#L20-L24
- https://github.com/llvm/llvm-project/blob/main/clang/tools/clang-shlib/CMakeLists.txt#L44-L50
It's possible to use cppyy for LibTooling access, if you build LLVM on Linux/MinGW with BUILD_SHARED_LIBS set to ON (I think it's also possible with LLVM_BUILD_DYLIB)
Doing this gives you:
API/codegen approach
I found two interesting approaches others have taken for the actual codegen that I have been experimenting with. Maybe you have some ideas?
The first
Create decorators in Python which hold rules, the argument is a clang::QualType or clang::Type. Nodes are evaluated against rules, the one matching is chosen for codegen.
Each class is a "frontend" driver/generator for a language to create from C++ headers. It's very clever! Currently he has:
- C
- Rust
- Lua bindings (C)
Here is the repo where I found this. In his approach, he has used PyBind for binding the Clang/LibTooling C++ API to Python, which could also be used instead of cppyy I suppose (not sure why you'd do that)
- https://github.com/Time0o/CPPBind/blob/7d7b578112416ea6fa13431edf77b57460799f6c/backend/impl/c/c_type_translator.py#L88-L116
- (PyBind) https://github.com/Time0o/CPPBind/blob/7d7b578112416ea6fa13431edf77b57460799f6c/source/Backend.cpp#L151-L210
class NimCodegen():
# t = clang::Type or clang::QualType
@rule(lambda t: t.is_pointer() or t.is_reference() and \
t.pointee().is_record_indirection())
def input(cls, t, args):
return f"{{interm}} = &{c_util.struct_cast(t, '{inp}')};"
@rule(lambda t: t.is_pointer() or t.is_reference())
def input(cls, t, args):
raise ValueError("unsupported input pointer/reference type {}".format(t))The second
Use template languages like Jinja/Moustache to create declarative template files for codegen. This allows easy visualization of what the output will be, and for people to tweak it or pass a custom template file easily.
I saw this approach here:
Generate arbitrary files from C/C++ header files using CppHeaderParser to read the input files and Jinja2 templates to generate the outputs.
This grew out of a desire to generate pybind11 wrapping code from C++ header files. pybind11gen was created, but then I wanted to generate more things...
{# Silly example that shows how to convert a header file to YAML #}
---
{% for header in headers %}
{% for fn in header.functions %}
{{ fn.name }}:
returns: {{ fn.returns }}
params:
{% for param in fn.parameters %}
- { name: {{ param.name }}, type: "{{ param.type }}" }
{% endfor %}
{% endfor %}
{% endfor %}I imagine that if clang nodes were passed into here, and a template language was used which allowed to evaluate expressions like t.is_pointer() then it could actually be pretty okay.
The approach with this library for evaluating expressions is by using a custom "hooks" file for defining pre-processing logic.
It's very basic though and does not use clang, but a custom Python preprocessor.



