Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a composite type from two enum classes, ready for STL map

Tags:

c++

enums

c++11

stl

I would like to create a composite type out of two enum classes.

enum class Color {RED, GREEN, BLUE};
enum class Shape {SQUARE, CIRCLE, TRIANGLE};

class Object {
  Color color;
  Shape shape;
public:
};

In order to use Object in an STL container like std::map<> I would need to overload the less-than operator. However, in order to flatten both enum classes into one linear index I somehow need the number of elements (NoE) of the enum classes:

friend bool operator< (const Object &lhs, const Object &rhs) {
  return NoE(Shape)*lhs.color+lhs.shape < NoE(Shape)*rhs.color+rhs.shape;
}

How can this be done without entering the same information (number of elements) in two places in the program in a nice way? (Nice way means no FIRST_ELEMENT, LAST_ELEMENT, preprocessor magic, etc.)

Question (Number of elements in an enum) is similar but does not address enum classes.

I would like to know what is the best way to implement this kind of composite types in C++11. Is the enum class definition strong enough, or is it necessary to say:?

enum class Color {RED=0, GREEN=1, BLUE=2};
enum class Shape {SQUARE=0, CIRCLE=1, TRIANGLE=2};
like image 286
ritter Avatar asked May 02 '13 14:05

ritter


6 Answers

As commented and as already stated by others, give precedence to either Shape or Color in the operator< and only compare the other if the first is equal. An alternative implementation for operator< using std::tie:

#include <tuple>
friend bool operator<(const Object& lhs, const Object& rhs)
{
    return std::tie(lhs.color, lhs.shape) < std::tie(rhs.color, rhs.shape);
}
like image 63
hmjd Avatar answered Oct 31 '22 13:10

hmjd


Consider using simply std::tuple<Color, Shape> as the "composite enum." This will come with comparison operators already defined for you, using a dictionary ordering. For example, valid code:

bool b = std::make_tuple(Color::RED, Shape::CIRCLE)
       < std::make_tuple(Color::GREEN, Shape::SQUARE);
like image 27
Timothy Shields Avatar answered Oct 31 '22 13:10

Timothy Shields


You don't need a linear index, you can simply compare them lexicographically:

friend bool operator< (const Object &lhs, const Object &rhs) {
  if (lhs.color < rhs.color) return true;
  else if (lhs.color > rhs.color) return false;
  else return lhs.shape < rhs.shape;
}
like image 4
Angew is no longer proud of SO Avatar answered Oct 31 '22 13:10

Angew is no longer proud of SO


That's a good question, but you don't actually need the number of Color to compare them :

friend bool operator< (const Object &lhs, const Object &rhs) {
    if(lhs.color > rhs.color) {
        return false;
    }
    if(lhs.color < rhs.color) { 
        return true;
    }
    return lhs.shape < rhs.shape;
}
like image 2
zakinster Avatar answered Oct 31 '22 14:10

zakinster


You only need to compare shape if color is the same for both.

Using a ternary you can make it look nice too:

friend bool operator< (const Object &lhs, const Object &rhs) {
     return lhs.color == rhs.color ? (lhs.shape < rhs.shape)
                                   : (lhs.color < rhs.color);
}
like image 2
huysentruitw Avatar answered Oct 31 '22 13:10

huysentruitw


What you are trying to express is that to determine the order of your Objects, you first need to compare the color, and then check the shape in case the color was the same. Instead of linearizing that, if would simply use boolean operators.

friend bool operator< (const Object &lhs, const Object &rhs) 
{
   return ( (lhs.color < rhs.color) 
           || ( (lhs.color == rhs.color ) && ( lhs.shape < rhs.color) ) )
}

EDIT: Actually, you can also use an upper bound for the number of objects, the behaviour will be the same:

friend bool operator< (const Object &lhs, const Object &rhs) {
  return 10000*lhs.color+lhs.shape < 10000*rhs.color+rhs.shape;
}

but that introduces a "magic number" (so not such a good idea).

like image 2
Dr_Sam Avatar answered Oct 31 '22 13:10

Dr_Sam