How can I set up a dynamic object factory based on FString?

I’d like to know if there is a convenient way to create objects at runtime, based on class name stored in FString (or other stirng-alike) variable.

Let’s consider following UC:

I have a UItem class, and I would like to spawn items dynamically using EXEC function. Assuming that UItem is a base class for all item classes.

Currently I have woraround for it by making a static factory, which enumerates all the possible classes (which is terrible to maintenance):

// Implementation in Item.cpp
UItem* UItem::Factory( FString ItemClassName )
{
	UItem* ret = NULL;

	if ( ItemClassName.Equals( TEXT( "UItemFoodWine" ) ) ) {
		ret = Cast<UItem>( NewObject<UItemFoodWine>( ) );
	} else if ( ItemClassName.Equals( TEXT( "UItemFoodApple" ) ) ) {
		ret = Cast<UItem>( NewObject<UItemFoodApple>( ) );
	} else if ( ItemClassName.Equals( TEXT( "UItemArmorCoat" ) ) ) {
		ret = Cast<UItem>( NewObject<UItemArmorCoat>( ) );
	}

	if ( !ret ) {
		UE_LOG( LogActor, Error, TEXT( "UItem::Factory - class %s not found" ), *ItemClassName );
	}

	return ret;
}

What I want to achieve is more dynamical function, I belive I should make a use of ConstructorHelpers::FClassFinder function, but I couldn’t make it work with FString.


Edit: a solution sent by Jamie Dale:

// Implementation in Item.cpp
UItem* UItem::Factory( FString ItemClassName )
{
    UClass* const ItemClass = FindObject<UClass>(ANY_PACKAGE, *ItemClassName);
    if ( ItemClass && ItemClass->IsChildOf(UItem::StaticClass()) ) {
       return NewObject<UItem>(GetTransientPackage(), ItemClass);
    }

    UE_LOG( LogActor, Error, TEXT( "UItem::Factory - class %s not found" ), *ItemClassName );
    return nullptr;
}

EDIT - Updated code.

// Implementation in Item.cpp
UItem* UItem::Factory( FString ItemClassName )
{
	UClass* const ItemClass = FindObject<UClass>(ANY_PACKAGE, *ItemClassName);
	if ( ItemClass && ItemClass->IsChildOf(UItem::StaticClass()) ) {
		return ConstructObject<UItem>(ItemClass);
	}

	UE_LOG( LogActor, Error, TEXT( "UItem::Factory - class %s not found" ), *ItemClassName );
	return nullptr;
}

EDIT - Original code.

Something like this?

// Implementation in Item.cpp
UItem* UItem::Factory( FString ItemClassName )
{
	UClass* const ItemClass = ConstructorHelpers::FClassFinder<UItem>(*ItemClassName).Class;
	return NewObject<UItem>(nullptr, ItemClass);
}

Note that FClassFinder asserts if it can’t find the class. If you don’t want that behaviour, then you can copy the guts of FClassFinder into your function; probably something like this:

// Implementation in Item.cpp
UItem* UItem::Factory( FString ItemClassName )
{
	FString ClassName;
	FString PathName;
	FPackageName::ParseExportTextPath(*ItemClassName, &ClassName, &PathName);
	UClass* const ItemClass = FindObject<UClass>(ANY_PACKAGE, *PathName);

	if ( ItemClass && ItemClass->IsChildOf(UItem::StaticClass()) ) {
		return NewObject<UItem>(nullptr, ItemClass);
	}

	UE_LOG( LogActor, Error, TEXT( "UItem::Factory - class %s not found" ), *ItemClassName );
	return nullptr;
}

Thanks, this answer seems to be promising!

I’m having hard times with figuring out value for ItemClassName in second case (first is harder to play with coz it crashes as you’ve warned).

Let’s assume that my project is called project and my sample class is UItemFoo.

When I tried:

// Implementation in Item.cpp
UItem* UItem::Factory( FString ItemClassName )
{
	ItemClassName = TEXT( "UItemFoo" ); // Literal just for testing.

	FString ClassName;
	FString PathName;
	FPackageName::ParseExportTextPath( *ItemClassName, &ClassName, &PathName );
	UClass* const ItemClass = FindObject<UClass>( ANY_PACKAGE, *PathName );

	if ( ItemClass && ItemClass->IsChildOf( UItem::StaticClass( ) ) ) {
		return NewObject<UItem>( nullptr, ItemClass );
	}

	UE_LOG( LogActor, Error, TEXT( "UItem::Factory - class %s not found" ), *ItemClassName );
	return nullptr;
}

