# How to Create Custom Function Libraries Using ExecutableFunctionLibrary in Ceres

> Learn to create custom function libraries in Ceres using ExecutableFunctionLibrary. Annotate static methods with ExecutableFunction for automatic runtime registration via source generators.

- Repository: [AkiKurisu/ceres](https://github.com/akikurisu/ceres)
- Tags: how-to-guide
- Published: 2026-02-24

---

**To create custom function libraries using ExecutableFunctionLibrary, define a partial class inheriting from ExecutableFunctionLibrary, annotate static methods with [ExecutableFunction], and let the Ceres source generator handle runtime registration automatically.**

The **Ceres** visual scripting system for Unity allows you to expose C# methods as executable nodes in Flow graphs. By creating custom function libraries with `ExecutableFunctionLibrary`, you can extend the node palette with domain-specific utilities while maintaining high-performance function pointer invocation.

## Understanding ExecutableFunctionLibrary Architecture

`ExecutableFunctionLibrary` serves as the abstract base class that bridges static C# methods with Ceres Flow's runtime execution engine. The architecture relies on a compile-time source generation pattern to eliminate reflection overhead at runtime.

### Core Components

The implementation spans three primary components in the **akikurisu/ceres** repository:

- **[`ExecutableFunctionLibrary.cs`](https://github.com/akikurisu/ceres/blob/main/ExecutableFunctionLibrary.cs)** – Located at [`Runtime/Flow/Models/Libraries/ExecutableFunctionLibrary.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/Libraries/ExecutableFunctionLibrary.cs), this abstract base provides the `CollectExecutableFunctions()` method and registration infrastructure.
- **[`ExecutableLibraryGenerator.cs`](https://github.com/akikurisu/ceres/blob/main/ExecutableLibraryGenerator.cs)** – The Roslyn source generator at `Runtime/SourceGenerators/Source~/Ceres.SourceGenerator/Generators/ExecutableLibraryGenerator.cs` that scans for `[ExecutableFunction]` attributes and emits partial class implementations.
- **`ExecutableReflection<T>`** – The runtime lookup table that maps function names to registered delegates, enabling `FlowNode_ExecuteFunction` nodes to resolve and invoke methods efficiently.

## Step-by-Step Implementation Guide

### 1. Declare the Partial Class

Create a **partial** class that inherits from `ExecutableFunctionLibrary`. The `partial` keyword is mandatory because the source generator will produce a companion file implementing `CollectExecutableFunctions()`.

```csharp
using Ceres.Graph.Flow;
using Ceres.Annotations;

[CeresGroup("Gameplay")]
public partial class GameplayFunctionLibrary : ExecutableFunctionLibrary
{
    // Methods will be defined here
}

```

### 2. Annotate Methods with [ExecutableFunction]

Mark any static method you want to expose with the `[ExecutableFunction]` attribute. These methods become available as nodes in the Flow graph editor.

```csharp
[ExecutableFunction]
public static float CalculateDistance(Vector3 a, Vector3 b)
{
    return Vector3.Distance(a, b);
}

```

Optional attribute parameters control execution behavior:
- **`ExecuteInDependency`** – Marks the function as a data retrieval operation that must complete before dependent nodes execute.
- **`IsScriptMethod`** – Indicates the function operates on a target instance.
- **`IsSelfTarget`** – Automatically passes the graph container as the first parameter.

### 3. Source Generation and Runtime Registration

During compilation, the source generator analyzes your partial class and generates a hidden file that:
- Overrides `CollectExecutableFunctions()`
- Calls `RegisterExecutableFunctionPtr<T>(name, paramCount, functionPtr)` for each annotated method
- Registers file-line metadata for debugging

When you instantiate your library (`new GameplayFunctionLibrary()`), the base constructor invokes the generated `CollectExecutableFunctions()` implementation. This registers each function pointer with `ExecutableReflection<TLibrary>.RegisterStaticExecutableFunctionPtr`, as implemented in [`ExecutableFunctionLibrary.cs`](https://github.com/akikurisu/ceres/blob/main/ExecutableFunctionLibrary.cs) lines 33-36.

## Practical Code Example

The following complete example demonstrates advanced configurations including dependency execution, self-targeting methods, and overloaded functions with labels:

```csharp
using Ceres.Graph.Flow;
using Ceres.Graph.Flow.Annotations;
using Ceres.Annotations;
using UnityEngine;

/// <summary>
/// Custom library exposing gameplay utilities to Flow graphs.
/// </summary>
[CeresGroup("Gameplay")]
public partial class GameplayFunctionLibrary : ExecutableFunctionLibrary
{
    // Simple utility – expose Vector3 distance
    [ExecutableFunction]
    public static float Flow_CalculateDistance(Vector3 a, Vector3 b)
    {
        return Vector3.Distance(a, b);
    }

    // Data-retrieval function that must run before dependent nodes
    [ExecutableFunction(ExecuteInDependency = true)]
    public static Vector3 Flow_GetPlayerPosition()
    {
        var player = GameObject.FindGameObjectWithTag("Player");
        return player != null ? player.transform.position : Vector3.zero;
    }

    // Instance-style method on a target GameObject
    [ExecutableFunction(IsScriptMethod = true, IsSelfTarget = true)]
    public static Component Flow_GetComponent(GameObject target, Component dummy)
    {
        // IsSelfTarget automatically passes the graph container as target
        return target.GetComponent(dummy.GetType());
    }

    // Overload example – use CeresLabel to differentiate in the UI
    [ExecutableFunction, CeresLabel("Log Message")]
    public static void Flow_Log(string msg) => Debug.Log(msg);

    [ExecutableFunction, CeresLabel("Log Message with Color")]
    public static void Flow_Log(string msg, Color col) =>
        Debug.Log($"<color=#{ColorUtility.ToHtmlStringRGB(col)}>{msg}</color>");
}

```

The source generator produces an auto-generated partial file that you should not edit manually, following the pattern documented in [`flow_function_library.md`](https://github.com/akikurisu/ceres/blob/main/flow_function_library.md).

## Key Configuration Attributes

Beyond basic exposure, several attributes refine how functions appear and behave in the Flow editor:

- **`[CeresGroup(string)]`** – Applied to the class level to categorize the library in the node palette (e.g., `[CeresGroup("Gameplay")]`).
- **`[CeresLabel(string)]`** – Differentiates overloaded methods by providing distinct display names in the UI.
- **`[ExecutableFunction(ResolveReturn = true)]`** – Configures return value handling for specific execution contexts.

For production libraries, follow the best practices outlined in the official documentation: keep parameter counts at six or fewer, guard against null references, and use descriptive method names prefixed with domain identifiers (e.g., `Flow_` or `Gameplay_`).

## Summary

- Create a **partial** class inheriting from `ExecutableFunctionLibrary` to enable source generator integration.
- Apply **[ExecutableFunction]** to static methods you want to expose as Flow nodes.
- The **Ceres source generator** automatically creates the registration boilerplate by overriding `CollectExecutableFunctions()`.
- At runtime, the library constructor registers function pointers via `ExecutableReflection<T>`, making methods available to `FlowNode_ExecuteFunction` nodes.
- Use **[CeresGroup]** for library categorization and **[CeresLabel]** to handle method overloads gracefully.

## Frequently Asked Questions

### Why must the class be declared as partial?

The **partial** keyword is required because [`ExecutableLibraryGenerator.cs`](https://github.com/akikurisu/ceres/blob/main/ExecutableLibraryGenerator.cs) emits a companion file during compilation that implements the `CollectExecutableFunctions()` method. This generated code registers each `[ExecutableFunction]` method with the runtime system using `RegisterExecutableFunctionPtr`. Without the partial modifier, the compiler cannot merge your handwritten code with the generated registration logic.

### How does the Flow engine resolve which function to execute?

When a `FlowNode_ExecuteFunction` node executes, it queries `ExecutableReflection<TTarget>.GetFunction` using the function name, static/instance flag, and parameter count. This lookup retrieves the function pointer registered during library instantiation, allowing direct invocation without reflection overhead.

### Can I create libraries for instance methods rather than static methods?

While `ExecutableFunctionLibrary` primarily exposes static methods, you can simulate instance behavior using the **`IsScriptMethod`** and **`IsSelfTarget`** parameters. When `IsSelfTarget` is true, the Flow system automatically passes the graph's container GameObject as the first parameter, enabling you to write methods that operate on specific instances while maintaining static signatures for registration.

### Where can I find reference implementations of custom libraries?

The repository includes [`UnityExecutableLibrary.cs`](https://github.com/akikurisu/ceres/blob/main/UnityExecutableLibrary.cs) at [`Runtime/Flow/Models/Libraries/UnityExecutableLibrary.cs`](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/Libraries/UnityExecutableLibrary.cs), which demonstrates best practices for exposing Unity engine utilities. Additionally, the documentation at `Documentation~/docs/flow_function_library.md` provides comprehensive attribute references and troubleshooting guidance for complex scenarios like generic methods or async operations.