What is the purpose of anonymous enum
declarations such as:
enum { color = 1 };
Why not just declare int color = 1
Readability and performance.
Details are describbed as notes to examples below.
In Unreal Engine 4 (C++ game engine), I have following property (engine exposed member variable):
/// Floor Slope.
UPROPERTY
(
Category = "Movement",
VisibleInstanceOnly,
BlueprintGetter = "BP_GetFloorSlope",
BlueprintReadOnly,
meta =
(
ConsoleVariable = "Movement.FloorSlope",
DisplayName = "Floor Slope",
ExposeOnSpawn = true,
NoAutoLoad
)
)
float FloorSlope = -1.f;
This is a value of floor slope player is standing on (value ∈ [0; 90)°), if any.
Because of engine limitations, it cannot be neither std::optional
nor TOptional.
I've came up with a solution to add another self explainable variable bIsOnFloor
.
bool bIsOnFloor = false;
My C++ only internal setter for FloorSlope
takes the following form:
void UMovement::SetFloorSlope(const float& FloorSlope) noexcept
contract [[expects audit: FloorSlope >= 0._deg && FloorSlope < 90._deg]]
{
this->bIsOnFloor = true;
this->FloorSlope = FloorSlope;
AUI::UI->Debug->FloorSlope = FString::Printf(L"Floor Slope: %2.0f", FloorSlope);
};
Adding special case where FloorSlope
parameter would take argument of -1.f
would be hard to guess and not user friendly.
Instead, I'd rather create False
enum
field:
enum { False };
This way, I can simply overload SetFloorSlope
function that takes intuitive False
instead of -1.f
.
void UMovement::SetFloorSlope([[maybe_unused]] const decltype(False)&) noexcept
{
this->bIsOnFloor = false;
this->FloorSlope = -1.f;
AUI::UI->Debug->FloorSlope = L"Floor Slope: —";
};
When a player character hits a floor upon applying gravity to it on tick, I simply call:
SetFloorSlope(FloorSlope);
… where FloorSlope
is a float
value ∈ [0; 90)°.
Otherwise (if it does not hits a floor), I call:
SetFloorSlope(False);
This form (as opposed to passing -1.f
) is much more readable, and self explanatory.
Another example may be to prevent or force initialization.
Mentioned above Unreal Engine 4 commonly uses FHitResult struct
containing information about one hit of a trace, such as point of impact and surface normal at that point.
This complex struct
calls Init
method by default, setting some values to certain member variables. This can be forced or prevented (public docs: FHitResult #constructor):
FHitResult()
{
Init();
}
explicit FHitResult(float InTime)
{
Init();
Time = InTime;
}
explicit FHitResult(EForceInit InInit)
{
Init();
}
explicit FHitResult(ENoInit NoInit)
{
}
Epic Games defines such enum
s similiar, but adds redundant enum
names:
enum EForceInit
{
ForceInit,
ForceInitToZero
};
enum ENoInit {NoInit};
Passing NoInit
to the constructor of FHitResult
prevents initialization, what can lead to performance gain by not initializing values that will be initialized elsewhere.
FHitResult(NoInit)
usage in DamirH's post on Comprehensive GameplayAbilities Analysis Series:
//A struct for temporary holding of actors (and transforms) of actors that we hit
//that don't have an ASC. Used for environment impact GameplayCues.
struct FNonAbilityTarget
{
FGameplayTagContainer CueContainer;
TWeakObjectPtr<AActor> TargetActor;
FHitResult TargetHitResult;
bool bHasHitResult;
public:
FNonAbilityTarget()
: CueContainer(FGameplayTagContainer())
, TargetActor(nullptr)
, TargetHitResult(FHitResult(ENoInit::NoInit))
, bHasHitResult(false)
{
}
// (…)
I dont see it mentioned, another use is to scope your constants. I currently work on code that was written using Visual Studio 2005, and it is now ported to android - g++. In VS2005 you could have code like this enum MyOpts { OPT1 = 1 };
and use it as MyOpts::OPT1 - and compiler did not complain about it, even though it is not valid. g++ reports such code as error, so one solution is to use anonymous enum as follows: struct MyOpts { enum {OPT1 =1}; };
, and now both compilers are happy.