error C2039: 'CreateTableFromCSVString' : is not a member of 'UDataTable'

Hi I have following error coming out only when packaging (when compiling via Visual Studio 2013 everything is ok):

The critical line is here I guess:

This is my BPFunctionLibrary.h:


#pragma once
 
 #include "Engine/DataTable.h"
 #include "Kismet/BlueprintFunctionLibrary.h"
 #include "BPFunctionLibrary.generated.h"
 
 /**
  * 
  */
 UCLASS()
 class VIRTUALHOME_API UBPFunctionLibrary : public UBlueprintFunctionLibrary
 {
     GENERATED_BODY()
     
 public:
     UFUNCTION(BlueprintCallable, Category = "BPFunctionLibrary")
         static UDataTable* ReimportDataTable();
     
     
 };

This is my BPFunctionLibrary.cpp:


 #include "Virtualhome.h"
 #include "BPFunctionLibrary.h"
 
 UDataTable* UBPFunctionLibrary::ReimportDataTable() {
 
     //Create DataTable object holding csv data
     //UDataTable* myDataTable = static_cast<UDataTable*>(StaticLoadObject(UDataTable::StaticClass(), NULL, TEXT("/Game/Data/Lista.Lista")));
     UDataTable* Lista;
     static ConstructorHelpers::FObjectFinder<UDataTable>
         Lista_BP(TEXT("myDataTable'/Game/Data/Lista.Lista'"));
     Lista = Lista_BP.Object;
 
     FString csvFile = FPaths::GameContentDir() + "Lista.csv";
 
     if (FPaths::FileExists(csvFile))
     {
         FString FileContent;
 
         //Read the csv file from game's content
         FFileHelper::LoadFileToString(FileContent, *csvFile);
         TArray<FString> problems = Lista->CreateTableFromCSVString(FileContent);
         UE_LOG(LogTemp, Warning, TEXT("Content is %s"), *FileContent);
 
         if (problems.Num() > 0)
         {
             for (int32 ProbIdx = 0; ProbIdx < problems.Num(); ProbIdx++)
             {
                 //Log the errors
                 UE_LOG(LogTemp, Warning, TEXT("Problem with reimport!"));    
             }
         }
         else
         {
             //Updated Successfully
             UE_LOG(LogTemp, Warning, TEXT("Successful reimport!"));
         }
 
 
     }
 
     return Lista;
 }
 
 
 

I think the problem is with #if WITH_EDITOR clause. Is there any workaround for this? I need to use this code in packaged build.

This is part that causes packaging error problem from engine’s native DataTable.h:


#if WITH_EDITOR

private:

	//when RowStruct is being modified, row data is stored serialized with tags
	TArray<uint8> RowsSerializedWithTags;
	TSet<UObject*> TemporarilyReferencedObjects;

public:

	ENGINE_API void CleanBeforeStructChange();
	ENGINE_API void RestoreAfterStructChange();

	/** Output entire contents of table as a string */
	ENGINE_API FString GetTableAsString() const;

	/** Output entire contents of table as CSV */
	ENGINE_API FString GetTableAsCSV() const;

	/** Output entire contents of table as JSON */
	ENGINE_API FString GetTableAsJSON() const;

	/** Output entire contents of table as JSON */
	ENGINE_API bool WriteTableAsJSON(const TSharedRef< TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR> > >& JsonWriter) const;

	/** Output the fields from a particular row (use RowMap to get RowData) to an existing JsonWriter */
	ENGINE_API bool WriteRowAsJSON(const TSharedRef< TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR> > >& JsonWriter, const void* RowData) const;

	/** 
	 *	Create table from CSV style comma-separated string. 
	 *	RowStruct must be defined before calling this function. 
	 *	@return	Set of problems encountered while processing input
	 */
	ENGINE_API TArray<FString> CreateTableFromCSVString(const FString& InString);

	/** 
	*	Create table from JSON style string. 
	*	RowStruct must be defined before calling this function. 
	*	@return	Set of problems encountered while processing input
	*/
	ENGINE_API TArray<FString> CreateTableFromJSONString(const FString& InString);

	/** Get an array of all the column titles, using the friendly display name from the property */
	ENGINE_API TArray<FString> GetColumnTitles() const;

	/** Get an array of all the column titles, using the unique name from the property */
	ENGINE_API TArray<FString> GetUniqueColumnTitles() const;

	TArray<UProperty*> GetTablePropertyArray(const TArray<const TCHAR*>& Cells, UStruct* RowStruct, TArray<FString>& OutProblems);

	/** Get array for each row in the table. The first row is the titles*/
	ENGINE_API TArray< TArray<FString> > GetTableData() const;
