FS_Test::ID is not initialized properly

Why do FGuids within c++ structs cause the error “FSomeStruct::SomeFGuid is not initialized properly.” in the following struct format?

USTRUCT(BlueprintType)
struct FS_Test : public FTableRowBase {
	GENERATED_BODY()

	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		FGuid ID;

	// Initialize
	FS_Test() {
		ID = FGuid::NewGuid();
	}
	FS_Test(FGuid InID) {
		ID = InID;
		if (!ID.IsValid()) {
			ID = FGuid::NewGuid();
		}
	}

	// Operators
	bool operator==(const FS_Test& Other) const {
		return (
			ID == Other.ID
		);
	}
}

*Edit so this is annoying, at first I thought the error appeared because the FGuid is set in the constructor, or maybe even because I use if / else, but after replacing the thing with an initialiser list the error remains. Am I missing something obvious?

USTRUCT(BlueprintType)
struct FS_SomeStruct : public FTableRowBase {
	GENERATED_BODY()

	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		FGuid ID;

	// Initialize
	FS_SomeStruct()
		: ID (FGuid())
	{
	}
	FS_SomeStruct(FGuid InID) 
		: ID (InID)
	{
		if (!ID.IsValid()) {
			ID = FGuid::NewGuid();
		}
	}

	// Operators
	bool operator==(const FS_SomeStruct& Other) const {
		return (
			ID == Other.ID
		);
	}
};

bump

Bump . If this is the correct way to do it, that would also be an answer. I think UE might just be acting weird on USTRUCT ? I have 3 structs where this happens and one where it doesn’t, using nearly the same implementation. The only difference being that the working UPROPERTY is marked (BlueprintReadOnly, VisibleAnywhere) while the non working ones are marked (BlueprintReadWrite, EditAnywhere). That would be strange wouldn’t it…

My guess here is that the FGuid default constructor does nothing, in which case it’ll be uninitialized memory. You may need to use an explicit initializer constructor for your default constructor.

But it does?

Runtime\Core\Public\Misc\Guid.h

struct FGuid
{
public:

	/** Default constructor. */
	FGuid()
		: A(0)
		, B(0)
		, C(0)
		, D(0)
	{ }

	explicit FGuid(uint32 InA, uint32 InB, uint32 InC, uint32 InD)
		: A(InA), B(InB), C(InC), D(InD)
	{ }

	explicit FGuid(const FString& InGuidStr)
	{
		if (!Parse(InGuidStr, *this))
		{
			Invalidate();
		}
	}

....

*Edit I used to initialize using NewGuid, but that gave the same result.

FGuid FGuid::NewGuid()
{
	FGuid Result(0, 0, 0, 0);
	FPlatformMisc::CreateGuid(Result);

	return Result;
}

Okay this actually explains it. The problem is that FGuid actually has two declarations - one in the header you mentioned, the other in NoExportTypes.h

The warning about uninitialized struct members comes from the Unreal Header Tool, and UHT parses the duplicate FGuid from NoExportTypes.h which has no initialization or constructors.

So really it’s an oversight from Epic. You can ignore the warning, since in actual C++ code the native FGuid will be used. The stub in NoExportTypes just generates the required reflection data to expose that type to Blueprint. There isn’t a workaround for this AFAIK.

3 Likes

Is it possible that certain UPROPERTY specifiers alter this behavior? It’s odd that this problem occurs in a few (but not all) structs with FGuid where the only difference are the specifiers and meta. I also see other data types in the NoExportTypes.h like FTransform but none of these generate the message. Why is that?

*Edit, forgot to note that the error is not a visual studio one but a log entry in UE.

1 Like

There’s a meta tag you can use: IgnoreForMemberInitializationTest.

Engine\Source\Runtime\CoreUObject\Public\UObject\ObjectMacros.h:

/// [PropertyMetadata] Used for bypassing property initialization tests when the property cannot be safely tested in a deterministic fashion. Example: random numbers, guids, etc.
		IgnoreForMemberInitializationTest,

Example usage:

UnrealEngine\Engine\Plugins\Runtime\ZoneGraph\Source\ZoneGraph\Public\ZoneGraphTypes.h:

	UPROPERTY(meta = (IgnoreForMemberInitializationTest))
	FGuid ID;
5 Likes

Thank you, but what does this mean? What initialization tests? How does this apply to an FGuid?

when the property cannot be safely tested in a deterministic fashion

If you are interested in how the check is done, take a look at FindUninitializedScriptStructMembers in Runtime\CoreUObject\Private\UObject\Class.cpp.

Basically it’s looking for uninitialized struct members to warn you about (undefined behavior is not fun).
At a glance, it seems like struct properties are checked by seeing if the given property will have the same value for 2 different variables. This just won’t work with things that are non-deterministic by design; random numbers, uids (if you make 2 uids, you want them to be different, otherwise it wouldn’t be very useful for identifying things), and the like… the test will just fail in case the property is initialized properly. For these cases you can use the meta tag to skip the check.

3 Likes

Thanks, I will check this. I was surprised to find many FGuid properties which do not generate the warning and these few which do. I have no clue why.

Related…

“Not initialized properly” warning now showing on any type even booleans if a struct / datatable bugs somehow. All affected properties are within datatables.

Did you find a solution to this problem, its happening for while packaging.

No I didn’t. It appears that it doesn’t pop up for all but very specific properties and when it does it keeps going on. Something must be broken in the engine.

No solution, but for anyone reading I tried creating a new clean project and migrating the affected structs, datatables and their dependencies to a new module with no success. Looking at the view count on this post it’s quite a common issue.

The error “is not initialized properly” essentially means “give me an initial value”. This applies only to structs, I believe. You can assign it on the declaration line or in a constructor function.
For example,
FGuid ID = FGuid(0,0,0,0);

Although you do assign it NewGuid() in the constructor. Maybe it’s seeing that as still uninitialized. A lot of times the default constructor in the docs will indicate that it is uninitialized.

1 Like

Thanks for the suggestion but this has already been tested in both the constructor and by initializing the GUID in the initializer list.