C++ Interface implemented in BP is null

Hi,
So im fighting with interfaces. :clap: :clap: :rips hair and cryies blood:
_
Lets declare a few classes:
cpp_Interface : aka= IDamageable
bp_Actor : implements cpp_Interface
cpp_Actor2 : implements cpp_Interface
bp_Actor3 : inherits from cpp_Actor2 (and therefore implements cpp_Interface)
cpp_ActorWhatever : calls methods
_

Problem 1
Casting and passing (in a method) interfaces implemented in BP is always null. Whereas if implemented in cpp they are valid. If I check for null in the BP it tells me its not null.

For example, if in cpp_ActorWhatever I try to use:
IDamageable* damageable = Cast(with a bp_Actor); damageable is null.
but
IDamageable* damageable = Cast(with a cpp_Actor2); damageable is valid
IDamageable* damageable = Cast(with a bp_Actor3); damageable is valid
_

Problem 2 (probably the same problem, but illustrated differently)
If in cpp_ActorWhatever I have a method:

void MethodFoo(const TScriptInterface "<"IDamageable “>” & v).
(ignore the ")

Calling it:
MethodFoo(with a bp_Actor): tells me its null
MethodFoo(with a cpp_Actor2): tells me its ok
MethodFoo(with a bp_Actor3): tells me its ok

##CODE: cpp_Interface

UINTERFACE(Blueprintable)
class UDamageable : public UInterface
{
	GENERATED_BODY()
};

class TPSMPPG_API IDamageable
{
	GENERATED_BODY()
public:
	UFUNCTION(BlueprintAuthorityOnly, BlueprintNativeEvent, BlueprintCallable, Category = "Life")
		void ReceiveDamage(int32 dmg);
};

God, this formatting is still dung.

Question: how TF am I supposed to declare and use interfaces so they dont return different values whereas implemented in cpp or bp ?

I hope my questions is clear, putting into words and scouring the net for a solution for this weird problem is difficult.

Thanks

1 Like
  1. You probably want parameters on your ReceiveDamage function.
  2. I’ve never used the TScriptInterface. Instead, I would use the IDamageable::Execute_ReceiveDamage(BP_Actor) function.

Yes, this is the particularly difficult part about dealing with interfaces and blueprints. IDamageable will always be null for blueprint implemented cases because it’s impossible to have an actual pointer to it.

Sadly the answer to this is: you don’t.

There are two main elements to working with interfaces in CPP when the interfaces can be implemented by either blueprints or in code.

First is using the static “Execute_” versions of function calls such as IDamagable::Execute_RecieveDamage( ... ). I know you didn’t show any of that code, but it’s an annoyingly important detail. The important element of this is that all the “Execute_” function take a UObject* parameter as the object to call the function on.

The second is that before calling an “Execute_” function you’ll have to check if the object implements the interface. You’ve already found that you can’t directly cast the object to the interface. If you have a UObject* you can call Implements< UDamagable >( ). I think that if you’ve got a TScriptInterface you can assume you can pass TScriptInterface::GetObject’s return value to an Execute function.

2 Likes

Thanks for your help.

  1. I edited the ReceiveDamage to take an Int32. As you imagined, the problem is still there :’(
  2. I use TScriptInterface to enforce passing an element implementing this interface at compilation. I want to avoid passing just any sort actor that would demand me to check and verify at runtime.
    And it works for everything that implements it in CPP, not in the PB. Thus the weird problem.
    Side note : IInterface* is not compilable in method parameters nor class.

I believe that TScriptInterface doesn’t work with BP, with the reason being that CPP doesn’t actually know anything within BP exists as BP is essentially a layer above CPP.

Alright, so: first of all thank you Dynamiquel and MagForceSeven for your help and insights <3

This is not a solution, but a summary of what to expect and the choices to make. There is no solution because its something Epic has to work on… (dont expect it to happen tho) or someone else maybe find a hack around.

Its a fact that cpp interfaces implemented in blueprints will always cast to null, see links below for why.

Sure there is another method to see if they implement the interface and use the interface’s methods… and these are reliable no matter if implementation is BP or CPP.

But there’s nothing safe for casting.
So if you got an actor by raycasting and a method that takes an interface, and want that method to take it: you can’t do nothing (well, you may still make another method that takes an actor, but thats what we’re trying to avoid).

From here on, you can make one of these 2 choices for your project:
A. NEVER implement interfaces in blueprints.
B. Implement interfaces in blueprint whenever.
(These are for good practices and consistency reasons).
I’m very noob on this and still have to grasp the full extent of these decisions, if you have insight, please share!

Why A:

  • Allow methods to enforce the usage of Interfaces as parameters. ie.: DoSomething(TScriptInterface& v). This is awesome because you never have to cast and verify at runtime, thus saving headaches. IMHO this is too useful.
  • Although blueprint ‘BP_Coconut’ may never directly implement an interface, you can make a cpp class ‘CPP_Coconut_base’ that implements the interface and then BP_Coconut can override these methods.
  • Works for casting to interfaces!
  • Never have to think if method M takes paramer P with interface I and if passed parameter P implements interface I. You cant go wrong, “the master works hard”*1.

Why B:

  • Freedom of implementing interfaces in BPs without a cpp overhead. Depending of your project, this may simplify work and readability.
    If B:
  • Never cast in cpp (not an issue if none of your methods take interfaces)
  • Never declare methods exposed to blueprints with explicit interfaces (always take in uobjects and verifiy at runtime)

*1 Sorry, those who know, know.

Good reads about interfaces by these fine fellas <3

1 Like

If you’ve going to do this (and it’s worth pointing out that although the alternative is annoying it’s sometimes the right call) you probably want to make sure to use meta = (CannotImplementInterfaceInBlueprint) in the UINTERFACE macro. This will prevent it from even being an option and will modify how Unreal Header Tool treats the functions you declare in the actual interface.

Thats exactly what I was looking for, :heart:!

However… it seems more difficult to use than I expected :grimacing:
By using this meta all my interface method’s meta must not contain [BlueprintImplementableEvent or BlueprintNativeEvent].
Thus my methods have to be abstract/pure.
Thus I have to drop the “_Implementation” in the classes that implement the interface.
And thus IInterface::Execute_MethodName() no longer compiles. And I need this.

:exploding_head:

Another thing;
[CannotImplementInterfaceInBlueprint] really seems to mean: You may not implement the interface NOR override any method in BPs. Which is sad becasue I do want to be able to override (some) methods.

Oh, yeah you’re right I forgot about that part.

If you go with CannotImplementInterfaceInBlueprint, then you can build and work with your interface as if blueprints don’t exist: a) you can just declare functions as virtual, b) you can declare them as pure virtual ( using “= 0” ) and c) you can call the functions directly though the interface pointer instead of using Execute_ versions.