#endif //WITH_EDITOR

Yes. Your packaging settings include a game build, which does not include editor code. The only workaround would be to remove the WITH_EDITOR clause from UDataTable code, but that code might in turn call more editor code, leading to cascading modifications.

But, more importantly, trying to compile in editor code could eventually pull editor modules into a shipping build, which goes against the UE4 EULA.

I’m not sure why you would need to reimport a CSV data table in a cooked build – once the table is imported into uasset format, it gets cooked alongside the build and you don’t need to reimport it.

Thanks for immediate response. Removing WITH_EDITOR clause from UDataTable produces following error:

The problem here is I really need to dynamically update my datatable during runtime from external location. If updating during runtime is not possible I would like to at least update it once, after starting application but this seems like a similar problem to what we have right now.

You need to also remove the WITH_EDITOR clause from the main body definition. A safer alternative would be to roll out your own runtime version of the FDataTableImporterCSV() used by that function, however. That way you control what makes it in or not, and you’ll know as you compile the game build whether you’re calling anything editor-specific.

Removing the WITH_EDITOR clause from the main body definition did not help. The same error still occur.

UnrealBuildTool: BPFunctionLibrary.cpp.obj : error LNK2019: unresolved external symbol "public: class TArray<class FString,class FDefaultAllocator> __cdecl UDataTable::CreateTableFromCSVString(class FString const &)" (?CreateTableFromCSVString@UDataTable@@QEAA?AV?$TArray@VFString@@VFDefaultAllocator@@@@AEBVFString@@@Z) referenced in function "public: static class UDataTable * __cdecl UBPFunctionLibrary::ReimportDataTable(void)" 

UDataTable might be MinimalAPI, and may not export. You can remove that from the UCLASS() macro at the top of the file.

Naturally, all of these changes aren’t recommended. No telling what this may actually do outside of the editor.

CreateTableFromCSVString is exported.


ENGINE_API TArray<FString> CreateTableFromCSVString(const FString& InString);

Yeah, UDataTable is MinimalAPI but as @anonymous_user_dd7d789e said it is already exported. Changing UCLASS parameter didn’t help.

Ok, I got rid of earlier errors (which was caused by .h and .cpp files mismatch). Now, I get following errors:


MainFrameActions: Packaging (Windows (64-bit)): UnrealBuildTool: C:\Program Files\Epic Games\4.9\Engine\Source\Runtime\Engine\Classes\Engine/DataTable.h(205) : error C2146: syntax error : missing ';' before identifier 'FDataTableRowHandle'
MainFrameActions: Packaging (Windows (64-bit)): UnrealBuildTool: C:\Program Files\Epic Games\4.9\Engine\Source\Runtime\Engine\Classes\Engine/DataTable.h(205) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
MainFrameActions: Packaging (Windows (64-bit)): UnrealBuildTool: C:\Program Files\Epic Games\4.9\Engine\Source\Runtime\Engine\Classes\Engine/DataTable.h(248) : error C2143: syntax error : missing ';' before 'const'
MainFrameActions: Packaging (Windows (64-bit)): UnrealBuildTool: C:\Program Files\Epic Games\4.9\Engine\Source\Runtime\Engine\Classes\Engine/DataTable.h(248) : error C4430: missing type specifier - int assumed. Note: C++ does not support defaul

Which corresponds to following line in DataTable.h file:



GENERATED_USTRUCT_BODY()

DataTable.h


// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "DataTableUtils.h" // Needed here for LogDataTable
#include "DataTable.generated.h"


// forward declare JSON writer
template <class CharType>
struct TPrettyJsonPrintPolicy;
template <class CharType, class PrintPolicy>
class TJsonWriter;


/**
 * Base class for all table row structs to inherit from.
 */
USTRUCT()
struct FTableRowBase
{
	GENERATED_USTRUCT_BODY()

	FTableRowBase() { }
};


/**
 * Imported spreadsheet table.
 */
