mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-08-01 16:28:36 -04:00
152 lines
6.1 KiB
Markdown
152 lines
6.1 KiB
Markdown
# Classes, structs and members
|
|
|
|
## Declaring classes
|
|
|
|
```
|
|
native class IPhysicsBody as NativeEngine.IPhysicsBody
|
|
{
|
|
void SetMass( float f );
|
|
float GetMass();
|
|
}
|
|
```
|
|
|
|
Every class declaration starts with `native` or `managed` - which side *implements* it:
|
|
|
|
- **`native class`** - implemented in C++, imported into managed. The generator emits a C# wrapper
|
|
struct holding the native pointer, with a method per function.
|
|
- **`managed class`** - implemented in C#, exported to native. The generator emits a C++ class
|
|
(in the native header) whose methods call into managed, and `[UnmanagedCallersOnly]` thunks on
|
|
the managed side. Instances are addressed by an object id resolved via `Sandbox.InteropSystem`.
|
|
|
|
The forms:
|
|
|
|
| Form | Meaning |
|
|
|------|---------|
|
|
| `native class X` | Instance class - wrapper carries a `self` pointer. |
|
|
| `native static class X` | No instances; members are called as `X::Member()`. A static class whose native name starts with `global` binds free functions at global scope (`::Member()`). |
|
|
| `native accessor g_pThing` | Like a static class, but members are reached through a global pointer: `g_pThing->Member()`. The generated thunk asserts the pointer isn't null. |
|
|
| `managed class X` / `managed static class X` | Same split, implemented in C#. |
|
|
|
|
### Names and aliasing
|
|
|
|
`as` gives the type a different name on the other side:
|
|
|
|
```
|
|
native class CVideoPlayer as NativeEngine.CVideoPlayer
|
|
managed static class Sandbox.Engine.Bootstrap
|
|
```
|
|
|
|
The first name is the implementing side's name, the `as` name is the other side's. Namespaces use
|
|
`.` (or `::` for native) and are split off automatically. Without `as`, both sides use the same
|
|
name.
|
|
|
|
### Base classes
|
|
|
|
```
|
|
native class CSceneObject as Sandbox.SceneObject
|
|
native class CSceneModel as Sandbox.SceneModel : Sandbox.SceneObject
|
|
```
|
|
|
|
`: Base` references another class *in the same def*. The derived wrapper inherits all of the base's
|
|
non-static functions and gets implicit/explicit conversion operators to and from every base. The
|
|
conversions go through native `dynamic_cast`, because with multiple inheritance base pointers can
|
|
differ from derived pointers.
|
|
|
|
## Functions
|
|
|
|
```
|
|
bool Play( string pUrl, string pExt );
|
|
static CVideoPlayer Create( Sandbox.VideoPlayer managedObject ); [new]
|
|
void Destroy(); [delete]
|
|
int GetMemRequired( int width, int height ) const;
|
|
```
|
|
|
|
- `static` members don't take a `self`/instance argument.
|
|
- A trailing `const` is accepted (and required to match a const native method on managed classes).
|
|
- Overloads are fine - mangled export names are de-duplicated automatically.
|
|
- A function named `GetType` is emitted as `GetType_Native` in C# (to avoid `object.GetType`).
|
|
|
|
**Specials** after the semicolon:
|
|
|
|
| Special | Meaning |
|
|
|---------|---------|
|
|
| `[new]` | The thunk runs `return new NativeType( args );` instead of calling a method. |
|
|
| `[delete]` | The thunk runs `delete (NativeType*)self;`. On the managed side the wrapper also nulls its own pointer afterwards. |
|
|
|
|
### Inline functions
|
|
|
|
A function starting with `inline` carries its native body in the def file, instead of binding to an
|
|
existing native method:
|
|
|
|
```
|
|
inline void SetBoneName( string boneName )
|
|
{
|
|
self->m_boneName = boneName;
|
|
self->m_pBoneName = self->m_boneName.String();
|
|
}
|
|
```
|
|
|
|
The body is emitted into the generated .cpp. Parameters arrive already converted to their native
|
|
types under their declared names (`self` is the typed instance pointer; `this->` is rewritten to
|
|
`self->`). `return` expressions are marshalled like a normal return value.
|
|
|
|
## Variables
|
|
|
|
A field declaration generates a get/set thunk pair and a C# property:
|
|
|
|
```
|
|
native class SheetSequence_t
|
|
{
|
|
uint m_nId;
|
|
bool m_bClamp;
|
|
float m_flTotalTime;
|
|
}
|
|
```
|
|
|
|
## Structs, enums and pointer handles
|
|
|
|
Value types that cross the boundary are declared at file level:
|
|
|
|
```
|
|
native struct MeshTraceInput
|
|
native enum ButtonCode_t is NativeEngine.ButtonCode;
|
|
native pointer SwapChainHandle_t;
|
|
```
|
|
|
|
- **`struct`** - both sides must define the type with an identical layout. Sizes are verified at
|
|
startup (`structSizes` exchange); a mismatch is a fatal error. Structs 8 bytes or larger are
|
|
passed by pointer when used as parameters; smaller structs must be marked `[small]` and are
|
|
passed by value.
|
|
- **`enum`** - passed as a 64-bit integer and cast on both sides.
|
|
- **`pointer`** - a native type that pretends not to be a pointer (`DECLARE_POINTER_HANDLE`).
|
|
Managed treats it as an opaque `IntPtr`-sized struct.
|
|
|
|
`as` / `is` (synonyms) alias the managed name, same as for classes.
|
|
|
|
## Attributes
|
|
|
|
Attributes are written on the line(s) before a declaration and apply to the next class, struct or
|
|
function parsed.
|
|
|
|
### On functions
|
|
|
|
| Attribute | Meaning |
|
|
|-----------|---------|
|
|
| `[nogc]` | Emits `[SuppressGCTransition]` - the call skips the GC transition, so it must be short, non-blocking and never call back into managed. On a class, applies to all of its functions. |
|
|
| `[callback]` | Marks a function that can re-enter managed. Cancels `[nogc]` inherited from the class. |
|
|
|
|
### On classes
|
|
|
|
| Attribute | Meaning |
|
|
|-----------|---------|
|
|
| `[Handle:Managed.Type]` | Instances cross the boundary as handle ids instead of raw pointers. Managed resolves them with `Sandbox.HandleIndex.Get<T>`, native with `GetManagedHandle`. |
|
|
| `[ResourceHandle:HModel]` | The class is accessed through a strong resource handle (`HModelStrong`). Thunks dereference the handle (returning default if unloaded), and the class automatically gains handle-management functions: `DestroyStrongHandle`, `IsStrongHandleValid`, `IsError`, `IsStrongHandleLoaded`, `CopyStrongHandle`, `GetBindingPtr`. |
|
|
| `[SharedDataPointer]` | For Qt-style classes that are just a smart pointer to shared data. The wrapper is a public class (not a struct) that copies by value across the boundary and queues a `Dispose` from its finalizer. |
|
|
| `[WindowsOnly]` | When generating on a non-Windows platform, the native thunks are emitted as stubs returning default values. |
|
|
|
|
### On structs
|
|
|
|
| Attribute | Meaning |
|
|
|-----------|---------|
|
|
| `[small]` | The struct is smaller than a pointer (8 bytes) and is passed by value. Required - the generated code `static_assert`s that unmarked structs are at least 8 bytes. |
|