Ability to call BlueprintCallable function for Class Default Object

I have noticed that BlueprintCallable are skipped when called for the Class Default Object. This would be a valueable for the Bluprint graph for data object.

I would like the ability to lay out my data object via the BlueprintGraph rather than having to rely on the Blueprint Data Only class default window. When I have a sufficiently deep hierarchy it becomes quite unwieldy.

// H

#pragma once

#include "Courage/CourageCommon.h"
#include "Courage/Core/CourageConstructionInterface.h"

#include "CoreMinimal.h"
#include "Delegates/Delegate.h"
#include "Delegates/DelegateCombinations.h"

#include "UObject/ObjectMacros.h"
#include "UObject/UnrealType.h"
#if WITH_EDITOR
#include "UObject/ObjectCompileContext.h"
#include "UObject/ObjectSaveContext.h"
#endif
#include "CourageObject.generated.h"

// https://heapcleaner.wordpress.com/2016/06/11/uobject-constructor-postinitproperties-and-postload/


USTRUCT(BlueprintType)
struct FCourageObjectPostCDOCompiledContext
{
	GENERATED_BODY()
public:

	/** True if the Blueprint is currently being regenerated for the first time after being loaded (aka, compile-on-load) */
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	bool bIsRegeneratingOnLoad = false;
	/** True if this notification was from a 'skeleton-only' compile */
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	bool bIsSkeletonOnly = false;
	
	FCourageObjectPostCDOCompiledContext() 
	{}


#if WITH_EDITOR
	FCourageObjectPostCDOCompiledContext(const FObjectPostCDOCompiledContext& Ctx)
		: bIsRegeneratingOnLoad(Ctx.bIsRegeneratingOnLoad)
		, bIsSkeletonOnly(Ctx.bIsSkeletonOnly)
	{	
	}
#endif
};

UCLASS(BlueprintType
, Blueprintable
, ClassGroup = "Courage"
, meta = (
PrioritizeCategories = "Courage Courage|Internal"
, AutoCollapseCategories = "Courage|Internal"
))
class COURAGE_API UCourageObject : public UObject
{
	GENERATED_BODY()

public:
	UCourageObject();

	UFUNCTION(BlueprintPure, CallInEditor)
	UObject* GetClassDefaultObject() const { return GetClass()->GetDefaultObject(); }

	UFUNCTION(BlueprintCallable, CallInEditor)
	void TESTFUNCTION() const;

	/**
	 * Called after the C++ constructor and after the properties have been initialized, including those loaded from config.
	 * This is called before any serialization or other setup has happened.
	 */
	virtual void PostInitProperties() override;

	UFUNCTION(BlueprintImplementableEvent, CallInEditor)
	void BlueprintPostInitProperties();


	/**
	 * Called after properties are overwritten, including after subobjects initialization from a CDO.
	 * This could be called multiple times during an object lifetime, which is not the case for PostInitProperties which is expected to be called only once.
	 */
	virtual void PostReinitProperties() override;

	UFUNCTION(BlueprintImplementableEvent, CallInEditor)
	void BlueprintPostReinitProperties();

	/**
	* Called after the C++ constructor has run on the Class Default Object (CDO) for a class. This is an obscure routine used to deal with the recursion
	* in the construction of the default materials
	*/
	virtual void PostCDOContruct() override;

	UFUNCTION(BlueprintImplementableEvent, CallInEditor)
	void BlueprintPostCDOContruct();


#if WITH_EDITOR
	/**
	 * Called after the Blueprint compiler has finished generating the Class Default Object (CDO) for a class. This can only happen in the editor.
	 * This is called when the CDO and its associated class structure have been fully generated and populated, and allows the assignment of cached/derived data,
	 * eg) caching the name/count of properties of a certain type, or inspecting the properties on the class and using their meta-data and CDO default values to derive game data.
	 */
	virtual void PostCDOCompiled(const Super::FPostCDOCompiledContext& Context) override;

