Export Functions
Guide to export functions from your plugin to be used by other language modules within Plugify.
In the Plugify ecosystem, C# plugins can export functions to make them accessible to other plugins. This guide explains how to define and export functions in C# and provides examples to help you integrate your plugins seamlessly.
Basic Type Mapping
The following table lists how types are exposed to the C# API:
| C++ Type | C# Type | Plugify Alias | Ref Support ? |
|---|---|---|---|
| void | void | void | ❌ |
| bool | Bool8 | bool | ✅ |
| char | Char8 | char8 | ✅ |
| char16_t | Char16 | char16 | ✅ |
| int8_t | sbyte | int8 | ✅ |
| int16_t | short | int16 | ✅ |
| int32_t | int | int32 | ✅ |
| int64_t | long | int64 | ✅ |
| uint8_t | byte | uint8 | ✅ |
| uint16_t | ushort | uint16 | ✅ |
| uint32_t | uint | uint32 | ✅ |
| uint64_t | ulong | uint64 | ✅ |
| uintptr_t | nint | ptr64 | ✅ |
| uintptr_t | nint | ptr32 | ✅ |
| float | float | float | ✅ |
| double | double | double | ✅ |
| void* | Delegate | function | ❌ |
| plg::string | string | string | ✅ |
| plg::any | object | any | ✅ |
| plg::vector<bool> | Bool8[] | bool[] | ✅ |
| plg::vector<char> | Char8[] | char8[] | ✅ |
| plg::vector<char16_t> | Char16[] | char16[] | ✅ |
| plg::vector<int8_t> | sbyte[] | int8[] | ✅ |
| plg::vector<int16_t> | short[] | int16[] | ✅ |
| plg::vector<int32_t> | int[] | int32[] | ✅ |
| plg::vector<int64_t> | long[] | int64[] | ✅ |
| plg::vector<uint8_t> | byte[] | uint8[] | ✅ |
| plg::vector<uint16_t> | ushort[] | uint16[] | ✅ |
| plg::vector<uint32_t> | uint[] | uint32[] | ✅ |
| plg::vector<uint64_t> | ulong[] | uint64[] | ✅ |
| plg::vector<uintptr_t> | nint[] | ptr64[] | ✅ |
| plg::vector<uintptr_t> | nint[] | ptr32[] | ✅ |
| plg::vector<float> | float[] | float[] | ✅ |
| plg::vector<double> | double[] | double[] | ✅ |
| plg::vector<plg::string> | string[] | string[] | ✅ |
| plg::vector<plg::any> | object[] | any[] | ✅ |
| plg::vector<plg::vec2> | Vector2[] | vec2[] | ✅ |
| plg::vector<plg::vec3> | Vector3[] | vec3[] | ✅ |
| plg::vector<plg::vec4> | Vector4[] | vec4[] | ✅ |
| plg::vector<plg::mat4x4> | Matrix4x4[] | mat4x4[] | ✅ |
| plg::vec2 | Vector2 | vec2 | ✅ |
| plg::vec3 | Vector3 | vec3 | ✅ |
| plg::vec4 | Vector4 | vec4 | ✅ |
| plg::mat4x4 | Matrix4x4 | mat4x4 | ✅ |
Exporting Functions in C#
Exporting functions in C# is straightforward because C# is a statically-typed language. You need to define the function and specify it in the plugin manifest. Plugify's C# Language Module handles the rest.
Basic Example
Here’s a simple example of exporting a function in a C# plugin:
Function Definition
Plugin Manifest
To export the function, describe it in the plugin manifest under the methods section:
Advanced Example: Exporting Complex Functions
Here’s an example of exporting a function with complex parameter and return types:
Function Definition
Plugin Manifest
Exporting Functions with References
Plugify supports reference parameters (also known as "out" or "ref" parameters) that allow functions to modify values and return them to the caller. In C#, reference parameters are declared using the ref keyword.
Function Definition with Reference Parameters
Plugin Manifest with Reference Parameters
In the manifest, mark parameters that are passed by reference using "ref": true:
Reference Parameter Support
Reference parameters work with most Plugify types as shown in the "Ref Support" column of the type mapping table. The following types do not support references:
void(cannot be passed by reference)function(callback/delegate types)
All other types including primitives, strings, arrays, and structs support reference parameters.
Handling Callbacks
Plugify allows you to export functions that accept callbacks as parameters. Here’s an example:
Function Definition
Plugin Manifest
Automating Manifest Generation
New: You can now automate the generation of the methods section in your plugin manifest using the Plugify.Generators Roslyn source generator!
Instead of manually writing the manifest JSON for each exported function, you can use the Plugify.Generators NuGet package to automatically generate manifest entries for functions marked with the [NativeExport] attribute.
Benefits of Automated Generation
- No Manual JSON Writing: Function signatures are automatically extracted from your code
- Complete Type Information: Parameter types and return types are automatically mapped
- Reduced Errors: Eliminates typos and type mismatches between code and manifest
- Easy Maintenance: Changes to function signatures are automatically reflected in the manifest
- Build-Time Generation: Manifest is generated during compilation, keeping it always in sync
Setup: Installing Plugify.Generators
Add the Plugify.Generators NuGet package to your C# plugin project:
Or add it directly to your .csproj file:
The Plugify package provides the [NativeExport] attribute, while Plugify.Generators is the Roslyn source generator that processes these attributes during compilation. Without Plugify.Generators, no manifest is written.
The generator fills the manifest header from the project properties: name from <AssemblyName> (use snake_case), version from <Version>, description from <Description>, author from <Company>, website from <Product> and license from <Copyright>. Set them all, otherwise the manifest does not pass validation.
Using the NativeExport Attribute
Mark your exported functions with the [NativeExport("FunctionName")] attribute:
How It Works
- During Compilation: The Roslyn source generator scans your code for methods marked with
[NativeExport] - Type Mapping: It automatically maps C# types to Plugify types (e.g.,
int→int32,string[]→string[]) - Manifest Generation: A complete manifest
<AssemblyName>.ppluginis written next to the intermediate assembly, inobj/<Configuration>/<TargetFramework>/. Exported methods go tomethods, delegate types toprototypes - Integration: Copy the manifest next to your plugin's
.dlland add what the generator does not know, such asdependencies
Generated Output
With the project properties shown above, the build writes obj/Release/net10.0/example_csharp_plugin.pplugin:
Copy this file next to example_csharp_plugin.dll in your plugin folder.
Advanced: Working with Delegates
The generator also supports delegate types for callback parameters:
The generator describes the delegate in prototypes and references it by name from the parameter:
Note: The source generator uses reflection and Roslyn analyzers to extract type information at compile time. Ensure your delegate types are defined in the same assembly or in referenced assemblies.
Best Practices
- Define Functions Clearly: Ensure your functions are well-documented and easy to understand.
- Follow Type Conventions: Adhere to Plugify's type conventions for parameters and return values.
- Use XML Documentation: Add XML doc comments (
/// <summary>,/// <param>,/// <returns>) for your IDE and readers. The generator does not copy them into the manifest. - Use NativeExport Attribute: When possible, use the
[NativeExport]attribute with Plugify.Generators to automate manifest generation. - Test Thoroughly: Test your exported functions to ensure they work as expected when called by other plugins.
- Keep Manifests Updated: If using automated generation, regenerate the manifest after making changes to function signatures.
Conclusion
Exporting functions in C# plugins is simple and straightforward. By defining your functions and describing them in the plugin manifest, you can create robust and interoperable plugins. With the new Plugify.Generators Roslyn source generator, you can automate manifest generation by simply marking functions with the [NativeExport] attribute, reducing manual effort and keeping your manifest synchronized with your code. For more advanced use cases, such as handling callbacks, use the techniques outlined in this guide.