How do I use a template function in a component?

I have a special component class called Game State Component, basically it’s an actor component that can only be added to the Game State. My Game State Component is also supposed to only have one instance of itself, i.e. I can only have one Attribute Manager Component (derived class), one Actor Manager Component (another derived class), etc.

To try avoiding having to create functions I have to override in every single child class (which may not work with Blueprints either) I’m trying the template function below (doesn’t work in Blueprints but still much cleaner than the alternative), but it just refuses to compile and keeps complaining about something ridiculous.

	/**
	 * Verifies that this component is the only one of its instance.
	 * If we have multiple components of the same class the game might not function properly.
	 * This allows us to check in one function, and just put a call in each child class on BeginPlay().
	 * ONLY WORKS IN C++. Blueprints don't take template functions.
	 */
	template<class AllocatorType>
	void VerifySingleInstance() const
	{
		typedef TPointedToType<AllocatorType> T;

		// Create array of components
		TArray<T*, AllocatorType> LocComponents;

		// Get components and fill array
		OwningGameState->GetComponents<AllocatorType>(LocComponents, true);

		// Ensure we don't have duplicates
		if(LocComponents.Num() > 1)
		{
			// We found a duplicate, crash the game
			const FString ErrorMessage = "There can only be ONE component of class (" + GetNameSafe(this) + ") in your Game State! Please check for duplicate components.";
			UConnGameplayStatics::PrintScreenLog(this, ErrorMessage, 5, EConnLogType::Fatal);
		}
	}

I tried using a function without templates but it ALWAYS used the Game State Component base class, which obviously isn’t any good if I have an Attribute Manager Component and an Actor Manager Component (adding extra functionality in case I ever need it), since they end up finding each other as the same base class and crashing the game.

And here’s the log file:

Build started 1/8/2023 1:57:36 PM.
Logging verbosity is set to: Normal.Project "C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\CONN_MN_SoR.sln" on node 1 (Build target(s)).
ValidateSolutionConfiguration:
  Building solution configuration "Development Editor|Win64".
ValidateProjects:
  The project "UE5" is not selected for building in solution configuration "Development Editor|Win64".
Project "C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\CONN_MN_SoR.sln" (1) is building "C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj" (2) on node 1 (default targets).
Build:
  C:\UE_5.1\Engine\Build\BatchFiles\Build.bat CONN_MN_SoREditor Win64 Development -Project="C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\CONN_MN_SoR.uproject" -WaitMutex -FromMsBuild
  Running UnrealBuildTool: dotnet "..\..\Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.dll" CONN_MN_SoREditor Win64 Development -Project="C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\CONN_MN_SoR.uproject" -WaitMutex -FromMsBuild
  Log file: C:\Users\impho\AppData\Local\UnrealBuildTool\Log.txt
  Invalidating makefile for CONN_MN_SoREditor (working set of source files changed)
  Parsing headers for CONN_MN_SoREditor
    Running Internal UnrealHeaderTool "C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\CONN_MN_SoR.uproject" "C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\Build\Win64\CONN_MN_SoREditor\Development\CONN_MN_SoREditor.uhtmanifest" -WarningsAsErrors -installed
  Total of 1 written
  Reflection code generated for CONN_MN_SoREditor in 2.1939658 seconds
  Building CONN_MN_SoREditor...
  Using Visual Studio 2022 14.34.31935 toolchain (C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.34.31933) and Windows 10.0.20348.0 SDK (C:\Program Files (x86)\Windows Kits\10).
  Determining max actions to execute in parallel (8 physical cores, 8 logical cores)
    Executing up to 8 processes, one per physical core
    Requested 1.5 GB free memory per action, 2.15 GB available: limiting max parallel actions to 1
  Building 7 actions with 1 process...
  [1/7] Link UnrealEditor-ConnCoreFramework.lib cancelled
  [2/7] Link UnrealEditor-ConnCoreFramework.dll cancelled
  [3/7] Compile Module.ConnCoreFramework.cpp
