Is partial class template specialization the answer to this design problem?
Say you have a class who's job it is to connect to a remote server. I want to abstract this class to provide two versions, one that connects through UDP and the other through TCP. I want to build the leanest runtime code possible and instead of using polymorphism I am considering templates. Here is what I'm envisioning but I'm not sure it's the best way of doing this:
class udp {};
class tcp {};
template<class T,typename X>
class service
{
private:
// Make this private so this non specialized version can't be used
service();
};
template<typename X>
class service<udp, X>
{
private:
udp _udp;
X _x;
};
template<typename X>
class service<tcp, X>
{
private:
tcp _tcp;
X _x;
};
So the end benefit is that the genericness of T is still available, but the very different code required to setup a UDP or TCP connection has been specialized. I suppose you could put it both into one class, or provide another class that adheres to some pure virtual interface for setting up the network connection, like IConnectionManager.
But this does leave the problem of the code for the generic T now having to be written in and maintained in both specialized versions, where they are ultimately the same. How best to address this? I have a feeling I am going about this all wrong.
This can be best done using a policy for the transport protocol:
template<typename Transport>
class service : Transport {
public:
typedef Transport transport_type;
// common code
void do_something() {
this->send(....);
}
};
class tcp {
public:
void send(....) {
}
};
class udp {
public:
void send(....) {
}
};
typedef service<tcp> service_tcp;
typedef service<udp> service_udp;
Note that this is also polymorphic. It's called compile time polymorphism. Putting the policy into a base class will benefit from the Empty-Base-Class-Optimization. That is, your base class does not need to take any space. Putting the policy as a member has the other drawback that you always have to delegate stuff to that member, which can become annoying with time. The book Modern C++ Design describes this pattern in-depth.
Ideally, the transport protocol doesn't need to know anything about the protocol above it. But if for some reason you have to get some information about it, you can use the crtp pattern wiki:
template<template<typename Service> class Transport>
class service : Transport<service> {
// since we derive privately, make the transport layer a friend of us,
// so that it can cast its this pointer down to us.
friend class Transport<service>;
public:
typedef Transport<service> transport_type;
// common code
void do_something() {
this->send(....);
}
};
template<typename Service>
class tcp {
public:
void send(....) {
}
};
template<typename Service>
class udp {
public:
void send(....) {
}
};
typedef service<tcp> service_tcp;
typedef service<udp> service_udp;
You don't have to put your templates into headers. If you explicitly instantiate them, you will gain faster compilation times, as much fewer code has to be included. Put this into service.cpp:
template class service<tcp>;
template class service<udp>;
Now, code that uses service does not need to know about the template code of service, since that code is already generated into the object file of service.cpp.
I would use the curious recuring template pattern, aka Five Point Palm Exploding Alexandrescu Technique:
template <typename Underlying>
class Transmit
{
public:
void send(...)
{
_U.send(...)
};
private:
Underlying _U;
};
class Tcp
{
public:
void send(...) {};
};
class Udp
{
public:
void send(...) {};
};
There would probably be many more template parameters and sub classes but you get the idea, you can also use static methods.
By the way template code is generally more efficient but also much bigger.
Templates are not necessary (though a possible solution). This is just dependency injection via templates rather than via a constructor. Personally I would do it via a constructor. But doing it via template gives you the dubious benifit of a cheaper method call (it does not need to be virtual). But also does allow for easier compiler optimization.
Both the udp and tcp objects must still support the same interface.
If you do it via inheritance they must both implement a common interface (virtual base class), it it is done via templates this is not necessary but the compiler will check that they support the same method calls that the Service object requires.
As asked in the original question, I see no explicit need(or benefit) for partial template specialization (in the situation as described).
Template Method
class udp {/*Interface Plop*/static void plop(Message&);};
class tcp {/*Interface Plop*/static void plop(Message&);};
template<typename T>
class Service
{
public:
void doPlop(Message& m) { T::plop(m);}
// Do not actually need to store an object if you make the methods static.
// Alternatively:
public:
void doPlop(Message& m) { protocol.plop(m);}
private:
T protocol;
};
Polymorphic Version
class Plop{virtual void plop(Message&) = 0;} // Destruct or omitted for brevity
class upd:public Plop {/*Interface Plop*/void plop(Message&);};
class tcp:public Plop {/*Interface Plop*/void plop(Message&);};
class Service
{
public:
Service(Plop& p):protocol(p) {};
void doPlop(Message& m) { protocol.plop(m);}
private:
Plop& protocol;
};
链接地址: http://www.djcxy.com/p/78546.html
上一篇: c ++中的protected和private派生之间有什么区别?
下一篇: 部分类模板专业化是这个设计问题的答案吗?