1

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)

user997112
  • 29,025
  • 43
  • 182
  • 361

2 Answers2

3

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);
}
bloody
  • 1,131
  • 11
  • 17
1

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);
}
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • I appreciate your answer. I accepted the other one because I am receiving messages remotely, the objects are very expensive and whilst I will try to instantiate all that is needed, I wanted to check and create any which aren't, on-the-fly. This is why the question focused on the mapping. – user997112 Feb 08 '21 at 23:39