When to Use the `gdsync` Attribute in gdext-nim for Godot Method Registration
The {.gdsync.} pragma macro registers Nim procedures, methods, classes, and signals with the Godot engine, making them callable from GDScript, C#, and the editor.
The gdsync attribute serves as the primary bridge between Nim and Godot in the godot-nim/gdext-nim extension. When applied to method definitions, this pragma instructs the code generator to create the necessary binding code that exposes your Nim code to Godot's scripting API and reflection system.
What the gdsync Attribute Does
{.gdsync.} is a compile-time macro that processes annotated definitions and generates registration code for the Godot engine. According to the source code in src/gdext/bridge.nim (lines 21‑28), the pragma supports classes, procedures, methods, signals, and virtual functions.
Class Registration
When applied to type definitions, {.gdsync.} registers the Nim type as a full Godot class. This allows instantiation from GDScript and visibility within the Godot editor's inspector. The macro description in bridge.nim handles the base registration that makes your custom types appear as native Godot classes.
Method and Virtual Method Binding
For procedures and methods, the attribute triggers two distinct code paths depending on the method type. The sync_methodDef macro in src/gdext/private/userclass/procs.nim (lines 97‑124) processes ordinary methods, while sync_virtualDef in src/gdext/private/userclass/virtuals.nim (lines 62‑71) handles virtual method overrides. These macros generate the boilerplate that translates between Godot's calling conventions and Nim's execution context.
Signal Registration
When combined with the {.signal.} pragma, {.gdsync.} registers a function as a Godot signal. As documented in src/gdext/bridge.nim (lines 39‑44), signal procedures must return the Error type. The attribute ensures the signal appears in Godot's signal connection dialog and can be emitted to other scripts.
When to Use gdsync on Method Definitions
Apply the gdsync attribute whenever the method must appear in Godot's reflection system. Omit it for purely internal Nim code.
-
Public API methods – Add
{.gdsync.}to gameplay logic, utility functions, or any procedure that GDScript or C# scripts need to invoke. This makes the method part of the Godot class interface. -
Virtual method overrides – Use
{.gdsync.}when overriding Godot-provided virtuals like_process,_physics_process, or custom virtuals defined in base classes. The attribute registers the override so the engine can call it at the appropriate time via thesync_virtualDefimplementation. -
Signal emitters – Apply both
{.gdsync.}and{.signal.}to procedures that returnErrorand need to be connectable from other scripts. -
RPC-enabled methods – Combine
{.gdsync.}with{.rpc.}for networked functions. This allows remote invocation while maintaining registration as a normal method. -
Internal helpers – Do not add
{.gdsync.}to private utility functions, forward declarations, or implementation details. The macro emits warnings when applied to internal methods and ignores them, as the engine never needs visibility into these procedures.
Practical Code Examples
Exporting a Public Method
This example from src/gdext/wizard/subcommands/extension/template/src/classes/gdmyclass.nim (lines 12‑14) shows a basic exported method:
type MyClass* {.gdsync.} = ptr object of Node
proc hello(self: MyClass; name: String): String {.gdsync.} =
## This method can be called from GDScript:
## var result = my_class.hello("world")
"Hello, " & name
Overriding Virtual Methods
Virtual methods require {.gdsync.} so Godot can dispatch engine callbacks to your Nim implementation. From testproject/runtime/nim/src/classes/gdvirtualnode01.nim (line 7):
type VirtualNode01* {.gdsync.} = ptr object of Node
method _process(self: VirtualNode01; delta: float) {.gdsync.} =
## Runs every physics frame in Godot.
self.rotate_y(delta)
Defining Signals
Signals must return Error and use both gdsync and signal pragmas. From gdmyclass.nim (lines 22‑23):
proc mySignal(self: MyClass): Error {.gdsync, signal.} =
## Emitted when something important happens.
ok
RPC-Enabled Methods
Combine gdsync with rpc to expose networked functions. From testproject/runtime/nim/src/classes/gdfunctiontester.nim (line 60):
proc syncValue(self: MyClass; v: int) {.gdsync, rpc(callLocal = true).} =
## Called remotely, but also registered as a normal method.
self.set_value(v)
Internal Helper Methods
Keep internal utilities free of the pragma to avoid unnecessary registration overhead:
proc helper(self: MyClass; x: int): int =
## Private helper; not exposed to Godot.
x * 2
How gdsync Works Under the Hood
The implementation relies on compile-time macro expansion defined across several core files. In src/gdext/private/userclass/procs.nim, the sync_methodDef macro (lines 97‑124) generates the binding code that wraps Nim procedures for Godot's method system. For virtual methods, src/gdext/private/userclass/virtuals.nim contains sync_virtualDef (lines 62‑71), which creates the virtual table entries Godot uses to dispatch calls to _process, _ready, and other lifecycle methods.
The central definition in src/gdext/bridge.nim coordinates these behaviors, accepting modifier pragmas like {.base.}, {.singleton.}, {.tool.}, and {.icon.} alongside {.gdsync.} to fine-tune registration parameters.
Summary
{.gdsync.}registers Nim definitions with Godot's engine, making them visible to GDScript and the editor.- Apply it to public API methods, virtual overrides, signals, and RPC functions that need engine visibility.
- Omit it for private helpers and internal implementation details to avoid compiler warnings and unnecessary binding generation.
- The pragma triggers
sync_methodDeffor regular procedures andsync_virtualDeffor virtual method overrides during compilation. - Combine with
{.signal.},{.rpc.}, or other modifiers to extend functionality while maintaining Godot interoperability.
Frequently Asked Questions
What happens if I forget to add gdsync to a public method?
The method compiles as normal Nim code but remains invisible to Godot. GDScript cannot call it, it won't appear in the editor's autocomplete, and the engine's reflection system won't recognize it as part of your class interface. You must add {.gdsync.} to generate the necessary binding code in sync_methodDef.
Can I combine gdsync with other pragmas?
Yes. The bridge.nim implementation supports combining {.gdsync.} with modifiers like {.signal.}, {.rpc.}, {.tool.}, {.singleton.}, and {.icon.}. These combinations fine-tune how the item appears and behaves within the Godot editor and runtime.
Does gdsync affect performance?
The pragma itself adds minimal runtime overhead since it operates at compile time to generate binding code. However, methods registered with {.gdsync.} participate in Godot's virtual dispatch system, which involves slight indirection compared to direct Nim procedure calls. For hot paths, keep critical internal logic in {.gdsync.}-free helper functions.
How do I expose virtual methods like _process or _ready?
Define them as Nim methods with {.gdsync.} and the appropriate signature. The sync_virtualDef macro in virtuals.nim registers these with Godot's virtual table, allowing the engine to call your implementation during the scene tree update cycles. Without the pragma, Godot cannot find your override and will use the base class implementation instead.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →