UE doesn't like my TOptionals...

The code atm:

    UFUNCTION(BlueprintCallable, Category = "SQLite")
        static void UpdateTileID_Row(
            int32 TileQ,
            int32 TileR,
            int32 TileS,
            TOptional<float> VecX,
            TOptional<float> VecY,
            TOptional<float> VecZ,
            AActor* Store,
            TOptional<int32> Row,
            TOptional<int32> Col
        );
void USQLiteDB::UpdateTileID_Row
    (
        int32 TileQ,
        int32 TileR,
        int32 TileS,
        TOptional<float> VecX,
        TOptional<float> VecY,
        TOptional<float> VecZ,
        AActor* Store,
        TOptional<int32> Row,
        TOptional<int32> Col
    )
{
    // Open the database
    sqlite3* db;
    FString dbPath = FPaths::ProjectContentDir().Append("Database/Current.btf");
    int rc = sqlite3_open(TCHAR_TO_UTF8(*dbPath), &db);
    if (rc != SQLITE_OK)
    {
        // Handle error
        return;
    }

    // Construct the SET clause
    FString setClause;
    if (VecX.IsSet())
        setClause.Append(FString::Printf(TEXT("VecX = %f,"), VecX.GetValue()));
    if (VecY.IsSet())
        setClause.Append(FString::Printf(TEXT("VecY = %f,"), VecY.GetValue()));
    if (VecZ.IsSet())
        setClause.Append(FString::Printf(TEXT("VecZ = %f,"), VecZ.GetValue()));
    if (Store != nullptr)
        setClause.Append(FString::Printf(TEXT("Store = '%s',"), *Store->GetName()));
    if (Row.IsSet())
        setClause.Append(FString::Printf(TEXT("Row = %d,"), Row.GetValue()));
    if (Col.IsSet())
        setClause.Append(FString::Printf(TEXT("Col = %d,"), Col.GetValue()));
    setClause.RemoveFromEnd(",");

    // Prepare the UPDATE statement
    FString sql = FString::Printf(TEXT("UPDATE TileIdentity SET %s WHERE TileQ = %d AND TileR = %d AND TileS = %d"), *setClause, TileQ, TileR, TileS);
    sqlite3_stmt* stmt;
    rc = sqlite3_prepare_v2(db, TCHAR_TO_UTF8(*sql), -1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
        // Handle error
        sqlite3_close(db);
        return;
    }

    // Execute the UPDATE statement
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
        // Handle error
        sqlite3_finalize(stmt);
        sqlite3_close(db);
        return;
    }

    // Finalize the statement and close the database
    sqlite3_finalize(stmt);
    sqlite3_close(db);
}

Gives the following compile error in the editor: Error: Unable to find ‘class’, ‘delegate’, ‘enum’, or ‘struct’ with name ‘TOptional’ for each line where the TOptional is declared…

I did some searching of the internets and added:

#include "Optional.h"

to my header. File unknown, so even further searches had me adding to my build.cs:

	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; //existing

		PublicIncludePaths.Add("Runtime/Core/Public"); //new-line

		PublicDependencyModuleNames.AddRange(new string[]  //existing
		{  //etc

I’m now at an absolute loss… any ideas?

1 Like

according to the Documentation it’s

#include "Misc/Optional.h"
1 Like

Well that fixed the file unknown error… :+1:

However the main error still remains - it doesn’t like the TOptionals (or at least, how I’ve used them).

Does the error remain if you remove UFUNCTION()? if it doesn’t, it’s most probably caused by Blueprints not supporting TOptionals.

[EDIT]
Just did some testing, it’s only showing an error if I mark the function as UFUNCTION().
So yeah, Blueprints doesn’t support TOptional.

3 Likes

Serious? That’s pretty limiting…

Appreciate your effort @Extrone

1 Like

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

So, I’ve done some testing and mucking around and it turns out you can use TOptionals with callable Blueprints, it’s just unecessarily hacky and long winded.

The solution is either a nested private function or sentinel variables. Here’s the blueprint and the returned setclause statement in yellow, bottomleft:

The header code:

	UFUNCTION(BlueprintCallable, Category = "Database")
	static void UpdateReferenceRow
	(
		UPARAM(ref) FIntVector& TileID,
		int32 EmpireID = -1,
		int32 IncidentID = -1,
		int32 NebulaID = -1,
		int32 StellarID = -1
	);

Sauce:

void UQDS_ReferenceTable::UpdateReferenceRow(UPARAM(ref) FIntVector& TileID, int32 EmpireID, int32 IncidentID, int32 NebulaID, int32 StellarID)
{
	// Pass on the IntVec values
	int32 TileQ = TileID.X;
	int32 TileR = TileID.Y;
	int32 TileS = TileID.Z;

	TOptional<int32> E_ID_Value;
	TOptional<int32> I_ID_Value;
	TOptional<int32> N_ID_Value;
	TOptional<int32> S_ID_Value;

	if (EmpireID != -1)
	{
		E_ID_Value = EmpireID;
	}
	
	if (IncidentID != -1)
	{
		I_ID_Value = IncidentID;
	}

	if (NebulaID != -1)
	{
		N_ID_Value = NebulaID;
	}

	if (StellarID != -1)
	{
		S_ID_Value = StellarID;
	}

	// Construct the SET clause
	FString setClause;
	if (E_ID_Value.IsSet())
		setClause.Append(FString::Printf(TEXT("EmpireID = %d,"), E_ID_Value.GetValue()));
	if (I_ID_Value.IsSet())
		setClause.Append(FString::Printf(TEXT("IncidentID = %d,"), I_ID_Value.GetValue()));
	if (N_ID_Value.IsSet())
		setClause.Append(FString::Printf(TEXT("NebulaID = %d,"), N_ID_Value.GetValue()));
	if (S_ID_Value.IsSet())
		setClause.Append(FString::Printf(TEXT("StellarID = %d,"), S_ID_Value.GetValue()));
	setClause.Append(FString::Printf(TEXT("TileQ = %d,"), TileQ));
	setClause.Append(FString::Printf(TEXT("TileR = %d,"), TileR));
	setClause.Append(FString::Printf(TEXT("TileS = %d,"), TileS));
	setClause.RemoveFromEnd(",");

	UE_LOG(LogTemp, Warning, TEXT("Set Clause: %s"), *setClause);
	UE_LOG(LogTemp, Warning, TEXT("Yes, you are here."));
}

It really should be much more straightforward to see if a blueprint node input pin has had a value passed through it, but this definitely works (and has a multitude of uses)…

Oh, and a shoutout to @Hourences for reopening the thread. :smiley:

1 Like

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