How to get an interface pointer variable to work w/UPROPERTY()

Hello, I am attempting to have an interface pointer variable that is declared in the header be VisibleAnywhere, BlueprintReadOnly in the UPROPERTY() but when I compile it gives me an error:
UPROPERTY pointers cannot be interface - did you mean TScriptInterface?

But I don’t know what that means or how TScriptInterface’s work…
If anybody can point me(excuse the pun) in the right direction or just flat out tell me how to fix this, that would greatly appreciated.

the header:

protected:

               UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Interface Variable")
               IMyInterface* SelfInterface;
1 Like

Hi, you need to do this:

UPROPERTY(...)
TScriptInterface<IMyInterface> SelfInterface;

Hope this helps,

Steve

2 Likes

Ok, but I have three questions. How would I set the value in code like say the Constructor I was setting the variable to nullptr how would I do that with TScriptInterface? And I also set the variable in BeginPlay using a cast what would be the equivalent of that for TScriptInterface? And how would I check if TScriptInterface is valid?

You can set the value in the constructor in the same way you would set a pointer. The pointer type must be null or one derived from UObject though - an IMyInterface* won’t work:

SelfInterface = nullptr;                   // ok
SelfInterface = UObjectPtr;                // ok
SelfInterface = (IMyInterface*)UObjectPtr; // won't work

For casting, you can just call GetObject() and cast as usual:

CastChecked<UWhatever>(SelfInterface.GetObject())->WhateverProperty = 10;

And checking if it’s valid is the same as any other pointer:

if (SelfInterface)
{
    ...
}

Steve

1 Like

Ok, thank you very much for the help. Just for clarification on the Cast would the below script work?

SelfInterface = CastChecked<IMyInterface>(SelfInterface.GetObjectRef());

If your interface is an UINTERFACE(), then yes, that should work.

You should use GetObject() rather that GetObjectRef() though. I don’t think GetObjectRef() should be public.

Steve

So I would put UMyInterface or add static class, instead of IMtInterface? When I enter GetObject it comes up with GetObjectRef, GetObjectW, and GetInterface. I really appreciate you helping clear this up for me :slight_smile:

So with TScriptInterface how would I use the cast to set the pointer to that interface that I would want it to be, so that its no longer null?

You would put IMyInterface:

IMyInterface* InterfacePtr = CastChecked<IMyInterface>(SelfInterface.GetObject());
InterfacePtr->DoThing();

But if SelfInterface is already an TScriptInterface then you don’t need a cast, because you know it’s already of that type. Instead, you can just call it directly:

SelfInterface->DoThing();

You would use your code above if you needed to discover an interface other than IMyInterface (and you’d probably use Cast instead of CastChecked, if you want to handle the possibility of failure).

GetObject() will work. GetInterface() should work but doesn’t because it’ll call the FScriptInterface base class function which just returns a void* instead of InterfaceType*. TScriptInterface is in need of some API love. :-p

GetObjectW is probably cropping up because of the Windows headers, which we do not have any control over:

#ifdef UNICODE
#define GetObject  GetObjectW
#else
#define GetObject  GetObjectA
#endif // !UNICODE

Steve

Ok I found out how to set the TScriptInterface’s interface to another interface. Thank you very much for you assistance! Example for better explanation below:

 Called when the game starts or when spawned
void ALeverActor::BeginPlay()
{
	Super::BeginPlay();

	//Since I'm doing this only in BeginPlay,
	//I'm gonna make a temp variable that gets deleted after BeginPlay is called
	IMyInterface* tempMyInterface = nullptr;

	tempMyInterface = Cast<IMyInterface>(this);

	//Both of these have to be set!
	SelfInterface.SetInterface(tempMyInterface);

	//Since I'm getting the interface in self/this I'm setting the object for this.
	SelfInterface.SetObject(this);

	if (SelfInterface)
	{
		UE_LOG(LogTemp, Warning, TEXT("SelfInterface is valid"));
	}

}

As I said above:

SelfInterface = UObjectPtr;

You can’t set it to an IMyInterfacePtr* directly, only to one which is both convertible to both UObject* and IMyInterfacePtr*.

Steve

You don’t need to do all of this:

IMyInterface* tempMyInterface = nullptr;
tempMyInterface = Cast<IMyInterface>(this);
SelfInterface.SetInterface(tempMyInterface);
SelfInterface.SetObject(this);

You just need to do this:

SelfInterface = this;

Steve

2 Likes

Hi, I follow what you said here. It work pretty well, appart when I’m testing if it’s valid. It’s always return false ! :confused:
I’ve tested with a log, and the "TScriptInterface<>->GetObject()->GetName() print me the good think. So my pointer is not false. So I have no idea why the test is always false.

// Called when the game starts
void UActionManager::BeginPlay()
{
	Super::BeginPlay();

	// ...
	if ( GetOwner()->GetClass()->ImplementsInterface( UActionable::StaticClass() ) )
	{
		OwnerAsActionable = GetOwner();
		UE_LOG( LogPacInit, Warning, TEXT( "%s -> %s (Component Owner) implement Actionable Interface" ), *GetName(), *OwnerAsActionable.GetObject()->GetName() );
	}
	else
	{
		UE_LOG( LogPacInit, Warning, TEXT( "%s -> %s (Component Owner) don't implement the Actionable Interface" ), *GetName(), *GetOwner()->GetName() );
	}
}
/........
void UActionManager::OnGainFocus( APawn * Instigator )
{
	if ( !OwnerAsActionable )
	{
		UE_LOG( LogPacActionSystem, Warning, TEXT( "%s->OnGainFocus : The component owner don't implement Actionable Interface !" ), *GetName());
		return;
	}
	OwnerAsActionable->OnGainFocus( Instigator );
}

The implementation of TScriptInterface::operator bool() and TScriptInterface::operator->() are both implemented in terms of the same function, so they should be consistent.

Steve

In fact, after some verifications, the SourceObject is good (When I use GetObject(), it return me the good pointer), but the InterfacePointer is Always false.

The thing is, that the actor that implement the interface is Blueprint Actor inherited from simple Actor. And in Class Settings, I implement the interface. Maybe that is the reason why it don’t work.

I can call Interface functions from blueprint, but when I call interface functions from C++ I got a crash (BReackpoint reached)

Are you setting this TScriptInterface as a property via BPs? It can only be set by native C++, not Blueprints or any other system.

TScriptInterface is like a tuple of UObject*andIMyInterface*. It holds a UObject*to keep the GC happy and aIMyInterface*to make calls against. Setting it in native code, you have to pass theIInterface*- it will copy that and then cast back to theUObject*` to store that too.

But if you set it in any other way (e.g. reflection, via BPs), then you will only be setting the UObject* part. That sounds like it could be the reason why you have an object but no interface.

Steve

1 Like

I’m setting the TScriptInterface via C++. But The Actor that implement the interface is created by BP. But I finally got it to Work like that :
IYourInterface::Execute_YourFunctionName( ActorPointerWhichExecuteTheFunction, params … );
THanks for the answers anyway.

But the thing is the IMyInterface is always at 0x00000 in debuger. (So nullptr)
I use MyTScriptInterface = MyObjectPtr to set it !