Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ default enum value usage

This discussion is about a name of default value: C#: Should the default value of an enum be None or Unknown?

However, large amount of people I spoken with recently considered default enum values harmful, unnecessary and potentially leading to a bad practice.

As an example consider the following:

enum eJobStates
{
    JOB_STATE_INITIALISING,
    JOB_STATE_PROCESSING,
    JOB_STATE_DONE
};

It would not make sense for a job to be in say, JOB_STATE_UNKNOWN, but you can imagine that any structure that might be used for monitoring of said jobs could use such value.

Are there any best practices / rules of thumb concerning creating a default value when defining an enum? Should they be avoided at all cost whenever possible?

like image 360
ellimilial Avatar asked Dec 17 '12 20:12

ellimilial


People also ask

What is the default value of enum in C?

Each enumeration-constant in an enumerator-list names a value of the enumeration set. By default, the first enumeration-constant is associated with the value 0. The next enumeration-constant in the list is associated with the value of ( constant-expression + 1 ), unless you explicitly associate it with another value.

What is the default value of an enum?

The default value for an enum is zero.

What is default value of first enum constant in C?

If the first value of the enum variable is not initialized then the C compiler automatically assigns the value 0.

Is the first enum always zero?

Unless you specify otherwise in the definition of the enumeration, the initial enumerator always has the value zero and the value of each subsequent enumerator is one greater than the previous enumerator. You do not need to specify a starting value of 0, it defaults to 0.


1 Answers

An invalid default value is basically a variant in your design. The object isn't valid when it's created. You should avoid that when it's reasonable. By no means should you avoid it "at all costs."

Some problems require you to start in a variant state. In that case, you have to reason about that invalid value mentally. If you avoid naming it you are actively making your code less expressive. Think about it in terms of communication between you and the person that will have to maintain the code later.

Dealing with it downwind is annoying. You start in a variant state, but by the time it's relevant you hope that it is no longer variant. The strategy I prefer is allow users to ignore the variant state and just throw when I've made a mistake.

namespace FooType {
  enum EnumValue {
    INVALID = 0
    ,valid
  };
}

struct Foo {
  Foo() : val(FooType::INVALID) {}
  FooType::EnumValue get() const {
    if (val == FooType::INVALID)
      throw std::logic_error("variant Foo state");
    return val;
  }
  FooType::EnumValue val;
};

This frees your users from having to reason about your variance, which is worth fighting for.

If you can't get away with that, I usually prefer to degrade to safe and unsafe interfaces.

struct Foo {
  Foo() : val(FooType::INVALID) {}
  bool get(FooType::EnumValue& val_) const {
    if (val == FooType::INVALID)
      return false;
    val_ = val;
    return true;
  }
  FooType::EnumValue get() const {
    FooType::EnumValue val_;
    if (!get(val_))
      throw std::logic_error("variant Foo state");
    return val_;
  }
  FooType::EnumValue get_or_default(FooType::EnumValue def) const {
    FooType::EnumValue val_;
    if (!get(val_))
      return def;
    return val_;
  }
  FooType::EnumValue val;
};

These sorts of interfaces are good for things like databases, where null values may be expected.

like image 150
Tom Kerr Avatar answered Oct 23 '22 03:10

Tom Kerr