Enum of String Type Vs Struct with Static Constant

Swift constants: Struct or Enum

Both structs and enumerations work. As an example, both

struct PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}

and

enum PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}

work and define a static property PhysicalConstants.speedOfLight.

Re: A struct will be copied every time i use it or not?

Both struct and enum are value types so that would apply to enumerations as well. But that is irrelevant here
because you don't have to create a value at all:
Static properties (also called type properties) are properties of the type itself, not of an instance of that type.

Re: What advantages has the choice of a struct or enum?

As mentioned in the linked-to article:

The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace.

So for a structure,

let foo = PhysicalConstants()

creates a (useless) value of type PhysicalConstants, but
for a case-less enumeration it fails to compile:

let foo = PhysicalConstants()
// error: 'PhysicalConstants' cannot be constructed because it has no accessible initializers

What does static imply on constant within an enum or struct

It means myVariable is a type property – a single property that all instances of the type can use. Essentially a global variable associated with MyEnum.

In the case of constants declared with let, this is a way of declaring constants that are scoped to the type that don’t take up space within each instance of that type, i.e.:

struct MySlimStruct {
static let myVariable = "some value"
}

sizeof(MySlimStruct) // returns 0

struct MyFatStruct {
let myVariable = "some value"
}

sizeof(MyFatStruct) // returns 24

In the case of static variables declared with var, this is a good way of introducing undiagnosable bugs and crashes into your program.

static const Member Value vs. Member enum : Which Method is Better & Why?

The enum hack used to be necessary because many compilers didn't support in-place initialization of the value. Since this is no longer an issue, go for the other option. Modern compilers are also capable of optimizing this constant so that no storage space is required for it.

The only reason for not using the static const variant is if you want to forbid taking the address of the value: you can't take an address of an enum value while you can take the address of a constant (and this would prompt the compiler to reserve space for the value after all, but only if its address is really taken).

Additionally, the taking of the address will yield a link-time error unless the constant is explicitly defined as well. Notice that it can still be initialized at the site of declaration:

struct foo {
static int const bar = 42; // Declaration, initialization.
};

int const foo::bar; // Definition.

static const vs #define vs enum

It depends on what you need the value for. You (and everyone else so far) omitted the third alternative:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

Ignoring issues about the choice of name, then:

  • If you need to pass a pointer around, you must use (1).
  • Since (2) is apparently an option, you don't need to pass pointers around.
  • Both (1) and (3) have a symbol in the debugger's symbol table - that makes debugging easier. It is more likely that (2) will not have a symbol, leaving you wondering what it is.
  • (1) cannot be used as a dimension for arrays at global scope; both (2) and (3) can.
  • (1) cannot be used as a dimension for static arrays at function scope; both (2) and (3) can.
  • Under C99, all of these can be used for local arrays. Technically, using (1) would imply the use of a VLA (variable-length array), though the dimension referenced by 'var' would of course be fixed at size 5.
  • (1) cannot be used in places like switch statements; both (2) and (3) can.
  • (1) cannot be used to initialize static variables; both (2) and (3) can.
  • (2) can change code that you didn't want changed because it is used by the preprocessor; both (1) and (3) will not have unexpected side-effects like that.
  • You can detect whether (2) has been set in the preprocessor; neither (1) nor (3) allows that.

So, in most contexts, prefer the 'enum' over the alternatives. Otherwise, the first and last bullet points are likely to be the controlling factors — and you have to think harder if you need to satisfy both at once.

If you were asking about C++, then you'd use option (1) — the static const — every time.

In a type trait, why do people use enum rather than static const for the value?

A notable difference is in the fact that the following code compiles and links:

template<typename>
struct is_pointer { };

template<typename T>
struct is_pointer<T*> {
enum { value = true };
};

void f(const bool &b) { }

int main() {
f(is_pointer<void*>::value);
}

The following does not work instead (you get an undefined reference to value):

template<typename>
struct is_pointer { };

template<typename T>
struct is_pointer<T*> {
static const bool value = true;
};

void f(const bool &b) { }

int main() {
f(is_pointer<void*>::value);
}

Of course, it doesn't work unless you add somewhere the following lines:

template<typename T>
const bool is_pointer<T*>::value;

That is because of [class.static.data]/3 (emphasis mine):

If a non-volatile non-inline const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression ([expr.const]). The member shall still be defined in a namespace scope if it is odr-used ([basic.def.odr]) in the program and the namespace scope definition shall not contain an initializer. [...]

