# How the gdext-nim Internal Bridge Communicates Between Nim and Godot’s C++ API

> Understand how the gdext-nim internal bridge connects Nim and Godot's C++ API. Discover its thin C-level translation layer and callbackTable for seamless integration.

- Repository: [godot-nim 4+/gdext-nim](https://github.com/godot-nim/gdext-nim)
- Tags: internals
- Published: 2026-03-02

---

**The gdext-nim internal bridge communicates between Nim and Godot’s C++ API by implementing a thin C-level translation layer that converts Nim objects and methods into the callback functions expected by Godot’s GDExtension interface, using a global `callbackTable` to map Godot `StringName` references to Nim implementations.**

The `godot-nim/gdext-nim` repository provides the infrastructure for writing Godot engine extensions in Nim. This internal bridge handles the complex interoperability between Nim’s garbage-collected runtime and Godot’s C++ object system, enabling developers to write game logic in Nim while maintaining full compatibility with Godot’s API expectations.

## Core Components of the Internal Bridge

The bridge architecture consists of four specialized modules that handle different aspects of the Nim-to-Godot translation.

### internalbridge.nim: Callback Implementation

Located at `src/gdext/private/internalbridge.nim`, this file supplies the concrete callback functions that Godot invokes directly. It implements generic callback procs including `set_func[T]`, `get_func[T]`, `notification_func[T]`, `create_instance_func[T]`, `free_instance_func[T]`, and `recreate_instance_func[T]`.

The `creationInfo` proc (lines 24–65) constructs a `ClassCreationInfo5` struct containing pointers to these callbacks. When `register MyNode` is called from user code, this struct is passed to `ClassDB.registerExtensionClass`, telling Godot exactly which functions to invoke when manipulating Nim objects.

### gdinterface.nim: C API Wrappers

The `src/gdext/private/gdinterface.nim` file wraps Godot’s raw C API (`interface_…` functions) in Nim procs marked with the `{.gdcall.}` pragma. This pragma ensures proper calling conventions when passing Nim functions as plain C function pointers to Godot.

This module also maintains the global `callbackTable` hash table, which maps a Godot `StringName` (the class identifier) to an `InstanceBindingCallbacks` record. This table enables the bridge to locate the correct Nim method tables when Godot requests property access or virtual method execution.

### bridge.nim: Public Macro Interface

`src/gdext/bridge.nim` provides the developer-facing macros `gdsync`, `gdexport`, and `register`. These macros generate the metadata required by the internal bridge and orchestrate the registration process.

When you apply `{.gdsync.}` to a type or method, the macro generates the necessary metadata and eventually invokes `internalbridge.creationInfo` to establish the C-level bindings.

### objectcallbacks.nim: Property Access Delegation

Located at `src/gdext/objectcallbacks.nim`, this module contains the helper routines that actually read and write properties. When Godot triggers `set_func[T]` or `get_func[T]` from `internalbridge.nim`, these callbacks delegate to `objectcallbacks.set` or `objectcallbacks.get`, which in turn invoke the Nim getter/setter procs generated by the `gdexport` macro.

## Object Lifecycle and Communication Flow

The internal bridge manages the complete lifecycle of Nim objects within Godot’s engine, from initial registration through destruction.

### Class Registration Process

When user code executes `register MyNode`, the following sequence occurs:

1. The `register` macro calls `internalbridge.creationInfo` to prepare the binding metadata
2. `creationInfo` instantiates a `ClassCreationInfo5` record with function pointers to the generic callbacks specialized for `MyNode`
3. The bridge invokes `ClassDB.registerExtensionClass` (wrapped in `gdinterface.nim`) to notify Godot’s C++ core about the new class
4. Godot stores these pointers and associates them with the `StringName` "MyNode"

### Instance Creation and Destruction

The `create_instance_func[T]` callback (defined in `internalbridge.nim`) handles object instantiation:

- It allocates the Nim object using Nim’s memory manager
- Calls `instantiate_internal[T]` (lines 24–33) to create the underlying Godot engine object
- Stores a reference to the Nim instance via `ObjectPtr.setInstanceBinding`, linking the C++ object to its Nim wrapper

When Godot destroys an object, `free_instance_func[T]` destroys the Nim instance, executes any user-defined `onDestroy` logic, and frees the memory.

### Property Access Mechanisms

When Godot needs to read or write a property on a Nim object:

1. Godot invokes the exported `set_func[T]` or `get_func[T]` callbacks stored in the `ClassCreationInfo5` struct
2. These wrappers delegate to `objectcallbacks.set` or `objectcallbacks.get`
3. The objectcallbacks module calls the actual Nim getter/setter procs generated by `gdexport` (see `internalbridge.nim` lines 96–108 for the registration logic)

### Virtual Methods and Notifications

The bridge handles Godot’s notification system through `notification_func[T]`, which forwards Godot notification IDs (such as `NotificationReady`) to the corresponding Nim `notification` method.

For virtual method overrides, the optional `get_virtual_func` callback (lines 103–108 in `internalbridge.nim`) provides the memory address of a Nim-implemented virtual method when Godot queries for it during runtime.

## Practical Implementation Examples

### Exposing a Nim Class to Godot

```nim
import gdext

type
  MyNode* {.gdsync.} = ptr object of Node
    counter: Int

proc _ready(self: MyNode) {.gdsync.} =
  print "MyNode ready!"

proc get_counter(self: MyNode): Int {.gdsync.} =
  result = self.counter

proc set_counter(self: MyNode; v: Int) {.gdsync.} =
  self.counter = v

gdexport "counter", MyNode, Int, get_counter, set_counter
register MyNode

```

Under the hood, `gdsync` registers `MyNode` as a Godot class, `gdexport` creates the property binding via `gdexport_internal`, and `register` triggers `creationInfo` to populate the `ClassCreationInfo5` struct with pointers to `set_func[MyNode]` and `get_func[MyNode]`.

### Implementing Virtual Methods

```nim
type
  MySprite* {.gdsync, base.} = ptr object of Sprite

proc _process(self: MySprite; delta: Float) {.gdsync, base.} =
  self.rotation += delta

register MySprite

```

The `base` pragma marks this as a virtual method override. The bridge stores this in the metadata, and when Godot queries for the virtual method through `get_virtual_func`, the internal bridge returns the Nim function pointer.

### Manual Object Instantiation

```nim
let myNode = instantiate_internal(MyNode)
myNode.set_name "MyNodeFromNim".newStringNameInternal
Engine.get_singleton("SceneTree").add_child(myNode)

```

The `instantiate_internal` proc creates the Godot engine object, binds the Nim instance using `setInstanceBinding`, and registers the necessary lifecycle callbacks.

## Summary

- The **internal bridge** in `src/gdext/private/internalbridge.nim` implements the C-level callbacks Godot expects, including `set_func`, `get_func`, and `create_instance_func`
- **gdinterface.nim** wraps Godot’s C API using the `{.gdcall.}` pragma and maintains the global `callbackTable` mapping `StringName` references to Nim bindings
- The **bridge.nim** module provides ergonomic macros (`gdsync`, `gdexport`, `register`) that generate metadata and trigger the low-level registration process
- Object lifecycle management uses `instantiate_internal` to bind Nim instances to Godot objects and `free_instance_func` to handle cleanup
- Property access flows from Godot’s C++ core through the callback table to `objectcallbacks.nim`, which invokes the actual Nim getter/setter procs

## Frequently Asked Questions

### What is the performance overhead of the gdext-nim internal bridge?

The overhead is minimal because the bridge uses thin C-level wrappers rather than complex marshalling layers. Once registered through `ClassCreationInfo5`, Godot calls the Nim callbacks directly as function pointers without intermediate translation layers. The primary cost occurs during initial class registration and object instantiation, not during steady-state property access or method calls.

### How does the internal bridge handle memory management between Nim and Godot?

The bridge implements a bidirectional ownership model. When `create_instance_func` instantiates a Nim object, it stores a reference via `ObjectPtr.setInstanceBinding`, ensuring the Nim garbage collector maintains the object while Godot holds a pointer. Conversely, `free_instance_func` ensures that when Godot destroys the C++ object, the corresponding Nim instance is properly deallocated and any user-defined `onDestroy` callbacks execute.

### Can I use the internal bridge without the `gdsync` macro?

While possible, manual registration requires directly populating `ClassCreationInfo5` structs and maintaining the `callbackTable` entries yourself. The `gdsync` macro in `bridge.nim` automates the generation of method signatures and the invocation of `internalbridge.creationInfo`. Without it, you would need to manually implement `set_func`, `get_func`, and lifecycle callbacks for each class in `internalbridge.nim`.

### How does the bridge map Godot virtual method calls to Nim implementations?

When Godot requests a virtual method pointer, the `get_virtual_func` callback (lines 103–108 in `internalbridge.nim`) queries the `callbackTable` using the class `StringName`. It returns the address of the corresponding Nim proc marked with the `base` pragma. This allows Nim methods to override Godot’s virtual functions like `_process` or `_physics_process` while maintaining the C++ vtable compatibility Godot requires.