Compilation for shipping fails even though using #if WITH_EDITOR

Hi,

we are trying to run some editor only code with Blutility, but the code keeps generating compiler errors, even though it is wrapped in the #if WITH_EDITOR directive. I also tried WITH_EDITORONLY_DATA without success:

Here is the Build.cs:

        if (Target.bBuildEditor)
        {
            PublicDependencyModuleNames.AddRange(new string[] { "UnrealEd" });
            PublicDependencyModuleNames.AddRange(new string[] { "Blutility" });
            PublicDependencyModuleNames.AddRange(new string[] { "UMGEditor" });
        }

Here is the function declaration (the function definition is also contained in the #if WITH_EDITOR makro in the .cpp file):

#if WITH_EDITOR
	UFUNCTION()
	static void OpenEditorUtilityWidget(TSubclassOf<UEditorUtilityWidget> WidgetClass);
#endif

and it keeps generating this error:

Helper.h(170): Error: Unable to find ‘class’ with name ‘UEditorUtilityWidget’

Line 170 being the static void OpenEditorUtilityWidget()… declaration I shared above.

If I remove the code from the .h and .cpp file, the shipping compilation succeeds. Why will the shipping compilation not respect the #if WITH_EDITOR makro? I am at a loss and I cant find the solution online…

Any help would be appreciated!

You need to add the same conditional inside the .cpp wrapping around OpenEditorUtilityWidget().
Right now you’re just removing the declaration, but you need to also remove the implementation.

Hey, thanks for the suggestion! I am doing that already as mentioned in the first post, I just didnt want to spam the thread with too much code. This is what the implementation looks like:

#if WITH_EDITOR
void UHelper::OpenEditorUtilityWidget(TSubclassOf<UEditorUtilityWidget> WidgetClass)
{
	if (!WidgetClass)
	{
		UE_LOG(LogTemp, Error, TEXT("WidgetClass is null"));
		return;
	}

	// Ensure the class is a UEditorUtilityWidgetBlueprint
	UEditorUtilityWidgetBlueprint* WidgetBlueprint = Cast<UEditorUtilityWidgetBlueprint>(WidgetClass->ClassGeneratedBy);

	if (WidgetBlueprint)
	{
		// Spawn the widget from blueprint
		UEditorUtilityWidget* WidgetInstance = CreateWidget<UEditorUtilityWidget>(GEditor->GetEditorWorldContext().World(), WidgetClass);

		if (WidgetInstance)
		{
			WidgetInstance->AddToViewport();
			WidgetInstance->SetVisibility(ESlateVisibility::Visible);
			UE_LOG(LogTemp, Log, TEXT("Widget successfully created and added to viewport"));
		}
		else
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to create widget"));
		}
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to cast WidgetClass to UEditorUtilityWidgetBlueprint"));
	}
}
#endif

Any chance this is related to this class being a BlueprintLibrary?

Have you though maybe about extracting this function lib into a plugin and then limiting it to editor only. This would isolate the functions if you only have editor widget tools in it.

Sorry, I missed that part.
Being a BFL shouldn’t matter… can you share the entire .cpp and .h files?
Though, Raven made a good suggestion too. Putting it in an editor module would be a great idea.

Thanks! I would probably use a separate plugin as a last resort, but it should work like this, no?

Here are the complete files:

Header:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"

#if WITH_EDITOR
#include "Editor/Blutility/Classes/EditorUtilityWidget.h"
#endif

#include "EditorOnlyHelpers.generated.h"

/**
 * 
 */
UCLASS()
class PROJECTNAME_API UEditorOnlyHelpers : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
	
public:

#if WITH_EDITOR
	UFUNCTION(BlueprintCallable)
	static void OpenEditorUtilityWidget(TSubclassOf<UEditorUtilityWidget> WidgetClass);
#endif
};

CPP:

// Fill out your copyright notice in the Description page of Project Settings.


#include "Util/EditorOnlyHelpers.h"

#if WITH_EDITOR
#include "Editor/Blutility/Classes/EditorUtilityWidgetBlueprint.h"
#include "Editor/Blutility/Classes/EditorUtilityWidget.h"
#endif

