Implement what has both enum and class behavior

Note that I have a "thing" that has both enumeration and class behavior. As an example, consider a world that has a concept of color, there are exactly 3: red, green and blue.

Based on this color, we also have functionality, for example, we can have a function that tells us if a color is a happy color or not, and some other functions:

isHappy: Color -> {yes, no}
intensity: Color -> value
rotate: Color -> Color

      

To complete the haskel-like syntax, we could do this:

data Color = Red | Green | Blue

      

and implement the above functions. But it is haskell which is not C ++ and has no OO concept like C ++. Continued in C ++:

The fact that we have exactly 3 colors and no more suggests using an enumeration; allowing us to use constants like red, blue and green anywhere in the source code. However, we cannot add methods to the enum, so the intensity and rotation of being will be implemented as functions (not methods).

The fact that we have these methods where the first parameter is color suggests using the class. However, then we could create as many classes as we want, especially more than 3. This means that the two variables representing red will be allocated in different places in memory. This is a bit odd, as Red will have a very "persistent" behavior, since it is immutable and only three different types of Color objects can be created. Moreover, we cannot use symbols like red, green and blue, but they will need to be stored in variables. Using globals would be very ugly imho.

We can also use inheritance where Red, Green and Blue inherit from the Color class. This allows us to easily customize the functionality as we can implement what we want in any class we want. However OO with inheritance in C ++ applies slicing. For example, it would be very difficult to create a vector containing a list (red, green, or blue). Or create a variable that stores one of three colors:

Color c1 = Red();
Color c2 = Blue();

      

The variables c1 and c2 will be assigned to other objects, but there is no way to distinguish them. This makes the executor operator == tricky:

class Red : Color { //...
  bool operator==(Color &c) const{
     // no way to determine whether c is Red, Green or Blue.
  }
}

      

Do you have a pattern or solution for this situation? I think this is a lot, so I am very curious. C ++ 14-only solutions are greatly appreciated as well!

EDIT . A lot of people seem to be commenting that the problems for the solutions I mention are not really problems. This is the actual point. However, I am not looking for a possible solution, I am looking for a solution that follows good C ++ design principles (11 | 14). I could also use:

#define RED 0
#define GREEN 1
#define BLUE 2

      

And it will work fine, however this is not a good design as it might clash with other functions using names like RED, BLUE or GREEN. It's also semantically weird, since I could say RED

To summarize, I would like to see a solution in the answer that adheres to good C ++ design principles. I don't need solutions that just work! It can be one of the three previously mentioned ways using enumeration, single class, or inheritance.

EDIT2: In the meantime, I also maintain a templated approach for the multiple inheritance case. However, to be honest, my knowledge of templates is not enough to create something "good". The idea is based on functions like std :: is_same and std :: is_functional of the type_traits header.

#include <iostream>

class Color {};

class Red : Color {};
class Green : Color {};
class Blue : Color {};

template<class C1, class C2>
bool is_same_color();

template<class C1>
bool is_happy();

int main() {
    // your code goes here
    return 0;
}

      

It doesn't work, but I hope the idea turns around. Also, I understand is_same_color and is_happy must be classes with a specific operator ().

EDIT3: You could say this is what I want:

enum Color {
  RED, GREEN, BLUE
  bool isHappy() { return this==GREEN || this==RED; }
  Color rotate() { return (this==RED ? GREEN (this==GREEN ? BLUE : RED)); }
  int intensity() { return (this==RED ? 11 (this==GREEN ? 12 : 4)); }
}

      

But this is of course not valid C ++.

+3


source to share


5 answers


You can use a class and use specific instances (a la singleton) for enum

:



class Color
{
public:
    bool isHappy() const { return this == &Green || this == &Red; }
    const Color* rotate() const { return (this == &Red ? &Green : (this == &Green ? &Blue : &Red)); }
    int intensity() const {return mIntensity; }

    static const Color* red() { return &Red; }
    static const Color* green() { return &Green; }
    static const Color* blue() { return &Blue; }

private:
    // forbid to construct new instance: use only red(), green(), blue()
    explicit Color(int intensity) : mIntensity(intensity) {}
    Color(const Color&) = delete; 
private:
    int mIntensity;

private:
    static const Color Red;
    static const Color Green;
    static const Color Blue;
};

const Color Color::Red(11);
const Color Color::Green(12);
const Color Color::Blue(4);

      

+2


source


Some other nice answers have been given, but I'm afraid they don't use C ++ expression and clarity and brevity to the fullest.

I suggest the following solution:



class Color
{
public:
    virtual bool is_happy() = 0;
    virtual Color* rotate() = 0;
    virtual int intensity() = 0;

    static Color* const Red;
    static Color* const Green;
    static Color* const Blue;

