Nelfeal
Nelfeal

Reputation: 13269

Adding code for each enumerator

I have some arbitrary enumerations like the following.

enum MyEnumWith2Items {
    Item1,
    Item2
};

enum MyEnumWith3Items {
    Item1,
    Item2,
    Item3
};

I would like to add some code dependant on each of the enumerators. For example, adding a field in a class corresponding to each item.

template<typename EnumType>
struct MyStruct {
    /* magic */
};

MyStruct<MyEnumWith2Items> a; // a has the fields i1 and i2, but not i3
MyStruct<MyEnumWith3Items> b; // b has i1, i2 and i3

Is that even possible?
How about with enum class?
How about with static fields or methods or any kind of code?

The class definition can take any form, my example is just an example.
I can use any version of C++.

Upvotes: 0

Views: 74

Answers (3)

Francis Cugler
Francis Cugler

Reputation: 7915

This may seem a little redundant but it does allow for polymorphic types if needed. I have wrapped the different enumeration types into a set of classes that are inherited from a base type class. Then I used a template structure with specialization. I also added in the ability to have the class set values either through the template parameter list or by publicly assigning them. Now this is just to work with the actual enumerated values; but you can easily add to this to add your fields for each type.

#include <iostream>

class Root {
public:
    enum Type {
        TYPE_1 = 1,
        TYPE_2,
        TYPE_3
    };
protected:
    Type type_;

public:
    explicit Root(Type type) : type_(type) {}
    virtual ~Root() {}
    Type getType() const {
        return type_;

    }
};

class Derived1 : public Root {
public:
    enum TwoItems {
        ITEM_1 = 1,
        ITEM_2
    } item_;

    Derived1() : Root(TYPE_1) {}
    explicit Derived1(unsigned itemValue) : 
        Root(TYPE_1), item_(static_cast<Derived1::TwoItems>(itemValue) ) {}

};

class Derived2 : public Root {
public:
    enum ThreeItems {
        ITEM_3 = 3,
        ITEM_4,
        ITEM_5
    } item_;

    Derived2() : Root(TYPE_2) {}
    explicit Derived2(unsigned itemValue) :
        Root(TYPE_2), item_(static_cast<Derived2::ThreeItems>(itemValue)) {}
};

class Derived3  : public Root {
public:
    enum FourItems {
        ITEM_6 = 6,
        ITEM_7,
        ITEM_8,
        ITEM_9
    } item_;

    Derived3() : Root(TYPE_3) {}
    explicit Derived3(unsigned itemValue) :
        Root(TYPE_3), item_(static_cast<Derived3::FourItems>(itemValue)) {}

};

template<typename ClassType, unsigned itemValue = 0>
struct MyStruct {
    ClassType derived_;
};

template<unsigned itemValue>
struct MyStruct<Derived1, itemValue> {
    Derived1 derived_{ itemValue };
};

template<unsigned itemValue>
struct MyStruct<Derived2, itemValue> {  
    Derived2 derived_{ itemValue };
};

template<unsigned itemValue>
struct MyStruct<Derived3, itemValue> {  
    Derived3 derived_{ itemValue };
};

int main() {

    MyStruct<Derived1, 2> structA;
    MyStruct<Derived2, 4> structB;
    MyStruct<Derived3, 8> structC;

    std::cout << structA.derived_.item_ << std::endl;
    std::cout << structB.derived_.item_ << std::endl;
    std::cout << structC.derived_.item_ << std::endl;
    std::cout << std::endl;

    structA.derived_.item_ = static_cast<Derived1::TwoItems>(1);
    structB.derived_.item_ = static_cast<Derived2::ThreeItems>(5);
    structC.derived_.item_ = static_cast<Derived3::FourItems>(9);

    std::cout << structA.derived_.item_ << std::endl;
    std::cout << structB.derived_.item_ << std::endl;
    std::cout << structC.derived_.item_ << std::endl;
    std::cout << std::endl;


    // Also

    MyStruct<Derived1> structA2;
    MyStruct<Derived2> structB2;
    MyStruct<Derived3> structC2;

    structA2.derived_.item_ = static_cast<Derived1::TwoItems>(1);
    structB2.derived_.item_ = static_cast<Derived2::ThreeItems>(3);
    structC2.derived_.item_ = static_cast<Derived3::FourItems>(7);

    std::cout << structA2.derived_.item_ << std::endl;
    std::cout << structB2.derived_.item_ << std::endl;
    std::cout << structC2.derived_.item_ << std::endl;

    char c;
    std::cout << "\nPress any key to quit.\n";
    std::cin >> c;
    return 0;
}

Upvotes: 0

max66
max66

Reputation: 66230

If you're interested in static members of myStruct, using C++14 (so static template members are available), you can define myStruct as follows

template <typename E>
struct myStruct
 {
   template <E I>
   struct wrp
    { int value; };

   template <E I>
   static wrp<I> item;
 };

template <typename E>
template <E I>
myStruct<E>::wrp<I> myStruct<E>::item { 0 };

and given the following enumerators

enum MyEnumWith2Items { Item1, Item2 };
enum MyEnumWith3Items { Item3, Item4, Item5 };

you can write

int main ()
 {    
   myStruct<MyEnumWith2Items>::item<Item1>.value = 1;
   myStruct<MyEnumWith2Items>::item<Item2>.value = 2;

   myStruct<MyEnumWith3Items>::item<Item3>.value = 3;
   myStruct<MyEnumWith3Items>::item<Item4>.value = 4;
   myStruct<MyEnumWith3Items>::item<Item5>.value = 5;
 }

Pre C++14 (and in C++14 itself) you can obtain a similar result using a static variable in a template method; the following is a full example

#include <iostream>

enum MyEnumWith2Items { Item1, Item2 };
enum MyEnumWith3Items { Item3, Item4, Item5 };

template <typename E>
struct myStruct
 {
   template <E I>
   int & item ()
    { static int value = 0; return value; }
 };

int main ()
 {
   myStruct<MyEnumWith2Items>  e2;
   myStruct<MyEnumWith3Items>  e3;

   e2.item<Item1>() = 1;
   e2.item<Item2>() = 2;

   e3.item<Item3>() = 3;
   e3.item<Item4>() = 4;
   e3.item<Item5>() = 5;

   std::cout << "e2: " << e2.item<Item1>() << ", " << e2.item<Item2>() 
      << std::endl;                              // print e2: 1, 2
   std::cout << "e3: " << e3.item<Item3>() << ", " << e3.item<Item4>() 
      << ", " << e3.item<Item5>() << std::endl;  // print e3: 3, 4, 5
 }

Upvotes: 4

Passer By
Passer By

Reputation: 21160

My first idea is to do this kind of thing, has a caveat though.

enum class Enum1 {a, b, size};
enum class Enum2 {c, d, e, size};

template<typename E>
struct S
{
    char field[size_t(E::size)];  
};

int main()
{
    S<Enum1> s1;
    S<Enum2> s2;
    std::cout << sizeof(s1.field) << std::endl << sizeof(s2.field) << endl;
    // 2 and 3
}

The caveat is of course that the enums must be [0, n) for the final size trick to work

Upvotes: 3

Related Questions