In other terms, static const bool value = true; is a declaration, not a definition and you cannot odr-use value.

On the other side, according with [dcl.enum/1] (emphasis mine):

An enumeration is a distinct type with named constants.

Those named constants can be const referenced as shown in the example above.


As a side note, something similar applies if you use static constexpr data members in C++11/14:

template<typename T>
struct is_pointer<T*> { static constexpr bool value = true; };

This doesn't work as well and that's how I discovered the subtle differences between them.

I found help here on SO getting some nice hints out of the answer I've been given.

References to the standard are a plus to better explain what's going on under the hood.

Note that a static constexpr data member declaration like the one above is also a definition since C++17. Therefore you won't have to define it anymore and you'll be able to odr-use it directly instead.


As mentioned in the comments (thanks to @Yakk that confirmed this) I'm also trying to explain how it happens that the above mentioned named constants bind to a const reference.

[expr.const/3] introduces the integral constant expression and mentions unscoped enums by saying that it's implicitly converted to a prvalue.

[dcl.init.ref/5] and [class.temporary/2] do the rest, for they rule on reference binding and temporaries.

Struct , class or enum for service object with static methods?

There are two key difference between a class and a struct in Swift. A class allows for inheritance and a class is a reference type while a struct is a value type.

Make your decision based on those few differences.

Since everything in Linker is static, the difference between reference and value becomes irrelevant since you won't have any instances.

So that leaves inheritance. Will you ever subclass Linker? If not, use a struct. If yes, use a class.

And now that you are asking about enum, you can probably rule that out over struct since Linker doesn't appear to have any constants, just methods.

Enum VS Static Class (Normal and With String Values)

From Enumeration Types (C# Programming Guide):

An enumeration type (also named an enumeration or an enum) provides an
efficient way to define a set of named integral constants that may be
assigned to a variable.

The following are advantages of using an enum instead of a numeric
type:

  1. You clearly specify for client code which values are valid for the variable.

  2. In Visual Studio, IntelliSense lists the defined values.

So if you pass enum, it is strongly typed, so you automatically get control over what you can pass into a method.

ScreenSizeEnum size = ScreenSizeEnum.Medium;
SetScreenSize(size);

When using const or static fields you definetely need to check whether the passed int value is taken from the expected diapason.

int somevalue = ...;//anything
SetScreenSize(somevalue); //compiles

private void SetScreenSize(int Screen)
{
switch (Screen)
{
case ScreenSizeClass.Large:
//Do Logic
break;
case ScreenSizeClass.Small:
//Do Logic
break;
default:
// something else, what to do??
break;
}
}

Based on comments:

If it's necessary to check, whether some int is defined in enum, one can do something like this:

int somevallue = 0;
if(Enum.IsDefined(typeof(ScreenSizeEnum), somevallue))
{
//it's ok
}

Or an extension method:

public static T GetEnumValue<T>(this string value) where T : struct
{
Type t = typeof(T);
if (!t.IsEnum)
throw new Exception("T must be an enum");
else
{
T result;
if (Enum.TryParse<T>(value, out result))
return result;
else
return default(T);
}
}

which could be used

int somevalue = 1;
ScreenSizeEnum size = somevalue.GetEnumValue<ScreenSizeEnum>();

As for the string (based on OP's edited question):

From enum (C# Reference):

The approved types for an enum are byte, sbyte, short, ushort, int,
uint, long, or ulong.

So you cannot have an enum of strings. But you can use names from enums, as ToString method returns the name, not the value of the enum.

ScreenSizeEnum.Small.ToString(); //Small

So you can have another extension method on strings:

public static T GetEnumValue<T>(this string value) where T : struct
{
Type t = typeof(T);
if (!t.IsEnum)
throw new Exception("T must be an enum");
else
{
if (Enum.IsDefined(t, value))
return (T)Enum.Parse(t, value);
else
return default(T);
}
}

So that

int i = (int)ScreenSizeEnum.Small;
string str = ScreenSizeEnum.Small.ToString();
ScreenSizeEnum isize = i.GetEnumValue<ScreenSizeEnum>();//ScreenSizeEnum.Small
ScreenSizeEnum strsize = str.GetEnumValue<ScreenSizeEnum>();//ScreenSizeEnum.Small


Related Topics



Leave a reply



Submit