Combinations which I’ve used:

// All attempts below fails, class not found.
ItemClassName = TEXT( "UItemFoo" ); 
ItemClassName = TEXT( "ItemFoo" );
ItemClassName = TEXT( "sample1.ItemFoo" );
ItemClassName = TEXT( "sample1.UItemFoo" );
ItemClassName = TEXT( "class'sample1.UItemFoo'" );

// All attempts below crashes, with crit log:
// LogWindows: Object is not packaged: ItemFoo None
ItemClassName = TEXT( "class'sample1.ItemFoo'" );
ItemClassName = TEXT( "class'ItemFoo'" ); // Same as one above

It seems that ConstructorHelpers::FClassFinder isn’t quite the correct thing to use here (it expects a full object path).

I’ve previously written a function in FStringClassReferenceCustomization called StringToClass which basically does what you need, so I’ve translated the important parts of that into your test code.

The class names are supposed to be the name of your class without the prefix, so Item, ItemFoo, etc.

// Implementation in Item.cpp
UItem* UItem::Factory( FString ItemClassName )
{
	UClass* const ItemClass = FindObject<UClass>(ANY_PACKAGE, *ItemClassName);
	if ( ItemClass && ItemClass->IsChildOf(UItem::StaticClass()) ) {
		return NewObject<UItem>(nullptr, ItemClass);
	}

	UE_LOG( LogActor, Error, TEXT( "UItem::Factory - class %s not found" ), *ItemClassName );
	return nullptr;
}

I guess that I’m missing something with packaging classes here, because with your updated solution, i’m still getting a crash (but only on valid/existing class names), and the log says:

[2014.05.10-16.38.23:786][ 12]LogWindows: Object is not packaged: ItemArmor None

All I do for code part is to compile my project in VS2013 (dropdown settings: DevEditor / Win64) and then i’m launching editor, and checking things in PIE. Do i need something extra for my classes to be packaged for UE4? maybe problem is with packaging?

My bad, all objects must be created inside a package (this is a UPackage instance, not a pak file). The default used by NewObject is GetTransientPackage(), so we can use that.

// Implementation in Item.cpp
UItem* UItem::Factory( FString ItemClassName )
{
	UClass* const ItemClass = FindObject<UClass>(ANY_PACKAGE, *ItemClassName);
	if ( ItemClass && ItemClass->IsChildOf(UItem::StaticClass()) ) {
		return NewObject<UItem>(GetTransientPackage(), ItemClass);
	}

	UE_LOG( LogActor, Error, TEXT( "UItem::Factory - class %s not found" ), *ItemClassName );
	return nullptr;
}

Actually, using ConstructObject directly might be neater in this case.

// Implementation in Item.cpp
UItem* UItem::Factory( FString ItemClassName )
{
	UClass* const ItemClass = FindObject<UClass>(ANY_PACKAGE, *ItemClassName);
	if ( ItemClass && ItemClass->IsChildOf(UItem::StaticClass()) ) {
		return ConstructObject<UItem>(ItemClass);
	}

	UE_LOG( LogActor, Error, TEXT( "UItem::Factory - class %s not found" ), *ItemClassName );
	return nullptr;
}

Awesome, it does work after this modification! Is there already any docs on the packaging thing? If yes, it would be coll, because as of now I don’t have full understanding.

Anyway, thank you so much for help!

I’ll admit I’m not exactly an expert on how UObject outers work. I can’t find any documentation about it specially, although I did find this answer that mentions them: Must I call 'delete'? - Programming & Scripting - Epic Developer Community Forums

The Outer is an optional parameter
that lets you specify where your
object is in a hierarchy. For example,
all assets live in a package (their
Outer is a UPackage), and all Actors
live in a level (their outer is a
ULevel).

In this case we’re using the default “transient” package, which just seems to be where everything that’s not a specific asset (.uasset/.umap) or actor goes to (unless it needs a more specific home).