	UFUNCTION(BlueprintImplementableEvent, CallInEditor)
	void BlueprintPostCDOCompiled(const FCourageObjectPostCDOCompiledContext& InContext);


	virtual void PreSaveRoot(FObjectPreSaveRootContext ObjectSaveContext) override;

	UFUNCTION(BlueprintImplementableEvent, CallInEditor)
	void BlueprintPreSaveRoot();


	virtual void PostSaveRoot(FObjectPostSaveRootContext ObjectSaveContext) override;

	UFUNCTION(BlueprintImplementableEvent, CallInEditor)
	void BlueprintPostSaveRoot();


	virtual void PreSave(FObjectPreSaveContext SaveContext) override;
	UFUNCTION(BlueprintImplementableEvent, CallInEditor)
	void BlueprintPreSave();
#endif

	/**
	 * Do any object-specific cleanup required immediately after loading an object.
	 * This is not called for newly-created objects, and by default will always execute on the game thread.
	 */
	virtual void PostLoad() override;
	UFUNCTION(BlueprintImplementableEvent, CallInEditor)
	void BlueprintPostLoad();
};

// CPP
#include "Courage/Core/CourageObject.h"

UCourageObject::UCourageObject()
: Super()
{
}

void UCourageObject::TESTFUNCTION() const
{
    UE_LOG(LogTemp, Warning, TEXT("TESTFUNCTION has been called"));
}

void UCourageObject::PostInitProperties()
{
    Super::PostInitProperties();
    BlueprintPostInitProperties();
    UE_LOG(LogTemp, Warning, TEXT("PostInitProperties has been called"));
}

void UCourageObject::PostReinitProperties()
{
    Super::PostReinitProperties();
    BlueprintPostReinitProperties();
    UE_LOG(LogTemp, Warning, TEXT("PostReinitProperties has been called"));
}


void UCourageObject::PostCDOContruct()
{
    Super::PostCDOContruct();
    BlueprintPostCDOContruct();
    UE_LOG(LogTemp, Warning, TEXT("PostCDOContruct has been called"));
}

void UCourageObject::PostLoad()
{
    Super::PostLoad();
    //BlueprintPostLoad();
}

#if WITH_EDITOR

void UCourageObject::PostCDOCompiled(const Super::FPostCDOCompiledContext& Context)
{
    Super::PostCDOCompiled(Context);
    FCourageObjectPostCDOCompiledContext Ctx(Context);
    BlueprintPostCDOCompiled(Ctx);
    UE_LOG(LogTemp, Warning, TEXT("PostCDOCompiled has been called"))
}


void UCourageObject::PreSaveRoot(FObjectPreSaveRootContext ObjectSaveContext)
{
    Super::PreSaveRoot(ObjectSaveContext);
    BlueprintPreSaveRoot();
    UE_LOG(LogTemp, Warning, TEXT("PreSaveRoot has been called"))
}

void UCourageObject::PostSaveRoot(FObjectPostSaveRootContext ObjectSaveContext)
{
    Super::PostSaveRoot(ObjectSaveContext);
    BlueprintPostSaveRoot();
    UE_LOG(LogTemp, Warning, TEXT("PostSaveRoot has been called"))
}

void UCourageObject::PreSave(FObjectPreSaveContext SaveContext)
{
    Super::PreSave(SaveContext);
    BlueprintPreSave();
    UE_LOG(LogTemp, Warning, TEXT("PreSave has been called"))
}
#endif



Can you provide some more information on what is it that you want to do using uobjects?
Usually they’re either used for data, or to execute const functions to process other external data

I’m not sure I understand why you want to run blueprint code to modify the actual objects themselves at editor time, it’s not something I’ve seen before

I want to define a hierarchy of UObjects for data only. The only thing, is that the hierarchy becomes quite complicated if I rely simply on UPROPERTY with Instanced keyword, or other UObject asset. I would like the ability to use the Blueprint graph to lay out the hierarchy instead.

This seems really intuitive, and the Unreal way of doing things. Editing deeply nested sets of properties seem like something that would’ve been done in Unity.