UCLASS()
class UDataTable
	: public UObject
{
	GENERATED_UCLASS_BODY()

	/** Structure to use for each row of the table, must inherit from FTableRowBase */
	UPROPERTY()
	UScriptStruct*			RowStruct;

	/** Map of name of row to row data structure. */
	TMap<FName, uint8*>		RowMap;

	// Begin UObject interface.
	ENGINE_API virtual void FinishDestroy() override;
	ENGINE_API virtual void Serialize(FArchive& Ar) override;
	ENGINE_API static void AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector);
	ENGINE_API virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const override;
	ENGINE_API virtual void PostInitProperties() override;
	ENGINE_API virtual void PostLoad() override;

	UPROPERTY(VisibleAnywhere, Instanced, Category=ImportSettings)
	class UAssetImportData* AssetImportData;

	/** The filename imported to create this object. Relative to this object's package, BaseDir() or absolute */
	UPROPERTY()
	FString ImportPath_DEPRECATED;


	// End  UObject interface

	// Begin UDataTable interface

	/** Function to find the row of a table given its name. */
	template <class T>
	T* FindRow(FName RowName, const FString& ContextString, bool bWarnIfRowMissing = true) const
	{
		if(RowStruct == nullptr)
		{
			UE_LOG(LogDataTable, Error, TEXT("UDataTable::FindRow : DataTable '%s' has no RowStruct specified (%s)."), *GetPathName(), *ContextString);
			return nullptr;
		}

		if(!RowStruct->IsChildOf(T::StaticStruct()))
		{
			UE_CLOG(bWarnIfRowMissing, LogDataTable, Error, TEXT("UDataTable::FindRow : Incorrect type specified for DataTable '%s' (%s)."), *GetPathName(), *ContextString);
			return nullptr;
		}

		if(RowName == NAME_None)
		{
			UE_CLOG(bWarnIfRowMissing, LogDataTable, Warning, TEXT("UDataTable::FindRow : NAME_None is invalid row name for DataTable '%s' (%s)."), *GetPathName(), *ContextString);
			return nullptr;
		}

		uint8* const* RowDataPtr = RowMap.Find(RowName);
		if (RowDataPtr == nullptr)
		{
			if (bWarnIfRowMissing)
			{
				UE_LOG(LogDataTable, Warning, TEXT("UDataTable::FindRow : Row '%s' not found in DataTable '%s' (%s)."), *RowName.ToString(), *GetPathName(), *ContextString);
			}
			return nullptr;
		}

		uint8* RowData = *RowDataPtr;
		check(RowData);

		return (T*)RowData;
	}

	/** Returns the column property where PropertyName matches the name of the column property. Returns nullptr if no match is found or the match is not a supported table property */
	ENGINE_API UProperty* FindTableProperty(const FName& PropertyName) const;

	uint8* FindRowUnchecked(FName RowName, bool MustExist=false) const
	{
		if(RowStruct == nullptr)
		{
			//UE_CLOG(MustExist, LogDataTable, Error, TEXT("UDataTable::FindRow : DataTable '%s' has no RowStruct specified (%s)."), *GetPathName(), *ContextString);
			return nullptr;
		}

		if(RowName == NAME_None)
		{
			//UE_CLOG(MustExist, LogDataTable, Warning, TEXT("UDataTable::FindRow : NAME_None is invalid row name for DataTable '%s' (%s)."), *GetPathName(), *ContextString);
			return nullptr;
		}

		uint8* const* RowDataPtr = RowMap.Find(RowName);

		if(RowDataPtr == nullptr)
		{
			return nullptr;
		}

		uint8* RowData = *RowDataPtr;
		check(RowData);

		return RowData;
	}

	/** Empty the table info (will not clear RowStruct) */
	ENGINE_API void EmptyTable();

	ENGINE_API TArray<FName> GetRowNames() const;



private:

	//when RowStruct is being modified, row data is stored serialized with tags
	TArray<uint8> RowsSerializedWithTags;
	TSet<UObject*> TemporarilyReferencedObjects;

public:

	ENGINE_API void CleanBeforeStructChange();
	ENGINE_API void RestoreAfterStructChange();

	/** Output entire contents of table as a string */
	ENGINE_API FString GetTableAsString() const;

	/** Output entire contents of table as CSV */
	ENGINE_API FString GetTableAsCSV() const;

	/** Output entire contents of table as JSON */
	ENGINE_API FString GetTableAsJSON() const;

	/** Output entire contents of table as JSON */
	ENGINE_API bool WriteTableAsJSON(const TSharedRef< TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR> > >& JsonWriter) const;

	/** Output the fields from a particular row (use RowMap to get RowData) to an existing JsonWriter */
	ENGINE_API bool WriteRowAsJSON(const TSharedRef< TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR> > >& JsonWriter, const void* RowData) const;

	/** 
	 *	Create table from CSV style comma-separated string. 
	 *	RowStruct must be defined before calling this function. 
	 *	@return	Set of problems encountered while processing input
	 */
	ENGINE_API TArray<FString> CreateTableFromCSVString(const FString& InString);

	/** 
	*	Create table from JSON style string. 
	*	RowStruct must be defined before calling this function. 
	*	@return	Set of problems encountered while processing input
	*/
	ENGINE_API TArray<FString> CreateTableFromJSONString(const FString& InString);

	/** Get an array of all the column titles, using the friendly display name from the property */
	ENGINE_API TArray<FString> GetColumnTitles() const;

	/** Get an array of all the column titles, using the unique name from the property */
	ENGINE_API TArray<FString> GetUniqueColumnTitles() const;

	TArray<UProperty*> GetTablePropertyArray(const TArray<const TCHAR*>& Cells, UStruct* RowStruct, TArray<FString>& OutProblems);

	/** Get array for each row in the table. The first row is the titles*/
	ENGINE_API TArray< TArray<FString> > GetTableData() const;


	// End UDataTable interface

