Files
sindri/docs/odin.md
T
2026-09-28 20:50:59 +02:00

94 lines
3.7 KiB
Markdown

# Odin
This document contains specifics about the workings of the Odin programming
language, dynamic linking and state management design patterns. These are
tested and validated findings, for continued reference by me.
## Compiler
Odin's compiler is demand-driver: a procedure's body is only semantically
analyzed when the proc is actually referenced. They are syntactically checked
always.
## Dynamic Library Memory
When a program loads in a dynamic library, what happens with package globals?
Findings (macOS):
- Package globals are shared between modules, but only indirectly
- This is due to the dynamic linker merging symbols into a (single) flat namespace
- Practically this means the lib's unset package globals get shadowed by the hosts'
- After hot reloading the lib, the memory of the packge is not refreshed,
it keeps pointing to the package global of the host
Findings (Windows):
- Package globals are not shared between modules, they have their own copy
Findings (Linux):
- Package globals are not shared between modules, they have their own copy
Conclusion: avoid package globals, or pass pointers to the dynamic library so
it can match its package globals with the host module.
## Dependency Patterns
1. Strict layering, only call downwards. If two modules need each other, one of
the modules is in a wrong layer. Move it, or extract the shared part into a
lower layer.
2. Dependency inversion: if required, define an interface in the lower layer,
upper layer registers itself.
3. Dependency Injection: Prevent dependencies via data, over includes. Pass what
a function needs as arguments instead of reaching another module's state.
4. Events instead of callbacks both ways If A and B would call each other,
have A push events into a queue and B consume them. Neither module includes
the other; they only share the event type (which lives in a lower layer).
5. Merge or split as a last resort, if two modules genuinely can't be untangled.
Sindri Engine:
```
+---------------------+ Game:
| Game | - Game
+---------------------+
|
v
+---------------------+ +---------------------+ Core:
| Core |<----+----| main | - Input
+---------------------+ | +---------------------+
| |
v |
+---------------------+ | Platform:
| Platform |<----+ - GLFW
+---------------------+ | - wgpu
| |
v |
+---------------------+ | Base:
| Base |<----+ - Hot Reload
+---------------------+ - Settings
- Event
- Keycodes
```
## Logging strategy
From the docs:
> While it is ok for simple apps to use the core:fmt package, libraries and
> complex apps should prefer the core:log package. By using the implicit logger
> library and application authors allow the caller to decide how to process log
> messages.
## Naming Conventions
From core.odin:
> In general, Ada_Case for types and snake_case for values
>
> - Package Name: snake_case (but prefer single word)
> - Import Name: snake_case (but prefer single word)
> - Types: Ada_Case
> - Enum Values: Ada_Case
> - Procedures: snake_case
> - Local Variables: snake_case
> - Constant Variables: SCREAMING_SNAKE_CASE