Skip to content
Open
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
5 changes: 5 additions & 0 deletions code-samples/appendices-annotations-c-api-annotation.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class \c_api\ val Adder
let _base: I64

new val create(base: I64) => _base = base
fun val add(x: I64): I64 => _base + x
6 changes: 6 additions & 0 deletions code-samples/c-ffi-exporting-basic.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class \c_api\ val Adder
let _base: I64

new val create(base: I64) => _base = base
fun val add(x: I64): I64 => _base + x
fun val base(): I64 => _base
8 changes: 8 additions & 0 deletions code-samples/c-ffi-exporting-generic-alias.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class MyBox[A: (Real[A] val & Integer[A] val)]
let _value: A

new val create(value: A) => _value = value
fun val get(): A => _value
fun val add(y: A): A => _value + y

type \c_api\ BoxedI64 is MyBox[I64]
3 changes: 3 additions & 0 deletions code-samples/c-ffi-exporting-primitive.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
primitive \c_api\ Math
fun val add(x: I64, y: I64): I64 => x + y
fun val mul(x: I64, y: I64): I64 => x * y
1 change: 1 addition & 0 deletions code-samples/c-ffi-exporting-use-alias.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use math = "mylib"
7 changes: 7 additions & 0 deletions code-samples/c-ffi-exporting-use-package.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use "mylib"
use @add_from_c[I64](adder: Adder, x: I64)

actor Main
new create(env: Env) =>
let adder = Adder(10)
env.out.print(@add_from_c(adder, 5).string())
11 changes: 11 additions & 0 deletions docs/appendices/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Here, `annotation1` and `annotation2` can be any valid Pony identifier, i.e. a s

Annotations are allowed after any scoping keyword or symbol. The full list is:

- `type`
- `actor`
- `class`
- `struct`
Expand Down Expand Up @@ -104,3 +105,13 @@ The annotation is also useful on matches that are already exhaustive as a future
```pony
--8<-- "appendices-annotations-exhaustive-annotation.pony:8:13"
```

#### `c_api`

Recognised on type declarations (`class`, `primitive`, `struct`, `actor`, `type`). Generates C-ABI wrapper functions for the type's eligible public methods and a `.h` header that declares them, so C code can call Pony methods on Pony objects. This feature is experimental and may change in future releases.

```pony
--8<-- "appendices-annotations-c-api-annotation.pony"
```

See [Exporting Pony Methods to C](/c-ffi/exporting.md) for the full details.
2 changes: 2 additions & 0 deletions docs/c-ffi/c-shims.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ include/version.h

Building and running prints `answer: 42` and `version: 7`. `answer` returns the macro set by `cdefine:`; `version` returns the constant from the header found via `cincludedir:`.

A shim can also call Pony methods on Pony objects through exported wrappers. The `\c_api\` annotation on a type declaration generates C-ABI wrappers and a header that any shim can include. See [Exporting Pony Methods to C](/c-ffi/exporting.md) for details.

That is everything you need to write and build a shim. The rest of this page is reference material — how shims are discovered and linked, how `--safe` applies, and which platforms are supported.

## How shims are discovered
Expand Down
143 changes: 143 additions & 0 deletions docs/c-ffi/exporting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Exporting Pony Methods to C

The `\c_api\` annotation on a type declaration generates C-callable wrapper functions for that type's public methods and a `.h` header that declares them. C code — including [C shims](/c-ffi/c-shims.md) — can then call Pony methods on Pony objects through these wrappers.

Without this annotation, calling a Pony method from C is not possible. Methods that are never called from Pony are not compiled at all, and methods that are compiled use LLVM's fast calling convention, which C cannot call. The annotation addresses both problems: it forces the methods to be compiled and generates a wrapper for each one that uses the C calling convention.

This feature is experimental and may change in future releases.

## A basic example

This example has two packages. The `Adder` class lives in a library package called `mylib/`, and the main package consumes it through a C shim.

Annotate the class with `\c_api\` to export its public methods:

```pony
--8<-- "c-ffi-exporting-basic.pony"
```

ponyc generates a wrapper function for each eligible public method (`add` and `base` in this example) and writes their declarations to `mylib_export.h`. A C shim in the main package can include that header and call the wrappers:

```c
// shim.c (in the main package directory)
#include "mylib_export.h"

