In your previous example you set the indices manually while reordering the list visually. If you use the indices in your switch they will require updating for your new example but using the variable name you dont right. We all know how it works…
Typically its easy just to add new values to the end of the enum or leave room for new values through the use of temporary names. You could just use the typical practice and add new values to the end so Im really not understanding why it is you want to do this!~ Youve explained the issue with serialization but a simply conversion to string and serializing that would suffice so maybe Im overcomplicating it with all the extra requirements.
If it just a matter of names then why does the list order matter? Because underneath the names arnt use thats why its called enum to byte, thats why enums in general have a maximum length.
It’s purely an organizational thing. Without getting too much into the specifics, it’s a stats tracking system. The values in the enum could be:
Kill
Kill_X
Kill_With_X
… A bunch more
Max
Then in a few months, we want to add Kill_X_With_Y. Sure, I can toss that at the end of the list, but it’s much nicer to have them all together in a logical way.
Another example would be a state machine of some kind where the order matters. Say I have states of NotStarted, InProgress, and Finished. Then later, I want to add Starting in between NotStarted and InProgress. Any script that’s using those values will be broken.
Thanks for the explanation but you see where Im coming from right? Im actually genuinely interested in hearing more about these other engines you mentioned and how they function in comparison or which language you might be referring to which has the feature set youre referencing.
Im glad you mention states because this is one of the prime factors I moved away from Enums to FGameplayTags. It allows me to have a hierarchy such as Dead.ReadyToSpawn, Dead.Ragdoll for example. @Siliex really hit the nail on the head and others have shown the performance is kinda negligible but I dont blame you for wanting that switch statement speed.
Ive wanted an unordered list of Fnames for the longest time but lookup was always the issue, its not slow for the list sizes I typically use but its not enum fast. Im definitely curious to see if you can resolve this some how but I feel like most of what we are talking about is code aesthetics. I can totally dig that as C++ especially in its UE flavour is kinda horrible to read and maintain.
Ive been avoiding not just enums but structs in my more recent projects because classes allow me to do a few things in terms of modability and extensibility structs dont allow. There are issues with waiting on Epic because of the size of the engine and the type it takes for changes to filter. I honestly have mostly replaced them with FGameplayTags, AngelScript has a nice way to resolve them because of how it runs JIT with the engine running so its aware of all the tags similar to enum autocompletion. (FGameplayTags also have really nice drop down capabilities and container support)
You can do this if you assign values to your enums. Perhaps you find that too ugly or something. This won’t reorder your dropdowns or in game menus though. But you should never rely on enum ordering for anything anyways. Display is usually localized so it needs to be converted regardless. If you want to have it automatically re-ordered in the editor, you need to serialize by list of FName’s.
I didn’t get the state machine example unless you’re incrementing the state values to get to the next state. That’s not how state machines work. Please don’t do this.
Or if you want GameplayTags but is annoyed by the selector always showing all tags instead of only the ones that you are interested: UPROPERTY(meta=(Categories=“abc”))
To be honest, no, I don’t see where you’re coming from. I mean, I get your concern about removing values but that’s always a concern when removing anything. The important part is that it can be detected and handled easily.
The other engines are all C++ and internal engines at larger game studios. I’ve been in the industry for more than 15 years and, as far as I can remember, this and Unity are the only engines I’ve worked with that didn’t save their enums as strings.
Maybe I haven’t been direct enough, but I keep explaining what I need and you keep coming back to FNames and tags but you still haven’t explained or given an example of how I can achieve my goals given those tools. I am not against using either but, as far as I can tell, the cannot do what I need them to do.
To reiterate, I need to be able to write the entire system in C++, meaning that all the possible “types”, for lack of a better term (i.e. the enum values), need to be known at compile time and need to be type safe. If you have a suggestion on how to do that with common types of FName and FGameplayTag that uses some Unreal magic, I’d love to hear it. Otherwise, enums are the only thing I can think of that does the job.
FWIW, I’ve just finished writing my enum wrapper to do this for me. I’ll post an approximation of the code once I’ve finished with it and sanitized it of confidential bits.
Here’s the code for the wrapper. You can also add meta = (ShowOnlyInnerProperties) to any UPROPERTY that uses the wrapper to make it look exactly like a normal enum would look.
//.h
USTRUCT(BlueprintType)
struct FMyEnumWrapper
{
GENERATED_BODY()
public:
bool Serialize(FArchive& Ar);
UPROPERTY(EditAnywhere)
EMyEnum EnumProp = EMyEnum::Max;
};
template<>
struct TStructOpsTypeTraits<FMyEnumWrapper> : public TStructOpsTypeTraitsBase2<FMyEnumWrapper>
{
enum
{
WithSerializer = true,
};
};
//.cpp
bool FMyEnumWrapper::Serialize(FArchive& Ar)
{
if (Ar.IsSaving())
{
FName EnumName = StaticEnum<EMyEnum>()->GetNameByValue((int64)EnumProp);
Ar << EnumName;
}
else
{
FName EnumName;
Ar << EnumName;
int64 EnumValue = StaticEnum<EMyEnum>()->GetValueByName(EnumName);
if (EnumValue == INDEX_NONE)
{
//This value must have been removed from the enum. Print a warning and set our value to Max
UE_LOG(MyLogCategory, Warning, TEXT("EMyEnum value no longer exists. Resetting to Max. Old Value: %s"), *EnumName.ToString());
EnumProp = EMyEnum::Max;
}
else
{
EnumProp = EMyEnum((uint8)EnumValue);
}
}
return true;
}
I came back to this late, but gameplay tags aren’t inherently data driven - you can define them statically from C++, and I left a link to how to do that in my previous post.
See the UE_DEFINE_GAMEPLAY_TAG series of macros.
You also can’t restrict C++ itself from assigning a wrong gameplay tag, but you can certainly force the editor to via the Categories meta specifier - All UPROPERTY Specifiers · ben🌱ui
Youre being too direct if Im honest! All I was saying is you cant have dynamic constant code, its simply not how C++ works and I see what you want but obviously without access to these other engines I have no idea if it even is possible entirely.
In all honestly Im not fussed about credentials, you clearly know what you want but with so limited information on your use case its actually difficult to provide any sort of solid example. Your question and thought process has been closed before you even posted, thanks for your time.
I missed the link to the C++ tags before. That’s really good to know and definitely makes using tags for this more relevant. This solves issues with typos, I’m guessing/hoping has some validation of duplicate tags, and can be somewhat “type” safe by using the Categories specifier. It’s still not a const expression which means no switch statements, so that’s an issue, but still worth investigating.
Yeah, it won’t create tags with the same name if you try to define it multiple times - you’ll just end up with multiple symbols that point to the same tag. You can throw tag declarations in a header so you don’t have to redefine the same tags over and over again though.
If you need absolute type safety then the wrapper you made is the way to go, just annoying to have to make that for every enum you want and tags have a bunch of extra features that you may or may not find useful in the future (and don’t really cost anything if you don’t end up using).