DataTable sometimes null

So I have a private: TObjectPtr<UDataTable> EffectsDataTable that is being static loaded by string in a UGameInstanceSubsystem::Initialize(). the dataTableRow in question has some int32s, floats, and a few Enumerations, and the DataTable is using integers as the Row Names.

the thing is when I try to access the DataTable through specific functions the DataTable is null, but in others it isn’t null. with nothing else happening in the level for the purpose of memory the Table is just null for specific function.

for example I have

// this function is called with an EXEC and works fine
void UAssetRepo::LogEntityEffects(const FString inStr)
{
	UE_LOG(LogTemp, Error, TEXT("LogEntityEffects Start"));
	if ( EffectsDataTable == nullptr )
	{
		UE_LOG(LogTemp, Error, TEXT("EntityEffectDataTable is null"));
		return;
	}
	TArray<FName> RowNames = EffectsDataTable->GetRowNames();
	int ii = 0;
	for ( FName name : RowNames )
	{
		UE_LOG(LogTemp, Display, TEXT("%i : \"%s\""), ii++, *name.ToString());
	}
	const FString ContextStr(TEXT("LogAllDatatTableRows"));
	for ( const FName& name : RowNames )
	{
		FEntityEffectRow* Row = EffectsDataTable->FindRow<FEntityEffectRow>(name, ContextStr);
		if ( Row )
		{
			UE_LOG(LogTemp, Display, TEXT("RowName %s, Data: %s"), *name.ToString(), *Row->ToString());
		}
		else
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to find row with name: %s"), *name.ToString());
		}
	}
	if ( inStr != TEXT("") && inStr.IsNumeric() )
	{
        // this conversion is to prove to myself that the conversion from int to FName is valid
		int32 num = FCString::Atoi(*inStr);
		FName RowName = FName(FString::FromInt(num));
		UE_LOG(LogTemp, Display, TEXT("RowName searched for: \"%s\""), *RowName.ToString());
		FEntityEffectRow* row = EffectsDataTable->FindRow<FEntityEffectRow>(RowName, ContextStr);
		if ( row )
		{
			UE_LOG(LogTemp, Display, TEXT("RowName %s, Data: %s"), *RowName.ToString(), *row->ToString());
		}
		else
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to find row with name: %s"), *RowName.ToString());
		}
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("Arg invalid"));
	}
	UE_LOG(LogTemp, Error, TEXT("LogEntityEffects End"));;
}

then I have another function

bool UAssetRepo::GetEffectSimFromID(int32 inID, FEntityEffectSim& outResult)
{
	const FName rowName = FName(FString::FromInt(inID));
	UE_LOG(LogTemp, Display, TEXT("GetEffectSimFromID(\"%i\")-> \"%s\""), inID, *rowName.ToString());
    // this crashes here every time.
/[101]/	if ( EffectsDataTable == nullptr )
	{
		UE_LOG(LogTemp, Error, TEXT("EntityEffectDataTable is null"));
		return false;
	}
	if ( inID > INDEX_NONE )
	{
		if ( FEntityEffectRow* row = EffectsDataTable->FindRow<FEntityEffectRow>(rowName, TEXT("AssetRepo::GetEffectSimFromID()")) )
		{
			outResult = *row;
			return true;
		}
	}
	return false;
}

for testing there is only 1 element in the DataTable with a Row Name of 92; when I call the exec function with “92” it will give me the expected result:

[2024.10.21-00.40.47:512][312]Cmd: LogEntityEffects 92
[2024.10.21-00.40.47:513][312]LogTemp: Error: LogEntityEffects Start
[2024.10.21-00.40.47:513][312]LogTemp: Display: 0 : "92"
[2024.10.21-00.40.47:513][312]LogTemp: Display: RowName 92, Data: ID: 92; Amount: 2
[2024.10.21-00.40.47:513][312]LogTemp: Display: RowName searched for: "92"
[2024.10.21-00.40.47:513][312]LogTemp: Display: RowName 92, Data: ID: 92; Amount: 2
[2024.10.21-00.40.47:513][312]LogTemp: Error: LogEntityEffects End
// ...
[2024.10.21-00.42.00:774][312]LogTemp: Display: GetEffectSimFromID("92")-> "92"
[2024.10.21-00.42.02:338][477]LogWindows: Error: === Critical error: ===
[2024.10.21-00.42.02:338][477]LogWindows: Error: 
[2024.10.21-00.42.02:338][477]LogWindows: Error: Fatal error!
[2024.10.21-00.42.02:338][477]LogWindows: Error: 
[2024.10.21-00.42.02:338][477]LogWindows: Error: Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x00000000000001d8
[2024.10.21-00.42.02:338][477]LogWindows: Error: 
[2024.10.21-00.42.02:338][477]LogWindows: Error: [Callstack] 0x00007ffe3895dec2 UnrealEditor-MyProject.dll!UAssetRepo::GetEffectSimFromID() [...AssetRepo.cpp:101]

no matter when I call the GetEffectSimFromID() with a valid rowName or not even if I just successfully accessed the DataTable through the exec function I get that it is when I try to get an element by RowName specifically. there is nothing going on for the GC to run away with my DataTable so it should still be there. the AssetRepo has some Async stuff going on in it, but the DataTable is only accessed in 3 function and none of them are async so thread blocking should not the issue.

how can I narrow down what is going on, and why just this function has the DataTable as null.

Considering that the second function contains a null check of the data table, I would assume that the issue is not that the table is null but rather the UAssetRepo object on which you are calling the GetEffectSimFromID() function. C++ is a bit weird in that it lets you call a function on a nullptr and it will work just fine until you try to access any data, at which point it throws a memory access violation. So check the code that calls this function, the issue is likely there rather than in the function itself.

1 Like

I was calling it from a cached pointer to the Subsystem… that I hadn’t actually cached…
thank you for figuring out my dumb mistake.

after doing some research, and thinking:
the Objects/Classes/Structs that are in scope live in RAM
but the Op-Code that is derived from the lines of code is stored in the Executable on the Disk separate from the Objects/Classes/Structs that are instantiated. This is to ensure that if you have 1000s of instances of an object that the Op-Code for those objects is not duplicated for each one.

So what ends up happening

if ( MyObject == nullptr )
if ( MyObject != nullptr )

takes the address of MyObject and compares its address to nullptr

if ( MyObject->MyFunction() )

goes and grabs the Op-Code from the executable for MyFunction(), and attempts to grab the data the MyObject pointer points to, from RAM carrying along any of the arguments the function received. Loading everything into the Processor Cache with overflow going to RAM. then attempts to run the Op-Code, and when in the function you have if( myMemberPtr != nullptr ) it is only then that the MyObject is actually referenced in the MyFunction() but because the MyObject didn’t actually exist the Access_Violation is given as an offset of where it should be, because “technically” a true NUL_REFFERNCE_EXCEPTION would have an address of 0x0

the “reason” there is no in-built exception for “calling a function on a null pointer” is in the name of “SPEED” to save a single instruction line per function call to a pointer object.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.