C ++ call different class constructors, avoid switching

I am collecting a network packet in my program as an array char*

. The first byte of the array represents the type of the packet received, and for each type, I want to have a separate child class of my common base class Packet

to which I pass the byte array and where it will be interpreted.

I would like to avoid using a switch statement that evaluates the first byte of a packet and calls the appropriate constructor. First, because in OOP, you should avoid switch clauses, and second, because I don't want to add a separate statement to the switch statement every time I add a package class.

I've looked at the Factory Method Pattern, but I'm not sure how it will help me in this situation or if it will solve my problem at all.

Basically, I want to avoid editing my code in 10 different places, just to add one package class.

+3


source to share


4 answers


If the packet type is always exactly one byte, you can create a simple lookup table like:

struct Packet { virtual ~Packet() {} /* ... */ };   // and abstract

std::map<char, std::unique_ptr<Packet>(*)(char const *, std::size_t)> factory;
std::map<char, std::size_t> packet_size;

      

Using example:

void handle_input(char const * buf, std::size_t available_size)
{
    if (available_size == 0) { return; }                   // no data

    if (packet_size[buf[0]] > available_size) { return }   // not enough data

    auto p = factory[buf[0]](buf, available_size);         // create packet

    // process p
    // reduce available size by packet_size[buf[0]]
}

      

Implementation:



struct Type05Packet : Packet
{
    static std::unique_ptr<Packet> make(char const * buf, std::size_t len)
    {
        return std::make_unique<Type05Packet>(buf, len);
    }

private:
   Type05Packet(char const * buf, std::size_t len) { /* populate */ }
};

      

And you need to add factory data (for example in main()

):

factory[5] = &Type05Packet::make;   // creation function for packet '5'
packet_size[5] = 20;                // packet '5' is 20 bytes long

      

Notes: There are several details that you need to improve.

  • The package creation function should first try to parse the data and only call the constructor if the data is valid, otherwise it should return an error condition (eg nullptr).
  • Factory and size maps can be filled with some kind of self-registering global constructor. They could also be combined into a single map of pair values ​​for consistency.
  • Map lookup should be used find(buf[0])

    to serve unrecognized packet types.
  • The use case is probably not very realistic. You should be in some kind of situation where you can put data from the buffer as you see fit; The point is that you only do this once, when there is enough data to form the entire packet.
+3


source


"I would like to avoid using the switch statement ..."

At some point, you will have to distinguish between this byte, whether you do it in a factory or elsewhere.



The way to avoid switch

is to create a map of functions create_class

, as well as search and call them on the map key (byte value).

This solution will have the advantage that you can easily add additional keys and create_class

functions without changing the underlying factory code.

+5


source


Yes, the factory pattern encapsulates object creation and you only need to pass in the type of object you want to create (via string / enum, etc.). One of the main advantages of the factory pattern over regular switch statements is that it localizes object creation in one place.

+1


source


Consider the use of a jump table. Allocate an array of function pointers indexed by something you can type into the message type, and then in the message handler, point to that array and call the function you want.

0


source







All Articles