Weird LNK2005 error...

Total Noob to C++ here…

So I have this error when packaging my project. Everything builds fine in VS.

It’s probably something stupid, but I’ve spent a while googling this and can’t seem to find anything that helps. Any help greatly appreciated!!

Looks like a function has been declared twice, or you’ve declared a function that will be auto-declared by UHT. If you can, paste the contents of DataTableFunctionLibrary.h (assuming that’s your own class?)

Hey, so it’s a function library I found here on the forums and copied to my project.


// Fill out your copyright notice in the Description page of Project Settings.

#pragma once
#include "Engine/DataTable.h"
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "DataTableFunctionLibrary.generated.h"


UCLASS()
class ROME_API UPsTableFunctionLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()

public:

UFUNCTION(BlueprintPure, Category = "DataTableFunctionLibrary")
static bool GetDataTableIndexOfRow(FDataTableRowHandle Handle, int32& RowIndex);

UFUNCTION(BlueprintPure, CustomThunk, Category = "DataTableFunctionLibrary", meta = (CustomStructureParam = "Row"))
static bool GetDataTableRowByIndexWildcard(UDataTable* Table, int32 RowIndex, FTableRowBase& Row);
static bool Generic_GetDataTableRowByIndexWildcard(UDataTable* Table, int32 RowIndex, void* OutRowPtr, UStructProperty* OutRowProperty);
DECLARE_FUNCTION(execGetDataTableRowByIndexWildcard)
{
P_GET_OBJECT(UDataTable, Table);
P_GET_PROPERTY(UIntProperty, RowIndex);
Stack.StepCompiledIn<UStructProperty>(NULL);
void* OutRowPtr = Stack.MostRecentPropertyAddress;
UStructProperty* OutRowProperty = Cast<UStructProperty>(Stack.MostRecentProperty);
P_FINISH;
P_NATIVE_BEGIN;
*(bool*)RESULT_PARAM = Generic_GetDataTableRowByIndexWildcard(Table, RowIndex, OutRowPtr, OutRowProperty);
P_NATIVE_END;
}

UFUNCTION(BlueprintPure, CustomThunk, Category = "DataTableFunctionLibrary", meta = (CustomStructureParam = "Row"))
static bool GetDataTableRowByNameWildcard(UDataTable* Table, FName RowName, FTableRowBase& Row);
static bool Generic_GetDataTableRowByNameWildcard(UDataTable* Table, FName RowName, void* OutRowPtr, UStructProperty* OutRowProperty);
DECLARE_FUNCTION(execGetDataTableRowByNameWildcard)
{
P_GET_OBJECT(UDataTable, Table);
P_GET_PROPERTY(UNameProperty, RowName);
Stack.StepCompiledIn<UStructProperty>(NULL);
void* OutRowPtr = Stack.MostRecentPropertyAddress;
UStructProperty* OutRowProperty = Cast<UStructProperty>(Stack.MostRecentProperty);
P_FINISH;
P_NATIVE_BEGIN;
*(bool*)RESULT_PARAM = Generic_GetDataTableRowByNameWildcard(Table, RowName, OutRowPtr, OutRowProperty);
P_NATIVE_END;
}

UFUNCTION(BlueprintPure, Category = "DataTableFunctionLibrary")
static int32 GetDataTableRowCount(UDataTable* Table);

UFUNCTION(BlueprintPure, CustomThunk, Category = "DataTableFunctionLibrary", meta = (CustomStructureParam = "Row"))
static bool GetDataTableRowWildcard(FDataTableRowHandle Handle, FTableRowBase& Row);
static bool Generic_GetDataTableRowWildcard(FDataTableRowHandle Handle, void* OutRowPtr, UStructProperty* OutRowProperty);
DECLARE_FUNCTION(execGetDataTableRowWildcard)
{
P_GET_STRUCT(FDataTableRowHandle, Handle);
Stack.StepCompiledIn<UStructProperty>(NULL);
void* OutRowPtr = Stack.MostRecentPropertyAddress;
UStructProperty* OutRowProperty = Cast<UStructProperty>(Stack.MostRecentProperty);
P_FINISH;
P_NATIVE_BEGIN;
*(bool*)RESULT_PARAM = Generic_GetDataTableRowWildcard(Handle, OutRowPtr, OutRowProperty);
P_NATIVE_END;
}





};

Remember to wrap code tags around code.

Yeah, missed that ha.

Hmm, not sure in that case. I’ve not done much with CustomThunk before.

I’m assuming all the functions you’ve declared have implementations in the .cpp too?

Here’s my .cpp file


#include "DataTableFunctionLibrary.h"
#include "SlavesOfRome.h"