private:
	void SaveStructData(FArchive& Ar);
	void LoadStructData(FArchive& Ar);
};


/** Handle to a particular row in a table*/
USTRUCT()
struct ENGINE_API FDataTableRowHandle
{
	GENERATED_USTRUCT_BODY()

	FDataTableRowHandle()
		: DataTable(nullptr)
		, RowName(NAME_None)
	{

	}

	/** Pointer to table we want a row from */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=DataTableRowHandle)
	const class UDataTable*	DataTable;

	/** Name of row in the table that we want */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=DataTableRowHandle)
	FName				RowName;

	static const FString Unknown;

	/** Get the row straight from the row handle */
	template <class T>
	T* GetRow(const FString& ContextString = Unknown) const
	{
		if(DataTable == nullptr)
		{
			if (RowName != NAME_None)
			{
				UE_LOG(LogDataTable, Warning, TEXT("FDataTableRowHandle::FindRow : No DataTable for row %s (%s)."), *RowName.ToString(), *ContextString);
			}
			return nullptr;
		}

		return DataTable->FindRow<T>(RowName, ContextString);
	}
};


/** Handle to a particular row in a table*/
USTRUCT()
struct ENGINE_API FDataTableCategoryHandle
{
	GENERATED_USTRUCT_BODY()

	/** Pointer to table we want a row from */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=DataTableCategoryHandle)
	const class UDataTable*	DataTable;

	/** Name of column in the table that we want */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=DataTableCategoryHandle)
	FName				ColumnName;

	/** Contents of rows in the table that we want */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=DataTableCategoryHandle)
	FName				RowContents;

	static const FString Unknown;

	/** Searches DataTable for all rows that contain entries with RowContents in the column named ColumnName and returns them. */
	template <class T>
	void GetRows(TArray<T*>& OutRows, const FString& ContextString = Unknown) const
	{
		OutRows.Empty();
		if (DataTable == nullptr)
		{
			if (RowContents != NAME_None)
			{
				UE_LOG(LogDataTable, Warning, TEXT("FDataTableCategoryHandle::FindRow : No DataTable for row %s (%s)."), *RowContents.ToString(), *ContextString);
			}

			return;
		}

		if (ColumnName == NAME_None)
		{
			if (RowContents != NAME_None)
			{
				UE_LOG(LogDataTable, Warning, TEXT("FDataTableCategoryHandle::FindRow : No Column selected for row %s (%s)."), *RowContents.ToString(), *ContextString);
			}

			return;
		}

		// Find the property that matches the desired column (ColumnName)
		UProperty* Property = DataTable->FindTableProperty(ColumnName);
		if (Property == nullptr)
		{
			return;
		}

		// check each row to see if the value in the Property element is the one we're looking for (RowContents). If it is, add the row to OutRows
		FString RowContentsAsString = RowContents.ToString();

		for (auto RowIt = DataTable->RowMap.CreateConstIterator(); RowIt; ++RowIt)
		{
			uint8* RowData = RowIt.Value();

			FString PropertyValue(TEXT(""));

			Property->ExportText_InContainer(0, PropertyValue, RowData, RowData, nullptr, PPF_None);

			if (RowContentsAsString == PropertyValue)
			{
				OutRows.Add((T*)RowData);
			}
		}

		return;
	}
};


/** Macro to call GetRow with a correct error info. Assumed to be called from within a UObject */
#define GETROW_REPORTERROR(Handle, Template) Handle.GetRow<Template>(FString::Printf(TEXT("%s.%s"), *GetPathName(), TEXT(#Handle)))
#define GETROWOBJECT_REPORTERROR(Object, Handle, Template) Handle.GetRow<Template>(FString::Printf(TEXT("%s.%s"), *Object->GetPathName(), TEXT(#Handle)))

DataTable.cpp


// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "EnginePrivate.h"
#include "Engine/DataTable.h"
#include "DataTableCSV.h"
#include "DataTableJSON.h"
#include "EditorFramework/AssetImportData.h"

