我来自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))
答案 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;
}