# How to allocate delegates with parameters as output pins in a K2Node ?

**URL:** <https://forums.unrealengine.com/t/how-to-allocate-delegates-with-parameters-as-output-pins-in-a-k2node/2129926>\
**Category:** C++\
**Tags:** unreal-engine, editor, custom-nodes, CPP, question\
**Created:** [November 17, 2024, 3:52am UTC](https://forums.unrealengine.com/t/how-to-allocate-delegates-with-parameters-as-output-pins-in-a-k2node/2129926 "2024-11-17T03:52:11Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Psahh](https://d1ap1mz92jnks1.cloudfront.net/user_avatar/forums.unrealengine.com/psahh/32/998276_2.png) [@Psahh](https://forums.unrealengine.com/u/Psahh)\
**Post date:** [November 17, 2024, 3:52am UTC](https://forums.unrealengine.com/t/how-to-allocate-delegates-with-parameters-as-output-pins-in-a-k2node/2129926/1 "2024-11-17T03:52:11Z")

</div>

Hi there, the issue I’m encountering seems pretty simple to solve but I don’t have the necessary knowledge in K2Nodes to find a proper solution.  
I’m simply trying to **connect a delegate** (passing an int) to an **output pin in a K2Node**.

* * *

# 1. Where I am right now

- I’m creating a custom blueprint callable node that replicates a simple for loop. So far, this works fine and looks like that in Unreal:  

- As the node look a bit ugly, I want to map it to a custom K2Node. I’m in this process right now, and it’s where things get messed up for me.

* * *

# 2. The doubts I have

- First off, I’m not so sure if the way I do things when creating my for loop is the right one _-with the delegates-_. Friendly reminder, I’m still fairly new to c++ so be kind please.

- While working on the `ExpandNode()` function in the K2Node, I can’t quite figure out how to properly connect the delegates with their parameters to the output pins and `Index` integer.

* * *

# 3. Some of my code

Here’s how I declare my dynamic delegates with parameters:

```cpp
/** Delegate for the loop body, passing the current index */
DECLARE_DYNAMIC_DELEGATE_OneParam(FLoopBodyDelegate, int32, Index);

/** Delegate for loop completion */
DECLARE_DYNAMIC_DELEGATE(FLoopCompletedDelegate);

/** Delegate for loop cancellation */
DECLARE_DYNAMIC_DELEGATE(FLoopCanceledDelegate);

```

So the two parameters I’m trying to connect are `Index` from the first `OnLoopBody` delegate, and `CanceledAtIndex` from the third delegate `OnTaskCanceled`.

So the `UFUNCTION`, public and private declarations looks like:

```cpp
public:
    UFUNCTION(BlueprintCallable, Category = "Editor Task|Slow Loops")
    static USlowTaskNodes* SlowForLoop(
        int32 StartIndex,
        int32 EndIndex,
        bool bCanCancelTask,
        FLoopBodyDelegate LoopBody,
        FLoopCompletedDelegate OnCompleted,
        FLoopCanceledDelegate OnCanceled);

    virtual void Activate() override;

private:
    int32 StartIndex;
    int32 EndIndex;
    bool bCanCancelTask;
    FLoopBodyDelegate LoopBodyDelegate;
    FLoopCompletedDelegate OnCompletedDelegate;
    FLoopCanceledDelegate OnCanceledDelegate;

```

The loop logic seems to be working fine as is:

```cpp
USlowTaskNodes* USlowTaskNodes::SlowForLoop(
    int32 InStartIndex,
    int32 InEndIndex,
    bool bInCanCancelTask,
    FLoopBodyDelegate InLoopBody,
    FLoopCompletedDelegate InOnCompleted,
    FLoopCanceledDelegate InOnCanceled)
{
    USlowTaskNodes* Node = NewObject<USlowTaskNodes>();
    Node->StartIndex = InStartIndex;
    Node->EndIndex = InEndIndex;
    Node->bCanCancelTask = bInCanCancelTask;
    Node->LoopBodyDelegate = InLoopBody;
    Node->OnCompletedDelegate = InOnCompleted;
    Node->OnCanceledDelegate = InOnCanceled;
    return Node;
}

void USlowTaskNodes::Activate()
{
    // Calculate the total number of iterations
    const int32 TotalIterations = EndIndex - StartIndex + 1;

    // Only create and show the dialog if running in the editor
    bool bShowDialog = GIsEditor;

    // Create a slow task
    FScopedSlowTask SlowTask(TotalIterations, FText::FromString(TEXT("Processing...")), bShowDialog);
    if (bShowDialog)
    {
        SlowTask.MakeDialog(bCanCancelTask); // Show cancel button if allowed
    }

    // Execute the loop
    bool bIsCanceled = false;
    for (int32 Index = StartIndex; Index <= EndIndex; ++Index)
    {
        // Check if the task was canceled (only relevant if bShowDialog is true)
        if (bShowDialog && SlowTask.ShouldCancel())
        {
            bIsCanceled = true;
            break;
        }

        // Update the slow task progress
        SlowTask.EnterProgressFrame(1,
            FText::Format(LOCTEXT("SlowTask", "Processing... {0} / {1}"), FText::AsNumber(Index - StartIndex + 1),
                FText::AsNumber(EndIndex - StartIndex + 1)));

        // Call the loop body delegate
        if (LoopBodyDelegate.IsBound())
        {
            LoopBodyDelegate.Execute(Index);
        }
    }

    // Handle task completion or cancellation
    if (bIsCanceled)
    {
        // Trigger the OnCanceled delegate
        if (OnCanceledDelegate.IsBound())
        {
            OnCanceledDelegate.Execute();
        }
    }
    else
    {
        // Trigger the OnCompleted delegate
        if (OnCompletedDelegate.IsBound())
        {
            OnCompletedDelegate.Execute();
        }
    }

    // Mark as ready for destruction
    SetReadyToDestroy();
}

```

* * *

On the K2Node side, the `AllocateDefaultPins()` function successfully binds everything:

```cpp
void USlowForLoop::AllocateDefaultPins()
{
    const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

    // Execution Input Pin
    CreatePin(EGPD_Input, K2Schema->PC_Exec, FName("Execute"));

    // Input Pins
    CreatePin(EGPD_Input, K2Schema->PC_Int, FName("Start Index"));
    CreatePin(EGPD_Input, K2Schema->PC_Int, FName("End Index"));
    CreatePin(EGPD_Input, K2Schema->PC_Boolean, FName("Can Cancel Task"));

    // Output Pins
    CreatePin(EGPD_Output, K2Schema->PC_Exec, FName("Loop Body"));
    CreatePin(EGPD_Output, K2Schema->PC_Int, FName("Index"));
    CreatePin(EGPD_Output, K2Schema->PC_Exec, FName("Completed"));
    CreatePin(EGPD_Output, K2Schema->PC_Exec, FName("Canceled"));
}

```

Now in the `ExpandNode()` function, things get dirty. I’ve managed to make everything work except the 3 delegates (one having the index parameter to pass _-LoopBody-_).  
That’s where I’m stuck right now:

```cpp
void USlowForLoop::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
    Super::ExpandNode(CompilerContext, SourceGraph);

    const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema();
    if (!Schema)
    {
        CompilerContext.MessageLog.Error(*LOCTEXT("SchemaError", "SlowForLoop: Schema not found. @@").ToString(), this);
        return;
    }

    // Track whether all operations are error-free
    bool bIsErrorFree = true;

    // Retrieve the SlowForLoop function
    UFunction* Function = USlowTaskNodes::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(USlowTaskNodes, SlowForLoop));
    if (!Function)
    {
        CompilerContext.MessageLog.Error(*LOCTEXT("FunctionError", "SlowForLoop: Function 'SlowForLoop' not found in USlowTaskNodes. @@").ToString(), this);
        return;
    }

    // Create CallFunction node
    UK2Node_CallFunction* CallSlowForLoop = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
    CallSlowForLoop->SetFromFunction(Function);
    CallSlowForLoop->AllocateDefaultPins();

    // Move input pins
    bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(TEXT("Start Index")), *CallSlowForLoop->FindPinChecked(TEXT("StartIndex"))).CanSafeConnect();
    bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(TEXT("End Index")), *CallSlowForLoop->FindPinChecked(TEXT("EndIndex"))).CanSafeConnect();
    bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(TEXT("Can Cancel Task")), *CallSlowForLoop->FindPinChecked(TEXT("bCanCancelTask"))).CanSafeConnect();

    // Move execution input pin
    bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(TEXT("Execute")), *CallSlowForLoop->GetExecPin()).CanSafeConnect();

    /////////////////////////////////////////////////////////
    // TODO: Move delegates (LoopBody, OnCompleted, OnCanceled) and Index output pins
    /////////////////////////////////////////////////////////
    
    // If any operation failed, log an error
    if (!bIsErrorFree)
    {
        CompilerContext.MessageLog.Error(*LOCTEXT("InternalConnectionError", "SlowForLoop: Internal connection error occurred while expanding node. @@").ToString(), this);
    }

    // Cleanup
    BreakAllNodeLinks();
}

```

* * *

So if you have ever found a solution for that, or have a logic rework idea, please give your insights, it would be much appreciated!