ENGINE_API const FString FDataTableRowHandle::Unknown(TEXT("UNKNOWN"));
ENGINE_API const FString FDataTableCategoryHandle::Unknown(TEXT("UNKNOWN"));


namespace
{
	void GatherDataTableForLocalization(const UObject* const Object, TArray<FGatherableTextData>& GatherableTextDataArray)
	{
		const UDataTable* const DataTable = CastChecked<UDataTable>(Object);

		const FString PathToObject = DataTable->GetPathName();
		for (const auto& Pair : DataTable->RowMap)
		{
			const FString PathToRow = PathToObject + TEXT(".") + Pair.Key.ToString();
			for (TFieldIterator<UProperty> PropIt(DataTable->RowStruct, EFieldIteratorFlags::IncludeSuper, EFieldIteratorFlags::ExcludeDeprecated, EFieldIteratorFlags::IncludeInterfaces); PropIt; ++PropIt)
			{
				FPropertyLocalizationDataGatherer PropertyLocalizationDataGatherer(GatherableTextDataArray);
				PropertyLocalizationDataGatherer.GatherLocalizationDataFromChildTextProperies(PathToRow, *PropIt, PropIt->ContainerPtrToValuePtr<void>(Pair.Value));
			}
		}
	}
}


UDataTable::UDataTable(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{

	struct FAutomaticRegistrationOfLocalizationGatherer
	{
		FAutomaticRegistrationOfLocalizationGatherer()
		{
			UPackage::GetTypeSpecificLocalizationDataGatheringCallbacks().Add(UDataTable::StaticClass(), &GatherDataTableForLocalization);
		}
	} AutomaticRegistrationOfLocalizationGatherer;
}

void UDataTable::LoadStructData(FArchive& Ar)
{
	UScriptStruct* LoadUsingStruct = RowStruct;
	if (LoadUsingStruct == NULL)
	{
		UE_LOG(LogDataTable, Error, TEXT("Missing RowStruct while loading DataTable '%s'!"), *GetPathName());
		LoadUsingStruct = FTableRowBase::StaticStruct();
	}

	int32 NumRows;
	Ar << NumRows;

	for (int32 RowIdx = 0; RowIdx < NumRows; RowIdx++)
	{
		// Load row name
		FName RowName;
		Ar << RowName;

		// Load row data
		uint8* RowData = (uint8*)FMemory::Malloc(LoadUsingStruct->PropertiesSize);
		LoadUsingStruct->InitializeStruct(RowData);
		// And be sure to call DestroyScriptStruct later
		LoadUsingStruct->SerializeTaggedProperties(Ar, RowData, LoadUsingStruct, NULL);

		// Add to map
		RowMap.Add(RowName, RowData);
	}
}

void UDataTable::SaveStructData(FArchive& Ar)
{
	// Don't even try to save rows if no RowStruct
	if (RowStruct != NULL)
	{
		int32 NumRows = RowMap.Num();
		Ar << NumRows;

		// Now iterate over rows in the map
		for (auto RowIt = RowMap.CreateIterator(); RowIt; ++RowIt)
		{
			// Save out name
			FName RowName = RowIt.Key();
			Ar << RowName;

			// Save out data
			uint8* RowData = RowIt.Value();
			RowStruct->SerializeTaggedProperties(Ar, RowData, RowStruct, NULL);
		}
	}
}

void UDataTable::Serialize( FArchive& Ar )
{
	Super::Serialize(Ar); // When loading, this should load our RowStruct!	

	if (RowStruct && RowStruct->HasAnyFlags(RF_NeedLoad))
	{
		auto RowStructLinker = RowStruct->GetLinker();
		if (RowStructLinker)
		{
			RowStructLinker->Preload(RowStruct);
		}
	}

	if(Ar.IsLoading())
	{
		EmptyTable();
		LoadStructData(Ar);
	}
	else if(Ar.IsSaving())
	{
		SaveStructData(Ar);
	}
}

void UDataTable::AddReferencedObjects(UObject* InThis, FReferenceCollector& Collector)
{	
	UDataTable* This = CastChecked<UDataTable>(InThis);

	// Need to emit references for referenced rows
	if(This->RowStruct != NULL)
	{
		// Now iterate over rows in the map
		for ( auto RowIt = This->RowMap.CreateIterator(); RowIt; ++RowIt )
		{
			uint8* RowData = RowIt.Value();

			if (RowData)
			{
				// Serialize all of the properties to make sure they get in the collector
				FSimpleObjectReferenceCollectorArchive ObjectReferenceCollector( This, Collector );
				This->RowStruct->SerializeBin(ObjectReferenceCollector, RowData);
			}
		}
	}

	Collector.AddReferencedObjects(This->TemporarilyReferencedObjects);

	Super::AddReferencedObjects( This, Collector );
}

void UDataTable::FinishDestroy()
{
	Super::FinishDestroy();
	if(!IsTemplate())
	{
		EmptyTable(); // Free memory when UObject goes away
	}
}

void UDataTable::GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const
{
	if (AssetImportData)
	{
		OutTags.Add( FAssetRegistryTag(SourceFileTagName(), AssetImportData->GetSourceData().ToJson(), FAssetRegistryTag::TT_Hidden) );
	}

	Super::GetAssetRegistryTags(OutTags);
}

void UDataTable::PostInitProperties()
{
	if (!HasAnyFlags(RF_ClassDefaultObject))
	{
		AssetImportData = NewObject<UAssetImportData>(this, TEXT("AssetImportData"));
	}

	Super::PostInitProperties();
}

void UDataTable::PostLoad()
{
	Super::PostLoad();
	if (!ImportPath_DEPRECATED.IsEmpty() && AssetImportData)
	{
		FAssetImportInfo Info;
		Info.Insert(FAssetImportInfo::FSourceFile(ImportPath_DEPRECATED));
		AssetImportData->SourceData = MoveTemp(Info);
	}
}

void UDataTable::EmptyTable()
{
	UScriptStruct* LoadUsingStruct = RowStruct;
	if (LoadUsingStruct == NULL)
	{
		UE_LOG(LogDataTable, Error, TEXT("Missing RowStruct while emptying DataTable '%s'!"), *GetPathName());
		LoadUsingStruct = FTableRowBase::StaticStruct();
	}

	// Iterate over all rows in table and free mem
	for (auto RowIt = RowMap.CreateIterator(); RowIt; ++RowIt)
	{
		uint8* RowData = RowIt.Value();
		LoadUsingStruct->DestroyStruct(RowData);
		FMemory::Free(RowData);
	}

	// Finally empty the map
	RowMap.Empty();
}

/** Returns the column property where PropertyName matches the name of the column property. Returns NULL if no match is found or the match is not a supported table property */
UProperty* UDataTable::FindTableProperty(const FName& PropertyName) const
{
	UProperty* Property = NULL;
	for (TFieldIterator<UProperty> It(RowStruct); It; ++It)
	{
		Property = *It;
		check(Property != NULL);
		if (PropertyName == Property->GetFName())
		{
			break;
		}
	}
	if (!DataTableUtils::IsSupportedTableProperty(Property))
	{
		Property = NULL;
	}

	return Property;
}


void UDataTable::CleanBeforeStructChange()
{
	RowsSerializedWithTags.Reset();
	TemporarilyReferencedObjects.Empty();
	{
		class FRawStructWriter : public FObjectWriter
		{
			TSet<UObject*>& TemporarilyReferencedObjects;
		public: 
			FRawStructWriter(TArray<uint8>& InBytes, TSet<UObject*>& InTemporarilyReferencedObjects) 
				: FObjectWriter(InBytes), TemporarilyReferencedObjects(InTemporarilyReferencedObjects) {}
			virtual FArchive& operator<<(class UObject*& Res) override
			{
				FObjectWriter::operator<<(Res);
				TemporarilyReferencedObjects.Add(Res);
				return *this;
			}
		};

		FRawStructWriter MemoryWriter(RowsSerializedWithTags, TemporarilyReferencedObjects);
		SaveStructData(MemoryWriter);
	}
	EmptyTable();
	Modify();
}

void UDataTable::RestoreAfterStructChange()
{
	EmptyTable();
	{
		class FRawStructReader : public FObjectReader
		{
		public:
			FRawStructReader(TArray<uint8>& InBytes) : FObjectReader(InBytes) {}
			virtual FArchive& operator<<(class UObject*& Res) override
			{
				UObject* Object = NULL;
				FObjectReader::operator<<(Object);
				FWeakObjectPtr WeakObjectPtr = Object;
				Res = WeakObjectPtr.Get();
				return *this;
			}
		};

		FRawStructReader MemoryReader(RowsSerializedWithTags);
		LoadStructData(MemoryReader);
	}
	TemporarilyReferencedObjects.Empty();
	RowsSerializedWithTags.Empty();
}

FString UDataTable::GetTableAsString() const
{
	FString Result;

	if(RowStruct != NULL)
	{
		Result += FString::Printf(TEXT("Using RowStruct: %s

"), *RowStruct->GetPathName());

		// First build array of properties
		TArray<UProperty*> StructProps;
		for (TFieldIterator<UProperty> It(RowStruct); It; ++It)
		{
			UProperty* Prop = *It;
			check(Prop != NULL);
			StructProps.Add(Prop);
		}

		// First row, column titles, taken from properties
		Result += TEXT("---");
		for(int32 PropIdx=0; PropIdx<StructProps.Num(); PropIdx++)
		{
			Result += TEXT(",");
			Result += StructProps[PropIdx]->GetName();
		}
		Result += TEXT("
");

		// Now iterate over rows
		for ( auto RowIt = RowMap.CreateConstIterator(); RowIt; ++RowIt )
		{
			FName RowName = RowIt.Key();
			Result += RowName.ToString();

			uint8* RowData = RowIt.Value();
			for(int32 PropIdx=0; PropIdx<StructProps.Num(); PropIdx++)
			{
				Result += TEXT(",");
				Result += DataTableUtils::GetPropertyValueAsString(StructProps[PropIdx], RowData);
			}
			Result += TEXT("
");			
		}
	}
	else
	{
		Result += FString(TEXT("Missing RowStruct!
"));
	}
	return Result;
}

FString UDataTable::GetTableAsCSV() const
{
	FString Result;
	if (!FDataTableExporterCSV(*this, Result).WriteTable())
	{
		Result = TEXT("Missing RowStruct!
");
	}
	return Result;
}

FString UDataTable::GetTableAsJSON() const
{
	FString Result;
	if (!FDataTableExporterJSON(*this, Result).WriteTable())
	{
		Result = TEXT("Missing RowStruct!
");
	}
	return Result;
}

bool UDataTable::WriteRowAsJSON(const TSharedRef< TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR> > >& JsonWriter, const void* RowData) const
{
	return FDataTableExporterJSON(*this, JsonWriter).WriteRow(RowData);
}

bool UDataTable::WriteTableAsJSON(const TSharedRef< TJsonWriter<TCHAR, TPrettyJsonPrintPolicy<TCHAR> > >& JsonWriter) const
{
	return FDataTableExporterJSON(*this, JsonWriter).WriteTable();
}

/** Get array of UProperties that corresponds to columns in the table */
TArray<UProperty*> UDataTable::GetTablePropertyArray(const TArray<const TCHAR*>& Cells, UStruct* InRowStruct, TArray<FString>& OutProblems)
{
	TArray<UProperty*> ColumnProps;

	// Get list of all expected properties from the struct
	TArray<FName> ExpectedPropNames = DataTableUtils::GetStructPropertyNames(InRowStruct);

	// Need at least 2 columns, first column is skipped, will contain row names
	if(Cells.Num() > 1)
	{
		ColumnProps.AddZeroed( Cells.Num() );

		// first element always NULL - as first column is row names

		for (int32 ColIdx = 1; ColIdx < Cells.Num(); ++ColIdx)
		{
			const TCHAR* ColumnValue = Cells[ColIdx];

			FName PropName = DataTableUtils::MakeValidName(ColumnValue);
			if(PropName == NAME_None)
			{
				OutProblems.Add(FString::Printf(TEXT("Missing name for column %d."), ColIdx));
			}
			else
			{
				UProperty* ColumnProp = FindField<UProperty>(InRowStruct, PropName);

				for (TFieldIterator<UProperty> It(InRowStruct); It && !ColumnProp; ++It)
				{
					const auto DisplayName = DataTableUtils::GetPropertyDisplayName(*It, FString());
					ColumnProp = (!DisplayName.IsEmpty() && DisplayName == ColumnValue) ? *It : NULL;
				}

				// Didn't find a property with this name, problem..
				if(ColumnProp == NULL)
				{
					OutProblems.Add(FString::Printf(TEXT("Cannot find Property for column '%s' in struct '%s'."), *PropName.ToString(), *InRowStruct->GetName()));
				}
				// Found one!
				else
				{
					// Check we don't have this property already
					if(ColumnProps.Contains(ColumnProp))
					{
						OutProblems.Add(FString::Printf(TEXT("Duplicate column '%s'."), *ColumnProp->GetName()));
					}
					// Check we support this property type
					else if( !DataTableUtils::IsSupportedTableProperty(ColumnProp) )
					{
						OutProblems.Add(FString::Printf(TEXT("Unsupported Property type for struct member '%s'."), *ColumnProp->GetName()));
					}
					// Looks good, add to array
					else
					{
						ColumnProps[ColIdx] = ColumnProp;
					}

					// Track that we found this one
					ExpectedPropNames.Remove(ColumnProp->GetFName());
				}
			}
		}
	}

	// Generate warning for any properties in struct we are not filling in
	for(int32 PropIdx=0; PropIdx < ExpectedPropNames.Num(); PropIdx++)
	{
		const UProperty* const ColumnProp = FindField<UProperty>(InRowStruct, ExpectedPropNames[PropIdx]);
		const FString DisplayName = DataTableUtils::GetPropertyDisplayName(ColumnProp, ExpectedPropNames[PropIdx].ToString());
		OutProblems.Add(FString::Printf(TEXT("Expected column '%s' not found in input."), *DisplayName));
	}

	return ColumnProps;
}

TArray<FString> UDataTable::CreateTableFromCSVString(const FString& InString)
{
	// Array used to store problems about table creation
	TArray<FString> OutProblems;

	FDataTableImporterCSV(*this, InString, OutProblems).ReadTable();

	return OutProblems;
}

TArray<FString> UDataTable::CreateTableFromJSONString(const FString& InString)
{
	// Array used to store problems about table creation
	TArray<FString> OutProblems;

	FDataTableImporterJSON(*this, InString, OutProblems).ReadTable();

	return OutProblems;
}

TArray<FString> UDataTable::GetColumnTitles() const
{
	TArray<FString> Result;
	Result.Add(TEXT("Name"));
	for (TFieldIterator<UProperty> It(RowStruct); It; ++It)
	{
		UProperty* Prop = *It;
		check(Prop != NULL);
		const FString DisplayName = DataTableUtils::GetPropertyDisplayName(Prop, Prop->GetName());
		Result.Add(DisplayName);
	}
	return Result;
}

TArray<FString> UDataTable::GetUniqueColumnTitles() const
{
	TArray<FString> Result;
	Result.Add(TEXT("Name"));
	for (TFieldIterator<UProperty> It(RowStruct); It; ++It)
	{
		UProperty* Prop = *It;
		check(Prop != NULL);
		const FString DisplayName = Prop->GetName();
		Result.Add(DisplayName);
	}
	return Result;
}

TArray< TArray<FString> > UDataTable::GetTableData() const
{
	 TArray< TArray<FString> > Result;

	 Result.Add(GetColumnTitles());

	 // First build array of properties
	 TArray<UProperty*> StructProps;
	 for (TFieldIterator<UProperty> It(RowStruct); It; ++It)
	 {
		 UProperty* Prop = *It;
		 check(Prop != NULL);
		 StructProps.Add(Prop);
	 }

	 // Now iterate over rows
	 for ( auto RowIt = RowMap.CreateConstIterator(); RowIt; ++RowIt )
	 {
		 TArray<FString> RowResult;
		 FName RowName = RowIt.Key();
		 RowResult.Add(RowName.ToString());

		 uint8* RowData = RowIt.Value();
		 for(int32 PropIdx=0; PropIdx<StructProps.Num(); PropIdx++)
		 {
			 RowResult.Add(DataTableUtils::GetPropertyValueAsString(StructProps[PropIdx], RowData));
		 }
		 Result.Add(RowResult);
	 }
	 return Result;

}


TArray<FName> UDataTable::GetRowNames() const
{
	TArray<FName> Keys;
	RowMap.GetKeys(Keys);
	return Keys;
}


It means it doesn’t know what FTableRowHandle is, i.e. it hasn’t been defined. You now need to find FTableRowHandle and expose that as well.

In all seriousness, this is probably time to rethink what you’re trying to do.

That’s strange because FTableRowHandle is defined:


/** Handle to a particular row in a table*/
USTRUCT()
struct ENGINE_API FDataTableRowHandle
{
	GENERATED_USTRUCT_BODY()

	FDataTableRowHandle()
		: DataTable(nullptr)
		, RowName(NAME_None)
	{

	}

	/** Pointer to table we want a row from */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=DataTableRowHandle)
	const class UDataTable*	DataTable;

	/** Name of row in the table that we want */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=DataTableRowHandle)
	FName				RowName;

	static const FString Unknown;

	/** Get the row straight from the row handle */
	template <class T>
	T* GetRow(const FString& ContextString = Unknown) const
	{
		if(DataTable == nullptr)
		{
			if (RowName != NAME_None)
			{
				UE_LOG(LogDataTable, Warning, TEXT("FDataTableRowHandle::FindRow : No DataTable for row %s (%s)."), *RowName.ToString(), *ContextString);
			}
			return nullptr;
		}

		return DataTable->FindRow<T>(RowName, ContextString);
	}
};

I dont understand where should I expose it to make it work because it’s still the way it was written by Epic Devs (except #WITH_EDITOR). Does anyone know how to fix this?

I have the error same your mistake, How do you solve finally! holp you can tell me!

I have the error same your mistake, How do you solve finally! holp you can tell me!