从网络应用程序中解压缩操作类型的正确方法

时间:2016-05-03 16:27:15

标签: c++ oop network-programming

我来自python世界,作为一个周末项目,我决定用c ++编写一个简单的UDP服务器。我有一个关于发现传入请求类型的正确方法的问题。我的方法是为每种可能的请求类型设置一个类。在数据包到达时,我必须解压缩它的OPID(操作ID)并实例化正确的类。要做到这一点,我必须将OPID与类绑定,并且我熟悉在c ++中这样做的唯一方法涉及巨大的switch:case块。这样做对我来说并不合适,如果我正确理解UncleBob,这也不符合OOP的做法。由于代码描述了最好的意图,这里的python等同于我尝试用c ++做的事情。

class BaseOperation:

    OPID = 0

    def process(packet_data):
        raise NotImplementedError("blah blah")

class FooOperation(BaseOperation):

    OPID = 1

    def process(packet_data):
        print("Foo on the packet!")

class BarOperation(BaseOperation):

    OPID = 2

    def process(packet_data):
        print("Bar on the packet!")

opid_mappings = {
    FooOperation.OPID: FooOperation,
    BarOperation.OPID: BarOperation
}

处理传入数据包的代码中的某处

def handle_connection(packet):
    try:
        operation = opid_mappings[get_opid(packet)]()
    except KeyError:
        print("Unknown OPID")
        return
    operation.process(get_data(packet))

1 个答案:

答案 0 :(得分:3)

非常快速破解基于对象的解决方案。这可能不是进入std::function精彩的新C ++ 11世界的正确方法。

如果BaseOperation的子节点需要存储状态,请转到对象!

#include <iostream>
#include <map>

class BaseOperation
{
protected:
    int  OPID;

public:
    virtual ~BaseOperation()
    {

    }
    virtual int operator()() = 0;

};

class FooOperation:public BaseOperation
{

public:
    static constexpr int OPID = 1;
    FooOperation()
    {

    }
    int operator()()
    {
        // do parsing
        return OPID; // just for convenience so we can tell who was called
    }
};

constexpr int FooOperation::OPID; // allocate storage for static

class BarOperation:public BaseOperation
{

public:
    static constexpr int OPID = 2;
    BarOperation()
    {

    }
    int operator()()
    {
        // do parsing
        return OPID; // just for convenience so we can tell who was called
    }
};

constexpr int BarOperation::OPID; // allocate storage for static

std::map<int, BaseOperation*> opid_mappings{
    {FooOperation::OPID, new FooOperation()},
    {BarOperation::OPID, new BarOperation()}
};

int main()
{

    std::cout << "calling OPID 1:" << (*opid_mappings[1])() << std::endl;
    std::cout << "calling OPID 2:" << (*opid_mappings[2])() << std::endl;

    for (std::pair<int, BaseOperation*> todel: opid_mappings)
    {
        delete todel.second;
    }
    return 0;
}

这也忽略了可能不需要地图的事实。如果OPID是顺序的,那么一个好的&#39;哑阵解决了这个问题。我喜欢地图,因为如果有人移动解析器处理程序或将其插入到列表中间,它就不会搞砸了。

无论如何,这有一堆内存管理问题,例如需要for循环删除main底部的解析器对象。这可以通过std::unique_ptr来解决,但这可能是我们不需要陷入的兔子洞。

解析器没有任何状态并且我们只能使用OPID和std::function的地图,这是非常好的。

#include <iostream>
#include <map>
#include <functional>

static constexpr int FooOPID = 1;
int fooOperation()
{
    // do parsing
    return FooOPID;
}

static constexpr int BarOPID = 2;
int BarOperation()
{
    // do parsing
    return BarOPID;
}

std::map<int, std::function<int()>> opid_mappings {
    {FooOPID, fooOperation},
    {BarOPID, BarOperation}
};

int main()
{
    std::cout << "calling OPID 1:" << opid_mappings[1]() << std::endl;
    std::cout << "calling OPID 2:" << opid_mappings[2]() << std::endl;

    return 0;
}

因为如果你没有传递任何内容,解析器就没用了,最后一个调整是:

#include <iostream>
#include <map>
#include <functional>

struct Packet
{
    //whatever you need here. Probably a buffer reference and a length
};

static constexpr int FooOPID = 1;
int fooOperation(Packet & packet)
{
    // do parsing
    return FooOPID;
}

static constexpr int BarOPID = 2;
int BarOperation(Packet & packet)
{
    // do parsing
    return BarOPID;
}

std::map<int, std::function<int(Packet &)>> opid_mappings {
    {FooOPID, fooOperation},
    {BarOPID, BarOperation}
};

int main()
{

    Packet packet;
    std::cout << "calling OPID 1:" << opid_mappings[1](packet) << std::endl;
    std::cout << "calling OPID 2:" << opid_mappings[2](packet) << std::endl;

    return 0;
}