111111
111111

Reputation: 16148

C++ design, how to represent different stages of a protocol with objects

This is not so much of a technical question, but rather a c++ design question.

Frequently it seems that I have to design programs which have to manage some a protocol which has some sort of connection, parsing stage and abstract view. Typically I try and design my programs with separation of concerns at the forefront.

I keep ending out with "stacks" of objects, a system sits ontop of a parser, which in turns sits ontop of a connection (often there are more layers). These objects then use member function calls to call a layer below it (Tx), and uses callbacks (std::function, usually) to capture information coming from the other directions (Rx).

This design seems really subpar, as it add complexity, and each layer has to have a progressively bigger constructor and so on. Also because the connection usually uses something like ASIO, the callbacks are generally on different threads so it's hard to reason about thread saftey.

Is there a design patterm, or idiom that represent this structure/functionality better?

EDIT

A simple example

class basic_connection {
     basic_connection(std::string address);

     void send(std::string);
     std::function<void(std::string)> on_receive;
};

I have a few classes like this, which hold that layer's state, and are glue together by their public member functions and callbacks.

The layer above this, receives command data processes for the network and calls basic_connection::send. And takes the raw data from basic_connection and converts into commands for the layer above it unprocessed.

EDIT2:

Another issue that I forgot to mention is that you end up forwarding some of the interface though the stack, for example, a top layer still needs to know the connection status.

Upvotes: 13

Views: 1117

Answers (3)

Andrew Tomazos
Andrew Tomazos

Reputation: 68638

It sounds like what you are trying to do is what is normally refered to as "constructing a pipeline".

Here is one simple way to connect two layers:

class I
{
    virtual void a() = 0;
    virtual void b() = 0;
}

class X
{
    I& m_i;

    X(I& i) : m_i(i) {}

    void onRecv(const char* data, size_t len)
    {
        for (size_t p = 0; p < len; p++)
            switch (data[p])
            {
            case 'a': m_i.a(); break;
            case 'b': m_i.b(); break;
            }
    }
}

class Y : public I
{
    void a() { ... }
    void b() { ... }
}

int main()
{
    X x;
    Y y(x);

    while (...)
        x.onRecv(data,len);
}

Upvotes: 1

bitmask
bitmask

Reputation: 34628

It seems to me that what you need is additional abstraction. I would begin by designing a general type to describe what a layer actually is and (if appropriate) refine that for each particular layer without taking into account your concrete protocols for these layers. So, you could say a layer-k protocol needs an object of type layer-(k-1).

From your description I assume your higher layers are constructing their immediate lower layer which makes the constructors inflate. Simply ask for a reference (probably best implemented by a shared_ptr or unique_ptr) for the next lower layer in your constructor and have the interface user bother about its instantiation.
Since you defined an abstract interface, you can still use the lower layer polymorphically, without having to bother with how it's implemented and what particular lower layer protocol is used.

For reception, you typically need callbacks which can be implemented in the same fashion. You can even install these in the constructor of higher layer objects and remove them in the destructor.


If you know at design time which protocol will play with which other protocol, you could also replace the polymorphic calls by making your protocol implementation a template that receives its lower protocol something like this: Tcp<Ip<Ethernet<Device>>>.

Upvotes: 0

SomeWittyUsername
SomeWittyUsername

Reputation: 18358

Without having a set of requirements it's hard to recommend anything. However, from the high level description in your question it seems that you might want to use the model-view-controller pattern, maybe in conjunction with others. Remember, design patterns are your friends and you're the one who decides if usage is appropriate and to what degree. Design pattern are very easy to abuse and it's happening all the time.

Upvotes: 1

Related Questions