3

There are a few good arguments for using strongly typed enums instead of enumns. However, the convertibility to int offered besides its unwanted risks some nice use cases. in my case, mostly throwing it into a stringstream for logging and comparison.

enum RiskLevel { None, Warn, Low, High, Critical };

void logStuff( RiskLevel rl ) {
    stringstream ss;
    ss << rl;
    LOG(s);
}

void compareEnum( RiskLevel rl ) {
    if ( rl > RiskLevel::Low ) {
        ... 
    }
}

I do miss these features of the old enums and I am probably not the only one. What are good ways to use strongly typed enums and still easily log them and compare them?

chrise
  • 4,039
  • 3
  • 39
  • 74

4 Answers4

4

You can use std::underlying_type:

void logStuff( RiskLevel rl ) {
    typedef std::underlying_type<RiskLevel>::type int_type;
    stringstream ss;
    ss << int_type(rl);
    Logger(ss);
}
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
2

You could (additionally) implement your own logging and comparison overloads, which gives you even more flexibility:

std::ostream& operator<<(ostream& os, RiskLevel rl) {
    os << std::underlying_type<RiskLevel>::type(rl);
    return os;
}

bool operator<(RiskLevel rl1, RiskLevel rl2) {
    return std::underlying_type<RiskLevel>::type(rl1) <
               std::underlying_type<RiskLevel>::type(rl2);
}

Then you could have the stream insertion operator actually log, say, the name of the enumerator.

qxz
  • 3,814
  • 1
  • 14
  • 29
  • That seems neat, but defining this for every enum would be verbose. Just out of curiosity, is it possible to write a template for all scoped enums only? – chrise Feb 01 '17 at 04:57
  • 1
    Yes; you could utilize [`std::enable_if`](http://www.cplusplus.com/reference/type_traits/enable_if/) with [`std::is_enum`](http://www.cplusplus.com/reference/type_traits/is_enum/). (If you want it _only_ for strongly-typed enums, and not regular enums, see [this question](http://stackoverflow.com/questions/15586163/c11-type-trait-to-differentiate-between-enum-class-and-regular-enum).) – qxz Feb 01 '17 at 16:46
2

Define the unary + operator to perform conversion to integer type.

enum RiskLevel { None, Warn, Low, High, Critical };

auto operator + ( RiskLevel value )
    { return std::underlying_type_t< RiskLevel >( value ); }

void logStuff( RiskLevel rl ) {
    stringstream ss;
    ss << + rl;
    LOG(s);
}

void compareEnum( RiskLevel rl ) {
    if ( + rl > + RiskLevel::Low ) {
        ... 
    }
}

More depth in this answer.

Community
  • 1
  • 1
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
2

Maybe take advantage of the enum class of c++11. You can explicitly specify what type your want your enum to be.

enum class Boo : char {
    START = 'S',
    END = 'E'
};

enum class Foo :  unsigned int {
    TOP = 1,
    BOTTOM = 2
};
greedy52
  • 1,345
  • 9
  • 8
  • I tried using enum class RiskLvl : int { NONE = 0, .. } but this gives me the error: cannot bind std::basic_ostream lvalue to std::basic_ostream&& – chrise Feb 01 '17 at 05:25
  • 1
    When using it with stream, you do need to manually `static_cast`. If you want to avoid cast like that, above answer using std::underlying_type is the best (it works with enum class). – greedy52 Feb 01 '17 at 05:37