#if WITH_EDITOR
void UEditorOnlyHelpers::OpenEditorUtilityWidget(TSubclassOf<UEditorUtilityWidget> WidgetClass)
{
	if (!WidgetClass)
	{
		UE_LOG(LogTemp, Error, TEXT("WidgetClass is null or not a subclass of UEditorUtilityWidget"));
		return;
	}

	// Ensure the class is a UEditorUtilityWidgetBlueprint
	UEditorUtilityWidgetBlueprint* WidgetBlueprint = Cast<UEditorUtilityWidgetBlueprint>(WidgetClass->ClassGeneratedBy);

	if (WidgetBlueprint)
	{
		// Spawn the widget from blueprint
		UEditorUtilityWidget* WidgetInstance = CreateWidget<UEditorUtilityWidget>(GEditor->GetEditorWorldContext().World(), WidgetClass);

		if (WidgetInstance)
		{
			WidgetInstance->AddToViewport();
			WidgetInstance->SetVisibility(ESlateVisibility::Visible);
			UE_LOG(LogTemp, Log, TEXT("Widget successfully created and added to viewport"));
		}
		else
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to create widget"));
		}
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to cast WidgetClass to UEditorUtilityWidgetBlueprint"));
	}
}
#endif

I noticed, if I change the function declaration from TSubclassOf<UEditorUtilityWidget> WidgetClass to TSubclassOf<UObject> WidgetClass and do a cast in the code, then it compiles. Is this intended behavior?

Alright, I found your problem- completely flew over my head.
You aren’t allowed to have condition-dependent UFUNCTIONs. Anything going through the reflection layer needs to always be there. So you can’t have UCLASSes, UFUNCTIONs, UPROPERTies, USTRUCTs, or UINTERFACEs that are not always present.

You’ll want to just go with a TSubclassOf<UUserWidget>, and instead of removing the function when without the editor, you need to make it just do nothing. This is often done one of three ways. It’s up to style to decide which method you go with.

The first being the header-heavy approach. You have the UFUNCTION on top, if WITH_EDITOR you declare it normally, otherwise declare it with an empty body.

	UFUNCTION(BlueprintCallable)
#if WITH_EDITOR
	static void OpenEditorUtilityWidget(TSubclassOf<UUserWidget> WidgetClass);
#else
	static void OpenEditorUtilityWidget(TSubclassOf<UUserWidget> WidgetClass) {}
#endif

Another way being a very normal looking header, but with the condition in the cpp.

// Header
	UFUNCTION(BlueprintCallable)
	static void OpenEditorUtilityWidget(TSubclassOf<UUserWidget> WidgetClass);
// CPP
#if WITH_EDITOR
void UEditorOnlyHelpers::OpenEditorUtilityWidget(TSubclassOf<UUserWidget> WidgetClass)
{
	// Normal logic
}
#else
void UEditorOnlyHelpers::OpenEditorUtilityWidget(TSubclassOf<UUserWidget> WidgetClass)
{
	// Empty
}
#endif

You can also put the condition within the function itself to not have two separate implementations.

void UEditorOnlyHelpers::OpenEditorUtilityWidget(TSubclassOf<UUserWidget> WidgetClass)
{
#if WITH_EDITOR
	// Normal logic
#endif
}
1 Like

EditOnly.zip (73.8 KB)

Ok I got it to compile. Extracted you library into a separate module and made it editor only. Moved the lib to the module and it shows up in editor but does not collide with the shipped compile.

The editor only modules are also moved to the separate module. So no plugin just a module split that is filtered in the uproject file.

Right click uproject file after unpacking and generate he project.

UFUNCTIONS do not work with WITH_EDITOR only WITH_EDITORONLY_DATA allows to define functions, but it still would fail in the package process.

1 Like

Thanks guys!

@rokenrock
Okay I understand the problem with reflection, that makes sense and its good to know (never read about this anywhere).

It is working now with your suggested changes!

Also thanks a ton for taking the time and posting the module @3dRaven ! I have a feeling I will move everything to a module in the future to have a better isolation of editor related stuff. Great to have a template for this, thanks!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.