How do I implement Pure Virtual Methods?

Hi, I’m wondering why if writing pure virtual methods in my abstract base classes is supported? Currently I’ve got a class that inherits from actor and I’ve marked it as UCLASS(abstract) with a pure virtual method.

I then have a class that inherits from this class, and implements the pure virtual method. When I compile this I get the following error.

Only functions in the second interface class can be declared abstract.

Does that mean if I want to use pure virtual methods in my abstract class that I have to use a separate interface? If that’s the case, then what is the purpose of declaring my class abstract? Aren’t pure virtual methods what make classes abstract?

UCLASS(abstract)
class ASpawnVolume : public AActor
{
	GENERATED_UCLASS_BODY()

public:
	UFUNCTION(BlueprintCallable, Category = "Spawning")
	virtual void SpawnUnitWithTransform(TSubclassOf<AActor> unitType, FTransform trans);

	UFUNCTION(BlueprintCallable, Category = "Spawning")
	virtual void SpawnUnit(TSubclassOf<AActor> unitType);

	UFUNCTION(BlueprintCallable, Category = "Spawning")
	virtual void SpawnUnits(TSubclassOf<AActor> unitType, uint32 count);

	UFUNCTION(BlueprintCallable, Category = "Spawning")
	virtual FTransform GetRandomTransformInVolume() = 0;
};
1 Like

UClasses cannot really be abstract in the C++ sense, because the UObject sub-system requires that each class can be instantiated (it creates at least one instance of each class as a so called Class Default Object [CDO] that holds the default properties of that class). Therefore, every class method must have an implementation, even if it does nothing.

That being said, you can get similar behavior by decorating your inline method implementations with the PURE_VIRTUAL macro. This will tell the UObject sub-system that your intent is to declare a pure virtual method. So even though the method is not pure virtual in the C++ sense - it has a (possibly empty) function body - the compiler can still ensure that all child classes do supply an actual implementation.

For example, you could do:

virtual void SpawnUnit(TSubclassOf<AActor> unitType) PURE_VIRTUAL(ASpawnVolume::CanRedo,);

You can find many more examples in the code base, including methods with return values by searching for PURE_VIRTUAL.

11 Likes

I’d like to add to this that the definition of the PURE_VIRTUAL macro is defined as

#define PURE_VIRTUAL(func,extra) { LowLevelFatalError(TEXT("Pure virtual not implemented (%s)"), TEXT(#func)); extra }.

The first argument is the name of the function for logging purpose, while the second argument is there to add extra code. This may be empty, but you’ll still have to add the comma between the brackets.

2 Likes

“the compiler can still ensure that all child classes do supply an actual implementation” Are you sure this is (still) the case? It seems to me (after what I experienced :slight_smile: ) that we will get an error at runtime, not compile time.

Is there a way to have a compile time check ?

The compiler will only verify when CHECK_PUREVIRTUALS == 0. See CoreMiscDefines.h. You can’t actually run the game with CHECK_PUREVIRTUALS == 0 because of the above reasons, but you can compile with it.

I don’t know if there’s anything automatic/easy to enable this checking within UBT.

Do we still have to use the “abstract” attribute inside UCLASS(abstract) to tell the editor to prevent us from creating instances of this class?

In case you get an error saying “Function must return a value”, it means your pure virtual function is not void. To return something, add the return code in the second parameter of PURE_VIRTUAL(functionName, code; code; return something; ). About the impossibility of enforcing any implementation: from 2014: Pure Virtual Functions - Epic Games Forums.

1 Like

By the way, @gmpreussner, your code is confusing because you give CanRedo as a parameter, which is anywhere in the question code. Shouldn’t it be the same name of the method for logging purposes?
virtual void SpawnUnit(TSubclassOf unitType) PURE_VIRTUAL(ASpawnVolume::SpawnUnit,);

2 Likes

In case you want a pure virtual with return type, the return must be part of the PURE_VIRTUAL macro. as it must be a complete function in the CPP sense.

	virtual bool IsValid() PURE_VIRTUAL(MyClass::IsValid, return false;);
4 Likes

The compiler should not allow you to instantiate abstract classes

@Sunday111 This isn’t an abstract class as far as C++ is concerned. It’s just marked up that way for UE. UE instantiates an instance of every UCLASS (see above about CDO), so we can’t do pure virtual functions For Real.

The alternative then becomes this bastardized horror we see before us. Err… “this solution we see before us”.

At least dump the call stack guys, jeez. “check(false)” can do it, so can this macro. The joys of an older code base: so many [mis]features. And the code base just keeps getting worse as it ages.

I wonder how many times someone has tried to get momentum behind rewriting UE internally? The business case for it is terrible, until it isn’t, and by then it’s too late. Hurray for software development!