C:\UE_5.1\Engine\Source\Runtime\CoreUObject\Public\UObject\ObjectPtr.h(917): error C2794: 'Type': is not a member of any direct or indirect base class of 'TPointedToTypeImpl<AllocatorType>' [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
          with
          [
              AllocatorType=UConnActorManagerComponent
          ]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(72): note: see reference to alias template instantiation 'TPointedToType<AllocatorType>' being compiled
          with
          [
              AllocatorType=UConnActorManagerComponent
          ]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Private\Source\ConnActorManagerComponent.cpp(25): note: see reference to function template instantiation 'void UConnGameStateComponent::VerifySingleInstance<UConnActorManagerComponent>(void) const' being compiled
C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(72): error C2938: 'TPointedToType' : Failed to specialize alias template [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(72): error C2062: type 'unknown-type' unexpected [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(75): error C2065: 'T': undeclared identifier [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(75): error C2059: syntax error: ',' [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): error C2665: 'AActor::GetComponents': no overloaded function could convert all the argument types [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\UE_5.1\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h(3615): note: could be 'void AActor::GetComponents<AllocatorType>(TArray<UActorComponent *,AllocatorType> &,bool) const'
          with
          [
              AllocatorType=UConnActorManagerComponent
          ]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: 'void AActor::GetComponents<AllocatorType>(TArray<UActorComponent *,AllocatorType> &,bool) const': cannot convert argument 1 from 'unknown-type' to 'TArray<UActorComponent *,AllocatorType> &'
          with
          [
              AllocatorType=UConnActorManagerComponent
          ]
  C:\UE_5.1\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h(3528): note: or       'void AActor::GetComponents<AllocatorType>(TSubclassOf<UActorComponent>,TArray<UActorComponent *,AllocatorType> &,bool) const'
          with
          [
              AllocatorType=UConnActorManagerComponent
          ]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: 'void AActor::GetComponents<AllocatorType>(TSubclassOf<UActorComponent>,TArray<UActorComponent *,AllocatorType> &,bool) const': cannot convert argument 2 from 'bool' to 'TArray<UActorComponent *,AllocatorType> &'
          with
          [
              AllocatorType=UConnActorManagerComponent
          ]
  C:\UE_5.1\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h(3594): note: or       'void AActor::GetComponents(TArray<TObjectPtr<T>,AllocatorType> &,bool) const'
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: 'void AActor::GetComponents(TArray<TObjectPtr<T>,AllocatorType> &,bool) const': could not deduce template argument for 'TArray<TObjectPtr<AllocatorType>,AllocatorType> &' from 'unknown-type'
          with
          [
              AllocatorType=UConnActorManagerComponent
          ]
  C:\UE_5.1\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h(3570): note: or       'void AActor::GetComponents(TArray<T*,AllocatorType> &,bool) const'
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: 'void AActor::GetComponents(TArray<T*,AllocatorType> &,bool) const': could not deduce template argument for 'TArray<AllocatorType*,AllocatorType> &' from 'unknown-type'
          with
          [
              AllocatorType=UConnActorManagerComponent
          ]
  C:\UE_5.1\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h(3548): note: or       'void AActor::GetComponents(TArray<T,AllocatorType> &,bool) const'
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: 'void AActor::GetComponents(TArray<T,AllocatorType> &,bool) const': could not deduce template argument for 'TArray<AllocatorType,AllocatorType> &' from 'unknown-type'
          with
          [
              AllocatorType=UConnActorManagerComponent
          ]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: while trying to match the argument list '(unknown-type, bool)'
  [4/7] Compile Module.ConnAttributeFramework.cpp
C:\UE_5.1\Engine\Source\Runtime\CoreUObject\Public\UObject\ObjectPtr.h(917): error C2794: 'Type': is not a member of any direct or indirect base class of 'TPointedToTypeImpl<AllocatorType>' [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
          with
          [
              AllocatorType=UConnAttributeManagerComponent
          ]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(72): note: see reference to alias template instantiation 'TPointedToType<AllocatorType>' being compiled
          with
          [
              AllocatorType=UConnAttributeManagerComponent
          ]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnToolkitCHISEL\ConnAttributeFramework\Private\Source\ConnAttributeManagerComponent.cpp(24): note: see reference to function template instantiation 'void UConnGameStateComponent::VerifySingleInstance<UConnAttributeManagerComponent>(void) const' being compiled
C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(72): error C2938: 'TPointedToType' : Failed to specialize alias template [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(72): error C2062: type 'unknown-type' unexpected [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(75): error C2065: 'T': undeclared identifier [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(75): error C2059: syntax error: ',' [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): error C2665: 'AActor::GetComponents': no overloaded function could convert all the argument types [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\UE_5.1\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h(3615): note: could be 'void AActor::GetComponents<UConnAttributeManagerComponent>(TArray<UActorComponent *,UConnAttributeManagerComponent> &,bool) const'
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: 'void AActor::GetComponents<UConnAttributeManagerComponent>(TArray<UActorComponent *,UConnAttributeManagerComponent> &,bool) const': cannot convert argument 1 from 'unknown-type' to 'TArray<UActorComponent *,UConnAttributeManagerComponent> &'
  C:\UE_5.1\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h(3528): note: or       'void AActor::GetComponents<AllocatorType>(TSubclassOf<UActorComponent>,TArray<UActorComponent *,UConnAttributeManagerComponent> &,bool) const'
          with
          [
              AllocatorType=UConnAttributeManagerComponent
          ]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: 'void AActor::GetComponents<AllocatorType>(TSubclassOf<UActorComponent>,TArray<UActorComponent *,UConnAttributeManagerComponent> &,bool) const': cannot convert argument 2 from 'bool' to 'TArray<UActorComponent *,UConnAttributeManagerComponent> &'
          with
          [
              AllocatorType=UConnAttributeManagerComponent
          ]
  C:\UE_5.1\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h(3594): note: or       'void AActor::GetComponents(TArray<TObjectPtr<T>,AllocatorType> &,bool) const'
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: 'void AActor::GetComponents(TArray<TObjectPtr<T>,AllocatorType> &,bool) const': could not deduce template argument for 'TArray<TObjectPtr<UConnAttributeManagerComponent>,AllocatorType> &' from 'unknown-type'
  C:\UE_5.1\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h(3570): note: or       'void AActor::GetComponents(TArray<T*,AllocatorType> &,bool) const'
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: 'void AActor::GetComponents(TArray<T*,AllocatorType> &,bool) const': could not deduce template argument for 'TArray<UConnAttributeManagerComponent*,AllocatorType> &' from 'unknown-type'
  C:\UE_5.1\Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h(3548): note: or       'void AActor::GetComponents(TArray<T,AllocatorType> &,bool) const'
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: 'void AActor::GetComponents(TArray<T,AllocatorType> &,bool) const': could not deduce template argument for 'TArray<UConnAttributeManagerComponent,AllocatorType> &' from 'unknown-type'
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): note: while trying to match the argument list '(unknown-type, bool)'
  [5/7] Link UnrealEditor-ConnAttributeFramework.dll cancelled
  [6/7] Link UnrealEditor-ConnAttributeFramework.lib cancelled
  [7/7] WriteMetadata CONN_MN_SoREditor.target cancelled
C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.MakeFile.Targets(44,5): error MSB3073: The command "C:\UE_5.1\Engine\Build\BatchFiles\Build.bat CONN_MN_SoREditor Win64 Development -Project="C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\CONN_MN_SoR.uproject" -WaitMutex -FromMsBuild" exited with code 6. [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
Done Building Project "C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj" (default targets) -- FAILED.
Done Building Project "C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\CONN_MN_SoR.sln" (Build target(s)) -- FAILED.

Build FAILED.

"C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\CONN_MN_SoR.sln" (Build target) (1) ->
"C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj" (default target) (2) ->
(Build target) -> 
  C:\UE_5.1\Engine\Source\Runtime\CoreUObject\Public\UObject\ObjectPtr.h(917): error C2794: 'Type': is not a member of any direct or indirect base class of 'TPointedToTypeImpl<AllocatorType>' [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(72): error C2938: 'TPointedToType' : Failed to specialize alias template [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(72): error C2062: type 'unknown-type' unexpected [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(75): error C2065: 'T': undeclared identifier [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(75): error C2059: syntax error: ',' [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): error C2665: 'AActor::GetComponents': no overloaded function could convert all the argument types [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\UE_5.1\Engine\Source\Runtime\CoreUObject\Public\UObject\ObjectPtr.h(917): error C2794: 'Type': is not a member of any direct or indirect base class of 'TPointedToTypeImpl<AllocatorType>' [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(72): error C2938: 'TPointedToType' : Failed to specialize alias template [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(72): error C2062: type 'unknown-type' unexpected [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(75): error C2065: 'T': undeclared identifier [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(75): error C2059: syntax error: ',' [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Plugins\ConnFramework\Source\ConnGameCore\ConnCoreFramework\Public\ConnGameStateComponent.h(78): error C2665: 'AActor::GetComponents': no overloaded function could convert all the argument types [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]
  C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.MakeFile.Targets(44,5): error MSB3073: The command "C:\UE_5.1\Engine\Build\BatchFiles\Build.bat CONN_MN_SoREditor Win64 Development -Project="C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\CONN_MN_SoR.uproject" -WaitMutex -FromMsBuild" exited with code 6. [C:\Users\impho\OneDrive\Rapidfire\Games\CONNIPTION\Mainline\Shadows of Rage\Game\CONN_MN_SoR\Intermediate\ProjectFiles\CONN_MN_SoR.vcxproj]

    0 Warning(s)
    13 Error(s)

Time Elapsed 00:00:10.32

If anyone can tell me exactly what I’m doing wrong here - as it seems to be an issue with my template syntax - that would be very much appreicated. I’m incredibly new to templates and am trying to use them more often to make my framework as expandable as possible so that future updates aren’t so difficult.

Other helpful points:

  • Game State Component and my other derived components are in separate modules.
  • Game State Component (UConnGameStateComponent) exists in a CoreFramework module which contains base classes (custom Actor Component base class, etc.)
  • Attribute Manager Component exists in an AttributeSystem module, and Actor Manager Component (currently unused, but it does exist and could be used later) exists in the same CoreFramework module as specified before.

If anything is too confusing or if you need more information let me know, I’m trying to give useful information as to what I want to do and the rationale behind WHY I want to do it, but I also talk a lot more than I probably should.

Thanks.

A question: Will your game be able to run normally WITHOUT these components?
If not, I wouldn’t go with component-based system and just bound this in your GameState class.
This will also eliminate the problem with multiple instances (gamestate is one per player)

Hello,

Thanks for the idea. I’ll answer the two parts of your question and explain why that’s not really a possibility for my design.

If not, I wouldn’t go with component-based system and just bound this in your GameState class.

This is a plugin/extension framework for Unreal Engine. It’s designed to slot into both new and existing projects alike. If I was to overwrite the Game State Base, the developer may be forced to choose between my Game State Base and a Game State Base that they already have or want to also use.

Let’s say a developer has a Game State Base from FooPlugin or already has one in FooProject. If they have to choose between FooGameState and ConnGameState, when FooGameState has a lot of features they want but ConnGameState is required to implement core framework functionality, that would be an incredibly tough pill to swallow for how much this framework will sell for - which to say the least would be quite a lot. Think of it like the reason subsystems were created.

That’s the same reason I’m already working with Game Instance subsystems and trying to find a solution to pre-bind very basic input (i.e. just common axis inputs and a basic action input system [that is NOT dependent on Enhanced Input since this extension must also work on Unreal Engine 4.22]) without having to override the Player Controller or Character directly.

Will your game be able to run normally WITHOUT these components?

Not exactly. For example, I utilize the Attribute Manager Component to handle “binding” attributes and updating attributes that are on a timer.

Consider the following scenario:

  • Attribute: Damage
  • Type: Float
  • Location: BP_MyGamePlayerState, in CPlayerStateAttributeComponent
  • Description: This attribute determines how much damage the player deals with melee weapons. This applies to all melee weapons that the PLAYER has equipped, and can be upgraded over time. When used, look for attribute “Health” of type “Float” on hit actor’s attribute component (if any), and decrease it by this amount. Negative values will heal.

In this scenario, I use the Attribute Manager Component to conditionally bind my weapon items’ Damage value to the one specified in the Player State. The Attribute Component doesn’t have to bear any of that workload of managing its own bindings (which could also be good for multiplayer) - the Attribute Manager Component, containing an array of Attribute Components, can handle all of that by itself and quite naturally.

The Actor Manager Component is currently unused because I found an alternative solution to its original purpose, but it may be utilized later to create custom “Pawn State” or “Actor State” classes if I decided to implement them for, let’s say, allies in a big RPG who might have upgradeable attributes of their own without having to tie it directly to the class.

I’m hoping that clears up anything about the actual design of the framework, and the reason why that wouldn’t work. However I understand why you would think that and I appreciate the help.

Generally you shouldn’t ever have to specify the “AllocatorType” template argument, whether you are in a template or not.

Your template should have the T argument instead. Not sure what you tried to do with typedef TPointedToType<AllocatorType> T, but it sounds like TArray<T*, AllocatorType> is always gonna be wrong.

	template<class T>
	void VerifySingleInstance() const
	{
		// Create array of components
		TArray<T*> LocComponents;

		// Get components and fill array
		OwningGameState->GetComponents<T>(LocComponents, true);

		// Ensure we don't have duplicates
		if(LocComponents.Num() > 1)
		{
			// We found a duplicate, crash the game
			const FString ErrorMessage = "There can only be ONE component of class (" + GetNameSafe(this) + ") in your Game State! Please check for duplicate components.";
			UConnGameplayStatics::PrintScreenLog(this, ErrorMessage, 5, EConnLogType::Fatal);
		}
	}

Regarding the non-template version, it should work and is probably even simpler, but you probably got stuck with base class in some way or another, ie. if you used GetComponents<UGameStateComponent> or if you used StaticClass() which is static, then it’s gonna use the base class when child classes are executing the code, which is not good.

Instead you should be using GetClass() which is polymorphic, so each subclass is gonna execute it with its own class. Then you don’t even need to do anything in child classes. Just if you override BeginPlay don’t forget to call Super.

void UGameStateComponent::BeginPlay()
{
    Super::BeginPlay();

    TArray<UActorComponent*> LocComponents;
    GetOwner<AActor>()->GetComponents(GetClass(), LocComponents, true);
    if (LocComponents.Num() > 1)
    {
        const FString ErrorMessage = "There can only be ONE component of class (" + GetClass() + ") in your Game State! Please check for duplicate components.";
        UConnGameplayStatics::PrintScreenLog(this, ErrorMessage, 5, EConnLogType::Fatal);
    }
}

If your base class is gonna be instanced -or- if you are using inheritance among children (for example attribute manager extends actor manager) then you’ll have to search exact classes, so pass false in GetComponents instead.

This is a plugin/extension framework for Unreal Engine. It’s designed to slot into both new and existing projects alike. If I was to overwrite the Game State Base, the developer may be forced to choose between my Game State Base and a Game State Base that they already have or want to also use.

Let’s say a developer has a Game State Base from FooPlugin or already has one in FooProject. If they have to choose between FooGameState and ConnGameState, when FooGameState has a lot of features they want but ConnGameState is required to implement core framework functionality, that would be an incredibly tough pill to swallow for how much this framework will sell for - which to say the least would be quite a lot. Think of it like the reason subsystems were created.

This is a fair point.

Without going too much into the details:
But then, if you don’t want to touch the GameState class then what’s the point of adding a component to it? Then the class, apart from being a “just a container” does not use the functionality of this component in any way. This looks more like needing for a dedicated Actor type than messing around with an existing class and potential conflicts with other plugins.

Hey thanks for the reply,

The idea of using a component is that if the developer has an existing Game State, they can add the component to their game state (or if it’s a C++ Game State, they could add it to a derived class, add a C++ derived component to the C++ class if it’s their own Game State, etc.).

But you do bring up a good point about this being a perfect job for a separate actor, and that’s something I’ll add to my backlog as I continue to work on this. It’s likely I would still need something to automatically spawn the actor - that, or just warn the developer (as I’ve done already) that they need the actor and just let the developer place that manually.

Thanks for the response.

Not sure what you tried to do with typedef TPointedToType<AllocatorType> T

Admittedly I saw this in Unreal’s GetComponents<>() function and just made a blind guess at it, lol

Regarding the non-template version, it should work and is probably even simpler, but you probably got stuck with base class in some way or another, ie. if you used GetComponents<UGameStateComponent> or if you used StaticClass() which is static, then it’s gonna use the base class when child classes are executing the code, which is not good.

That is indeed what I had tried before, but thank you for suggesting GetClass() instead. I will try this and mark this as the solution if it works!

Good news, the bottom works flawlessly!

The function I ended up writing:

void UConnGameStateComponent::VerifySingleInst() const
{
	// Local component array
	TArray<UActorComponent*> LocComponents;

	// Fill array with matching components by class
	GetOwner<AActor>()->GetComponents(GetClass(), LocComponents, false);

	// If we find duplicates...
	if (LocComponents.Num() > 1)
	{
		// ... crash the engine and print a hopefully useful message
		const FString ErrorMessage = "There can only be ONE component of class (" + GetNameSafe(GetClass()) + ") in your Game State! Please check for duplicate components.";
		UConnGameplayStatics::PrintScreenLog(this, ErrorMessage, 5, EConnLogType::Fatal);
	}
}

I will definitely look into Emaer’s suggestion, I think they’ve got a good point and I’m sure I could try something similar with actual actors (in fact, it might even be easier). Thanks for the help!