[Feature request] Typesafety for unreal interfaces!

Hi!

Wouldn’t it be nice to being able to use the unreal interfaces as UPROPERTY pointers? I.e. use unreal interfaces as you would use C# or Java interfaces. There are many reasons for introducing this feature, obviously this will have great improvements on software design. From a designing perspective the level of abstraction interfaces introduces is invaluable.

Apart from that BIG improvement this will also leads to typesafety. Typesafety will have a lot of great effects, that’s why all big languages supports it like C#, Java or native C++! I don’t see why unreal engine shouldn’t support this as well for UObjects and Actors. This will make designing of games easier and result in better code quality. This would also remove a lot of code duplication in form of dynamic casts and if clauses all over the application and therefore produce more clean code, i.e. more readable and understandable. Reducing the complexity of reading and understanding the code can both save you time and bugs. It could even save you performance even though it’s unlikely to have any major impacts.

So what needs to be done?

  1. You must be able to store an unreal interface pointer as an UPROPERTY such that it isn’t collected by the garbage collector.
  2. You may want to be able to check if the pointer is still valid since uobjects can be removed from the world.

Also cut out the UInterface part completely from the interface code and generate that instead since now it’s just a chunk of confusing junk code that must be there.

Example 1:
IInventory is an unreal interface.

No typesafety:



// header
UPROPERTY()
AActor *MyAInventory;

// implementation

AInventory *castedInvetory= Cast<IInventory>(MyAInventory);
if (castedInvetory)
{
	castedInvetory->addWeapon(someWeapon);
}


With typesafety:



// header
UPROPERTY()
IInventory *MyInventory;

// implementation
MyInventory->addWeapon(someWeapon);



Example 2:
When an actor can be null or invalid.

No typesafety:



// header
UPROPERTY()
AActor *FollowingAMinion;

// implementation

if(FollowingAMinion && FollowingAMinion->IsValidLowLevel())
{
	IMinion *castedMinion= Cast<IMinion>(FollowingAMinion);
	if (castedMinion)
	{
		castedMinion->Order(NewOrder);
	}
}


With typesafety:



// header
UPROPERTY()
IMinion *FollowingMinion;

// implementation
if(FollowingMinion && FollowingMinion->IsValidLowLevel())
{
	FollowingMinion->Order(NewOrder);
}



I hope this is a feature you really consider implementing since I’m missing it a lot and I guess a lot of other software designers do as well.
Thanks!

I believe this is the biggest design flaw in the engine. Removing interfaces and replacing them with dynamic casts is a big step back.

Hi all,

There is some level of support for interface properties via the not-very-well-known TScriptInterface smart pointer:


UPROPERTY()
TScriptInterface<IMinion> FollowingMinion;

A smart pointer is needed because the garbage collector is not interface-aware. Some love is needed on that smart pointer type, as it’s a bit clunky. You need to assign it via the UObject type instead of the interface type and nulling it will require an explicit cast, e.g.:


FollowingMinion = (IMinion*)nullptr;

But it should at least free you from needing dynamic casts. If you do end up using it, please report back any problems; it doesn’t seem particularly well-utilised at the moment and so may be lacking support in some areas.

Steve

I will definitely check this out. This may work as a substitute until interfaces are fully supported.

And can we also get Hindley-Milner type inference? That’d be great! Thanks.

That’s a language feature, so I wouldn’t expect to see that in C++ soon (if ever). The closest you get is probably templates and the auto keyword.

Sorry to necro that thread, but I’d like to know if Unreal Interfaces and how to handle them changed since the last posts here.

After some time and the help of Steve Robbs post I finally managed to get my Interface to work, but I’m not entirely sure if I’m using it correctly or if there isn’t a nicer way to do it.

I am using it like this now:

in header:



	UPROPERTY()
	TScriptInterface<IVRNetGrabInterface> GrabbedObjReference 

in cpp:


 
		if (IVRNetGrabInterface* Grabbabel = Cast<IVRNetGrabInterface>(replicatedObject))
		{
			GrabbedObject = TScriptInterface<IVRNetGrabInterface>();
			GrabbedObject.SetInterface(Grabbabel);
			GrabbedObject.SetObject(replicatedObject);
			GrabbedObject->Execute_Grab(replicatedObject, this);
		}



 
		if (GrabbedObject)
		{
			GrabbedObject->Execute_Drop(GrabbedObject.GetObjectRef(), this);
			GrabbedObject = nullptr;
		}


I also tried to cast the nullptr for nulling it, as suggested by Steve Robb, like this:


GrabbedObjReference = (IVRNetGrabInterface*)nullptr; 

But I get the following compile error if I do that:
Error C2664 ‘void FScriptInterface::SetObject(UObject *)’: cannot convert argument 1 from ‘IVRNetGrabInterface *’ to ‘UObject *’

Am I doing something wrong here?
I also didn’t quite understand why it is necessary to use Execute_InterfaceMethod with the reference to the object… it makes the code uglier and harder to read, especially if you call many interface functions with many parameters, which goes against one thing I am trying to achieve with using Interfaces…

Would be using an abstract class instead a better idea, if only for cleaner code?

Abstract classes can work but only for very basic constructs. Having multiple inherence with uobjects gives me nightmares just think of it, you probably must redesign the whole engine to get that working.

My conclusion is, NO type safety can be used with uinterfaces. It seems like this is not a prioritized issue to fix either. If you are working in a production environment I would recommend you to save yourself the trouble. Basically, use dynamic casts whenever you need to access an interface, it is still a lot better than stop using interfaces. I guess we will have to live with it for a long time. It is usable even if it is dirty and looks ugly.

Personally however, I wonder what they were thinking when designing this uinterface system. Still think this is the biggest design flaw in the engine. How can a software designer have missed understanding the value of abstraction interfaces have in software design? Also, heard about type safety? Still, the guys at EPIC are pretty cool though, I give them that.

I tried Gnurr’s solution,works great.than i noticed the main difference is the param of Execute_XXX . so i tried this


IIPropertyBound* bound = Cast<IIPropertyBound>(upperBound->_getUObject());
float upperBoundValue = bound->Execute_GetBoundValue(upperBound->_getUObject());

the upperBound is a UProperty var of TScriptInterface

also worked.
so i think maybe just a little tweak in TScriptInterface,may solve this

and,i convert above code to a macro,for easy use. but of course,this interface implementation is so weird :confused:


#define CALL_INTERFACE(interfaceType, interfaceObj,funcName) Cast<interfaceType>(interfaceObj->_getUObject())->Execute_##funcName(interfaceObj->_getUObject())


float upperBoundValue =    CALL_INTERFACE(IIPropertyBound, upperBound, GetBoundValue);