Thank you C++ for … strongly typed enumerations

enum needs a scope

“Enumerations are scope-leeches – they infect the outer scope.”

C++03

class Foo {
	enum Single {
		ONE = 1, TWO = 2, THREE = 3
	};

	enum Double {
		ONE = 2, TWO = 4, THREE = 6
	};
};

Result

error: declaration of ‘ONE’ conflicts with previous declaration ‘Foo::Bar Foo::ONE

error: declaration of ‘TWO’ conflicts with previous declaration ‘Foo::Bar Foo::TWO

error: declaration of ‘THREE’ conflicts with previous declaration ‘Foo::Bar Foo::THREE

Solution #1: Prefix

class Foo {
public:
	enum Single {
		Single_ONE = 1, Single_TWO = 2, Single_THREE = 3
	};

	enum Double {
		Double_ONE = 2, Double_TWO = 4, Double_THREE = 6
	};
};

int main() {
	// Prints: 1
	std::cout << Foo::Single_ONE << std::endl;
	// Prints: 4
	std::cout << Foo::Double_TWO << std::endl;

Solution #2: Wrap

class Foo {
	struct Single {
		enum Enum {
			ONE = 1, TWO = 2, THREE = 3
		};
	};

	struct Double {
		enum Enum {
			ONE = 2, TWO = 4, THREE = 6
		};
	};
};

int main() {
	// New way of declaring the above
	// enums (note the required Enum)
	Foo::Single::Enum s;
	Foo::Double::Enum d;

	// Prints 1
	std::cout << Foo::Single::ONE << std::endl;
	// Prints 4
	std::cout << Foo::Double::TWO << std::endl;
}

C++11

class Foo {
public:
	enum class Single {
		ONE = 1, TWO = 2, THREE = 3
	};

	enum class Double {
		ONE = 2, TWO = 4, THREE = 6
	};
};

int main() {
	Foo::Single s; // Typical way to use
	Foo::Double d; // the enumerations

	// The reason to cast to int will be discussed later.
	// Prints: 1
	std::cout << (int)Foo::Single::ONE << std::endl;
	// Prints: 4
	std::cout << (int)Foo::Double::TWO << std::endl;
}

enum‘s double life as an int

“Enumerations are implicitly convertible between each other, as they are ints in nature.”

C++03

// Ordered by their strength
enum Unit {
	LIGHT_INFANTRY,
	HEAVY_INFANTRY,
	COMMANDO
};

// Ordered by range
enum UnitRange {
	SMALL,
	MEDIUM,
	LARGE
};

/// @return Biggest unit range
UnitRange GetBest() {
	return LARGE;
}

Unit GetUnitToBuild() {
	// Poor programmer assumed GetBest() will
	// return the best unit the AI could build.
	if (HEAVY_INFANTRY >= GetBest()) {
		return HEAVY_INFANTRY;
	} else {
		// Will always choose this path.
		return LIGHT_INFANTRY;
	}
}

C++11

// Ordered by their strength
enum class Unit {
	LIGHT_INFANTRY,
	HEAVY_INFANTRY,
	COMMANDO
};

// Ordered by range
enum class UnitRange {
	SMALL,
	MEDIUM,
	LARGE
};

/// @return Biggest unit range
UnitRange GetBest() {
	return UnitRange::LARGE;
}

Unit GetUnitToBuild() {
	if (Unit::HEAVY_INFANTRY >= GetBest()) {
		return Unit::HEAVY_INFANTRY;
	} else {
		return Unit::LIGHT_INFANTRY;
	}
}

Result

error: no match for ‘operator>=’ in ‘(Unit)1 >= GetBest()

enum are of some type

“Enumerations are internally ints, even if they don’t have to be.”

C++03

enum Recipent {
	ALL,
	SERVER,
	CLIENT,
	NODE
};

enum Response {
	NONE,
	IMMEDIATE,
	DELAYED,
	PERIODIC
};

// Size of our packet is 24B.
struct Packet {
	unsigned char data[16];
	Recipent reci;
	Response resp;
};

C++11

enum Recipent : unsigned char {
	ALL,
	SERVER,
	CLIENT,
	NODE
};

enum Response : unsigned char {
	NONE,
	IMMEDIATE,
	DELAYED,
	PERIODIC
};

// Now the size is only 18B.
struct Packet {
	unsigned char data[16];
	Recipent reci;
	Response resp;
};

Comment

While the bit fields provide even better ‘compression’ they are not so easy to use and require careful casting from/to enum.

Advertisements

Tags: , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: