How to map different C++ classes to enum class values

255 views Asked by At

I produce messages and each is receive by one object, chosen by an enum class member:

enum class ReceiverID
{
    R1,
    R2,
    MAX_NUM_RECEIVERS
};

struct Msg
{
    ReceiverID _receiverID;
    Data _data;
};

The receiving classes are stored in an array. The enum member indexes the array to access the receiving object:

void receive(const Msg& msg)
{
    const size_t arrIndex = static_cast<size_t>(msg._receiverID);
    
    if(nullptr == _array[arrIndex])
    {
        _array[arrIndex] = ???  // How do I associate the enum to the class?
    }
    
     _array[arrIndex].processMsg(msg);
}

It is possible the receiving object is missing. If this happens I'd like to use the enum to instantiate the missing object. However, this would require mapping the enum values to the receiving object type.

How can I map a class to each enum? (for all enums).

I'd like to generate a compiler error if a new enum is added but without a corresponding receiver class.

UPDATE

The receiving objects are polymorphic and so have a base class. The array is:

std::array<Base*, MAX_NUM_RECEIVERS> _array;

(removed unique_ptr to simplify question)

2

There are 2 answers

4
bloody On BEST ANSWER

For on-the-fly creation of objects we could go for some kind of a factory method, e.g.:

//In the Base class:
static Base* createReceiver(ReceiverID recvID) //static member function
{
    switch (recvID)
    {
        case ReceiverID::R1: return new R1Receiver();
        case ReceiverID::R2: return new R2Receiver();
        //...
        default: throw std::logic_error("Invalid ReceiverID");
    }
}

//...
void receive(const Msg& msg) except(true)
{
    const size_t arrIndex = static_cast<size_t>(msg._receiverID);
    if(nullptr == _array[arrIndex])
    {
        _array[arrIndex] = Base::createReceiver(msg._receiverID);
    }    
    _array[arrIndex]->processMsg(msg);
}
1
Mooing Duck On

Instead of having a global std::array<Base*, MAX_NUM_RECEIVERS> _array; and then lazily filling it out on demand, I believe the normal thing to do is make it filled out at construction time:

std::array<Base*, MAX_NUM_RECEIVERS>& _array() {
    //use a method to bypass https://stackoverflow.com/questions/1005685/c-static-initialization-order
    static std::array<Base*, MAX_NUM_RECEIVERS> array = make_array();
    return array;
}
std::array<Base*, MAX_NUM_RECEIVERS> make_array() {
    std::array<Base*, MAX_NUM_RECEIVERS> array;
    array[static_cast<size_t>(R1)] = &myR1ProcessorObject();
    array[static_cast<size_t>(R2)] = &myR2ProcessorObject();
    return array;
}

Then your receive method is simple:

void receive(const Msg& msg)
{
    const size_t arrIndex = static_cast<size_t>(msg._receiverID);
    assert(arrIndex< MAX_NUM_RECEIVERS);
     _array()[arrIndex].processMsg(msg);
}