int64_t add_from_c(void* adder, int64_t x) {
return mylib_Adder_add(adder, x);
}
```

The main package's Pony code calls the shim function via FFI, which in turn calls the Pony method through the generated wrapper:

```pony
--8<-- "c-ffi-exporting-use-package.pony"
```

```text
mylib/
mylib.pony (the Adder class)
main.pony (use "mylib", FFI declaration, Main)
shim.c (calls mylib_Adder_add)
```

## Which types can be exported

The annotation is valid on `class`, `primitive`, `struct`, `actor`, and `type` (type alias) declarations. It is not valid on `trait` or `interface` declarations, on methods, or on private types (names starting with `_`). Annotating any of these produces a compile error.

Generic types cannot be exported directly. To export a concrete reification of a generic type, use a type alias — see [Exporting generic types](#exporting-generic-types) below.

## How C names are derived

The C-facing name of each wrapper function is `[prefix_]TypeName_method_name`.

The prefix comes from the `use` statement in the package that imports the exported type's package. With `use "mylib"`, the prefix is `mylib`. With an alias — `use math = "mylib"` — the prefix is the alias:

```pony
--8<-- "c-ffi-exporting-use-alias.pony"
```

| `use` statement | Wrapper name | Header file |
|---|---|---|
| `use "mylib"` | `mylib_Adder_add` | `mylib_export.h` |
| `use math = "mylib"` | `math_Adder_add` | `math_export.h` |
| Main package (no `use`) | `Adder_add` | `<binary>_export.h` |

Characters that are not valid in C identifiers are replaced with underscores.

## Which methods are exported

Only public, non-generic `fun` methods with no tuple parameters or return types and no `?` (partial) marker are exported. Everything else is excluded:

- **Constructors** (`new`) — C code cannot construct Pony objects through this mechanism.
- **Behaviors** (`be`) — asynchronous message sends with no return value meaningful to C.
- **Private methods** — names starting with `_`.
- **Partial methods** — Pony's error mechanism has no C equivalent.
- **Methods with tuple parameters or return types** — Pony tuples have no direct C representation.
- **Bare methods** (`@`) — already use the C calling convention by definition.
- **Generic methods** — there is no way to determine which concrete types to instantiate.

If every public method on an exported type falls into one of these categories, the compiler reports an error: the type has no exportable methods.

## Primitives

Pony primitives are stateless singletons. When the compiler generates a wrapper for a primitive method, it omits the `self` parameter from the C signature and supplies the singleton internally. The C caller does not need to obtain or pass a receiver:

```pony
--8<-- "c-ffi-exporting-primitive.pony"
```

The generated C declarations for `Math` have no `self` parameter:

```c
extern int64_t Math_add(int64_t x, int64_t y);
extern int64_t Math_mul(int64_t x, int64_t y);
```

For classes, structs, and actors, the first parameter is `void* pony_this` — the Pony object the method is called on:

```c
extern int64_t Adder_add(void* pony_this, int64_t x);
extern int64_t Adder_base(void* pony_this);
```

## Exporting generic types

A generic type cannot be annotated with `\c_api\` directly — there is no way to determine which type arguments to use. Instead, create a type alias that pins the type parameters to concrete types, and annotate the alias:

```pony
--8<-- "c-ffi-exporting-generic-alias.pony"
```

The alias name becomes the type name in the C symbols:

```c
extern int64_t BoxedI64_get(void* pony_this);
extern int64_t BoxedI64_add(void* pony_this, int64_t y);
```

The alias must refer to a single concrete type — not a union or intersection — and the underlying type must be a class, primitive, struct, or actor.

## Type mapping

Pony types map to C types in the generated header as follows:

| Pony type | C type |
|---|---|
| `Bool` | `bool` |
| `I8` / `I16` / `I32` / `I64` | `int8_t` / `int16_t` / `int32_t` / `int64_t` |
| `I128` | `__int128_t` |
| `ILong` | `long` |
| `ISize` | `intptr_t` |
| `U8` / `U16` / `U32` / `U64` | `uint8_t` / `uint16_t` / `uint32_t` / `uint64_t` |
| `U128` | `__uint128_t` |
| `ULong` | `unsigned long` |
| `USize` | `size_t` |
| `F32` / `F64` | `float` / `double` |
| `None` | `void` |
| Any other type | `void*` |

## Working with C shims

The generated header is written to the output directory before C shim files are compiled. Packages that `use` a package with exports get `-I<output-dir>` added to their C compiler flags automatically, so `#include "mylib_export.h"` resolves without any `cincludedir:` configuration.

A typical pattern: a Pony library package annotates its types with `\c_api\`, and a consuming package provides a C shim that includes the generated header, defines C functions, and calls the exported wrappers. The Pony code in the consuming package then calls those C functions via FFI.
2 changes: 1 addition & 1 deletion docs/c-ffi/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

Pony supports integration with other native languages through the Foreign Function Interface (FFI). The FFI library provides a stable and portable API and high-level programming interface allowing Pony to integrate with native libraries easily.

Native code can reach your program two ways: you can link a library, or you can write a small piece of C yourself and let ponyc compile it for you as a [C shim](/c-ffi/c-shims.md).
Native code can reach your program two ways: you can link a library, or you can write a small piece of C yourself and let ponyc compile it for you as a [C shim](/c-ffi/c-shims.md). You can also go the other direction — [export Pony methods](/c-ffi/exporting.md) so that C code can call them.

Note that calling C (or other low-level languages) is inherently dangerous. C code fundamentally has access to all memory in the process and can change any of it, either deliberately or due to bugs. This is one of the language's most useful, but also most dangerous, features. Calling well written, bug-free, C code will have no ill effects on your program. However, calling buggy or malicious C code or calling C incorrectly can cause your Pony program to go wrong, including corrupting data and crashing. Consequently, all of the Pony guarantees regarding not crashing, memory safety and concurrent correctness can be voided by calling FFI functions.
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ plugins:
- c-ffi/calling-c.md
- c-ffi/linking-c.md
- c-ffi/c-shims.md
- c-ffi/exporting.md
- c-ffi/callbacks.md
Runtime Basics:
- runtime-basics/index.md
Expand Down Expand Up @@ -245,6 +246,7 @@ nav:
- Calling C from Pony: "c-ffi/calling-c.md"
- Linking to C Libraries: "c-ffi/linking-c.md"
- C Shims: "c-ffi/c-shims.md"
- Exporting Pony Methods to C: "c-ffi/exporting.md"
- Callbacks: "c-ffi/callbacks.md"
- Runtime Basics:
- Overview: "runtime-basics/index.md"
Expand Down
Loading