From 252b9607d463f588528c05658b88794ac16e7752 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 13 Jul 2026 17:51:48 -0700 Subject: [PATCH] Add docs for library use and the XML binding format Adds a doc covering the non-obvious differences between ClangSharp and libClang for consuming the library directly, and a doc describing the generator's XML output format. Links both from the README. Co-authored-by: aka-nse Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- README.md | 8 ++ docs/using-clangsharp-as-a-library.md | 158 +++++++++++++++++++++ docs/xml-binding-format.md | 191 ++++++++++++++++++++++++++ 3 files changed, 357 insertions(+) create mode 100644 docs/using-clangsharp-as-a-library.md create mode 100644 docs/xml-binding-format.md diff --git a/README.md b/README.md index 58843b7e..c4f96ab8 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ Source browsing is available via: https://source.clangsharp.dev/ * [Building Native](#building-native) * [Generating Bindings](#generating-bindings) * [Best practices](docs/generating-bindings-best-practices.md) + * [XML binding format](docs/xml-binding-format.md) +* [Using as a Library](#using-as-a-library) * [Using locally built versions](#using-locally-built-versions) * [Spotlight](#spotlight) @@ -143,6 +145,8 @@ At a minimum, the command line expects one or more input files (`-f`), an output For an opinionated walkthrough of how to structure a real generation project — response-file composition, the key options and when to use them, incremental regeneration, and common pitfalls — see [Generating bindings: best practices](docs/generating-bindings-best-practices.md). +The generator can also emit its bindings as XML rather than C# via `--output-mode Xml`; the shape of that output is described in [The XML binding format](docs/xml-binding-format.md). + The full set of available switches: ``` ClangSharpPInvokeGenerator @@ -276,6 +280,10 @@ Options: log-visited-files A list of the visited files should be generated. This can help identify traversal issues. ``` +### Using as a Library + +In addition to generating bindings, ClangSharp can be consumed directly as a library to parse C/C++ and inspect the resulting AST. This is an advanced scenario that assumes familiarity with the Clang APIs, which remain the source of truth. For the ClangSharp-specific conventions — how the `clang_*` C functions and the Clang C++ AST map onto the `ClangSharp.Interop` and `ClangSharp` surfaces, lifetime/`IDisposable` handling, and the required package references — see [Using ClangSharp as a library](docs/using-clangsharp-as-a-library.md). + ### Using locally built versions After you build local version, you can use executable from build location. diff --git a/docs/using-clangsharp-as-a-library.md b/docs/using-clangsharp-as-a-library.md new file mode 100644 index 00000000..1c65ac64 --- /dev/null +++ b/docs/using-clangsharp-as-a-library.md @@ -0,0 +1,158 @@ +# Using ClangSharp as a library + +ClangSharp is first and foremost a *bindings* library. It exposes Clang's C API (libClang) and a +higher-level surface that mirrors Clang's C++ AST to .NET. It is explicitly **not** a goal of this +repository to document how to use Clang itself — Clang provides its own documentation and that is +always the source of truth: + +* [Clang documentation index](https://clang.llvm.org/docs/index.html) +* [libClang: C Interface to Clang](https://clang.llvm.org/docs/LibClang.html) +* [Introduction to the Clang AST](https://clang.llvm.org/docs/IntroductionToTheClangAST.html) + +Working with ClangSharp is an **advanced** scenario. It assumes you are already comfortable with the +Clang APIs (which are documented in terms of C and C++), with translating those concepts into +equivalent C# (`IDisposable`, spans, pointers, etc.), and with consuming NuGet packages. If you can +follow the upstream Clang docs, the code translates over almost directly. + +This document only covers the **non-obvious differences** between ClangSharp and the underlying Clang +APIs, so that you can follow that upstream documentation directly rather than re-learning it here. + +## Two layers + +ClangSharp ships two related but distinct surfaces: + +* **`ClangSharp.Interop`** — the low-level, effectively 1:1 bindings over libClang's stable C API. The + `clang_*` functions and `CX*` types documented in + [LibClang](https://clang.llvm.org/docs/LibClang.html) live here. +* **`ClangSharp`** — a higher-level layer that mirrors the Clang C++ AST (`Cursor`, `Decl`, `Stmt`, + `Type`, and their many subclasses). This is the surface described by + [Introduction to the Clang AST](https://clang.llvm.org/docs/IntroductionToTheClangAST.html). + +Because both layers deliberately mirror their upstream counterparts, the upstream documentation +remains valid — you are mostly applying the naming and lifetime conventions below. + +## How libClang maps to `ClangSharp.Interop` + +The libClang C functions are all prefixed with `clang_` and take the object they operate on as the +first parameter, for example: + +```c +CXType clang_getCursorType(CXCursor C); +``` + +In ClangSharp these are exposed as static methods on the `clang` class with the `clang_` prefix +dropped and the leading character left as-is: + +```csharp +CXType type = clang.getCursorType(cursor); +``` + +Where it reads more naturally, the common getters are *also* surfaced as instance members on the +`CX*` types, so the call above can equivalently be written: + +```csharp +CXType type = cursor.Type; +``` + +The `CX*` handle types (`CXIndex`, `CXTranslationUnit`, `CXCursor`, `CXType`, ...) keep their upstream +names, so anything you read in the libClang docs has an obvious ClangSharp equivalent. + +## How the Clang C++ AST maps to `ClangSharp` + +The high-level layer mirrors the Clang C++ class hierarchy, so the concepts from +[Introduction to the Clang AST](https://clang.llvm.org/docs/IntroductionToTheClangAST.html) carry +over directly: + +* `Cursor` is the base type; `Decl`, `Stmt`, `Expr`, and `Type` (and their subclasses such as + `FunctionDecl`, `CallExpr`, `ReturnStmt`) derive from it and match the upstream names. +* You obtain the root from a `TranslationUnit` via `TranslationUnit.TranslationUnitDecl` and walk it + from there. + +Instances are cached: rather than constructing these types yourself, you get them through +`TranslationUnit.GetOrCreate(...)`, which returns the same managed object for a given underlying +handle. This means reference equality is meaningful and you should not `new` them up directly. + +## Lifetime and disposal + +Several Clang objects own native resources and must be released. In C++/libClang this is done with +explicit `clang_dispose*` calls; in ClangSharp the equivalent types implement `IDisposable`, so wrap +them in `using`: + +* `CXIndex` — created with `CXIndex.Create()`, disposed via its `Dispose()`. +* `TranslationUnit` — obtained via `TranslationUnit.GetOrCreate(...)`; disposing it releases the + underlying `CXTranslationUnit`. + +As with any unsafe/interop code, the runtime cannot validate that handles are still alive — using a +cursor or type after its owning `TranslationUnit` has been disposed is undefined behavior, exactly as +it would be in C++. + +## Package references + +Consuming ClangSharp only requires the `ClangSharp` package itself — it brings the native `libClang` +and `libClangSharp` runtimes in transitively. You do, however, need to specify a `RuntimeIdentifier` +so the correct platform-specific runtime package is restored: + +```xml + + + + Exe + net10.0 + enable + + + win-x64 + + + + + + + + +``` + +`libclang` and `libClangSharp` are meta-packages that point at platform-specific runtime packages +(for example `libClangSharp.runtime.win-x64`). Several manual steps may be required depending on your +setup; see the discussion in [#46](https://github.com/dotnet/ClangSharp/issues/46) and +[#118](https://github.com/dotnet/ClangSharp/issues/118). + +## A minimal example + +The following shows the conventions above in practice — creating an index, parsing a source file, and +walking the AST. Everything it does maps directly onto the libClang and Clang AST documentation linked +at the top of this page: + +```csharp +using ClangSharp; +using ClangSharp.Interop; + +using CXIndex index = CXIndex.Create(); + +ReadOnlySpan args = ["--language=c"]; +ReadOnlySpan unsavedFiles = []; +CXTranslationUnit handle = CXTranslationUnit.CreateFromSourceFile(index, "main.c", args, unsavedFiles); + +using TranslationUnit tu = TranslationUnit.GetOrCreate(handle); +PrintDecl(tu.TranslationUnitDecl, indent: ""); + +static void PrintDecl(Decl decl, string indent) +{ + Console.WriteLine($"{indent}{decl.DeclKindName} {decl.Spelling}"); + + foreach (Decl child in decl.Decls) + { + PrintDecl(child, indent + " "); + } +} +``` + +For anything beyond this — what a given cursor, declaration, statement, or type *means* — refer to the +upstream Clang documentation. ClangSharp intentionally follows it closely. + +## Using the generator instead + +If your goal is to produce P/Invoke bindings for a C or C++ library rather than to inspect the AST +yourself, you likely want the `ClangSharpPInvokeGenerator` tool rather than the raw library. See +[Generating bindings: best practices](generating-bindings-best-practices.md) and the +[main README](../README.md#generating-bindings). diff --git a/docs/xml-binding-format.md b/docs/xml-binding-format.md new file mode 100644 index 00000000..10ff1d2f --- /dev/null +++ b/docs/xml-binding-format.md @@ -0,0 +1,191 @@ +# The XML binding format + +`ClangSharpPInvokeGenerator` can emit its bindings as XML instead of C# by passing +`--output-mode Xml` (`-om Xml`). This document describes the shape of that XML. + +The XML output is a **structured serialization of the same information the C# emitter produces** — the +generator's final decisions about names, types, access, layout, calling conventions, and so on — not a +dump of the raw Clang AST. It exists so that downstream tooling can consume, diff, post-process, or +re-emit the bindings without having to parse C#. + +> [!NOTE] +> This format is specific to this generator and mirrors the C# emitter. There is no formal schema +> (XSD), and the element/attribute shape can change alongside the C# output. Treat it as a convenience +> for tooling built against this repo rather than a stable, versioned contract. The examples below are +> taken directly from the generator's own baseline tests, so they reflect exactly what it emits. + +## Producing XML output + +The mode is selected on the command line and is otherwise driven by the same options and response +files as C# generation: + +``` +ClangSharpPInvokeGenerator -om Xml -n MyNamespace -m Methods -o bindings.xml -f header.h +``` + +Everything that shapes the C# output — `--methodClassName`, remappings, `--config` options, etc. — +applies identically here; only the serialization differs. + +## Document structure + +A generated file is a single `` document. The declared namespace and, for free functions, +the static method class are represented as nesting `` and `` elements: + +```xml + + + + + + void + + float* + + + + + +``` + +* `` — the document root. +* `` — emitted immediately inside `` when a `--headerFile` / header text is + configured; it carries that verbatim header text. +* `` — the output namespace (from `-n`). +* `` — the static holder for free functions (from + `-m` / `--methodClassName`). Types such as structs and enums are emitted directly under + `` rather than inside this class. It gains `unsafe="true"` when the class contains unsafe + members. + +In single-file mode one `` wraps the whole output; with `--multi-file`, each generated file +is its own complete `` document. + +## Types (``) + +Almost every declaration carries a `` child describing the emitted C# type as text. When the +original C/C++ spelling differs from the emitted type, it is preserved on the `native` attribute: + +```xml + + uint + +``` + +In value contexts (enumerators, constants) `` also carries `primitive="True|False"` indicating +whether the value is a primitive. + +## Structs (``) + +```xml + + + uint + + + void* + + +``` + +`` attributes (each emitted only when applicable): + +* `name`, `access` — the escaped name and access specifier. +* `native` — the original native type name, when it differs. +* `parent` — the native base type, for inherited layouts. +* `uuid` — the associated GUID, when present. +* `vtbl="true"` — the struct has a vtable. +* `unsafe="true"` — the struct requires an unsafe context. +* `layout` / `pack` — the `StructLayout` kind and packing, when explicitly emitted. + +`` elements carry `name`, `access`, and optionally `inherited` (the type a field is inherited +from) and `offset`. A fixed-size buffer adds `count` and `fixed` attributes to its ``. + +## Enums (``) + +```xml + + int + + int + + 1 + + + + int + + +``` + +The `` `` is the underlying integral type. Each `` optionally contains a +`` wrapping the initializer; the initializer expression itself is emitted inside `` (see +[Embedded C# expressions](#embedded-c-expressions)). + +## Constants and fields (`` / ``) + +Value declarations are emitted as `` when constant, or `` otherwise, following the +same `` + optional `` shape: + +```xml + + float + + 1_024.0 + + +``` + +## Functions and delegates (`` / ``) + +Non-virtual functions are ``; virtual methods (function-pointer/vtable slots) are +``. The `` immediately inside is the return type; parameters follow as ``: + +```xml + + void + + int + + +``` + +Common attributes: + +* `name`, `access` — as elsewhere. +* `lib` — the P/Invoke library, for `DllImport` functions. +* `convention` — the calling convention, when it is not the default `Winapi`. +* `entrypoint` — the native entry point, when it differs from `name`. +* `setlasterror="true"` — sets `SetLastError`. +* `static="true"`, `readonly="true"`, `unsafe="true"` — as applicable. +* `vtblindex` — the vtable slot index, for virtual methods. + +`` elements carry a `name` and a ``; a default argument is emitted inside ``. A +method body, when generated, is emitted inside ``. + +## Properties and indexers + +Property accessors and indexers reuse the surrounding declaration and add accessor elements: + +* `` / `` — accessor bodies; `inlining="aggressive"` marks aggressive inlining. +* `` with a `` for the element type. + +## Embedded C# expressions (``) + +Where the binding requires C# that has no structured XML representation — initializer expressions, +generated helper bodies, and similar — the generator falls back to emitting the raw C# text inside a +`` element (as seen in the enum and constant examples above). + +## Other elements + +* `` — the configured header text, at the top of the document. +* `` — a custom attribute attached to the following declaration. +* `` — an interface IID. +* `` — an explicit vtable struct. +* ``, ``, ``, `` — expression-shaping wrappers used within values and + bodies. + +## When to use it + +Prefer the default C# output unless you specifically need a machine-readable description of the +bindings — for example to drive a custom code generator, produce documentation, or diff the generator's +decisions across runs. For everything else, see +[Generating bindings: best practices](generating-bindings-best-practices.md).