bool UPsTableFunctionLibrary::GetDataTableIndexOfRow(FDataTableRowHandle Handle, int32& RowIndex)
{
    RowIndex = INDEX_NONE;

    if (!Handle.DataTable)
        return false;

    TArray<FName> RowNames = Handle.DataTable->GetRowNames();
    RowIndex = RowNames.IndexOfByKey(Handle.RowName);
    return RowIndex != INDEX_NONE;
}

bool UPsTableFunctionLibrary::GetDataTableRowByIndexWildcard(UDataTable* Table, int32 RowIndex, FTableRowBase& Row)
{
    checkNoEntry();
    return false;
}

bool UPsTableFunctionLibrary::Generic_GetDataTableRowByIndexWildcard(UDataTable* Table, int32 RowIndex, void* OutRowPtr, UStructProperty* OutRowProperty)
{
    if (!OutRowPtr)
        return false;

    if (!OutRowProperty)
        return false;

    if (!Table)
        return false;

    TArray<FName> RowNames = Table->GetRowNames();
    if (RowIndex < 0 || RowIndex >= RowNames.Num())
        return false;

    FName RowName = RowNames[RowIndex];
    void* RowPtr = Table->FindRowUnchecked(RowName);
    if (!RowPtr)
        return false;

    UScriptStruct* TableRowStruct = Table->RowStruct;
    if (!TableRowStruct)
        return false;

    UScriptStruct* OutRowStruct = OutRowProperty->Struct;
    if (!OutRowStruct)
        return false;

    if (!TableRowStruct->IsChildOf(OutRowStruct))
        return false;

    OutRowStruct->CopyScriptStruct(OutRowPtr, RowPtr);
    return true;
}

bool UPsTableFunctionLibrary::GetDataTableRowByNameWildcard(UDataTable* Table, FName RowName, FTableRowBase& Row)
{
    checkNoEntry();
    return false;
}

bool UPsTableFunctionLibrary::Generic_GetDataTableRowByNameWildcard(UDataTable* Table, FName RowName, void* OutRowPtr, UStructProperty* OutRowProperty)
{
    if (!OutRowPtr)
        return false;

    if (!OutRowProperty)
        return false;

    if (!Table)
        return false;

    void* RowPtr = Table->FindRowUnchecked(RowName);
    if (!RowPtr)
        return false;

    UScriptStruct* TableRowStruct = Table->RowStruct;
    if (!TableRowStruct)
        return false;

    UScriptStruct* OutRowStruct = OutRowProperty->Struct;
    if (!OutRowStruct)
        return false;

    if (!TableRowStruct->IsChildOf(OutRowStruct))
        return false;

    OutRowStruct->CopyScriptStruct(OutRowPtr, RowPtr);
    return true;
}

int32 UPsTableFunctionLibrary::GetDataTableRowCount(UDataTable* Table)
{
    if (!Table)
        return 0;

    TArray<FName> RowNames = Table->GetRowNames();
    return RowNames.Num();
}

bool UPsTableFunctionLibrary::GetDataTableRowWildcard(FDataTableRowHandle Handle, FTableRowBase& Row)
{
    checkNoEntry();
    return false;
}

bool UPsTableFunctionLibrary::Generic_GetDataTableRowWildcard(FDataTableRowHandle Handle, void* OutRowPtr, UStructProperty* OutRowProperty)
{
    if (!OutRowPtr)
        return false;

    if (!OutRowProperty)
        return false;

    const UDataTable* Table = Handle.DataTable;
    if (!Table)
        return false;

    void* RowPtr = Table->FindRowUnchecked(Handle.RowName);
    if (!RowPtr)
        return false;

    UScriptStruct* TableRowStruct = Table->RowStruct;
    if (!TableRowStruct)
        return false;

    UScriptStruct* OutRowStruct = OutRowProperty->Struct;
    if (!OutRowStruct)
        return false;

    if (!TableRowStruct->IsChildOf(OutRowStruct))
        return false;

    OutRowStruct->CopyScriptStruct(OutRowPtr, RowPtr);
    return true;
}


UHT is expecting some method that should be defined during code generation. My guess is that since you’re providing a CustomThunk that it’s skipping that portion of the generation and the code you copied is likely out of date with what UHT expects.

Honestly, looking at the code, I don’t see any reason why this needs a CustomThunk. It looks like the original author was using it to call that Generic_GetDataTableRowByIndexWildCard method, but you could easily wrap that in a namespace or just keep it as a static method in the CPP and have all your blueprint function libs call into that.

Thanks! I wish I understood everything you just said haha. Totally new to this so I’m scratching my head a bit!

After some googling it seems the CustomThunk is necessary to use a wildcard struct, I might be wrong but that’s what it seems like anyway. I have no idea where to go from here though! If anyone can help me out here I’ll be forever grateful!

I’ve found a way to do what I wanted without using this function library so it’s no longer an issue! Thanks everyone :slight_smile: