PawnOwner, CharacterOwner, etc

Hi! I am working with UE4.23 and in some cases face strong class dependencies (for example, Pawn and Movement classes, Animation and Character classes, etc). I am thinking that if base component class (for example UActorComponent) just implements some Interface as the code below, classes can become more independent and code can become more modular.

UINTERFACE(MinimalAPI)
class UOwnerMulticasterComponentBase : public UInterface {
	GENERATED_BODY()
};

class REBELHERO_API IOwnerMulticasterComponentBase {
	GENERATED_BODY()

		// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:

	virtual UActorComponent* GetSelfAsActorComponent() { return nullptr; };

	template<typename T>
	T* GetOwnerAs() {
		static TMap<IOwnerMulticasterComponentBase*, T*> mapping;

		if (!mapping.Contains(this)) {
			if (auto component = GetSelfAsActorComponent()) {
				mapping.Add(this, Cast<T>(component->GetOwner()));
			}
			else {
				mapping.Add(this, nullptr);
			}
		}

		return mapping[this];
	}
};

However, this is not fine. Because in the heap absolute memory addresses change from time to time. So, only if we find the way that Unreal smart pointers update itself we can then try to use it in full with Unreal.

Make fully working example, I am be glad for any further suggestions…

#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "OwnerMulticasterComponentBase.generated.h"

class UActorComponent;

DECLARE_MULTICAST_DELEGATE(FOnComponentEndPlayEvent);

UINTERFACE(MinimalAPI)
class UOwnerMulticasterComponentBase : public UInterface {
	GENERATED_BODY()
};

class REBELHERO_API IOwnerMulticasterComponentBase {
	GENERATED_BODY()

protected:
	
	FCriticalSection Locker;

	template<typename T>
	TMap<IOwnerMulticasterComponentBase*, T*>& GetMapping_TOwner() {
		static TMap<IOwnerMulticasterComponentBase*, T*> ownerMapping;
		return ownerMapping;
	}

	template<typename T>
	TMap<IOwnerMulticasterComponentBase*, FDelegateHandle>& GetMapping_Callback() {
		static TMap<IOwnerMulticasterComponentBase*, FDelegateHandle> callbackMapping;
		return callbackMapping;
	}

	template<typename T>
	void OnComponentEndPlayCallback() {

		FScopeLock Lock(&Locker);

		auto ownerMapping = GetMapping_TOwner<T>();
		ownerMapping.Remove(this);

		auto callbackMapping = GetMapping_Callback<T>();
	if (callbackMapping.Contains(this)) {
		OnComponentEndPlay.Remove(callbackMapping[this]);
		callbackMapping.Remove(this);
	}
	}

public:

	FOnComponentEndPlayEvent OnComponentEndPlay;

	virtual UActorComponent* GetSelfAsActorComponent() { return nullptr; };

	template<typename T>
	T* GetOwnerAs() {
		
		FScopeLock Lock(&Locker);

		auto& ownerMapping = GetMapping_TOwner<T>();
		if (!ownerMapping.Contains(this)) {
			
			T* castedOwner = nullptr;
			if (auto component = GetSelfAsActorComponent()) {
				castedOwner = Cast<T>(component->GetOwner());
			}
			ownerMapping.Add(this, castedOwner);

			auto callbackMapping = GetMapping_Callback<T>();
			callbackMapping.Add(this, OnComponentEndPlay.AddRaw(this, &IOwnerMulticasterComponentBase::OnComponentEndPlayCallback<T>));
		}

		return ownerMapping[this];
	}
};

Here is the next iteration of all this and at the moment I am not facing any errors. In prev comment I was wrong. The errors there comes not from global changes of memory addresses, but rather from tricky memory allocation of TMap container. So, when I move to simple c++ std::map class, those strange pointer mistakes go away

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include <map>
#include "OwnerMulticasterComponentBase.generated.h"

class UActorComponent;

DECLARE_MULTICAST_DELEGATE(FOnComponentEndPlayEvent);

UINTERFACE(MinimalAPI)
class UOwnerMulticasterComponentBase : public UInterface {

	GENERATED_BODY()

};

class REBELHERO_API IOwnerMulticasterComponentBase {

	GENERATED_BODY()

protected:
	
	FCriticalSection Locker;

	template<typename T>
	std::map<IOwnerMulticasterComponentBase*, T*>& GetMapping_TOwner() {
		static std::map<IOwnerMulticasterComponentBase*, T*> ownerMapping;
		return ownerMapping;
	}

	template<typename T>
	std::map<IOwnerMulticasterComponentBase*, FDelegateHandle>& GetMapping_Callback() {
		static std::map<IOwnerMulticasterComponentBase*, FDelegateHandle> callbackMapping;
		return callbackMapping;
	}

	template<typename T>
	void OnComponentEndPlayCallback() {

		FScopeLock Lock(&Locker);

		auto& ownerMapping = GetMapping_TOwner<T>();
		ownerMapping.erase(this);

		auto& callbackMapping = GetMapping_Callback<T>();
		OnComponentEndPlay.Remove(callbackMapping[this]);
		callbackMapping.erase(this);
	}

public:

	FOnComponentEndPlayEvent OnComponentEndPlay;

	virtual UActorComponent* GetSelfAsActorComponent() { return nullptr; };

	template<typename T>
	T* GetOwnerAs() {

		FScopeLock Lock(&Locker);

		auto& ownerMapping = GetMapping_TOwner<T>();
		auto It = ownerMapping.find(this);
		if (It != ownerMapping.end()) return It->second;

		T* castedOwner = nullptr;
		if (auto component = GetSelfAsActorComponent()) {
			castedOwner = Cast<T>(component->GetOwner());
		}
		ownerMapping.emplace(this, castedOwner);

		auto& callbackMapping = GetMapping_Callback<T>();
		callbackMapping.emplace(this, OnComponentEndPlay.AddRaw(this, &IOwnerMulticasterComponentBase::OnComponentEndPlayCallback<T>));

		return castedOwner;
	}
};