Yeah, I guess it’s depends on how you look at it. Overriding a virtual function or Blueprint event is “implementing” that function and the meta says blueprint isn’t allowed to do that.

I don’t know what you mean tbh. There is a solution to your problems but for some reason, you do not wish to use it.
Casting and IsValid checks are just the nature of UE4. It’s not like it’s an expensive operation. Most of the time, you don’t even need to do an IsValid check, but a simple null check.

Pretty much every Kismet (BP) function will do IsValid checks for you anyway so it’s not like you’re saving yourself anything.

Hmm ill re-explain the problem:
Sure, Casting and checking IsValid for an interface is a (partial) solution. The cost of the operation is not a matter. But its not the ideal solution and can completely fail unexpectedly.

The problem is that sometimes I/You/We want to pass an interface as a method parameter, because We know we want that, and nothing else that may or may not cast to the interface.
Sure we can still choose to send whatever and cast/check; but then how do we inform the user that this method expects something that casts to the interface? Let us not call the variable “weapon_Please Make Sure It Casts To Interface IWeapon”…
And then theres also intellisense to take into consideration.

The bigger problem, and unavoidable, is that CPP interfaces should NOT be implemented in a BP class if it doesnt have a parent CPP class that implements the interface before.
If we have a BP class directly implement a CPP interface: casting and checking null will always return null. Bummer.

I hope I make more sense now :slight_smile:
The solution, conclusion and details are above.

Is this still the case in UE 5.0 final?

IMHO casting to the interface is a more comfortable approach to using the Execute_ methods as it’s more in line with how you would use interfaces in other programming languages.

Yes, this is still the case if UE5.

This may be true, but unfortunately it’s an inherent limitation of how blueprint works. There’s no way for the C++ compilation to include something that exists purely in data (since we’re talking about blueprint). That “something” being class offset/vtable information that would be necessary to generate that interface class pointer.

If you want to be able to guarantee that you can cast to an interface in C++, you still have to implement the interface through a C++, non-interface, class before extending that class into a Blueprint. If an interface is extensible directly through a Blueprint, you’ll have to keep this casting/Execute restriction in mind.

2 Likes