Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
158 changes: 158 additions & 0 deletions docs/using-clangsharp-as-a-library.md
Original file line number Diff line number Diff line change
@@ -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
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>

<!-- Required so the platform-specific libClang/libClangSharp runtime package is restored -->
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>

<ItemGroup>
<!-- The native libClang/libClangSharp runtimes come in transitively -->
<PackageReference Include="ClangSharp" Version="21.1.8.3" />
</ItemGroup>

</Project>
```

`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<string> args = ["--language=c"];
ReadOnlySpan<CXUnsavedFile> 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).
191 changes: 191 additions & 0 deletions docs/xml-binding-format.md
Original file line number Diff line number Diff line change
@@ -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 `<bindings>` document. The declared namespace and, for free functions,
the static method class are represented as nesting `<namespace>` and `<class>` elements:

```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<bindings>
<namespace name="ClangSharp.Test">
<class name="Methods" access="public" static="true">
<function name="MyFunction" access="public" lib="ClangSharpPInvokeGenerator" convention="Cdecl" static="true" unsafe="true">
<type>void</type>
<param name="color">
<type>float*</type>
</param>
</function>
</class>
</namespace>
</bindings>
```

* `<bindings>` — the document root.
* `<comment>` — emitted immediately inside `<bindings>` when a `--headerFile` / header text is
configured; it carries that verbatim header text.
* `<namespace name="...">` — the output namespace (from `-n`).
* `<class name="..." access="public" static="true">` — the static holder for free functions (from
`-m` / `--methodClassName`). Types such as structs and enums are emitted directly under
`<namespace>` rather than inside this class. It gains `unsafe="true"` when the class contains unsafe
members.

In single-file mode one `<bindings>` wraps the whole output; with `--multi-file`, each generated file
is its own complete `<bindings>` document.

## Types (`<type>`)

Almost every declaration carries a `<type>` 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
<field name="r" access="public">
<type native="unsigned int">uint</type>
</field>
```

In value contexts (enumerators, constants) `<type>` also carries `primitive="True|False"` indicating
whether the value is a primitive.

## Structs (`<struct>`)

```xml
<struct name="MyStruct2" access="public" unsafe="true" layout="Sequential" pack="4">
<field name="Field1" access="public">
<type native="unsigned int">uint</type>
</field>
<field name="Field2" access="public">
<type>void*</type>
</field>
</struct>
```

`<struct>` 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.

`<field>` 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 `<type>`.

## Enums (`<enumeration>`)

```xml
<enumeration name="MyEnum" access="public">
<type>int</type>
<enumerator name="MyEnum_Value1" access="public">
<type primitive="False">int</type>
<value>
<code>1</code>
</value>
</enumerator>
<enumerator name="MyEnum_Value2" access="public">
<type primitive="False">int</type>
</enumerator>
</enumeration>
```

The `<enumeration>` `<type>` is the underlying integral type. Each `<enumerator>` optionally contains a
`<value>` wrapping the initializer; the initializer expression itself is emitted inside `<code>` (see
[Embedded C# expressions](#embedded-c-expressions)).

## Constants and fields (`<constant>` / `<field>`)

Value declarations are emitted as `<constant>` when constant, or `<field>` otherwise, following the
same `<type>` + optional `<value>` shape:

```xml
<constant name="x" access="private">
<type primitive="True">float</type>
<value>
<code>1_024.0</code>
</value>
</constant>
```

## Functions and delegates (`<function>` / `<delegate>`)

Non-virtual functions are `<function>`; virtual methods (function-pointer/vtable slots) are
`<delegate>`. The `<type>` immediately inside is the return type; parameters follow as `<param>`:

```xml
<function name="MyFunction" access="public" lib="clang" convention="Cdecl" static="true" unsafe="true">
<type>void</type>
<param name="index">
<type>int</type>
</param>
</function>
```

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.

`<param>` elements carry a `name` and a `<type>`; a default argument is emitted inside `<init>`. A
method body, when generated, is emitted inside `<body>`.

## Properties and indexers

Property accessors and indexers reuse the surrounding declaration and add accessor elements:

* `<get>` / `<set>` — accessor bodies; `inlining="aggressive"` marks aggressive inlining.
* `<indexer access="..." unsafe="...">` with a `<type>` for the element type.

## Embedded C# expressions (`<code>`)

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
`<code>` element (as seen in the enum and constant examples above).

## Other elements

* `<comment>` — the configured header text, at the top of the document.
* `<attribute>` — a custom attribute attached to the following declaration.
* `<iid name="..." value="..." />` — an interface IID.
* `<vtbl>` — an explicit vtable struct.
* `<value>`, `<cast>`, `<deref>`, `<unchecked>` — 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).
Loading