    Color(Color const&) = delete;

private:
    Color(){}

    template<bool happiness, Color* const* rotation_result, int intensity_value>
    class Color_Generator;

    template<bool happiness, Color* const* rotation_result, int intensity_value>
    friend class Color_Generator;
};

template<bool happiness, Color* const* rotation_result, int intensity_value>
class Color::Color_Generator : public Color
{
public:
    bool is_happy()
    {
        return happiness;
    }

    Color* rotate()
    {
        return *rotation_result;
    }

    int intensity()
    {
        return intensity_value;
    }

    static Color_Generator<happiness, rotation_result, intensity_value> Instance;
};

template<bool happiness, Color* const* rotation_result, int intensity_value>
Color::Color_Generator<happiness, rotation_result, intensity_value>
Color::Color_Generator<happiness, rotation_result, intensity_value>::Instance;

Color* const Color::Red = &Color_Generator<true, &Green, 11>::Instance;
Color* const Color::Green = &Color_Generator<true, &Blue, 12>::Instance;
Color* const Color::Blue = &Color_Generator<false, &Red, 4>::Instance;

//==============
// Some usage follows

#include <iostream>

int main()
{
    Color* a = Color::Red;
    Color* b = Color::Green;
    Color* c = Color::Blue;

    std::cout << a->intensity() << std::endl;
    std::cout << b->is_happy() << std::endl;
    std::cout << (b->rotate() == c) << std::endl;
}

      

There are a few more C ++ features to further improve this code. For example, you can use virtual inheritance to separate the definition of is_happy

, rotate

and intensity

to their classes "facet" in the true spirit of C ++.

+2


source


Two givens: if you want member functions (which seems reasonable) then it should be a class; and things that differ only in attributes (like Red

, Green

and Blue

) shouldn't have different types.

It's not clear why you want to limit the number of colors to 3, but in the end it comes down to the fact that you have exactly 3 instances of your class, and nothing more. Simplest This is done to make the constructor private and make instances of the static members:

class Color
{
    Color( /* whatever parameters are needed */ );
public:
    static Color red;
    static Color green;
    static Color blue;
    //  ...
};

      

Users will then use Color :: red, Color :: green, and Color :: blue (which has the added advantage that you can do something similar to the mood and Mood :: blue will not cause a naming conflict).

+2


source


Update: simplified code

You can implement it as a base class Color

, which has three subclasses Red

, Green

and Blue

and has static constants Red

, Green

and Blue

accordingly:

class Red;
class Green;
class Blue;

class Color;
typedef const Color* Color_t;

class Color {
    friend class Red;
    friend class Green;
    friend class Blue;

public:

    static Color_t RED;
    static Color_t GREEN;
    static Color_t BLUE;

    virtual std::string name() const = 0;

private:

    // prohibit instantiation of non-friend subclasses
    virtual ~Color() = default;

    static const Red RED_;
    static const Green GREEN_;
    static const Blue BLUE_;
};

class Red : public Color {
    friend class Color;
private:
    Red() {};  // prohibit instantiation other than by Color
    std::string name() const {return "Red";}
};

class Green : public Color {
    friend class Color;
private:
    Green() {};
    std::string name() const {return "Green";}
};

class Blue : public Color {
    friend class Color;
private:
    Blue() {};
    std::string name() const {return "Blue";}
};

const Red Color::RED_;
const Green Color::GREEN_;
const Blue Color::BLUE_;

Color_t Color::RED = &RED_;
Color_t Color::GREEN = &GREEN_;
Color_t Color::BLUE = &BLUE_;

int main() {
    Color_t c = Color::GREEN;
    c = Color::BLUE;
    if (c == Color::GREEN) {
        std::cout << c-> name() << " is green" << std::endl;
    } else {
        std::cout << c-> name() << " is not green" << std::endl;
    }

    // we can make collections of colors easily
    std::vector<Color_t> colors = { Color::RED, Color::RED, Color::GREEN, Color::BLUE, Color::GREEN };
    return 0;
}

      

Here is a demo

This is actually similar to how enums are implemented in Java.

+1


source


If all you need is enum

both functions acting on this enum

and a way to let the world know that these functions and this one enum

belong together, then perhaps the namespace

tool is for you.

namespace Color
{
enum EColor // or in C++11 use an enum class and set the underlying type to char or such
{
    RED,
    GREEN,
    BLUE
};
bool IsHappy(const EColor & color);
int Intensity(const EColor & color);
EColor Rotate(const EColor & color);
}

      

You can just as easily make this class with member functions, as each function takes EColor

to work, but using a namespace can make it easier for you to split the functionality into separate modules if maybe not every user EColor

cares about that color happiness, or have you have a bunch of functions related to EColor

but not requiring one EColor

as a parameter.

0


source







All Articles